nodes/data.pyΒΆ

Part of Tanks of Freedom.

  1"""Game constants and data tables for Tanks of Freedom.
  2
  3Unit stats are lifted from upstream `scripts/objects/units/{soldier,tank,
  4helicopter}.gd`; the map layout and starting line-up are this port's own,
  5sized for a single 12x12 skirmish.
  6"""
  7
  8from __future__ import annotations
  9
 10# ============================================================================
 11# Window / world layout
 12# ============================================================================
 13
 14WINDOW_WIDTH = 1280
 15WINDOW_HEIGHT = 720
 16
 17# Map size: 12x12 fits a small skirmish in screen space comfortably
 18MAP_W = 12
 19MAP_H = 12
 20
 21# Iso tile dimensions. Upstream uses 64x32 for the diamond; using same here.
 22TILE_W = 64
 23TILE_H = 32
 24
 25# World origin offset so the diamond sits centred horizontally below the HUD bar
 26WORLD_OFFSET_X = WINDOW_WIDTH * 0.5
 27WORLD_OFFSET_Y = 120
 28
 29HUD_HEIGHT = 60  # top bar
 30INFO_PANEL_W = 280  # right-hand info panel
 31
 32# ============================================================================
 33# Player ids
 34# ============================================================================
 35
 36PLAYER_BLUE = 0
 37PLAYER_RED = 1
 38PLAYER_NEUTRAL = -1
 39
 40PLAYER_COLOUR = {
 41    PLAYER_BLUE: (0.30, 0.55, 0.95, 1.0),
 42    PLAYER_RED: (0.95, 0.35, 0.30, 1.0),
 43    PLAYER_NEUTRAL: (0.75, 0.75, 0.78, 1.0),
 44}
 45
 46PLAYER_NAME = {
 47    PLAYER_BLUE: "BLUE",
 48    PLAYER_RED: "RED",
 49    PLAYER_NEUTRAL: "NEUTRAL",
 50}
 51
 52# ============================================================================
 53# Unit types  (from upstream unit.gd subclasses)
 54# ============================================================================
 55
 56UNIT_SOLDIER = 0
 57UNIT_TANK = 1
 58UNIT_HELICOPTER = 2
 59
 60UNIT_STATS = {
 61    UNIT_SOLDIER: {
 62        "name": "INFANTRY",
 63        "life": 10,
 64        "max_life": 10,
 65        "attack": 5,
 66        "max_ap": 4,
 67        "attack_ap": 1,
 68        "max_attacks": 1,
 69        "can_capture": True,
 70    },
 71    UNIT_TANK: {
 72        "name": "TANK",
 73        "life": 15,
 74        "max_life": 15,
 75        "attack": 10,
 76        "max_ap": 6,
 77        "attack_ap": 1,
 78        "max_attacks": 1,
 79        "can_capture": False,
 80    },
 81    UNIT_HELICOPTER: {
 82        "name": "HELICOPTER",
 83        "life": 10,
 84        "max_life": 10,
 85        "attack": 8,
 86        "max_ap": 8,
 87        "attack_ap": 1,
 88        "max_attacks": 1,
 89        "can_capture": False,
 90    },
 91}
 92
 93# How far a unit can reach a target, in grid steps. All three units are
 94# melee-range in this port, matching upstream's adjacency-only combat.
 95ATTACK_RANGE = {
 96    UNIT_SOLDIER: 1,
 97    UNIT_TANK: 1,
 98    UNIT_HELICOPTER: 1,
 99}
100
101# ============================================================================
102# Building types  (from building.gd)
103# ============================================================================
104
105# Upstream builds units out of barracks / factory / airport; this port has no
106# production, so the four kinds differ only in look and in the HQ win condition.
107BLDG_HQ = 0
108BLDG_BARRACKS = 1
109BLDG_FACTORY = 2
110BLDG_AIRPORT = 3
111
112BUILDING_STATS = {
113    BLDG_HQ: {"name": "HQ"},
114    BLDG_BARRACKS: {"name": "BARRACKS"},
115    BLDG_FACTORY: {"name": "FACTORY"},
116    BLDG_AIRPORT: {"name": "AIRPORT"},
117}
118
119# ============================================================================
120# Terrain  (impassable bits: placed on tile map by world)
121# ============================================================================
122
123TERR_GRASS = 0
124TERR_WATER = 1  # impassable for ground units
125TERR_TREE = 2  # impassable for everyone except heli
126TERR_MOUNTAIN = 3  # impassable
127
128TERRAIN_FROM_CHAR = {
129    "g": TERR_GRASS,
130    "w": TERR_WATER,
131    "f": TERR_TREE,
132    "t": TERR_TREE,
133    "m": TERR_MOUNTAIN,
134    "r": TERR_GRASS,  # 'road' visually identical to grass for our procedural look
135}
136
137# 12x12 map. 'g' grass, 'f' forest/tree, 'w' water, 'm' mountain.
138# Symmetric: blue south-west, red north-east, with a neutral mid corridor.
139MAP_LAYOUT_DEFAULT = [
140    "ggggwwggffgg",
141    "gggwwggggfgg",
142    "ggffmgggrrgg",
143    "gffggggggrgg",
144    "ggggggwwgggg",
145    "gggggggwgggg",
146    "ggggwggggggg",
147    "ggggwwgggggg",
148    "ggrggggggffg",
149    "ggrrgggmffgg",
150    "ggfggggmmggg",
151    "ggffggggwwgg",
152]
153
154# ============================================================================
155# Starting state: buildings & units. Tied to MAP_LAYOUT_DEFAULT.
156# ============================================================================
157
158# (x, y, building_type, owner)
159STARTING_BUILDINGS = [
160    (1, 10, BLDG_HQ, PLAYER_BLUE),
161    (3, 10, BLDG_BARRACKS, PLAYER_BLUE),
162    (1, 8, BLDG_FACTORY, PLAYER_BLUE),
163    (3, 8, BLDG_AIRPORT, PLAYER_BLUE),
164    (10, 1, BLDG_HQ, PLAYER_RED),
165    (8, 1, BLDG_BARRACKS, PLAYER_RED),
166    (10, 3, BLDG_FACTORY, PLAYER_RED),
167    (8, 3, BLDG_AIRPORT, PLAYER_RED),
168    # Neutral mid-map captures
169    (5, 5, BLDG_BARRACKS, PLAYER_NEUTRAL),
170    (6, 6, BLDG_FACTORY, PLAYER_NEUTRAL),
171]
172
173# (x, y, unit_type, owner)
174STARTING_UNITS = [
175    (2, 10, UNIT_SOLDIER, PLAYER_BLUE),
176    (3, 9, UNIT_SOLDIER, PLAYER_BLUE),
177    (2, 8, UNIT_TANK, PLAYER_BLUE),
178    (4, 9, UNIT_HELICOPTER, PLAYER_BLUE),
179    (9, 1, UNIT_SOLDIER, PLAYER_RED),
180    (8, 2, UNIT_SOLDIER, PLAYER_RED),
181    (9, 3, UNIT_TANK, PLAYER_RED),
182    (7, 2, UNIT_HELICOPTER, PLAYER_RED),
183]
184
185# ============================================================================
186# Audio (CC-BY-SA assets bundled from upstream)
187# ============================================================================
188
189SFX_SELECT = "select.wav"
190SFX_MOVE = "move.wav"
191SFX_END_TURN = "end_turn.wav"
192SFX_EXPLOSION = "explosion.wav"
193SFX_HURT = "hurt.wav"
194SFX_BUTTON = "button.wav"
195SFX_NO_MOVES = "no_moves.wav"
196
197
198# ============================================================================
199# Combat tables
200# ============================================================================
201
202# Attacker -> Defender ban list (upstream: tank can't shoot helicopters).
203_NO_ATTACK = {(UNIT_TANK, UNIT_HELICOPTER)}
204
205
206def can_attack_unit_type(att_type: int, def_type: int) -> bool:
207    return (att_type, def_type) not in _NO_ATTACK