void InitialGameStateExtractor::extractBuildings(GameState &state, UString bldFileName, sp<City> city) { auto &data = this->ufo2p; auto fileName = "xcom3/ufodata/" + bldFileName + ".bld"; auto inFile = fw().data->fs.open(fileName); if (!inFile) { LogError("Failed to open \"%s\"", fileName.c_str()); } auto fileSize = inFile.size(); auto bldCount = fileSize / sizeof(struct bld_file_entry); LogInfo("Loading %lu buildings from %s", (unsigned long)bldCount, fileName.c_str()); for (unsigned i = 0; i < bldCount; i++) { struct bld_file_entry entry; inFile.read((char *)&entry, sizeof(entry)); auto b = mksp<Building>(); b->name = data.building_names->get(entry.name_idx); b->owner = {&state, data.get_org_id(entry.owner_idx)}; // Our rects are exclusive of p2 b->bounds = {entry.x0, entry.y0, entry.x1 + 1, entry.y1 + 1}; auto id = UString::format("%s%s", Building::getPrefix().c_str(), canon_string(b->name).c_str()); city->buildings[id] = b; } }
void InitialGameStateExtractor::extractBuildingFunctions(GameState &state) const { auto &data = this->ufo2p; for (unsigned i = 0; i < data.building_functions->count(); i++) { auto f = mksp<BuildingFunction>(); f->name = data.building_functions->get(i); if (i < data.infiltration_speed_building->count()) { f->infiltrationSpeed = data.infiltration_speed_building->get(i).speed; } if (i < buildingFunctionDetectionWeights.size()) { f->detectionWeight = buildingFunctionDetectionWeights[i]; } auto id = format("%s%s", BuildingFunction::getPrefix(), canon_string(f->name)); auto ped = format("%s%s", UfopaediaEntry::getPrefix(), canon_string(f->name)); f->ufopaedia_entry = {&state, ped}; state.building_functions[id] = f; } }
void InitialGameStateExtractor::extractBuildings(GameState &state, UString bldFileName, sp<City> city, bool alienBuilding) { auto &data = this->ufo2p; auto fileName = "xcom3/ufodata/" + bldFileName + ".bld"; auto inFile = fw().data->fs.open(fileName); if (!inFile) { LogError("Failed to open \"%s\"", fileName.cStr()); } auto fileSize = inFile.size(); auto bldCount = fileSize / sizeof(struct BldFileEntry); LogInfo("Loading %lu buildings from %s", (unsigned long)bldCount, fileName.cStr()); for (unsigned i = 0; i < bldCount; i++) { struct BldFileEntry entry; inFile.read((char *)&entry, sizeof(entry)); auto b = mksp<Building>(); if (alienBuilding) { LogInfo("Alien bld %d func %d", entry.name_idx, entry.function_idx); // FIXME: albld.bld seems to have unexpected name_idx and function_idx? b->name = data.alien_building_names->get(i); b->function = b->name; } else { b->name = data.building_names->get(entry.name_idx); b->function = data.building_functions->get(entry.function_idx); } b->owner = {&state, data.getOrgId(entry.owner_idx)}; // Our rects are exclusive of p2 // Shift position by 20 tiles b->bounds = {entry.x0 + 20, entry.y0 + 20, entry.x1 + 21, entry.y1 + 21}; auto id = UString::format("%s%s", Building::getPrefix(), canon_string(b->name)); city->buildings[id] = b; } }
void InitialGameStateExtractor::extractBuildings(GameState &state, UString bldFileName, sp<City> city, bool alienBuilding) const { auto &data = this->ufo2p; auto fileName = "xcom3/ufodata/" + bldFileName + ".bld"; auto inFile = fw().data->fs.open(fileName); if (!inFile) { LogError("Failed to open \"%s\"", fileName); } auto fileSize = inFile.size(); auto bldCount = fileSize / sizeof(struct BldFileEntry); LogInfo("Loading %lu buildings from %s", (unsigned long)bldCount, fileName); for (unsigned i = 0; i < bldCount; i++) { struct BldFileEntry entry; inFile.read((char *)&entry, sizeof(entry)); auto b = mksp<Building>(); if (alienBuilding) { b->name = data.alien_building_names->get(entry.function_idx); b->function = {&state, format("%s%s", BuildingFunction::getPrefix(), canon_string(b->name))}; LogInfo("Alien bld %d %s func %d %s", entry.name_idx, b->name, entry.function_idx, b->function.id); b->accessTopic = {&state, format("RESEARCH_UNLOCK_ALIEN_BUILDING_%d", i)}; if (i < 9) { b->researchUnlock.emplace_back(&state, format("RESEARCH_UNLOCK_ALIEN_BUILDING_%d", i + 1)); } else { b->victory = true; } // Load crew auto crew = ufo2p.crew_alien_building->get(entry.function_idx); UFO2P::fillCrew(state, crew, b->preset_crew); } else { b->name = data.building_names->get(entry.name_idx); b->function = {&state, format("%s%s", BuildingFunction::getPrefix(), canon_string(data.building_functions->get(entry.function_idx)))}; } int battle_map_index = entry.function_idx - 1 + (alienBuilding ? 39 : 0); // Fix battle index for buildings that use other maps switch (battle_map_index) { // 24 Car Factory uses 25 Flying Car Factory case 23: battle_map_index = 24; break; // 13 Car Park is not used in vanilla // Still, we should provide a reasonable substitute case 12: battle_map_index = 10; // 11 Procreation Park break; // 16 Hotel is not used in vanilla // Still, we should provide a reasonable substitute case 15: battle_map_index = 14; // 15 Luxury Appartments break; // 17 Atmosphere Processor is not used in vanilla // Still, we should provide a reasonable substitute case 16: battle_map_index = 19; // 20 Water Purifier break; // 29 Ruins is not used in vanilla // Still, we should provide a reasonable substitute case 28: battle_map_index = 10; // 11 Procreation Park break; // 31 Space Ship is not used in vanilla // Still, we should provide a reasonable substitutecase 31: battle_map_index = 7; // 08 Space Port break; // 34 Outdoor Parks is not used in vanilla // Still, we should provide a reasonable substitute case 33: battle_map_index = 10; // 11 Procreation Park break; default: break; } b->battle_map = { &state, format("%s%s", BattleMap::getPrefix(), this->battleMapPaths[battle_map_index])}; b->owner = {&state, data.getOrgId(entry.owner_idx)}; // Our rects are exclusive of p2 // Shift position by 20 tiles b->bounds = {entry.x0 + 20, entry.y0 + 20, entry.x1 + 21, entry.y1 + 21}; auto id = format("%s%s", Building::getPrefix(), canon_string(b->name)); b->city = {&state, city->id}; city->buildings[id] = b; } }
void InitialGameStateExtractor::extractResearch(GameState &state, Difficulty) { auto &data = this->ufo2p; for (unsigned i = 0; i < data.research_data->count(); i++) { auto rdata = data.research_data->get(i); auto r = mksp<ResearchTopic>(); r->name = data.research_names->get(i); auto id = ResearchTopic::getPrefix() + canon_string(r->name); r->description = data.research_descriptions->get(i); r->ufopaedia_entry = ""; r->man_hours = rdata.skillHours; r->man_hours_progress = 0; switch (rdata.researchGroup) { case 0: r->type = ResearchTopic::Type::BioChem; break; case 1: r->type = ResearchTopic::Type::Physics; break; default: LogError("Unexpected researchGroup 0x%02x for research item %s", (unsigned)rdata.researchGroup, id.cStr()); } switch (rdata.labSize) { case 0: r->required_lab_size = ResearchTopic::LabSize::Small; break; case 1: r->required_lab_size = ResearchTopic::LabSize::Large; break; default: LogError("Unexpected labSize 0x%02x for research item %s", (unsigned)rdata.labSize, id.cStr()); } // FIXME: this assumed all listed techs are reqired, which is not true for some topics // (It's possible that an unknown member in ResearchData marks this, or it's done // in-code) // This should be fixed up in the patch. ResearchDependency dependency; dependency.type = ResearchDependency::Type::All; for (int pre = 0; pre < 3; pre++) { if (rdata.prereqTech[pre] != 0xffff) { auto prereqId = ResearchTopic::getPrefix() + canon_string(data.research_names->get(rdata.prereqTech[pre])); dependency.topics.emplace(StateRef<ResearchTopic>{&state, prereqId}); } } r->dependencies.research.push_back(dependency); r->score = rdata.score; if (state.research.topics.find(id) != state.research.topics.end()) { LogError("Multiple research topics with ID \"%s\"", id.cStr()); } state.research.topics[id] = r; // FIXME: The ufopaedia entries here don't seem to directly map to the IDs we're currently using? // May also be a many:1 ratio (e.g. the "alien gas" research topic unlocks multiple ufopaedia // entries) making this more complex #if 0 auto ufopaediaEntryID = "PAEDIAENTRY_" + canon_string(r->name); auto ufopaediaCatID = "PAEDIACATEGORY_" + canon_string(data.ufopaedia_group->get(rdata.ufopaediaGroup)); auto paediaCat = state.ufopaedia[ufopaediaCatID]; if (!paediaCat) { state.ufopaedia[ufopaediaCatID] = mksp<UfopaediaCategory>(); paediaCat = state.ufopaedia[ufopaediaCatID]; } auto paediaEntry = paediaCat->entries[ufopaediaEntryID]; if (!paediaEntry) { paediaCat->entries[ufopaediaEntryID] = mksp<UfopaediaEntry>(); paediaEntry = paediaCat->entries[ufopaediaEntryID]; } if (paediaEntry->required_research) { LogError("Multiple required research for UFOPaedia topic \"%s\" - \"%s\" and \"%s\"", ufopaediaEntryID.cStr(), r->name.cStr(), paediaEntry->required_research->name.cStr()); } paediaEntry->required_research = {&state, id}; #endif } state.research.updateTopicList(); }
void InitialGameStateExtractor::extractBuildings(GameState &state, UString bldFileName, sp<City> city, bool alienBuilding) const { auto &data = this->ufo2p; auto fileName = "xcom3/ufodata/" + bldFileName + ".bld"; auto inFile = fw().data->fs.open(fileName); if (!inFile) { LogError("Failed to open \"%s\"", fileName); } auto fileSize = inFile.size(); auto bldCount = fileSize / sizeof(struct BldFileEntry); LogInfo("Loading %lu buildings from %s", (unsigned long)bldCount, fileName); for (unsigned i = 0; i < bldCount; i++) { struct BldFileEntry entry; inFile.read((char *)&entry, sizeof(entry)); auto b = mksp<Building>(); if (alienBuilding) { LogInfo("Alien bld %d func %d", entry.name_idx, entry.function_idx); // FIXME: albld.bld seems to have unexpected name_idx and function_idx? b->name = data.alien_building_names->get(i); b->function = b->name; } else { b->name = data.building_names->get(entry.name_idx); b->function = data.building_functions->get(entry.function_idx); } int battle_map_index = entry.function_idx - 1 + (alienBuilding ? 39 : 0); // Fix battle index for buildings that use other maps switch (battle_map_index) { // 24 Car Factory uses 25 Flying Car Factory case 23: battle_map_index = 24; break; // 13 Car Park is not used in vanilla // Still, we should provide a reasonable substitute case 12: battle_map_index = 10; // 11 Procreation Park break; // 16 Hotel is not used in vanilla // Still, we should provide a reasonable substitute case 15: battle_map_index = 14; // 15 Luxury Appartments break; // 17 Atmosphere Processor is not used in vanilla // Still, we should provide a reasonable substitute case 16: battle_map_index = 19; // 20 Water Purifier break; // 29 Ruins is not used in vanilla // Still, we should provide a reasonable substitute case 28: battle_map_index = 10; // 11 Procreation Park break; // 31 Space Ship is not used in vanilla // Still, we should provide a reasonable substitutecase 31: battle_map_index = 7; // 08 Space Port break; // 34 Outdoor Parks is not used in vanilla // Still, we should provide a reasonable substitute case 33: battle_map_index = 10; // 11 Procreation Park break; } b->battle_map = { &state, format("%s%s", BattleMap::getPrefix(), this->battleMapPaths[battle_map_index])}; b->owner = {&state, data.getOrgId(entry.owner_idx)}; // Our rects are exclusive of p2 // Shift position by 20 tiles b->bounds = {entry.x0 + 20, entry.y0 + 20, entry.x1 + 21, entry.y1 + 21}; auto id = format("%s%s", Building::getPrefix(), canon_string(b->name)); city->buildings[id] = b; } }
int main(int argc, char **argv) { UFO2P data; tinyxml2::XMLDocument doc; doc.InsertFirstChild(doc.NewDeclaration()); auto *node = doc.NewElement("openapoc"); doc.InsertEndChild(node); auto *parentNode = node; node = doc.NewElement("vehicles"); parentNode->InsertFirstChild(node); parentNode = node; std::cerr << "Number of vehicle strings: " << data.vehicle_names->readStrings.size() << "\n"; for (int i = 0; i < data.vehicle_data->count(); i++) { auto v = data.vehicle_data->get(i); node = doc.NewElement("vehicle"); parentNode->InsertEndChild(node); std::string id = canon_string(data.vehicle_names->get(i)); node->SetAttribute("id", id.c_str()); node->SetAttribute("name", std::string("STR_" + id + "_NAME").c_str()); node->SetAttribute("manufacturer", canon_string(data.organisation_names->get(v.manufacturer)).c_str()); if (v.movement_type == 0) { node->SetAttribute("type", "GROUND"); } else if (v.movement_type == 1) { if (v.animation_type == 0) { node->SetAttribute("type", "UFO"); int animFrames = UFOAnimationFrames[id]; auto *animNode = doc.NewElement("animation"); node->InsertEndChild(animNode); for (int i = 0; i < animFrames; i++) { auto *frameNode = doc.NewElement("frame"); animNode->InsertEndChild(frameNode); std::stringstream ss; ss << "PCK:UFODATA/SAUCER.PCK:UFODATA/SAUCER.TAB:" << std::dec << (v.graphic_frame + i) << ":UFODATA/PAL_01.DAT"; frameNode->SetText(ss.str().c_str()); } auto *crashNode = doc.NewElement("crashed"); node->InsertEndChild(crashNode); std::stringstream ss; ss << "PCK:UFODATA/SAUCER.PCK:UFODATA/SAUCER.TAB:" << std::dec << (v.graphic_frame + animFrames) << ":UFODATA/PAL_01.DAT"; crashNode->SetText(ss.str().c_str()); } else { node->SetAttribute("type", "FLYING"); std::vector<std::string> direction = {"N","NE","E","SE","S","SW","W","NW"}; std::vector<std::string> banking = {"flat","decending","ascending","banking_right","banking_left"}; int image_offset = 0; for (auto &bank : banking) { auto *bankNode = doc.NewElement(bank.c_str()); node->InsertEndChild(bankNode); for (auto &dir : direction) { std::stringstream ss; ss << "PCK:UFODATA/SAUCER.PCK:UFODATA/SAUCER.TAB:" << std::dec << (v.graphic_frame + image_offset++) << ":UFODATA/PAL_01.DAT"; auto *dirNode = doc.NewElement(dir.c_str()); bankNode->InsertEndChild(dirNode); dirNode->SetText(ss.str().c_str()); } //XXX HACK - The space liner doesn't have banking/ascending/decendimg images if (id == std::string("SPACE_LINER")) break; } } } else { node->SetAttribute("type", "UNKNOWN"); } } doc.SaveFile("vehicles.xml"); doc.Clear(); doc.InsertFirstChild(doc.NewDeclaration()); node = doc.NewElement("openapoc"); doc.InsertEndChild(node); parentNode = node; for (int i = 0; i < data.vehicle_data->count(); i++) { std::string id = canon_string(data.vehicle_names->get(i)); node = doc.NewElement("string"); parentNode->InsertEndChild(node); node->SetAttribute("id", std::string("STR_" + id + "_NAME").c_str()); auto *enNode = doc.NewElement("en"); node->InsertFirstChild(enNode); enNode->SetText(data.vehicle_names->get(i).c_str()); } doc.SaveFile("vehicles_strings.xml"); return 0; }
void InitialGameStateExtractor::extractAgentTypes(GameState &state, Difficulty) { const int HUMAN_FEMALE_PORTRAIT_START = 0; const int HUMAN_FEMALE_PORTRAIT_END = 30; // const int HYBRID_FEMALE_PORTRAIT_START = 30; // const int HYBRID_FEMALE_PORTRAIT_END = 35; const int HUMAN_MALE_PORTRAIT_START = 35; const int HUMAN_MALE_PORTRAIT_END = 65; // const int HYBRID_MALE_PORTRAIT_START = 65; // const int HYBRID_MALE_PORTRAIT_END = 70; // const int ANDROID_MALE_PORTRAIT_START = 70; // const int ANDROID_MALE_PORTRAIT_END = 75; const int ALIEN_PORTRAIT_OFFSET = 74; const int UNIT_TYPE_BIOCHEMIST = 1; const int UNIT_TYPE_ENGINEER = 2; const int UNIT_TYPE_QUANTUM_PHYSIST = 3; const int UNIT_TYPE_UPPER_CLASS_FEMALE_1 = 16; const int UNIT_TYPE_UPPER_CLASS_FEMALE_2 = 17; const int UNIT_TYPE_UPPER_CLASS_FEMALE_3 = 18; const int UNIT_TYPE_CIVILIAN_FEMALE_1 = 22; const int UNIT_TYPE_CIVILIAN_FEMALE_2 = 23; const int UNIT_TYPE_CIVILIAN_FEMALE_3 = 24; const int UNIT_TYPE_LOWER_CLASS_FEMALE_1 = 31; const int UNIT_TYPE_LOWER_CLASS_FEMALE_2 = 32; const int UNIT_TYPE_LOWER_CLASS_FEMALE_3 = 33; const int UNIT_TYPE_FIRST_ALIEN_ENTRY = 34; const UString loftempsFile = "xcom3/tacdata/loftemps.dat"; const UString loftempsTab = "xcom3/tacdata/loftemps.tab"; auto &data_t = this->tacp; auto &data_u = this->ufo2p; // Portraits auto portraitSmallTabFileName = UString("xcom3/ufodata/agntico.tab"); auto portraitSmallTabFile = fw().data->fs.open(portraitSmallTabFileName); if (!portraitSmallTabFile) { LogError("Failed to open small portrait TAB file \"%s\"", portraitSmallTabFileName.cStr()); return; } size_t portraitSmallCount = portraitSmallTabFile.size() / 4; auto portraitLargeTabFileName = UString("xcom3/ufodata/photo.tab"); auto portraitLargeTabFile = fw().data->fs.open(portraitLargeTabFileName); if (!portraitLargeTabFile) { LogError("Failed to open Large portrait TAB file \"%s\"", portraitLargeTabFileName.cStr()); return; } size_t portraitLargeCount = portraitLargeTabFile.size() / 4; std::vector<AgentPortrait> portraits; for (unsigned i = 0; i < portraitSmallCount; i++) { auto p = AgentPortrait(); p.icon = fw().data->loadImage(UString::format( "PCK:xcom3/ufodata/agntico.pck:xcom3/ufodata/agntico.tab:%d:xcom3/ufodata/pal_01.dat", i)); if (i < portraitLargeCount) p.photo = fw().data->loadImage(UString::format( "PCK:xcom3/ufodata/photo.pck:xcom3/ufodata/photo.tab:%d:xcom3/ufodata/pal_01.dat", i)); portraits.push_back(p); } // Unit Types // X-Com Agents are hand-filled in the patch xml, because they're not stored in the files // Therefore, we skip #0 for (unsigned i = 1; i < data_u.agent_types->count(); i++) { auto a = mksp<AgentType>(); auto data = data_u.agent_types->get(i); a->name = data_u.agent_type_names->get(i); UString id = UString::format("%s%s", AgentType::getPrefix(), canon_string(a->name)); a->id = id; switch (i) { case UNIT_TYPE_BIOCHEMIST: a->role = AgentType::Role::BioChemist; a->playable = true; break; case UNIT_TYPE_ENGINEER: a->role = AgentType::Role::Engineer; a->playable = true; break; case UNIT_TYPE_QUANTUM_PHYSIST: a->role = AgentType::Role::Physicist; a->playable = true; break; default: a->role = AgentType::Role::Soldier; a->playable = false; break; } switch (i) { case UNIT_TYPE_BIOCHEMIST: case UNIT_TYPE_ENGINEER: case UNIT_TYPE_QUANTUM_PHYSIST: a->possible_genders.insert(a->possible_genders.end(), AgentType::Gender::Male); a->possible_genders.insert(a->possible_genders.end(), AgentType::Gender::Female); a->gender_chance[AgentType::Gender::Male] = 1; a->gender_chance[AgentType::Gender::Female] = 1; for (unsigned p = HUMAN_MALE_PORTRAIT_START; p < HUMAN_MALE_PORTRAIT_END; p++) a->portraits[AgentType::Gender::Male][p - HUMAN_MALE_PORTRAIT_START] = portraits[p]; for (unsigned p = HUMAN_FEMALE_PORTRAIT_START; p < HUMAN_FEMALE_PORTRAIT_END; p++) a->portraits[AgentType::Gender::Female][p - HUMAN_FEMALE_PORTRAIT_START] = portraits[p]; break; case UNIT_TYPE_UPPER_CLASS_FEMALE_1: case UNIT_TYPE_UPPER_CLASS_FEMALE_2: case UNIT_TYPE_UPPER_CLASS_FEMALE_3: case UNIT_TYPE_CIVILIAN_FEMALE_1: case UNIT_TYPE_CIVILIAN_FEMALE_2: case UNIT_TYPE_CIVILIAN_FEMALE_3: case UNIT_TYPE_LOWER_CLASS_FEMALE_1: case UNIT_TYPE_LOWER_CLASS_FEMALE_2: case UNIT_TYPE_LOWER_CLASS_FEMALE_3: a->possible_genders.insert(a->possible_genders.end(), AgentType::Gender::Female); a->gender_chance[AgentType::Gender::Female] = 1; a->portraits[AgentType::Gender::Female][0] = portraits[data.image]; break; default: a->possible_genders.insert(a->possible_genders.end(), AgentType::Gender::Male); a->gender_chance[AgentType::Gender::Male] = 1; // Aliens start at id 34, their portraits start at 75, // first alien has image index of 1 rather than 0, so we shift by 74 if (i >= UNIT_TYPE_FIRST_ALIEN_ENTRY) a->portraits[AgentType::Gender::Male][0] = portraits[data.image + ALIEN_PORTRAIT_OFFSET]; else a->portraits[AgentType::Gender::Male][0] = portraits[data.image]; break; } a->min_stats.accuracy = 100 - data.accuracy_base - data.accuracy_inc; a->max_stats.accuracy = 100 - data.accuracy_base; a->min_stats.biochem_skill = data.biochemistry_base; a->max_stats.biochem_skill = data.biochemistry_base + data.biochemistry_inc; a->min_stats.bravery = data.bravery_base * 10; a->max_stats.bravery = data.bravery_base * 10 + data.bravery_inc * 10; a->min_stats.engineering_skill = data.engineering_base; a->max_stats.engineering_skill = data.engineering_base + data.engineering_inc; a->min_stats.health = data.health_base; a->max_stats.health = data.health_base + data.health_inc; a->min_stats.physics_skill = data.quantum_physics_base; a->max_stats.physics_skill = data.quantum_physics_base + data.quantum_physics_inc; a->min_stats.psi_attack = data.psi_attack_base; a->max_stats.psi_attack = data.psi_attack_base + data.psi_attack_inc; a->min_stats.psi_defence = data.psi_defense_base; a->max_stats.psi_defence = data.psi_defense_base + data.psi_defense_inc; a->min_stats.psi_energy = data.psi_energy_base; a->max_stats.psi_energy = data.psi_energy_base + data.psi_energy_inc; a->min_stats.reactions = data.reactions_base; a->max_stats.reactions = data.reactions_base + data.reactions_inc; a->min_stats.speed = data.speed_base; a->max_stats.speed = data.speed_base + data.speed_inc; a->min_stats.stamina = data.stamina_base; a->max_stats.stamina = data.stamina_base + data.stamina_inc; a->min_stats.strength = data.strength_base; a->max_stats.strength = data.strength_base + data.strength_inc; switch (data.movement_type) { case AGENT_MOVEMENT_TYPE_STATIONARY: a->movement_type = AgentType::MovementType::Stationary; break; case AGENT_MOVEMENT_TYPE_STANDART: a->movement_type = AgentType::MovementType::Standart; break; case AGENT_MOVEMENT_TYPE_FLYING: a->movement_type = AgentType::MovementType::Flying; break; case AGENT_MOVEMENT_TYPE_STANDART_LARGE: a->movement_type = AgentType::MovementType::StandartLarge; break; case AGENT_MOVEMENT_TYPE_FLYING_LARGE: a->movement_type = AgentType::MovementType::FlyingLarge; break; } a->large = (data.loftemps_height > 40) || (a->movement_type == AgentType::MovementType::StandartLarge) || (a->movement_type == AgentType::MovementType::FlyingLarge); // FIXME: Need to scale voxelmap to fit twice the size a->voxelMap = a->large ? mksp<VoxelMap>(Vec3<int>{48, 48, 40}) : mksp<VoxelMap>(Vec3<int>{24, 24, 20}); if (data.loftemps_idx != 0) { for (int slice = 0; slice < data.loftemps_height / 2; slice++) { auto lofString = UString::format("LOFTEMPS:%s:%s:%u", loftempsFile.cStr(), loftempsTab.cStr(), (unsigned int)data.loftemps_idx); a->voxelMap->slices[slice] = fw().data->loadVoxelSlice(lofString); } } a->armor[AgentType::BodyPart::Body] = data.armor_body; a->armor[AgentType::BodyPart::Helmet] = data.armor_head; a->armor[AgentType::BodyPart::LeftArm] = data.armor_left; a->armor[AgentType::BodyPart::Legs] = data.armor_leg; a->armor[AgentType::BodyPart::RightArm] = data.armor_right; a->damage_modifier = { &state, UString::format("%s%s", DamageModifier::getPrefix(), canon_string(data_t.damage_modifier_names->get(data.damage_modifier)))}; a->inventory = data.inventory == 1; if (!data.inventory) { if (data.equipment_sets[0] != 0xff) { // Equipment sets (built-in) have complex structure with several possible items and // weapons and clips defined, and unit types have 5 equipment sets to choose from, // but all that is irrelevant. Game only uses equipment sets for aliens who have no // inventory, they are never defined for anyone else without inventory, all 5 sets // are always the same and all sets only contain a built-in weapon which is always // self-recharging. Therefore, we only need to store built-in weapons. auto es_data = data_t.agent_equipment_set_built_in->get(data.equipment_sets[0]); if (es_data.weapons[0].weapon_idx != 0xffffffff) a->built_in_weapon_right = { &state, UString::format("%s%s", AEquipmentType::getPrefix(), canon_string(data_u.agent_equipment_names->get( es_data.weapons[0].weapon_idx)))}; if (es_data.weapons[1].weapon_idx != 0xffffffff) a->built_in_weapon_left = { &state, UString::format("%s%s", AEquipmentType::getPrefix(), canon_string(data_u.agent_equipment_names->get( es_data.weapons[1].weapon_idx)))}; } // FIXME: Fill layouts for units without inventory // That is, give them left and right hand slots! // std::list<EquipmentLayoutSlot> equipment_layout_slots; } else { // FIXME: Fill layouts for units with inventory // std::list<EquipmentLayoutSlot> equipment_layout_slots; } a->can_improve = false; a->score = data.score; // FIXME: Extract agent animation and frames state.agent_types[id] = a; } }