/** * Allocate a Structure. * * @param index The index to use, or STRUCTURE_INDEX_INVALID to find an unused index. * @param typeID The type of the new Structure. * @return The Structure allocated, or NULL on failure. */ Structure *Structure_Allocate(uint16 index, uint8 type) { Structure *s = NULL; switch (type) { case STRUCTURE_SLAB_1x1: index = STRUCTURE_INDEX_SLAB_1x1; s = Structure_Get_ByIndex(index); break; case STRUCTURE_SLAB_2x2: index = STRUCTURE_INDEX_SLAB_2x2; s = Structure_Get_ByIndex(index); break; case STRUCTURE_WALL: index = STRUCTURE_INDEX_WALL; s = Structure_Get_ByIndex(index); break; default: if (index == STRUCTURE_INDEX_INVALID) { /* Find the first unused index */ for (index = 0; index < STRUCTURE_INDEX_MAX_SOFT; index++) { s = Structure_Get_ByIndex(index); if (!s->o.flags.s.used) break; } if (index == STRUCTURE_INDEX_MAX_SOFT) return NULL; } else { s = Structure_Get_ByIndex(index); if (s->o.flags.s.used) return NULL; } g_structureFindArray[g_structureFindCount++] = s; break; } assert(s != NULL); /* Initialize the Structure */ memset(s, 0, sizeof(Structure)); s->o.index = index; s->o.type = type; s->o.linkedID = 0xFF; s->o.flags.s.used = true; s->o.flags.s.allocated = true; s->o.script.delay = 0; return s; }
/** * Check if the given tile is a valid destination. In case of for example * a carry-all it checks if the unit carrying can be placed on destination. * In case of structures, it checks if you can walk into it. * * Stack: 1 - An encoded tile, indicating the destination. * * @param script The script engine to operate on. * @return ??. */ uint16 Script_Unit_IsValidDestination(ScriptEngine *script) { Unit *u; Unit *u2; uint16 encoded; uint16 index; u = g_scriptCurrentUnit; encoded = STACK_PEEK(1); index = Tools_Index_Decode(encoded); switch (Tools_Index_GetType(encoded)) { case IT_TILE: if (!Map_IsValidPosition(index)) return 1; if (u->o.linkedID == 0xFF) return 1; u2 = Unit_Get_ByIndex(u->o.linkedID); u2->o.position = Tools_Index_GetTile(encoded); if (!Unit_IsTileOccupied(u2)) return 0; u2->o.position.tile = 0xFFFFFFFF; return 1; case IT_STRUCTURE: { Structure *s; s = Structure_Get_ByIndex(index); if (s->o.houseID == Unit_GetHouseID(u)) return 0; if (u->o.linkedID == 0xFF) return 1; u2 = Unit_Get_ByIndex(u->o.linkedID); return Unit_IsValidMovementIntoStructure(u2, s) != 0 ? 1 : 0; } default: return 1; } }
/** * @brief f__167E_0005_0013_AF0C. * @details Removed guard for IT_UNIT. */ bool Tools_Index_IsValid(uint16 encoded) { if (encoded == 0) return false; uint16 index = Tools_Index_Decode(encoded); switch (Tools_Index_GetType(encoded)) { case IT_UNIT: { const Unit* u = Unit_Get_ByIndex(index); return u->o.flags.s.used && u->o.flags.s.allocated; } case IT_STRUCTURE: { const Structure* s = Structure_Get_ByIndex(index); return s->o.flags.s.used; } case IT_TILE: case IT_NONE: default: return true; } return false; }
/** * Load all Structures from a file. * @param fp The file to load from. * @param length The length of the data chunk. * @return True if and only if all bytes were read successful. */ bool Structure_Load(FILE *fp, uint32 length) { while (length > 0) { Structure *s; Structure sl; memset(&sl, 0, sizeof(sl)); /* Read the next Structure from disk */ if (!SaveLoad_Load(s_saveStructure, fp, &sl)) return false; length -= SaveLoad_GetLength(s_saveStructure); sl.o.script.scriptInfo = g_scriptStructure; sl.o.script.script = g_scriptStructure->start + (size_t)sl.o.script.script; if (sl.upgradeTimeLeft == 0) sl.upgradeTimeLeft = Structure_IsUpgradable(&sl) ? 100 : 0; /* Get the Structure from the pool */ s = Structure_Get_ByIndex(sl.o.index); if (s == NULL) return false; /* Copy over the data */ *s = sl; } if (length != 0) return false; Structure_Recount(); return true; }
/** * Find the first matching Structure based on the PoolFindStruct filter data. * * @param find A pointer to a PoolFindStruct which contains filter data and * last known tried index. Calling this functions multiple times with the * same 'find' parameter walks over all possible values matching the filter. * @return The Structure, or NULL if nothing matches (anymore). */ Structure *Structure_Find(PoolFindStruct *find) { if (find->index >= g_structureFindCount + 3 && find->index != 0xFFFF) return NULL; find->index++; /* First, we always go to the next index */ assert(g_structureFindCount <= STRUCTURE_INDEX_MAX_SOFT); for (; find->index < g_structureFindCount + 3; find->index++) { Structure *s = NULL; if (find->index < g_structureFindCount) { s = g_structureFindArray[find->index]; } else { /* There are 3 special structures that are never in the Find array */ assert(find->index - g_structureFindCount < 3); switch (find->index - g_structureFindCount) { case 0: s = Structure_Get_ByIndex(STRUCTURE_INDEX_WALL); if (s->o.index != STRUCTURE_INDEX_WALL) continue; break; case 1: s = Structure_Get_ByIndex(STRUCTURE_INDEX_SLAB_2x2); if (s->o.index != STRUCTURE_INDEX_SLAB_2x2) continue; break; case 2: s = Structure_Get_ByIndex(STRUCTURE_INDEX_SLAB_1x1); if (s->o.index != STRUCTURE_INDEX_SLAB_1x1) continue; break; } } if (s == NULL) continue; if (s->o.flags.s.isNotOnMap && g_validateStrictIfZero == 0) continue; if (find->houseID != HOUSE_INVALID && find->houseID != s->o.houseID) continue; if (find->type != STRUCTURE_INDEX_INVALID && find->type != s->o.type) continue; return s; } return NULL; }
/** * Get the object on the given packed tile. * @param packed The packed tile to get the object from. * @return The object. */ Object* Object_GetByPackedTile(uint16 packed) { Tile* t; if (Tile_IsOutOfMap(packed)) return NULL; t = &g_map[packed]; if (t->hasUnit) return &Unit_Get_ByIndex(t->index - 1)->o; if (t->hasStructure) return &Structure_Get_ByIndex(t->index - 1)->o; return NULL; }
/** * Handles Click event for a sprite/text button. * * @param w The widget. * @return False, always. */ bool GUI_Widget_SpriteTextButton_Click(Widget *w) { Structure *s; VARIABLE_NOT_USED(w); s = Structure_Get_ByPackedTile(g_selectionPosition); switch (g_productionStringID) { default: break; case STR_PLACE_IT: if (s->o.type == STRUCTURE_CONSTRUCTION_YARD) { Structure *ns; ns = Structure_Get_ByIndex(s->o.linkedID); g_structureActive = ns; g_structureActiveType = s->objectType; g_selectionState = Structure_IsValidBuildLocation(g_selectionRectanglePosition, g_structureActiveType); g_structureActivePosition = g_selectionPosition; s->o.linkedID = STRUCTURE_INVALID; GUI_ChangeSelectionType(SELECTIONTYPE_PLACE); } break; case STR_ON_HOLD: s->o.flags.s.repairing = false; s->o.flags.s.onHold = false; s->o.flags.s.upgrading = false; break; case STR_BUILD_IT: Structure_BuildObject(s, s->objectType); break; case STR_LAUNCH: case STR_FREMEN: case STR_SABOTEUR: Structure_ActivateSpecial(s); break; case STR_D_DONE: s->o.flags.s.onHold = true; break; } return false; }
/** * Recount all Structures, ignoring the cache array. Also set the structureCount * of all houses to zero. */ void Structure_Recount(void) { uint16 index; PoolFindStruct find = { -1, -1, -1 }; House *h = House_Find(&find); while (h != NULL) { h->unitCount = 0; h = House_Find(&find); } g_structureFindCount = 0; for (index = 0; index < STRUCTURE_INDEX_MAX_SOFT; index++) { Structure *s = Structure_Get_ByIndex(index); if (s->o.flags.s.used) g_structureFindArray[g_structureFindCount++] = s; } }
static uint32 SaveLoad_StructureActive(void *object, uint32 value, bool loading) { VARIABLE_NOT_USED(object); if (loading) { if ((uint16)value != 0xFFFF) { g_structureActive = Structure_Get_ByIndex((uint16)value); } else { g_structureActive = NULL; } return 0; } if (g_structureActiveType != 0xFFFF) { return g_structureActive->o.index; } else { return 0xFFFF; } }
/** * Loop over all houses, preforming various of tasks. */ void GameLoop_House(void) { PoolFindStruct find; House *h = NULL; bool tickHouse = false; bool tickPowerMaintenance = false; bool tickStarport = false; bool tickReinforcement = false; bool tickMissileCountdown = false; bool tickStarportAvailability = false; if (g_debugScenario) return; if (s_tickHouseHouse <= g_timerGame) { tickHouse = true; s_tickHouseHouse = g_timerGame + 900; } if (g_tickHousePowerMaintenance <= g_timerGame) { tickPowerMaintenance = true; g_tickHousePowerMaintenance = g_timerGame + 10800; } if (s_tickHouseStarport <= g_timerGame) { tickStarport = true; s_tickHouseStarport = g_timerGame + 180; } if (s_tickHouseReinforcement <= g_timerGame) { tickReinforcement = true; s_tickHouseReinforcement = g_timerGame + (g_debugGame ? 60 : 600); } if (s_tickHouseMissileCountdown <= g_timerGame) { tickMissileCountdown = true; s_tickHouseMissileCountdown = g_timerGame + 60; } if (s_tickHouseStarportAvailability <= g_timerGame) { tickStarportAvailability = true; s_tickHouseStarportAvailability = g_timerGame + 1800; } if (tickMissileCountdown && g_houseMissileCountdown != 0) { g_houseMissileCountdown--; Sound_Output_Feedback(g_houseMissileCountdown + 41); if (g_houseMissileCountdown == 0) Unit_LaunchHouseMissile(Map_FindLocationTile(4, g_playerHouseID)); } if (tickStarportAvailability) { uint16 type; /* Pick a random unit to increase starport availability */ type = Tools_RandomLCG_Range(0, UNIT_MAX - 1); /* Increase how many of this unit is available via starport by one */ if (g_starportAvailable[type] != 0 && g_starportAvailable[type] < 10) { if (g_starportAvailable[type] == -1) { g_starportAvailable[type] = 1; } else { g_starportAvailable[type]++; } } } if (tickReinforcement) { Unit *nu = NULL; int i; for (i = 0; i < 16; i++) { uint16 locationID; bool deployed; Unit *u; if (g_scenario.reinforcement[i].unitID == UNIT_INDEX_INVALID) continue; if (g_scenario.reinforcement[i].timeLeft == 0) continue; if (--g_scenario.reinforcement[i].timeLeft != 0) continue; u = Unit_Get_ByIndex(g_scenario.reinforcement[i].unitID); locationID = g_scenario.reinforcement[i].locationID; deployed = false; if (locationID >= 4) { if (nu == NULL) { nu = Unit_Create(UNIT_INDEX_INVALID, UNIT_CARRYALL, u->o.houseID, Tile_UnpackTile(Map_FindLocationTile(Tools_Random_256() & 3, u->o.houseID)), 100); if (nu != NULL) { nu->o.flags.s.byScenario = true; Unit_SetDestination(nu, Tools_Index_Encode(Map_FindLocationTile(locationID, u->o.houseID), IT_TILE)); } } if (nu != NULL) { u->o.linkedID = nu->o.linkedID; nu->o.linkedID = (uint8)u->o.index; nu->o.flags.s.inTransport = true; g_scenario.reinforcement[i].unitID = UNIT_INDEX_INVALID; deployed = true; } else { /* Failed to create carry-all, try again in a short moment */ g_scenario.reinforcement[i].timeLeft = 1; } } else { deployed = Unit_SetPosition(u, Tile_UnpackTile(Map_FindLocationTile(locationID, u->o.houseID))); } if (deployed && g_scenario.reinforcement[i].repeat != 0) { tile32 tile; tile.x = 0xFFFF; tile.y = 0xFFFF; g_validateStrictIfZero++; u = Unit_Create(UNIT_INDEX_INVALID, u->o.type, u->o.houseID, tile, 0); g_validateStrictIfZero--; if (u != NULL) { g_scenario.reinforcement[i].unitID = u->o.index; g_scenario.reinforcement[i].timeLeft = g_scenario.reinforcement[i].timeBetween; } } } } find.houseID = HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { h = House_Find(&find); if (h == NULL) break; if (tickHouse) { /* ENHANCEMENT -- Originally this code was outside the house loop, which seems very odd. * This problem is considered to be so bad, that the original code has been removed. */ if (h->index != g_playerHouseID) { if (h->creditsStorage < h->credits) { h->credits = h->creditsStorage; } } else { uint16 maxCredits = max(h->creditsStorage, g_playerCreditsNoSilo); if (h->credits > maxCredits) { h->credits = maxCredits; GUI_DisplayText(String_Get_ByIndex(STR_INSUFFICIENT_SPICE_STORAGE_AVAILABLE_SPICE_IS_LOST), 1); } } if (h->index == g_playerHouseID) { if (h->creditsStorage > g_playerCreditsNoSilo) { g_playerCreditsNoSilo = 0; } if (g_playerCreditsNoSilo == 0 && g_campaignID > 1 && h->credits != 0) { if (h->creditsStorage != 0 && ((h->credits * 256 / h->creditsStorage) > 200)) { GUI_DisplayText(String_Get_ByIndex(STR_SPICE_STORAGE_CAPACITY_LOW_BUILD_SILOS), 0); } } if (h->credits < 100 && g_playerCreditsNoSilo != 0) { GUI_DisplayText(String_Get_ByIndex(STR_CREDITS_ARE_LOW_HARVEST_SPICE_FOR_MORE_CREDITS), 0); } } } if (tickHouse) House_EnsureHarvesterAvailable((uint8)h->index); if (tickStarport && h->starportLinkedID != UNIT_INDEX_INVALID) { Unit *u = NULL; h->starportTimeLeft--; if ((int16)h->starportTimeLeft < 0) h->starportTimeLeft = 0; if (h->starportTimeLeft == 0) { Structure *s; s = Structure_Get_ByIndex(g_structureIndex); if (s->o.type == STRUCTURE_STARPORT && s->o.houseID == h->index) { u = Unit_CreateWrapper((uint8)h->index, UNIT_FRIGATE, Tools_Index_Encode(s->o.index, IT_STRUCTURE)); } else { PoolFindStruct find2; find2.houseID = h->index; find2.index = 0xFFFF; find2.type = STRUCTURE_STARPORT; while (true) { s = Structure_Find(&find2); if (s == NULL) break; if (s->o.linkedID != 0xFF) continue; u = Unit_CreateWrapper((uint8)h->index, UNIT_FRIGATE, Tools_Index_Encode(s->o.index, IT_STRUCTURE)); break; } } if (u != NULL) { u->o.linkedID = (uint8)h->starportLinkedID; h->starportLinkedID = UNIT_INDEX_INVALID; u->o.flags.s.inTransport = true; Sound_Output_Feedback(38); } h->starportTimeLeft = (u != NULL) ? g_table_houseInfo[h->index].starportDeliveryTime : 1; } } if (tickHouse) { House_CalculatePowerAndCredit(h); Structure_CalculateHitpointsMax(h); if (h->timerUnitAttack != 0) h->timerUnitAttack--; if (h->timerSandwormAttack != 0) h->timerSandwormAttack--; if (h->timerStructureAttack != 0) h->timerStructureAttack--; if (h->harvestersIncoming > 0 && Unit_CreateWrapper((uint8)h->index, UNIT_HARVESTER, 0) != NULL) h->harvestersIncoming--; } if (tickPowerMaintenance) { uint16 powerMaintenanceCost = (h->powerUsage / 32) + 1; h->credits -= min(h->credits, powerMaintenanceCost); } } }