/** * Destroy a structure and spawn soldiers around the place. * * Stack: *none* * * @param script The script engine to operate on. * @return Always 0. */ uint16 Script_Structure_Destroy(ScriptEngine *script) { Structure *s; uint16 position; uint16 layout; uint16 i; VARIABLE_NOT_USED(script); s = g_scriptCurrentStructure; layout = g_table_structureInfo[s->o.type].layout; position = Tile_PackTile(s->o.position); Structure_Remove(s); for (i = 0; i < g_table_structure_layoutTileCount[layout]; i++) { tile32 tile; Unit *u; tile = Tile_UnpackTile(position + g_table_structure_layoutTiles[layout][i]); if (g_table_structureInfo[s->o.type].o.spawnChance < Tools_Random_256()) continue; u = Unit_Create(UNIT_INDEX_INVALID, UNIT_SOLDIER, s->o.houseID, tile, Tools_Random_256()); if (u == NULL) continue; u->o.hitpoints = g_table_unitInfo[UNIT_SOLDIER].o.hitpoints * (Tools_Random_256() & 3) / 256; if (s->o.houseID != g_playerHouseID) { Unit_SetAction(u, ACTION_ATTACK); continue; } Unit_SetAction(u, ACTION_MOVE); tile = Tile_MoveByRandom(u->o.position, 32, true); u->targetMove = Tools_Index_Encode(Tile_PackTile(tile), IT_TILE); } if (g_debugScenario) return 0; if (s->o.houseID != g_playerHouseID) return 0; if (g_config.language == LANGUAGE_FRENCH) { GUI_DisplayText("%s %s %s", 0, String_Get_ByIndex(g_table_structureInfo[s->o.type].o.stringID_full), g_table_houseInfo[s->o.houseID].name, String_Get_ByIndex(0x85)); } else { GUI_DisplayText("%s %s %s", 0, g_table_houseInfo[s->o.houseID].name, String_Get_ByIndex(g_table_structureInfo[s->o.type].o.stringID_full), String_Get_ByIndex(0x85)); } return 0; }
/** * Create a new soldier unit. * * Stack: 1 - Action for the new Unit. * * @param script The script engine to operate on. * @return 1 if a new Unit has been created, 0 otherwise. */ uint16 Script_Unit_RandomSoldier(ScriptEngine *script) { Unit *u; Unit *nu; tile32 position; u = g_scriptCurrentUnit; if (Tools_Random_256() >= g_table_unitInfo[u->o.type].o.spawnChance) return 0; position = Tile_MoveByRandom(u->o.position, 20, true); nu = Unit_Create(UNIT_INDEX_INVALID, UNIT_SOLDIER, u->o.houseID, position, Tools_Random_256()); if (nu == NULL) return 0; nu->deviated = u->deviated; Unit_SetAction(nu, STACK_PEEK(1)); return 1; }
static void Scenario_Load_Reinforcement(const char *key, char *settings) { uint8 index, houseType, unitType, locationID; uint16 timeBetween; tile32 position; bool repeat; Unit *u; char *split; index = atoi(key); /* The value should have 4 values separated by a ',' */ split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* First value is the House type */ houseType = House_StringToType(settings); if (houseType == HOUSE_INVALID) return; /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Second value is the Unit type */ unitType = Unit_StringToType(settings); if (unitType == UNIT_INVALID) return; /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Third value is the location of the reinforcement */ if (strcasecmp(settings, "NORTH") == 0) locationID = 0; else if (strcasecmp(settings, "EAST") == 0) locationID = 1; else if (strcasecmp(settings, "SOUTH") == 0) locationID = 2; else if (strcasecmp(settings, "WEST") == 0) locationID = 3; else if (strcasecmp(settings, "AIR") == 0) locationID = 4; else if (strcasecmp(settings, "VISIBLE") == 0) locationID = 5; else if (strcasecmp(settings, "ENEMYBASE") == 0) locationID = 6; else if (strcasecmp(settings, "HOMEBASE") == 0) locationID = 7; else return; /* Fourth value is the time between reinforcement */ settings = split + 1; timeBetween = atoi(settings) * 6 + 1; repeat = (settings[strlen(settings) - 1] == '+') ? true : false; /* ENHANCEMENT -- Dune2 makes a mistake in reading the '+', causing repeat to be always false */ if (!g_dune2_enhanced) repeat = false; position.s.x = 0xFFFF; position.s.y = 0xFFFF; u = Unit_Create(UNIT_INDEX_INVALID, unitType, houseType, position, 0); if (u == NULL) return; g_scenario.reinforcement[index].unitID = u->o.index; g_scenario.reinforcement[index].locationID = locationID; g_scenario.reinforcement[index].timeLeft = timeBetween; g_scenario.reinforcement[index].timeBetween = timeBetween; g_scenario.reinforcement[index].repeat = repeat ? 1 : 0; }
/** * 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); } } }