/** * Finds a structure. * * Stack: 1 - A structure type. * * @param script The script engine to operate on. * @return An encoded structure index, or 0 if none found. */ uint16 Script_Unit_FindStructure(ScriptEngine *script) { Unit *u; PoolFindStruct find; u = g_scriptCurrentUnit; find.houseID = Unit_GetHouseID(u); find.index = 0xFFFF; find.type = STACK_PEEK(1); while (true) { Structure *s; s = Structure_Find(&find); if (s == NULL) break; if (s->state != STRUCTURE_STATE_IDLE) continue; if (s->o.linkedID != 0xFF) continue; if (s->o.script.variables[4] != 0) continue; return Tools_Index_Encode(s->o.index, IT_STRUCTURE); } return 0; }
/** * Update the CreditsStorage by walking over all structures and checking what * they can hold. * @param houseID The house to check the storage for. */ void House_UpdateCreditsStorage(uint8 houseID) { PoolFindStruct find; uint32 creditsStorage; uint16 oldValidateStrictIfZero = g_validateStrictIfZero; g_validateStrictIfZero = 0; find.houseID = houseID; find.index = 0xFFFF; find.type = 0xFFFF; creditsStorage = 0; while (true) { const StructureInfo *si; Structure *s; s = Structure_Find(&find); if (s == NULL) break; si = &g_table_structureInfo[s->o.type]; creditsStorage += si->creditsStorage; } if (creditsStorage > 32000) creditsStorage = 32000; House_Get_ByIndex(houseID)->creditsStorage = creditsStorage; g_validateStrictIfZero = oldValidateStrictIfZero; }
/** * Gives a harvester to the given house if it has a refinery and no harvesters. * * @param houseID The index of the house to give a harvester to. */ void House_EnsureHarvesterAvailable(uint8 houseID) { PoolFindStruct find; Structure *s; find.houseID = houseID; find.type = 0xFFFF; find.index = 0xFFFF; while (true) { s = Structure_Find(&find); if (s == NULL) break; /* ENHANCEMENT -- Dune2 checked the wrong type to skip. LinkedID is a structure for a Construction Yard */ if (!g_dune2_enhanced && s->o.type == STRUCTURE_HEAVY_VEHICLE) continue; if (g_dune2_enhanced && s->o.type == STRUCTURE_CONSTRUCTION_YARD) continue; if (s->o.linkedID == UNIT_INVALID) continue; if (Unit_Get_ByIndex(s->o.linkedID)->o.type == UNIT_HARVESTER) return; } find.houseID = houseID; find.type = UNIT_CARRYALL; find.index = 0xFFFF; while (true) { Unit *u; u = Unit_Find(&find); if (u == NULL) break; if (u->o.linkedID == UNIT_INVALID) continue; if (Unit_Get_ByIndex(u->o.linkedID)->o.type == UNIT_HARVESTER) return; } if (Unit_IsTypeOnMap(houseID, UNIT_HARVESTER)) return; find.houseID = houseID; find.type = STRUCTURE_REFINERY; find.index = 0xFFFF; s = Structure_Find(&find); if (s == NULL) return; if (Unit_CreateWrapper(houseID, UNIT_HARVESTER, Tools_Index_Encode(s->o.index, IT_STRUCTURE)) == NULL) return; if (houseID != g_playerHouseID) return; GUI_DisplayText(String_Get_ByIndex(STR_HARVESTER_IS_HEADING_TO_REFINERY), 0); }
/** * Calculate the power usage and production, and the credits storage. * * @param h The house to calculate the numbers for. */ void House_CalculatePowerAndCredit(House *h) { PoolFindStruct find; if (h == NULL) return; h->powerUsage = 0; h->powerProduction = 0; h->creditsStorage = 0; find.houseID = h->index; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { const StructureInfo *si; Structure *s; s = Structure_Find(&find); if (s == NULL) break; /* ENHANCEMENT -- Only count structures that are placed on the map, not ones we are building. */ if (g_dune2_enhanced && s->o.flags.s.isNotOnMap) continue; si = &g_table_structureInfo[s->o.type]; h->creditsStorage += si->creditsStorage; /* Positive values means usage */ if (si->powerUsage >= 0) { h->powerUsage += si->powerUsage; continue; } /* Negative value and full health means everything goes to production */ if (s->o.hitpoints >= si->o.hitpoints) { h->powerProduction += -si->powerUsage; continue; } /* Negative value and partial health, calculate how much should go to production (capped at 50%) */ /* ENHANCEMENT -- The 50% cap of Dune2 is silly and disagress with the GUI. If your hp is 10%, so should the production. */ if (!g_dune2_enhanced && s->o.hitpoints <= si->o.hitpoints / 2) { h->powerProduction += (-si->powerUsage) / 2; continue; } h->powerProduction += (-si->powerUsage) * s->o.hitpoints / si->o.hitpoints; } /* Check if we are low on power */ if (h->index == g_playerHouseID && h->powerUsage > h->powerProduction) { GUI_DisplayText(String_Get_ByIndex(STR_INSUFFICIENT_POWER_WINDTRAP_IS_NEEDED), 1); } /* If there are no buildings left, you lose your right on 'credits without storage' */ if (h->index == g_playerHouseID && h->structuresBuilt == 0 && g_validateStrictIfZero == 0) { g_playerCreditsNoSilo = 0; } }
/** * Check if a level is won, based on the values in LoseFlags. * * @return True if and only if the level has been won by the human. */ static bool GameLoop_IsLevelWon(void) { bool win = false; if (s_debugForceWin) return true; /* Check for structure counts hitting zero */ if ((g_scenario.loseFlags & 0x3) != 0) { PoolFindStruct find; uint16 countStructureEnemy = 0; uint16 countStructureFriendly = 0; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Calculate how many structures are left on the map */ while (true) { Structure *s; s = Structure_Find(&find); if (s == NULL) break; if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (s->o.type == STRUCTURE_TURRET) continue; if (s->o.type == STRUCTURE_ROCKET_TURRET) continue; if (s->o.houseID == g_playerHouseID) { countStructureFriendly++; } else { countStructureEnemy++; } } win = true; if ((g_scenario.loseFlags & 0x1) != 0) { win = win && (countStructureEnemy == 0); } if ((g_scenario.loseFlags & 0x2) != 0) { win = win && (countStructureFriendly != 0); } } /* Check for reaching spice quota */ if (!win && (g_scenario.loseFlags & 0x4) != 0 && g_playerCredits != 0xFFFF) { win = (g_playerCredits >= g_playerHouse->creditsQuota); } /* Check for reaching timeout */ if (!win && (g_scenario.loseFlags & 0x8) != 0) { win = (g_timerGame < s_tickGameTimeout); } return win; }
/** * Move the unit to the first available structure it can find of the required * type. * * Stack: 1 - Type of structure. * * @param script The script engine to operate on. * @return An encoded structure index. */ uint16 Script_Unit_MoveToStructure(ScriptEngine *script) { Unit *u; PoolFindStruct find; u = g_scriptCurrentUnit; if (u->o.linkedID != 0xFF) { Structure *s; s = Tools_Index_GetStructure(Unit_Get_ByIndex(u->o.linkedID)->originEncoded); if (s != NULL && s->state == STRUCTURE_STATE_IDLE && s->o.script.variables[4] == 0) { uint16 encoded; encoded = Tools_Index_Encode(s->o.index, IT_STRUCTURE); Object_Script_Variable4_Link(Tools_Index_Encode(u->o.index, IT_UNIT), encoded); u->targetMove = u->o.script.variables[4]; return encoded; } } find.houseID = Unit_GetHouseID(u); find.index = 0xFFFF; find.type = STACK_PEEK(1); while (true) { Structure *s; uint16 encoded; s = Structure_Find(&find); if (s == NULL) break; if (s->state != STRUCTURE_STATE_IDLE) continue; if (s->o.script.variables[4] != 0) continue; encoded = Tools_Index_Encode(s->o.index, IT_STRUCTURE); Object_Script_Variable4_Link(Tools_Index_Encode(u->o.index, IT_UNIT), encoded); u->targetMove = encoded; return encoded; } return 0; }
/** * Save all Structures to a file. It converts pointers to indices where needed. * @param fp The file to save to. * @return True if and only if all bytes were written successful. */ bool Structure_Save(FILE *fp) { PoolFindStruct find; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; while (true) { Structure *s; Structure ss; s = Structure_Find(&find); if (s == NULL) break; ss = *s; if (!SaveLoad_Save(s_saveStructure, fp, &ss)) return false; } return true; }
/** * Makes the current unit to go to the closest structure of the given type. * * Stack: 1 - The type of the structure. * * @param script The script engine to operate on. * @return The value 1 if and only if a structure has been found. */ uint16 Script_Unit_GoToClosestStructure(ScriptEngine *script) { Unit *u; Structure *s = NULL; PoolFindStruct find; uint16 distanceMin =0; u = g_scriptCurrentUnit; find.houseID = Unit_GetHouseID(u); find.index = 0xFFFF; find.type = STACK_PEEK(1); while (true) { Structure *s2; uint16 distance; s2 = Structure_Find(&find); if (s2 == NULL) break; if (s2->state != STRUCTURE_STATE_IDLE) continue; if (s2->o.linkedID != 0xFF) continue; if (s2->o.script.variables[4] != 0) continue; distance = Tile_GetDistanceRoundedUp(s2->o.position, u->o.position); if (distance >= distanceMin && distanceMin != 0) continue; distanceMin = distance; s = s2; } if (s == NULL) return 0; Unit_SetAction(u, ACTION_MOVE); Unit_SetDestination(u, Tools_Index_Encode(s->o.index, IT_STRUCTURE)); return 1; }
static void Skirmish_FindClosestStructures(HouseType houseID, uint16 packed, uint16* dist_ally, uint16* dist_enemy) { PoolFindStruct find; Structure* s; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = STRUCTURE_INDEX_INVALID; *dist_ally = 0xFFFF; *dist_enemy = 0xFFFF; while ((s = Structure_Find(&find)) != NULL) { if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; const uint16 dist = Tile_GetDistancePacked(Tile_PackTile(s->o.position), packed); if (House_AreAllied(houseID, s->o.houseID)) *dist_ally = min(dist, *dist_ally); else *dist_enemy = min(dist, *dist_enemy); } }
static void Skirmish_GenUnitsAI(HouseType houseID) { const uint16 unacceptableLst = (1 << LST_WALL) | (1 << LST_STRUCTURE) | (1 << LST_BLOOM_FIELD); Structure* factory[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; unsigned int nfactories = 0; PoolFindStruct find; find.houseID = houseID; find.type = 0xFFFF; find.index = STRUCTURE_INDEX_INVALID; Structure* s; while ((nfactories < lengthof(factory)) && ((s = Structure_Find(&find)) != NULL)) { /* Do not produce from hi-tech. */ if (s->o.type == STRUCTURE_LIGHT_VEHICLE || s->o.type == STRUCTURE_HEAVY_VEHICLE || s->o.type == STRUCTURE_WOR_TROOPER || s->o.type == STRUCTURE_BARRACKS) { factory[nfactories] = s; nfactories++; } } if (nfactories == 0) return; for (int count = 8; count > 0;) { const uint16 packed = Skirmish_PickRandomLocation(0xFF, unacceptableLst); if (packed == 0) continue; uint16 dist_ally; uint16 dist_enemy; Skirmish_FindClosestStructures(houseID, packed, &dist_ally, &dist_enemy); if (dist_ally > 8) continue; /* If there's a mountain here, build infantry. */ const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); UnitType type; if (lst == LST_ENTIRELY_MOUNTAIN || lst == LST_PARTIAL_MOUNTAIN) { type = House_GetInfantrySquad(houseID); } /* Otherwise, build a random vehicle. */ else { const int r = Tools_RandomLCG_Range(0, nfactories - 1); type = (UnitType)StructureAI_PickNextToBuild(factory[r]); if (type == UNIT_INVALID) continue; } const UnitActionType actionType = ((Tools_Random_256() & 0x3) == 0) ? ACTION_AMBUSH : ACTION_AREA_GUARD; const tile32 position = Tile_UnpackTile(packed); Scenario_Create_Unit(houseID, type, 256, position, 127, actionType); count--; } }
static bool Skirmish_GenUnitsHuman(HouseType houseID, SkirmishData* sd) { const int delta[7] = { 0, -4, 4, -MAP_SIZE_MAX * 3 - 2, -MAP_SIZE_MAX * 3 + 2, MAP_SIZE_MAX * 3 - 2, MAP_SIZE_MAX * 3 + 2, }; const MapInfo* mi = &g_mapInfos[0]; /* Pick a tile that is not too close to the edge, and not too * close to the enemy. */ int r; for (int attempts = 0; attempts < 100; attempts++) { const int island = Skirmish_PickRandomIsland(sd); if (island < 0) return false; r = Tools_RandomLCG_Range(sd->island[island].start, sd->island[island].end - 1); if (!(mi->minX + 4 <= sd->buildable[r].x && sd->buildable[r].x < mi->minX + mi->sizeX - 4)) continue; if (!(mi->minY + 3 <= sd->buildable[r].y && sd->buildable[r].y < mi->minY + mi->sizeY - 3)) continue; PoolFindStruct find; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = STRUCTURE_INDEX_INVALID; Structure* s = Structure_Find(&find); for (; s != NULL; s = Structure_Find(&find)) { if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (House_AreAllied(g_playerHouseID, s->o.houseID)) continue; const uint16 dist = Tile_GetDistancePacked(Tile_PackTile(s->o.position), sd->buildable[r].packed); if (dist < 24) break; } if (s == NULL) { break; } else { r = -1; } } if (r < 0) return false; bool mcvPlaced = false; for (int i = 0; i < 7; i++) { const uint16 packed = sd->buildable[r].packed + delta[i]; const tile32 position = Tile_UnpackTile(packed); UnitType type; if (!mcvPlaced) type = UNIT_MCV; else if (i >= 0 && i < 3) type = UNIT_SIEGE_TANK; else if (i >= 3 && i < 4) type = House_GetIXVehicle(houseID); else if (i >= 4 && i < 5) type = House_GetMediumVehicle(houseID); else if (i >= 5 && i < 6) type = UNIT_QUAD; else if (i >= 6) type = House_GetInfantrySquad(houseID); const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); /* If there's a structure or a bloom here, tough luck. */ if (lst == LST_STRUCTURE || lst == LST_BLOOM_FIELD) continue; /* If there's a mountain here, build infantry instead. */ if (lst == LST_ENTIRELY_MOUNTAIN || lst == LST_PARTIAL_MOUNTAIN) type = House_GetInfantrySquad(houseID); Scenario_Create_Unit(houseID, type, 256, position, 127, (UnitActionType)g_table_unitInfo[type].o.actionsPlayer[3]); if (type == UNIT_MCV) mcvPlaced = true; } return true; }
/** * Save the game to a filename * * @param fp The filename of the savegame. * @param description The description of the savegame. * @return True if and only if all bytes were written successful. */ bool SaveFile(char *filename, char *description) { FILE *fp; char filenameComplete[1024]; bool res; /* In debug-scenario mode, the whole map is uncovered. Cover it now in * the savegame based on the current position of the units and * structures. */ if (g_debugScenario) { PoolFindStruct find; uint16 i; /* Add fog of war for all tiles on the map */ for (i = 0; i < 0x1000; i++) { Tile *tile = &g_map[i]; tile->isUnveiled = false; tile->overlaySpriteID = g_veiledSpriteID; } find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Remove the fog of war for all units */ while (true) { Unit *u; u = Unit_Find(&find); if (u == NULL) break; Unit_RemoveFog(u); } find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Remove the fog of war for all structures */ while (true) { Structure *s; s = Structure_Find(&find); if (s == NULL) break; Structure_RemoveFog(s); } } snprintf(filenameComplete, sizeof(filenameComplete), "data/%s", filename); fp = fopen(filenameComplete, "wb"); if (fp == NULL) { Error("Failed to open file '%s' for writing.\n", filenameComplete); return false; } g_var_38BC++; res = Save_Main(fp, description); g_var_38BC--; fclose(fp); if (!res) { /* TODO -- Also remove the savegame now */ Error("Error while writing savegame.\n"); return false; } return true; }
/** * Check if a level is finished, based on the values in WinFlags. * * @return True if and only if the level has come to an end. */ static bool GameLoop_IsLevelFinished(void) { bool finish = false; if (s_debugForceWin) return true; /* You have to play at least 7200 ticks before you can win the game */ if (g_timerGame - g_tickScenarioStart < 7200) return false; /* Check for structure counts hitting zero */ if ((g_scenario.winFlags & 0x3) != 0) { PoolFindStruct find; uint16 countStructureEnemy = 0; uint16 countStructureFriendly = 0; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Calculate how many structures are left on the map */ while (true) { Structure *s; s = Structure_Find(&find); if (s == NULL) break; if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (s->o.type == STRUCTURE_TURRET) continue; if (s->o.type == STRUCTURE_ROCKET_TURRET) continue; if (s->o.houseID == g_playerHouseID) { countStructureFriendly++; } else { countStructureEnemy++; } } if ((g_scenario.winFlags & 0x1) != 0 && countStructureEnemy == 0) { finish = true; } if ((g_scenario.winFlags & 0x2) != 0 && countStructureFriendly == 0) { finish = true; } } /* Check for reaching spice quota */ if ((g_scenario.winFlags & 0x4) != 0 && g_playerCredits != 0xFFFF) { if (g_playerCredits >= g_playerHouse->creditsQuota) { finish = true; } } /* Check for reaching timeout */ if ((g_scenario.winFlags & 0x8) != 0) { /* XXX -- This code was with '<' instead of '>=', which makes * no sense. As it is unused, who knows what the intentions * were. This at least makes it sensible. */ if (g_timerGame >= s_tickGameTimeout) { finish = true; } } return finish; }
/** * Pickup a unit (either from structure or on the map). The unit that does the * picking up returns the unit to his last position. * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 0. Always. */ uint16 Script_Unit_Pickup(ScriptEngine *script) { Unit *u; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; if (u->o.linkedID != 0xFF) return 0; switch (Tools_Index_GetType(u->targetMove)) { case IT_STRUCTURE: { Structure *s; Unit *u2; s = Tools_Index_GetStructure(u->targetMove); /* There was nothing to pickup here */ if (s->state != STRUCTURE_STATE_READY) { Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; return 0; } u->o.flags.s.inTransport = true; Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; u2 = Unit_Get_ByIndex(s->o.linkedID); /* Pickup the unit */ u->o.linkedID = u2->o.index & 0xFF; s->o.linkedID = u2->o.linkedID; u2->o.linkedID = 0xFF; if (s->o.linkedID == 0xFF) Structure_SetState(s, STRUCTURE_STATE_IDLE); /* Check if the unit has a return-to position or try to find spice in case of a harvester */ if (u2->targetLast.tile != 0) { u->targetMove = Tools_Index_Encode(Tile_PackTile(u2->targetLast), IT_TILE); } else if (u2->o.type == UNIT_HARVESTER && Unit_GetHouseID(u2) != g_playerHouseID) { u->targetMove = Tools_Index_Encode(Map_SearchSpice(Tile_PackTile(u->o.position), 20), IT_TILE); } Unit_UpdateMap(2, u); return 1; } case IT_UNIT: { Unit *u2; Structure *s = NULL; PoolFindStruct find; int16 minDistance = 0; u2 = Tools_Index_GetUnit(u->targetMove); if (!u2->o.flags.s.allocated) return 0; find.houseID = Unit_GetHouseID(u); find.index = 0xFFFF; find.type = 0xFFFF; /* Find closest refinery / repair station */ while (true) { Structure *s2; int16 distance; s2 = Structure_Find(&find); if (s2 == NULL) break; distance = Tile_GetDistanceRoundedUp(s2->o.position, u->o.position); if (u2->o.type == UNIT_HARVESTER) { if (s2->o.type != STRUCTURE_REFINERY || s2->state != STRUCTURE_STATE_IDLE || s2->o.script.variables[4] != 0) continue; if (minDistance != 0 && distance >= minDistance) break; minDistance = distance; s = s2; break; } if (s2->o.type != STRUCTURE_REPAIR || s2->state != STRUCTURE_STATE_IDLE || s2->o.script.variables[4] != 0) continue; if (minDistance != 0 && distance >= minDistance) continue; minDistance = distance; s = s2; } if (s == NULL) return 0; /* Deselect the unit as it is about to be picked up */ if (u2 == g_unitSelected) Unit_Select(NULL); /* Pickup the unit */ u->o.linkedID = u2->o.index & 0xFF; u->o.flags.s.inTransport = true; Unit_UpdateMap(0, u2); Unit_Hide(u2); /* Set where we are going to */ Object_Script_Variable4_Link(Tools_Index_Encode(u->o.index, IT_UNIT), Tools_Index_Encode(s->o.index, IT_STRUCTURE)); u->targetMove = u->o.script.variables[4]; Unit_UpdateMap(2, u); if (u2->o.type != UNIT_HARVESTER) return 0; /* Check if we want to return to this spice field later */ if (Map_SearchSpice(Tile_PackTile(u2->o.position), 2) == 0) { u2->targetPreLast.tile = 0; u2->targetLast.tile = 0; } return 0; } default: return 0; } }
/** * Check if a level is won, based on the values in LoseFlags. * * @return True if and only if the level has been won by the human. */ static bool GameLoop_IsLevelWon() { bool win = false; if (s_debugForceWin) return true; /* Check for structure counts hitting zero */ if ((g_scenario.loseFlags & 0x3) != 0) { PoolFindStruct find; uint16 countStructureEnemy = 0; uint16 countStructureFriendly = 0; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Calculate how many structures are left on the map */ while (true) { Structure* s; s = Structure_Find(&find); if (s == NULL) break; if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (s->o.type == STRUCTURE_TURRET) continue; if (s->o.type == STRUCTURE_ROCKET_TURRET) continue; if (House_AreAllied(s->o.houseID, g_playerHouseID)) countStructureFriendly++; else countStructureEnemy++; } win = true; if ((g_scenario.loseFlags & 0x1) != 0) win = win && (countStructureEnemy == 0); if ((g_scenario.loseFlags & 0x2) != 0) win = win && (countStructureFriendly != 0); } /* Check for reaching spice quota */ if (!win && (g_scenario.loseFlags & 0x4) != 0 && g_playerCredits != 0xFFFF) win = (g_playerCredits >= g_playerHouse->creditsQuota); /* Check for reaching timeout */ if (!win && (g_scenario.loseFlags & 0x8) != 0) { /* ENHANCEMENT -- Same deal as for winFlags above. * This way we can make win-after-timeout (survival) and * lose-after-timeout (countdown) missions. * * survival: winFlags = 11, loseFlags = 9. * lose: winFlags = 11, loseFlags = 1. */ win = (g_timerGame - g_tickScenarioStart >= g_scenario.timeOut); } return win; }
/** * 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); } } }
/** * Prepare the map (after loading scenario or savegame). Does some basic * sanity-check and corrects stuff all over the place. */ void Game_Prepare(void) { PoolFindStruct find; uint16 oldSelectionType; Tile *t; int i; g_validateStrictIfZero++; oldSelectionType = g_selectionType; g_selectionType = SELECTIONTYPE_MENTAT; Structure_Recount(); Unit_Recount(); Team_Recount(); t = &g_map[0]; for (i = 0; i < 64 * 64; i++, t++) { Structure *s; Unit *u; u = Unit_Get_ByPackedTile(i); s = Structure_Get_ByPackedTile(i); if (u == NULL || !u->o.flags.s.used) t->hasUnit = false; if (s == NULL || !s->o.flags.s.used) t->hasStructure = false; if (t->isUnveiled) Map_UnveilTile(i, g_playerHouseID); } find.houseID = HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Unit *u; u = Unit_Find(&find); if (u == NULL) break; if (u->o.flags.s.isNotOnMap) continue; Unit_RemoveFog(u); Unit_UpdateMap(1, u); } find.houseID = HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Structure *s; s = Structure_Find(&find); if (s == NULL) break; if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (s->o.flags.s.isNotOnMap) continue; Structure_RemoveFog(s); if (s->o.type == STRUCTURE_STARPORT && s->o.linkedID != 0xFF) { Unit *u = Unit_Get_ByIndex(s->o.linkedID); if (!u->o.flags.s.used || !u->o.flags.s.isNotOnMap) { s->o.linkedID = 0xFF; s->countDown = 0; } else { Structure_SetState(s, STRUCTURE_STATE_READY); } } Script_Load(&s->o.script, s->o.type); if (s->o.type == STRUCTURE_PALACE) { House_Get_ByIndex(s->o.houseID)->palacePosition = s->o.position; } if ((House_Get_ByIndex(s->o.houseID)->palacePosition.x != 0) || (House_Get_ByIndex(s->o.houseID)->palacePosition.y != 0)) continue; House_Get_ByIndex(s->o.houseID)->palacePosition = s->o.position; } find.houseID = HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { House *h; h = House_Find(&find); if (h == NULL) break; h->structuresBuilt = Structure_GetStructuresBuilt(h); House_UpdateCreditsStorage((uint8)h->index); House_CalculatePowerAndCredit(h); } GUI_Palette_CreateRemap(g_playerHouseID); Map_SetSelection(g_selectionPosition); if (g_structureActiveType != 0xFFFF) { Map_SetSelectionSize(g_table_structureInfo[g_structureActiveType].layout); } else { Structure *s = Structure_Get_ByPackedTile(g_selectionPosition); if (s != NULL) Map_SetSelectionSize(g_table_structureInfo[s->o.type].layout); } Voice_LoadVoices(g_playerHouseID); g_tickHousePowerMaintenance = max(g_timerGame + 70, g_tickHousePowerMaintenance); g_viewport_forceRedraw = true; g_playerCredits = 0xFFFF; g_selectionType = oldSelectionType; g_validateStrictIfZero--; }