1# Copyright 2013-2017 the openage authors. See copying.md for legal info. 2 3# TODO pylint: disable=C,R,too-many-lines 4 5from ..dataformat.exportable import Exportable 6from ..dataformat.member_access import READ, READ_EXPORT 7from ..dataformat.members import EnumLookupMember, ContinueReadMember, IncludeMembers, SubdataMember 8 9 10class UnitCommand(Exportable): 11 """ 12 also known as "Task" according to ES debug code, 13 this structure is the master for spawn-unit actions. 14 """ 15 name_struct = "unit_command" 16 name_struct_file = "unit" 17 struct_description = "a command a single unit may receive by script or human." 18 19 data_format = [ 20 (READ, "command_used", "int16_t"), # Type (0 = Generic, 1 = Tribe) 21 (READ_EXPORT, "id", "int16_t"), # Identity 22 (READ, "is_default", "int8_t"), 23 (READ_EXPORT, "type", EnumLookupMember( 24 raw_type = "int16_t", 25 type_name = "command_ability", 26 lookup_dict = { 27 # the Action-Types found under RGE namespace: 28 0: "UNUSED", 29 1: "MOVE_TO", 30 2: "FOLLOW", 31 3: "GARRISON", # also known as "Enter" 32 4: "EXPLORE", 33 5: "GATHER", 34 6: "NATURAL_WONDERS_CHEAT", # also known as "Graze" 35 7: "COMBAT", # this is converted to action-type 9 when once instanciated 36 8: "MISSILE", # for projectiles 37 9: "ATTACK", 38 10: "BIRD", # flying. 39 11: "PREDATOR", # scares other animals when hunting 40 12: "TRANSPORT", 41 13: "GUARD", 42 14: "TRANSPORT_OVER_WALL", 43 20: "RUN_AWAY", 44 21: "MAKE", 45 # Action-Types found under TRIBE namespace: 46 101: "BUILD", 47 102: "MAKE_OBJECT", 48 103: "MAKE_TECH", 49 104: "CONVERT", 50 105: "HEAL", 51 106: "REPAIR", 52 107: "CONVERT_AUTO", # "Artifact": can get auto-converted 53 108: "DISCOVERY", 54 109: "SHOOTING_RANGE_RETREAT", 55 110: "HUNT", 56 111: "TRADE", 57 120: "WONDER_VICTORY_GENERATE", 58 121: "DESELECT_ON_TASK", 59 122: "LOOT", 60 123: "HOUSING", 61 124: "PACK", 62 125: "UNPACK_ATTACK", 63 130: "OFF_MAP_TRADE_0", 64 131: "OFF_MAP_TRADE_1", 65 132: "PICKUP_UNIT", 66 133: "PICKUP_133", 67 134: "PICKUP_134", 68 135: "KIDNAP_UNIT", 69 136: "DEPOSIT_UNIT", 70 149: "SHEAR", 71 150: "REGENERATION", 72 151: "FEITORIA", 73 768: "UNKNOWN_768", 74 1024: "UNKNOWN_1024", 75 }, 76 )), 77 (READ_EXPORT, "class_id", "int16_t"), 78 (READ_EXPORT, "unit_id", "int16_t"), 79 (READ_EXPORT, "terrain_id", "int16_t"), 80 (READ_EXPORT, "resource_in", "int16_t"), # carry resource 81 (READ_EXPORT, "resource_multiplier", "int16_t"), # resource that multiplies the amount you can gather 82 (READ_EXPORT, "resource_out", "int16_t"), # drop resource 83 (READ_EXPORT, "unused_resource", "int16_t"), 84 (READ_EXPORT, "work_value1", "float"), # quantity 85 (READ_EXPORT, "work_value2", "float"), # execution radius? 86 (READ_EXPORT, "work_range", "float"), 87 (READ, "search_mode", "int8_t"), 88 (READ, "search_time", "float"), 89 (READ, "enable_targeting", "int8_t"), 90 (READ, "combat_level_flag", "int8_t"), 91 (READ, "gather_type", "int16_t"), 92 (READ, "work_mode2", "int16_t"), 93 (READ_EXPORT, "owner_type", EnumLookupMember( 94 # what can be selected as a target for the unit command? 95 raw_type = "int8_t", 96 type_name = "selection_type", 97 lookup_dict = { 98 0: "ANY_0", # select anything 99 1: "OWNED_UNITS", # your own things 100 2: "NEUTRAL_ENEMY", # enemy and neutral things (->attack) 101 3: "NOTHING", 102 4: "GAIA_OWNED_ALLY", # any of gaia, owned or allied things 103 5: "GAYA_NEUTRAL_ENEMY", # any of gaia, neutral or enemy things 104 6: "NOT_OWNED", # all things that aren't yours 105 7: "ANY_7", 106 }, 107 )), 108 (READ, "carry_check", "int8_t"), # TODO: what does it do? right click? 109 (READ, "state_build", "int8_t"), 110 (READ_EXPORT, "move_sprite_id", "int16_t"), # walking with tool but no resource 111 (READ_EXPORT, "proceed_sprite_id", "int16_t"), # proceeding resource gathering or attack 112 (READ_EXPORT, "work_sprite_id", "int16_t"), # actual execution or transformation graphic 113 (READ_EXPORT, "carry_sprite_id", "int16_t"), # display resources in hands 114 (READ_EXPORT, "resource_gather_sound_id", "int16_t"), # sound to play when execution starts 115 (READ_EXPORT, "resource_deposit_sound_id", "int16_t"), # sound to play on resource drop 116 ] 117 118 119class UnitHeader(Exportable): 120 name_struct = "unit_header" 121 name_struct_file = "unit" 122 struct_description = "stores a bunch of unit commands." 123 124 data_format = [ 125 (READ, "exists", ContinueReadMember("uint8_t")), 126 (READ, "unit_command_count", "uint16_t"), 127 (READ_EXPORT, "unit_commands", SubdataMember( 128 ref_type=UnitCommand, 129 length="unit_command_count", 130 )), 131 ] 132 133 134# Only used in SWGB 135class UnitLine(Exportable): 136 name_struct = "unit_header" 137 name_struct_file = "unit" 138 struct_description = "stores a bunch of unit commands." 139 140 data_format = [ 141 (READ, "name_length", "uint16_t"), 142 (READ, "name", "char[name_length]"), 143 (READ, "unit_ids_counter", "uint16_t"), 144 (READ, "unit_ids", "int16_t[unit_ids_counter]"), 145 ] 146 147 148class ResourceStorage(Exportable): 149 name_struct = "resource_storage" 150 name_struct_file = "unit" 151 struct_description = "determines the resource storage capacity for one unit mode." 152 153 data_format = [ 154 (READ, "type", "int16_t"), 155 (READ, "amount", "float"), 156 (READ, "used_mode", EnumLookupMember( 157 raw_type = "int8_t", 158 type_name = "resource_handling", 159 lookup_dict = { 160 0: "DECAYABLE", 161 1: "KEEP_AFTER_DEATH", 162 2: "RESET_ON_DEATH_INSTANT", 163 4: "RESET_ON_DEATH_WHEN_COMPLETED", 164 }, 165 )), 166 ] 167 168 169class DamageGraphic(Exportable): 170 name_struct = "damage_graphic" 171 name_struct_file = "unit" 172 struct_description = "stores one possible unit image that is displayed at a given damage percentage." 173 174 data_format = [ 175 (READ_EXPORT, "graphic_id", "int16_t"), 176 (READ_EXPORT, "damage_percent", "int8_t"), 177 (READ, "old_apply_mode", "int8_t"), # gets overwritten in aoe memory by the real apply_mode: 178 (READ_EXPORT, "apply_mode", EnumLookupMember( 179 raw_type = "int8_t", 180 type_name = "damage_draw_type", 181 lookup_dict = { 182 0: "TOP", # adds graphics on top (e.g. flames) 183 1: "RANDOM", # adds graphics on top randomly 184 2: "REPLACE", # replace original graphics (e.g. damaged walls) 185 }, 186 )), 187 ] 188 189 190class HitType(Exportable): 191 name_struct = "hit_type" 192 name_struct_file = "unit" 193 struct_description = "stores attack amount for a damage type." 194 195 data_format = [ 196 (READ, "type_id", EnumLookupMember( 197 raw_type = "int16_t", 198 type_name = "hit_class", 199 lookup_dict = { 200 -1: "NONE", 201 0: "UNKNOWN_0", 202 1: "INFANTRY", 203 2: "SHIP_TURTLE", 204 3: "UNITS_PIERCE", 205 4: "UNITS_MELEE", 206 5: "WAR_ELEPHANT", 207 8: "CAVALRY", 208 11: "BUILDINGS_NO_PORT", 209 13: "STONE_DEFENSES", 210 14: "UNKNOWN_14", 211 15: "ARCHERS", 212 16: "SHIPS_CAMELS_SABOTEURS", 213 17: "RAMS", 214 18: "TREES", 215 19: "UNIQUE_UNITS", 216 20: "SIEGE_WEAPONS", 217 21: "BUILDINGS", 218 22: "WALLS_GATES", 219 23: "UNKNOWN_23", 220 24: "BOAR", 221 25: "MONKS", 222 26: "CASTLE", 223 27: "SPEARMEN", 224 28: "CAVALRY_ARCHER", 225 29: "EAGLE_WARRIOR", 226 30: "UNKNOWN_30", 227 }, 228 )), 229 (READ, "amount", "int16_t"), 230 ] 231 232 233class ResourceCost(Exportable): 234 name_struct = "resource_cost" 235 name_struct_file = "unit" 236 struct_description = "stores cost for one resource for creating the unit." 237 238 data_format = [ 239 (READ, "type_id", EnumLookupMember( 240 raw_type = "int16_t", 241 type_name = "resource_types", 242 lookup_dict = { 243 -1: "NONE", 244 0: "FOOD_STORAGE", 245 1: "WOOD_STORAGE", 246 2: "STONE_STORAGE", 247 3: "GOLD_STORAGE", 248 4: "POPULATION_HEADROOM", 249 5: "CONVERSION_RANGE", 250 6: "CURRENT_AGE", 251 7: "OWNED_RELIC_COUNT", 252 8: "TRADE_BONUS", 253 9: "TRADE_GOODS", 254 10: "TRADE_PRODUCTION", 255 11: "POPULATION", # both current population and population headroom 256 12: "CORPSE_DECAY_TIME", 257 13: "DISCOVERY", 258 14: "RUIN_MONUMENTS_CAPTURED", 259 15: "MEAT_STORAGE", 260 16: "BERRY_STORAGE", 261 17: "FISH_STORAGE", 262 18: "UNKNOWN_18", # in starwars: power core range 263 19: "TOTAL_UNITS_OWNED", # or just military ones? used for counting losses 264 20: "UNITS_KILLED", 265 21: "RESEARCHED_TECHNOLOGIES_COUNT", 266 22: "MAP_EXPLORED_PERCENTAGE", 267 23: "CASTLE_AGE_TECH_INDEX", # default: 102 268 24: "IMPERIAL_AGE_TECH_INDEX", # default: 103 269 25: "FEUDAL_AGE_TECH_INDEX", # default: 101 270 26: "ATTACK_WARNING_SOUND", 271 27: "ENABLE_MONK_CONVERSION", 272 28: "ENABLE_BUILDING_CONVERSION", 273 30: "BUILDING_COUNT", # default: 500 274 31: "FOOD_COUNT", 275 32: "BONUS_POPULATION", 276 33: "MAINTENANCE", 277 34: "FAITH", 278 35: "FAITH_RECHARGE_RATE", # default: 1.6 279 36: "FARM_FOOD_AMOUNT", # default: 175 280 37: "CIVILIAN_POPULATION", 281 38: "UNKNOWN_38", # starwars: shields for bombers/fighters 282 39: "ALL_TECHS_ACHIEVED", # default: 178 283 40: "MILITARY_POPULATION", # -> largest army 284 41: "UNITS_CONVERTED", # monk success count 285 42: "WONDERS_STANDING", 286 43: "BUILDINGS_RAZED", 287 44: "KILL_RATIO", 288 45: "SURVIVAL_TO_FINISH", # bool 289 46: "TRIBUTE_FEE", # default: 0.3 290 47: "GOLD_MINING_PRODUCTIVITY", # default: 1 291 48: "TOWN_CENTER_UNAVAILABLE", # -> you may build a new one 292 49: "GOLD_COUNTER", 293 50: "REVEAL_ALLY", # bool, ==cartography discovered 294 51: "HOUSES_COUNT", 295 52: "MONASTERY_COUNT", 296 53: "TRIBUTE_SENT", 297 54: "RUINES_CAPTURED_ALL", # bool 298 55: "RELICS_CAPTURED_ALL", # bool 299 56: "ORE_STORAGE", 300 57: "CAPTURED_UNITS", 301 58: "DARK_AGE_TECH_INDEX", # default: 104 302 59: "TRADE_GOOD_QUALITY", # default: 1 303 60: "TRADE_MARKET_LEVEL", 304 61: "FORMATIONS", 305 62: "BUILDING_HOUSING_RATE", # default: 20 306 63: "GATHER_TAX_RATE", # default: 32000 307 64: "GATHER_ACCUMULATOR", 308 65: "SALVAGE_DECAY_RATE", # default: 5 309 66: "ALLOW_FORMATION", # bool, something with age? 310 67: "ALLOW_CONVERSIONS", # bool 311 68: "HIT_POINTS_KILLED", # unused 312 69: "KILLED_PLAYER_1", # bool 313 70: "KILLED_PLAYER_2", # bool 314 71: "KILLED_PLAYER_3", # bool 315 72: "KILLED_PLAYER_4", # bool 316 73: "KILLED_PLAYER_5", # bool 317 74: "KILLED_PLAYER_6", # bool 318 75: "KILLED_PLAYER_7", # bool 319 76: "KILLED_PLAYER_8", # bool 320 77: "CONVERSION_RESISTANCE", 321 78: "TRADE_VIG_RATE", # default: 0.3 322 79: "STONE_MINING_PRODUCTIVITY", # default: 1 323 80: "QUEUED_UNITS", 324 81: "TRAINING_COUNT", 325 82: "START_PACKED_TOWNCENTER", # or raider, default: 2 326 83: "BOARDING_RECHARGE_RATE", 327 84: "STARTING_VILLAGERS", # default: 3 328 85: "RESEARCH_COST_MULTIPLIER", 329 86: "RESEARCH_TIME_MULTIPLIER", 330 87: "CONVERT_SHIPS_ALLOWED", # bool 331 88: "FISH_TRAP_FOOD_AMOUNT", # default: 700 332 89: "HEALING_RATE_MULTIPLIER", 333 90: "HEALING_RANGE", 334 91: "STARTING_FOOD", 335 92: "STARTING_WOOD", 336 93: "STARTING_STONE", 337 94: "STARTING_GOLD", 338 95: "TOWN_CENTER_PACKING", # or raider, default: 3 339 96: "BERSERKER_HEAL_TIME", # in seconds 340 97: "DOMINANT_ANIMAL_DISCOVERY", # bool, sheep/turkey 341 98: "SCORE_OBJECT_COST", # object cost summary, economy? 342 99: "SCORE_RESEARCH", 343 100: "RELIC_GOLD_COLLECTED", 344 101: "TRADE_PROFIT", 345 102: "TRIBUTE_P1", 346 103: "TRIBUTE_P2", 347 104: "TRIBUTE_P3", 348 105: "TRIBUTE_P4", 349 106: "TRIBUTE_P5", 350 107: "TRIBUTE_P6", 351 108: "TRIBUTE_P7", 352 109: "TRIBUTE_P8", 353 110: "KILL_SCORE_P1", 354 111: "KILL_SCORE_P2", 355 112: "KILL_SCORE_P3", 356 113: "KILL_SCORE_P4", 357 114: "KILL_SCORE_P5", 358 115: "KILL_SCORE_P6", 359 116: "KILL_SCORE_P7", 360 117: "KILL_SCORE_P8", 361 118: "RAZING_COUNT_P1", 362 119: "RAZING_COUNT_P2", 363 120: "RAZING_COUNT_P3", 364 121: "RAZING_COUNT_P4", 365 122: "RAZING_COUNT_P5", 366 123: "RAZING_COUNT_P6", 367 124: "RAZING_COUNT_P7", 368 125: "RAZING_COUNT_P8", 369 126: "RAZING_SCORE_P1", 370 127: "RAZING_SCORE_P2", 371 128: "RAZING_SCORE_P3", 372 129: "RAZING_SCORE_P4", 373 130: "RAZING_SCORE_P5", 374 131: "RAZING_SCORE_P6", 375 132: "RAZING_SCORE_P7", 376 133: "RAZING_SCORE_P8", 377 134: "STANDING_CASTLES", 378 135: "RAZINGS_HIT_POINTS", 379 136: "KILLS_BY_P1", 380 137: "KILLS_BY_P2", 381 138: "KILLS_BY_P3", 382 139: "KILLS_BY_P4", 383 140: "KILLS_BY_P5", 384 141: "KILLS_BY_P6", 385 142: "KILLS_BY_P7", 386 143: "KILLS_BY_P8", 387 144: "RAZINGS_BY_P1", 388 145: "RAZINGS_BY_P2", 389 146: "RAZINGS_BY_P3", 390 147: "RAZINGS_BY_P4", 391 148: "RAZINGS_BY_P5", 392 149: "RAZINGS_BY_P6", 393 150: "RAZINGS_BY_P7", 394 151: "RAZINGS_BY_P8", 395 152: "LOST_UNITS_SCORE", 396 153: "LOST_BUILDINGS_SCORE", 397 154: "LOST_UNITS", 398 155: "LOST_BUILDINGS", 399 156: "TRIBUTE_FROM_P1", 400 157: "TRIBUTE_FROM_P2", 401 158: "TRIBUTE_FROM_P3", 402 159: "TRIBUTE_FROM_P4", 403 160: "TRIBUTE_FROM_P5", 404 161: "TRIBUTE_FROM_P6", 405 162: "TRIBUTE_FROM_P7", 406 163: "TRIBUTE_FROM_P8", 407 164: "SCORE_UNITS_CURRENT", 408 165: "SCORE_BUILDINGS_CURRENT", # default: 275 409 166: "COLLECTED_FOOD", 410 167: "COLLECTED_WOOD", 411 168: "COLLECTED_STONE", 412 169: "COLLECTED_GOLD", 413 170: "SCORE_MILITARY", 414 171: "TRIBUTE_RECEIVED", 415 172: "SCORE_RAZINGS", 416 173: "TOTAL_CASTLES", 417 174: "TOTAL_WONDERS", 418 175: "SCORE_ECONOMY_TRIBUTES", 419 176: "CONVERT_ADJUSTMENT_MIN", # used for resistance against monk conversions 420 177: "CONVERT_ADJUSTMENT_MAX", 421 178: "CONVERT_RESIST_ADJUSTMENT_MIN", 422 179: "CONVERT_RESIST_ADJUSTMENT_MAX", 423 180: "CONVERT_BUILDIN_MIN", # default: 15 424 181: "CONVERT_BUILDIN_MAX", # default: 25 425 182: "CONVERT_BUILDIN_CHANCE", # default: 25 426 183: "REVEAL_ENEMY", 427 184: "SCORE_SOCIETY", # wonders, castles 428 185: "SCORE_FOOD", 429 186: "SCORE_WOOD", 430 187: "SCORE_STONE", 431 188: "SCORE_GOLD", 432 189: "CHOPPING_PRODUCTIVITY", # default: 1 433 190: "FOOD_GATHERING_PRODUCTIVITY", # default: 1 434 191: "RELIC_GOLD_PRODUCTION_RATE", # default: 30 435 192: "CONVERTED_UNITS_DIE", # bool 436 193: "THEOCRACY_ACTIVE", # bool 437 194: "CRENELLATIONS_ACTIVE", # bool 438 195: "CONSTRUCTION_RATE_MULTIPLIER", # except for wonders 439 196: "HUN_WONDER_BONUS", 440 197: "SPIES_DISCOUNT", # or atheism_active? 441 } 442 )), 443 (READ, "amount", "int16_t"), 444 (READ, "enabled", "int16_t"), 445 ] 446 447 448class BuildingAnnex(Exportable): 449 450 name_struct = "building_annex" 451 name_struct_file = "unit" 452 struct_description = "a possible building annex." 453 454 data_format = [ 455 (READ_EXPORT, "unit_id", "int16_t"), 456 (READ_EXPORT, "misplaced0", "float"), 457 (READ_EXPORT, "misplaced1", "float"), 458 ] 459 460 461class UnitObject(Exportable): 462 """ 463 base properties for every unit entry. 464 """ 465 466 name_struct = "unit_object" 467 name_struct_file = "unit" 468 struct_description = "base properties for all units." 469 470 data_format = [ 471 (READ, "name_length", "uint16_t"), 472 (READ_EXPORT, "id0", "int16_t"), 473 (READ_EXPORT, "language_dll_name", "uint16_t"), 474 (READ_EXPORT, "language_dll_creation", "uint16_t"), 475 (READ_EXPORT, "unit_class", EnumLookupMember( 476 raw_type = "int16_t", 477 type_name = "unit_classes", 478 lookup_dict = { 479 -1: "NONE", 480 0: "ARCHER", 481 1: "ARTIFACT", 482 2: "TRADE_BOAT", 483 3: "BUILDING", 484 4: "CIVILIAN", 485 5: "SEA_FISH", 486 6: "SOLDIER", 487 7: "BERRY_BUSH", 488 8: "STONE_MINE", 489 9: "PREY_ANIMAL", 490 10: "PREDATOR_ANIMAL", 491 11: "OTHER", 492 12: "CAVALRY", 493 13: "SIEGE_WEAPON", 494 14: "TERRAIN", 495 15: "TREES", 496 16: "UNKNOWN_16", 497 18: "PRIEST", 498 19: "TRADE_CART", 499 20: "TRANSPORT_BOAT", 500 21: "FISHING_BOAT", 501 22: "WAR_BOAT", 502 23: "CONQUISTADOR", 503 27: "WALLS", 504 28: "PHALANX", 505 29: "ANIMAL_DOMESTICATED", 506 30: "FLAGS", 507 32: "GOLD_MINE", 508 33: "SHORE_FISH", 509 34: "CLIFF", 510 35: "PETARD", 511 36: "CAVALRY_ARCHER", 512 37: "DOLPHIN", 513 38: "BIRDS", 514 39: "GATES", 515 40: "PILES", 516 41: "PILES_OF_RESOURCE", 517 42: "RELIC", 518 43: "MONK_WITH_RELIC", 519 44: "HAND_CANNONEER", 520 45: "TWO_HANDED_SWORD", 521 46: "PIKEMAN", 522 47: "SCOUT_CAVALRY", 523 48: "ORE_MINE", 524 49: "FARM", 525 50: "SPEARMAN", 526 51: "PACKED_SIEGE_UNITS", 527 52: "TOWER", 528 53: "BOARDING_BOAT", 529 54: "UNPACKED_SIEGE_UNITS", 530 55: "SCORPION", 531 56: "RAIDER", 532 57: "CAVALRY_RAIDER", 533 58: "SHEEP", 534 59: "KING", 535 61: "HORSE", 536 }, 537 )), 538 (READ_EXPORT, "graphic_standing0", "int16_t"), 539 (READ_EXPORT, "graphic_standing1", "int16_t"), 540 (READ_EXPORT, "dying_graphic", "int16_t"), 541 (READ_EXPORT, "undead_graphic", "int16_t"), 542 (READ, "death_mode", "int8_t"), # 1 = become `dead_unit_id` (reviving does not make it usable again) 543 (READ_EXPORT, "hit_points", "int16_t"), # unit health. -1=insta-die 544 (READ, "line_of_sight", "float"), 545 (READ, "garrison_capacity", "int8_t"), # number of units that can garrison in there 546 (READ_EXPORT, "radius_x", "float"), # size of the unit 547 (READ_EXPORT, "radius_y", "float"), 548 (READ_EXPORT, "radius_z", "float"), 549 (READ_EXPORT, "train_sound", "int16_t"), 550 ] 551 552 # TODO: Enable conversion for AOE1; replace "damage_sound" 553 # =========================================================================== 554 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 555 # data_format.append((READ_EXPORT, "damage_sound", "int16_t")) 556 # =========================================================================== 557 data_format.append((READ_EXPORT, "damage_sound", "int16_t")) 558 559 data_format.extend([ 560 (READ_EXPORT, "dead_unit_id", "int16_t"), # unit id to become on death 561 (READ, "placement_mode", "int8_t"), # 0=placable on top of others in scenario editor, 5=can't 562 (READ, "can_be_built_on", "int8_t"), # 1=no footprints 563 (READ, "icon_id", "int16_t"), # frame id of the icon slp (57029) to place on the creation button 564 (READ, "hidden_in_editor", "int8_t"), 565 (READ, "old_portrait_icon_id", "int16_t"), 566 (READ, "enabled", "int8_t"), # 0=unlocked by research, 1=insta-available 567 ]) 568 569 # TODO: Enable conversion for AOE1; replace "disabled" 570 # =========================================================================== 571 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 572 # data_format.append((READ, "disabled", "int8_t")) 573 # =========================================================================== 574 data_format.append((READ, "disabled", "int8_t")) 575 576 data_format.extend([ 577 (READ, "placement_side_terrain0", "int16_t"), # terrain id that's needed somewhere on the foundation (e.g. dock water) 578 (READ, "placement_side_terrain1", "int16_t"), # second slot for ^ 579 (READ, "placement_terrain0", "int16_t"), # terrain needed for placement (e.g. dock: water) 580 (READ, "placement_terrain1", "int16_t"), # alternative terrain needed for placement (e.g. dock: shallows) 581 (READ, "clearance_size_x", "float"), # minimum space required to allow placement in editor 582 (READ, "clearance_size_y", "float"), 583 (READ_EXPORT, "building_mode", EnumLookupMember( 584 raw_type = "int8_t", 585 type_name = "building_modes", 586 lookup_dict = { 587 0: "NON_BUILDING", # gates, farms, walls, towers 588 2: "TRADE_BUILDING", # towncenter, port, trade workshop 589 3: "ANY", 590 }, 591 )), 592 (READ_EXPORT, "visible_in_fog", EnumLookupMember( 593 raw_type = "int8_t", 594 type_name = "fog_visibility", 595 lookup_dict = { 596 0: "INVISIBLE", # people etc 597 1: "VISIBLE", # buildings 598 3: "ONLY_IN_FOG", 599 }, 600 )), 601 (READ_EXPORT, "terrain_restriction", EnumLookupMember( 602 raw_type = "int16_t", # determines on what type of ground the unit can be placed/walk 603 type_name = "ground_type", # is actually the id of the terrain_restriction entry! 604 lookup_dict = { 605 -0x01: "NONE", 606 0x00: "ANY", 607 0x01: "SHORELINE", 608 0x02: "WATER", 609 0x03: "WATER_SHIP_0x03", 610 0x04: "FOUNDATION", 611 0x05: "NOWHERE", # can't place anywhere 612 0x06: "WATER_DOCK", # shallow water for dock placement 613 0x07: "SOLID", 614 0x08: "NO_ICE_0x08", 615 0x0A: "NO_ICE_0x0A", 616 0x0B: "FOREST", 617 0x0C: "UNKNOWN_0x0C", 618 0x0D: "WATER_0x0D", # great fish 619 0x0E: "UNKNOWN_0x0E", 620 0x0F: "WATER_SHIP_0x0F", # transport ship 621 0x10: "GRASS_SHORELINE", # for gates and walls 622 0x11: "WATER_ANY_0x11", 623 0x12: "UNKNOWN_0x12", 624 0x13: "FISH_NO_ICE", 625 0x14: "WATER_ANY_0x14", 626 0x15: "WATER_SHALLOW", 627 }, 628 )), 629 (READ_EXPORT, "fly_mode", "int8_t"), # determines whether the unit can fly 630 (READ_EXPORT, "resource_capacity", "int16_t"), 631 (READ_EXPORT, "resource_decay", "float"), # when animals rot, their resources decay 632 (READ_EXPORT, "blast_defense_level", EnumLookupMember( 633 # receive blast damage from units that have lower or same blast_attack_level. 634 raw_type = "int8_t", 635 type_name = "blast_types", 636 lookup_dict = { 637 0: "UNIT_0", # projectile, dead, fish, relic, tree, gate, towncenter 638 1: "OTHER", # 'other' things with multiple rotations 639 2: "BUILDING", # buildings, gates, walls, towncenter, fishtrap 640 3: "UNIT_3", # boar, farm, fishingship, villager, tradecart, sheep, turkey, archers, junk, ships, monk, siege 641 } 642 )), 643 (READ_EXPORT, "combat_level", EnumLookupMember( 644 raw_type = "int8_t", 645 type_name = "combat_levels", 646 lookup_dict = { 647 0: "PROJECTILE_DEAD_RESOURCE", 648 1: "BOAR", 649 2: "BUILDING", 650 3: "CIVILIAN", 651 4: "MILITARY", 652 5: "OTHER", 653 } 654 )), 655 (READ_EXPORT, "interaction_mode", EnumLookupMember( 656 # what can be done with this unit? 657 raw_type = "int8_t", 658 type_name = "interaction_modes", 659 lookup_dict = { 660 0: "NOTHING_0", 661 1: "NOTHING_1", 662 2: "SELECTABLE", 663 3: "SELECT_ATTACK", 664 4: "SELECT_ATTACK_MOVE", 665 5: "SELECT_MOVE", 666 }, 667 )), 668 (READ_EXPORT, "map_draw_level", EnumLookupMember( 669 # how does the unit show up on the minimap? 670 raw_type = "int8_t", 671 type_name = "minimap_modes", 672 lookup_dict = { 673 0: "NO_DOT_0", 674 1: "SQUARE_DOT", # turns white when selected 675 2: "DIAMOND_DOT", # dito 676 3: "DIAMOND_DOT_KEEPCOLOR", # doesn't turn white when selected 677 4: "LARGEDOT", # observable by all players, no attacked-blinking 678 5: "NO_DOT_5", 679 6: "NO_DOT_6", 680 7: "NO_DOT_7", 681 8: "NO_DOT_8", 682 9: "NO_DOT_9", 683 10: "NO_DOT_10", 684 }, 685 )), 686 (READ_EXPORT, "unit_level", EnumLookupMember( 687 # selects the available ui command buttons for the unit 688 raw_type = "int8_t", 689 type_name = "command_attributes", 690 lookup_dict = { 691 0: "LIVING", # commands: delete, garrison, stop, attributes: hit points 692 1: "ANIMAL", # animal 693 2: "NONMILITARY_BULIDING", # civilian building (build page 1) 694 3: "VILLAGER", # villager 695 4: "MILITARY_UNIT", # military unit 696 5: "TRADING_UNIT", # trading unit 697 6: "MONK_EMPTY", # monk 698 7: "TRANSPORT_SHIP", # transport ship 699 8: "RELIC", # relic / monk with relic 700 9: "FISHING_SHIP", # fishing ship 701 10: "MILITARY_BUILDING", # military building (build page 2) 702 11: "SHIELDED_BUILDING", # shield building (build page 3) 703 12: "UNKNOWN_12", 704 }, 705 )), 706 (READ, "attack_reaction", "float"), 707 (READ_EXPORT, "minimap_color", "int8_t"), # palette color id for the minimap 708 (READ_EXPORT, "language_dll_help", "int32_t"), # help text for this unit, stored in the translation dll. 709 (READ_EXPORT, "language_dll_hotkey_text", "int32_t"), 710 (READ, "hot_keys", "int32_t"), # language dll dependent (kezb lazouts!) 711 (READ, "reclyclable", "int8_t"), 712 (READ, "enable_auto_gather", "int8_t"), 713 (READ, "doppelgaenger_on_death", "int8_t"), 714 (READ, "resource_gather_drop", "int8_t"), 715 ]) 716 717 # TODO: Enable conversion for AOE1, AOK; replace 6 values below 718 # =========================================================================== 719 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 720 # # bit 0 == 1 && val != 7: mask shown behind buildings, 721 # # bit 0 == 0 && val != {6, 10}: no mask displayed, 722 # # val == {-1, 7}: in open area mask is partially displayed 723 # # val == {6, 10}: building, causes mask to appear on units behind it 724 # data_format.extend([ 725 # (READ, "occlusion_mask", "int8_t"), 726 # (READ, "obstruction_type", EnumLookupMember( 727 # # selects the available ui command buttons for the unit 728 # raw_type = "int8_t", 729 # type_name = "obstruction_types", 730 # lookup_dict = { 731 # 0: "PASSABLE", # farm, gate, dead bodies, town center 732 # 2: "BUILDING", 733 # 3: "BERSERK", 734 # 5: "UNIT", 735 # 10: "MOUNTAIN", # mountain (matches occlusion_mask) 736 # }, 737 # )), 738 # # There shouldn't be a value here according to genieutils 739 # # What is this? 740 # (READ_EXPORT, "selection_shape", "int8_t"), # 0=square, 1<=round 741 # ]) 742 # 743 # if GameVersion-age2_aok not in game_versions: 744 # # bitfield of unit attributes: 745 # # bit 0: allow garrison, 746 # # bit 1: don't join formation, 747 # # bit 2: stealth unit, 748 # # bit 3: detector unit, 749 # # bit 4: mechanical unit, 750 # # bit 5: biological unit, 751 # # bit 6: self-shielding unit, 752 # # bit 7: invisible unit 753 # data_format.extend([ 754 # (READ, "trait", "uint8_t"), 755 # (READ, "civilisation", "int8_t"), 756 # (READ, "attribute_piece", "int16_t"), # leftover from trait+civ variable 757 # ]) 758 # =========================================================================== 759 760 # bit 0 == 1 && val != 7: mask shown behind buildings, 761 # bit 0 == 0 && val != {6, 10}: no mask displayed, 762 # val == {-1, 7}: in open area mask is partially displayed 763 # val == {6, 10}: building, causes mask to appear on units behind it 764 data_format.extend([ 765 (READ, "occlusion_mask", "int8_t"), 766 (READ, "obstruction_type", EnumLookupMember( 767 # selects the available ui command buttons for the unit 768 raw_type = "int8_t", 769 type_name = "obstruction_types", 770 lookup_dict = { 771 0: "PASSABLE", # farm, gate, dead bodies, town center 772 2: "BUILDING", 773 3: "BERSERK", 774 5: "UNIT", 775 10: "MOUNTAIN", # mountain (matches occlusion_mask) 776 }, 777 )), 778 # There shouldn't be a value here according to genieutils 779 # What is this? 780 (READ_EXPORT, "selection_shape", "int8_t"), # 0=square, 1<=round 781 782 # bitfield of unit attributes: 783 # bit 0: allow garrison, 784 # bit 1: don't join formation, 785 # bit 2: stealth unit, 786 # bit 3: detector unit, 787 # bit 4: mechanical unit, 788 # bit 5: biological unit, 789 # bit 6: self-shielding unit, 790 # bit 7: invisible unit 791 (READ, "trait", "uint8_t"), 792 (READ, "civilisation", "int8_t"), 793 (READ, "attribute_piece", "int16_t"), # leftover from trait+civ variable 794 ]) 795 # =========================================================================== 796 797 data_format.extend([ 798 (READ_EXPORT, "selection_effect", EnumLookupMember( 799 # things that happen when the unit was selected 800 raw_type = "int8_t", 801 type_name = "selection_effects", 802 lookup_dict = { 803 0: "NONE", 804 1: "HPBAR_ON_OUTLINE_DARK", # permanent, editor only 805 2: "HPBAR_ON_OUTLINE_NORMAL", 806 3: "HPBAR_OFF_SELECTION_SHADOW", 807 4: "HPBAR_OFF_OUTLINE_NORMAL", 808 5: "HPBAR_ON_5", 809 6: "HPBAR_OFF_6", 810 7: "HPBAR_OFF_7", 811 8: "HPBAR_ON_8", 812 9: "HPBAR_ON_9", 813 }, 814 )), 815 (READ, "editor_selection_color", "uint8_t"), # 0: default, -16: fish trap, farm, 52: deadfarm, OLD-*, 116: flare, whale, dolphin -123: fish 816 (READ_EXPORT, "selection_shape_x", "float"), 817 (READ_EXPORT, "selection_shape_y", "float"), 818 (READ_EXPORT, "selection_shape_z", "float"), 819 (READ_EXPORT, "resource_storage", SubdataMember( 820 ref_type=ResourceStorage, 821 length=3, 822 )), 823 (READ, "damage_graphic_count", "int8_t"), 824 (READ_EXPORT, "damage_graphic", SubdataMember( 825 ref_type=DamageGraphic, 826 length="damage_graphic_count", 827 )), 828 (READ_EXPORT, "sound_selection", "int16_t"), 829 (READ_EXPORT, "sound_dying", "int16_t"), 830 (READ_EXPORT, "old_attack_mode", EnumLookupMember( # obsolete, as it's copied when converting the unit 831 raw_type = "int8_t", # things that happen when the unit was selected 832 type_name = "attack_modes", 833 lookup_dict = { 834 0: "NO", # no attack 835 1: "FOLLOWING", # by following 836 2: "RUN", # run when attacked 837 3: "UNKNOWN3", 838 4: "ATTACK", 839 }, 840 )), 841 842 (READ, "convert_terrain", "int8_t"), 843 (READ_EXPORT, "name", "char[name_length]"), 844 ]) 845 846 # TODO: Enable conversion for SWGB 847 # =========================================================================== 848 # if (GameVersion.swgb_10 or GameVersion.swgb_cc) in game_versions: 849 # data_format.extend([(READ, "name2_length", "uint16_t"), 850 # (READ, "name2", "char[name2_length]"), 851 # (READ, "unit_line", "int16_t"), 852 # (READ, "min_tech_level", "int8_t"), 853 # ]) 854 # =========================================================================== 855 856 data_format.append((READ_EXPORT, "id1", "int16_t")) 857 858 # TODO: Enable conversion for AOE1; replace "id2" 859 # =========================================================================== 860 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 861 # data_format.append((READ_EXPORT, "id2", "int16_t")) 862 # =========================================================================== 863 data_format.append((READ_EXPORT, "id2", "int16_t")) 864 865 866class TreeUnit(UnitObject): 867 """ 868 type_id == 90 869 """ 870 871 name_struct = "tree_unit" 872 name_struct_file = "unit" 873 struct_description = "just a tree unit." 874 875 data_format = [ 876 (READ_EXPORT, None, IncludeMembers(cls=UnitObject)), 877 ] 878 879 880class AnimatedUnit(UnitObject): 881 """ 882 type_id >= 20 883 Animated master object 884 """ 885 886 name_struct = "animated_unit" 887 name_struct_file = "unit" 888 struct_description = "adds speed property to units." 889 890 data_format = [ 891 (READ_EXPORT, None, IncludeMembers(cls=UnitObject)), 892 (READ_EXPORT, "speed", "float"), 893 ] 894 895 896class DoppelgangerUnit(AnimatedUnit): 897 """ 898 type_id >= 25 899 """ 900 901 name_struct = "doppelganger_unit" 902 name_struct_file = "unit" 903 struct_description = "weird doppelganger unit thats actually the same as an animated unit." 904 905 data_format = [ 906 (READ_EXPORT, None, IncludeMembers(cls=AnimatedUnit)), 907 ] 908 909 910class MovingUnit(DoppelgangerUnit): 911 """ 912 type_id >= 30 913 Moving master object 914 """ 915 916 name_struct = "moving_unit" 917 name_struct_file = "unit" 918 struct_description = "adds walking graphics, rotations and tracking properties to units." 919 920 data_format = [ 921 (READ_EXPORT, None, IncludeMembers(cls=DoppelgangerUnit)), 922 (READ_EXPORT, "walking_graphics0", "int16_t"), 923 (READ_EXPORT, "walking_graphics1", "int16_t"), 924 (READ, "turn_speed", "float"), 925 (READ, "old_size_class", "int8_t"), 926 (READ, "trail_unit_id", "int16_t"), # unit id for the ground traces 927 (READ, "trail_opsions", "uint8_t"), # ground traces: -1: no tracking present, 2: projectiles with tracking unit 928 (READ, "trail_spacing", "float"), # ground trace spacing: 0: no tracking, 0.5: trade cart, 0.12: some projectiles, 0.4: other projectiles 929 (READ, "old_move_algorithm", "int8_t"), 930 ] 931 932 # TODO: Enable conversion for AOE1; replace 5 values below 933 # =========================================================================== 934 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 935 # data_format.extend([ 936 # (READ, "turn_radius", "float"), 937 # (READ, "turn_radius_speed", "float"), 938 # (READ, "max_yaw_per_sec_moving", "float"), 939 # (READ, "stationary_yaw_revolution_time", "float"), 940 # (READ, "max_yaw_per_sec_stationary", "float"), 941 # ]) 942 # =========================================================================== 943 data_format.extend([ 944 (READ, "turn_radius", "float"), 945 (READ, "turn_radius_speed", "float"), 946 (READ, "max_yaw_per_sec_moving", "float"), 947 (READ, "stationary_yaw_revolution_time", "float"), 948 (READ, "max_yaw_per_sec_stationary", "float"), 949 ]) 950 951 952class ActionUnit(MovingUnit): 953 """ 954 type_id >= 40 955 Action master object 956 """ 957 958 name_struct = "action_unit" 959 name_struct_file = "unit" 960 struct_description = "adds search radius and work properties, as well as movement sounds." 961 962 data_format = [ 963 (READ_EXPORT, None, IncludeMembers(cls=MovingUnit)), 964 # callback unit action id when found. 965 # monument and sheep: 107 = enemy convert. 966 # all auto-convertible units: 0, most other units: -1 967 (READ, "default_task_id", "int16_t"), # e.g. when sheep are discovered 968 (READ, "search_radius", "float"), 969 (READ_EXPORT, "work_rate", "float"), 970 (READ_EXPORT, "drop_site0", "int16_t"), # unit id where gathered resources shall be delivered to 971 (READ_EXPORT, "drop_site1", "int16_t"), # alternative unit id 972 (READ_EXPORT, "task_by_group", "int8_t"), # if a task is not found in the current unit, other units with the same group id are tried. 973 # 1: male villager; 2: female villager; 3+: free slots 974 # basically this creates a "swap group id" where you can place different-graphic units together. 975 (READ_EXPORT, "command_sound_id", "int16_t"), # sound played when a command is instanciated 976 (READ_EXPORT, "stop_sound_id", "int16_t"), # sound when the command is done (e.g. unit stops at target position) 977 (READ, "run_pattern", "int8_t"), # how animals run around randomly 978 ] 979 980 # TODO: Enable conversion for AOE1 981 # =========================================================================== 982 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) in game_versions: 983 # data_format.extend([ 984 # (READ_EXPORT, "unit_count", "uint16_t"), 985 # (READ_EXPORT, "unit_commands", SubdataMember( 986 # ref_type=UnitCommand, 987 # length="unit_count", 988 # )), 989 # ]) 990 # =========================================================================== 991 992 993class ProjectileUnit(ActionUnit): 994 """ 995 type_id >= 60 996 Projectile master object 997 """ 998 999 name_struct = "projectile_unit" 1000 name_struct_file = "unit" 1001 struct_description = "adds attack and armor properties to units." 1002 1003 data_format = [ 1004 (READ_EXPORT, None, IncludeMembers(cls=ActionUnit)), 1005 ] 1006 1007 # TODO: Enable conversion for AOE1; replace "default_armor" 1008 # =========================================================================== 1009 # if (GameVersion.aoe_1 or GameVersion.aoe_ror or GameVersion.age2_aok) in game_versions: 1010 # data_format.append((READ, "default_armor", "uint8_t")) 1011 # else: 1012 # data_format.append((READ, "default_armor", "int16_t")) 1013 # =========================================================================== 1014 data_format.append((READ, "default_armor", "int16_t")) 1015 1016 data_format.extend([ 1017 (READ, "attack_count", "uint16_t"), 1018 (READ, "attacks", SubdataMember(ref_type=HitType, length="attack_count")), 1019 (READ, "armor_count", "uint16_t"), 1020 (READ, "armors", SubdataMember(ref_type=HitType, length="armor_count")), 1021 (READ_EXPORT, "boundary_id", EnumLookupMember( 1022 # the damage received by this unit is multiplied by 1023 # the accessible values on the specified terrain restriction 1024 raw_type = "int16_t", 1025 type_name = "boundary_ids", 1026 lookup_dict = { 1027 -1: "NONE", 1028 4: "BUILDING", 1029 6: "DOCK", 1030 10: "WALL", 1031 }, 1032 )), 1033 (READ_EXPORT, "weapon_range_max", "float"), 1034 (READ, "blast_range", "float"), 1035 (READ, "attack_speed", "float"), # = "reload time" 1036 (READ_EXPORT, "missile_unit_id", "int16_t"), # which projectile to use? 1037 (READ, "base_hit_chance", "int16_t"), # probablity of attack hit in percent 1038 (READ, "break_off_combat", "int8_t"), # = tower mode?; not used anywhere 1039 (READ, "frame_delay", "int16_t"), # the frame number at which the missile is fired, = delay 1040 (READ, "weapon_offset", "float[3]"), # graphics displacement in x, y and z 1041 (READ_EXPORT, "blast_level_offence", EnumLookupMember( 1042 # blasts damage units that have higher or same blast_defense_level 1043 raw_type = "int8_t", 1044 type_name = "range_damage_type", 1045 lookup_dict = { 1046 0: "RESOURCES", 1047 1: "TREES", 1048 2: "NEARBY_UNITS", 1049 3: "TARGET_ONLY", 1050 6: "UNKNOWN_6", 1051 }, 1052 )), 1053 (READ, "weapon_range_min", "float"), # minimum range that this projectile requests for display 1054 ]) 1055 1056 # TODO: Enable conversion for AOE1; replace "accuracy_dispersion" 1057 # =========================================================================== 1058 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 1059 # data_format.append((READ, "accuracy_dispersion", "float")) 1060 # =========================================================================== 1061 data_format.append((READ, "accuracy_dispersion", "float")) 1062 1063 data_format.extend([ 1064 (READ_EXPORT, "fight_sprite_id", "int16_t"), 1065 (READ, "melee_armor_displayed", "int16_t"), 1066 (READ, "attack_displayed", "int16_t"), 1067 (READ, "range_displayed", "float"), 1068 (READ, "reload_time_displayed", "float"), 1069 ]) 1070 1071 1072class MissileUnit(ProjectileUnit): 1073 """ 1074 type_id == 60 1075 Missile master object 1076 """ 1077 1078 name_struct = "missile_unit" 1079 name_struct_file = "unit" 1080 struct_description = "adds missile specific unit properties." 1081 1082 data_format = [ 1083 (READ_EXPORT, None, IncludeMembers(cls=ProjectileUnit)), 1084 (READ, "projectile_type", "int8_t"), # 0 = default; 1 = projectile falls vertically to the bottom of the map; 3 = teleporting projectiles 1085 (READ, "smart_mode", "int8_t"), # "better aiming". tech attribute 19 changes this: 0 = shoot at current pos; 1 = shoot at predicted pos 1086 (READ, "drop_animation_mode", "int8_t"), # 1 = disappear on hit 1087 (READ, "penetration_mode", "int8_t"), # 1 = pass through hit object; 0 = stop projectile on hit; (only for graphics, not pass-through damage) 1088 (READ, "area_of_effect_special", "int8_t"), 1089 (READ_EXPORT, "projectile_arc", "float"), 1090 ] 1091 1092 1093class LivingUnit(ProjectileUnit): 1094 """ 1095 type_id >= 70 1096 """ 1097 1098 name_struct = "living_unit" 1099 name_struct_file = "unit" 1100 struct_description = "adds creation location and garrison unit properties." 1101 1102 data_format = [ 1103 (READ_EXPORT, None, IncludeMembers(cls=ProjectileUnit)), 1104 (READ_EXPORT, "resource_cost", SubdataMember( 1105 ref_type=ResourceCost, 1106 length=3, 1107 )), 1108 (READ_EXPORT, "creation_time", "int16_t"), # in seconds 1109 (READ_EXPORT, "creation_location_id", "int16_t"), # e.g. 118 = villager 1110 1111 # where to place the button with the given icon 1112 # creation page: 1113 # +------------------------+ 1114 # | 01 | 02 | 03 | 04 | 05 | 1115 # |----|----|----|----|----| 1116 # | 06 | 07 | 08 | 09 | 10 | 1117 # |----|----|----|----|----| 1118 # | 11 | 12 | 13 | 14 | 15 | 1119 # +------------------------+ 1120 # 1121 # additional page (dock): 1122 # +------------------------+ 1123 # | 21 | 22 | 23 | 24 | 25 | 1124 # |----|----|----|----|----| 1125 # | 26 | 27 | 28 | 29 | 30 | 1126 # |----|----|----|----|----| 1127 # | 31 | 32 | 33 | 34 | 35 | 1128 # +------------------------+ 1129 (READ, "creation_button_id", "int8_t"), 1130 ] 1131 1132 # TODO: Enable conversion for AOE1; replace 13 values below 1133 # =========================================================================== 1134 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 1135 # data_format.extend([( 1136 # (READ, "rear_attack_modifier", "float"), 1137 # (READ, "flank_attack_modifier", "float"), 1138 # (READ_EXPORT, "creatable_type", EnumLookupMember( 1139 # raw_type = "int8_t", 1140 # type_name = "creatable_types", 1141 # lookup_dict = { 1142 # 0: "NONHUMAN", # building, animal, ship 1143 # 1: "VILLAGER", # villager, king 1144 # 2: "MELEE", # soldier, siege, predator, trader 1145 # 3: "MOUNTED", # camel rider 1146 # 4: "RELIC", 1147 # 5: "RANGED_PROJECTILE", # archer 1148 # 6: "RANGED_MAGIC", # monk 1149 # 21: "TRANSPORT_SHIP", 1150 # }, 1151 # )), 1152 # (READ, "hero_mode", "int8_t"), # if building: "others" tab in editor, if living unit: "heroes" tab, regenerate health + monk immunity 1153 # (READ_EXPORT, "garrison_graphic", "int32_t"), # graphic to display when units are garrisoned 1154 # (READ, "attack_projectile_count", "float"), # projectile count when nothing garrisoned, including both normal and duplicated projectiles 1155 # (READ, "attack_projectile_max_count", "int8_t"), # total projectiles when fully garrisoned 1156 # (READ, "attack_projectile_spawning_area_width", "float"), 1157 # (READ, "attack_projectile_spawning_area_length", "float"), 1158 # (READ, "attack_projectile_spawning_area_randomness", "float"), # placement randomness, 0=from single spot, 1=random, 1<less random 1159 # (READ, "attack_projectile_secondary_unit_id", "int32_t"), # uses its own attack values 1160 # (READ, "special_graphic_id", "int32_t"), # used just before unit reaches its target enemy, configuration: 1161 # (READ, "special_activation", "int8_t"), # determines adjacent unit graphics, if 1: building can adapt graphics by adjacent buildings 1162 # # 0: default: only works when facing the hit angle. 1163 # # 1: block: activates special graphic when receiving damage and not pursuing the attacker. 1164 # # while idle, blocking decreases damage taken by 1/3. 1165 # # also: a wall changes the graphics (when not-an-end piece) because of this. 1166 # # 2: counter charge: activates special graphic when idle and enemy is near. 1167 # # while idle, attacks back once on first received hit. 1168 # # enemy must be unit type 70 and have less than 0.2 max range. 1169 # # 3: charge: activates special graphic when closer than two tiles to the target. 1170 # # deals 2X damage on 1st hit 1171 # ]) 1172 # =========================================================================== 1173 data_format.extend([ 1174 (READ, "rear_attack_modifier", "float"), 1175 (READ, "flank_attack_modifier", "float"), 1176 (READ_EXPORT, "creatable_type", EnumLookupMember( 1177 raw_type = "int8_t", 1178 type_name = "creatable_types", 1179 lookup_dict = { 1180 0: "NONHUMAN", # building, animal, ship 1181 1: "VILLAGER", # villager, king 1182 2: "MELEE", # soldier, siege, predator, trader 1183 3: "MOUNTED", # camel rider 1184 4: "RELIC", 1185 5: "RANGED_PROJECTILE", # archer 1186 6: "RANGED_MAGIC", # monk 1187 21: "TRANSPORT_SHIP", 1188 }, 1189 )), 1190 (READ, "hero_mode", "int8_t"), # if building: "others" tab in editor, if living unit: "heroes" tab, regenerate health + monk immunity 1191 (READ_EXPORT, "garrison_graphic", "int32_t"), # graphic to display when units are garrisoned 1192 (READ, "attack_projectile_count", "float"), # projectile count when nothing garrisoned, including both normal and duplicated projectiles 1193 (READ, "attack_projectile_max_count", "int8_t"), # total projectiles when fully garrisoned 1194 (READ, "attack_projectile_spawning_area_width", "float"), 1195 (READ, "attack_projectile_spawning_area_length", "float"), 1196 (READ, "attack_projectile_spawning_area_randomness", "float"), # placement randomness, 0=from single spot, 1=random, 1<less random 1197 (READ, "attack_projectile_secondary_unit_id", "int32_t"), # uses its own attack values 1198 (READ, "special_graphic_id", "int32_t"), # used just before unit reaches its target enemy, configuration: 1199 (READ, "special_activation", "int8_t"), # determines adjacent unit graphics, if 1: building can adapt graphics by adjacent buildings 1200 # 0: default: only works when facing the hit angle. 1201 # 1: block: activates special graphic when receiving damage and not pursuing the attacker. 1202 # while idle, blocking decreases damage taken by 1/3. 1203 # also: a wall changes the graphics (when not-an-end piece) because of this. 1204 # 2: counter charge: activates special graphic when idle and enemy is near. 1205 # while idle, attacks back once on first received hit. 1206 # enemy must be unit type 70 and have less than 0.2 max range. 1207 # 3: charge: activates special graphic when closer than two tiles to the target. 1208 # deals 2X damage on 1st hit 1209 ]) 1210 # =========================================================================== 1211 1212 data_format.append((READ, "pierce_armor_displayed", "int16_t")) # unit stats display of pierce armor 1213 1214 1215class BuildingUnit(LivingUnit): 1216 """ 1217 type_id >= 80 1218 """ 1219 1220 name_struct = "building_unit" 1221 name_struct_file = "unit" 1222 struct_description = "construction graphics and garrison building properties for units." 1223 1224 data_format = [ 1225 (READ_EXPORT, None, IncludeMembers(cls=LivingUnit)), 1226 (READ_EXPORT, "construction_graphic_id", "int16_t"), 1227 ] 1228 1229 # TODO: Enable conversion for AOE1; replace "snow_graphic_id" 1230 # =========================================================================== 1231 # if (GameVersion.aoe_1 or GameVersion.aoe_ror or age2_aok) not in game_versions: 1232 # data_format.append((READ, "snow_graphic_id", "int16_t")) 1233 # =========================================================================== 1234 data_format.append((READ, "snow_graphic_id", "int16_t")) 1235 1236 data_format.extend([ 1237 (READ, "adjacent_mode", "int8_t"), # 1=adjacent units may change the graphics 1238 (READ, "graphics_angle", "int16_t"), 1239 (READ, "disappears_when_built", "int8_t"), 1240 (READ_EXPORT, "stack_unit_id", "int16_t"), # second building to place directly on top 1241 (READ_EXPORT, "foundation_terrain_id", "int16_t"), # change underlying terrain to this id when building completed 1242 (READ, "old_overlay_id", "int16_t"), # deprecated terrain-like structures knowns as "Overlays" from alpha AOE used for roads 1243 (READ, "research_id", "int16_t"), # research_id to be enabled when building creation 1244 ]) 1245 1246 # TODO: Enable conversion for AOE1; replace 5 values below 1247 # =========================================================================== 1248 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 1249 # data_format.extend([ 1250 # (READ, "can_burn", "int8_t"), 1251 # (READ_EXPORT, "building_annex", SubdataMember( 1252 # ref_type=BuildingAnnex, 1253 # length=4 1254 # )), 1255 # (READ, "head_unit_id", "int16_t"), # building at which an annex building is attached to 1256 # (READ, "transform_unit_id", "int16_t"), # destination unit id when unit shall transform (e.g. unpack) 1257 # (READ, "transform_sound_id", "int16_t"), 1258 # ]) 1259 # =========================================================================== 1260 data_format.extend([ 1261 (READ, "can_burn", "int8_t"), 1262 (READ_EXPORT, "building_annex", SubdataMember( 1263 ref_type=BuildingAnnex, 1264 length=4 1265 )), 1266 (READ, "head_unit_id", "int16_t"), # building at which an annex building is attached to 1267 (READ, "transform_unit_id", "int16_t"), # destination unit id when unit shall transform (e.g. unpack) 1268 (READ, "transform_sound_id", "int16_t"), 1269 ]) 1270 # =========================================================================== 1271 1272 data_format.append((READ, "construction_sound_id", "int16_t")) 1273 1274 # TODO: Enable conversion for AOE1; replace 5 values below 1275 # =========================================================================== 1276 # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions: 1277 # data_format.extend([ 1278 # (READ_EXPORT, "garrison_type", EnumLookupMember( 1279 # raw_type = "int8_t", 1280 # type_name = "garrison_types", 1281 # lookup_dict = { # TODO: create bitfield 1282 # 0x00: "NONE", 1283 # 0x01: "VILLAGER", 1284 # 0x02: "INFANTRY", 1285 # 0x04: "CAVALRY", 1286 # 0x08: "MONK", 1287 # 0x0b: "NOCAVALRY", 1288 # 0x0f: "ALL", 1289 # }, 1290 # )), 1291 # (READ, "garrison_heal_rate", "float"), 1292 # (READ, "garrison_repair_rate", "float"), 1293 # (READ, "salvage_unit_id", "int16_t"), # id of the unit used for salvages 1294 # (READ, "salvage_attributes", "int8_t[6]"), # list of attributes for salvages (looting table) 1295 # ]) 1296 # =========================================================================== 1297 data_format.extend([ 1298 (READ_EXPORT, "garrison_type", EnumLookupMember( 1299 raw_type = "int8_t", 1300 type_name = "garrison_types", 1301 lookup_dict = { # TODO: create bitfield 1302 0x00: "NONE", 1303 0x01: "VILLAGER", 1304 0x02: "INFANTRY", 1305 0x04: "CAVALRY", 1306 0x08: "MONK", 1307 0x0b: "NOCAVALRY", 1308 0x0f: "ALL", 1309 }, 1310 )), 1311 (READ, "garrison_heal_rate", "float"), 1312 (READ, "garrison_repair_rate", "float"), 1313 (READ, "salvage_unit_id", "int16_t"), # id of the unit used for salvages 1314 (READ, "salvage_attributes", "int8_t[6]"), # list of attributes for salvages (looting table) 1315 ]) 1316 # =========================================================================== 1317 1318 1319# unit type id => human readable name 1320# used as member name in the resulting struct 1321unit_type_lookup = { 1322 10: "object", 1323 20: "animated", 1324 25: "doppelganger", 1325 30: "moving", 1326 40: "action", 1327 60: "missile", 1328 70: "living", 1329 80: "building", 1330 90: "tree", 1331} 1332 1333 1334# name => attribute class 1335unit_type_class_lookup = { 1336 "object": UnitObject, 1337 "animated": AnimatedUnit, 1338 "doppelganger": DoppelgangerUnit, 1339 "moving": MovingUnit, 1340 "action": ActionUnit, 1341 "missile": MissileUnit, 1342 "living": LivingUnit, 1343 "building": BuildingUnit, 1344 "tree": TreeUnit, 1345} 1346