/** ** Show Map Location ** ** @param l Lua state. */ static int CclShowMapLocation(lua_State *l) { // Put a unit on map, use its properties, except for // what is listed below LuaCheckArgs(l, 4); const char *unitname = LuaToString(l, 5); CUnitType *unitType = UnitTypeByIdent(unitname); if (!unitType) { DebugPrint("Unable to find UnitType '%s'" _C_ unitname); return 0; } CUnit *target = MakeUnit(*unitType, ThisPlayer); if (target != NULL) { target->Variable[HP_INDEX].Value = 0; target->tilePos.x = LuaToNumber(l, 1); target->tilePos.y = LuaToNumber(l, 2); target->TTL = GameCycle + LuaToNumber(l, 4); target->CurrentSightRange = LuaToNumber(l, 3); //Wyrmgus start UpdateUnitSightRange(*target); //Wyrmgus end MapMarkUnitSight(*target); } else { DebugPrint("Unable to allocate Unit"); } return 0; }
static void RemoveIndividualUpgradeModifier(CUnit &unit, const CUpgradeModifier *um) { Assert(um); if (um->Modifier.Variables[SIGHTRANGE_INDEX].Value) { if (!unit.Removed) { MapUnmarkUnitSight(unit); unit.CurrentSightRange = unit.Variable[SIGHTRANGE_INDEX].Value - um->Modifier.Variables[SIGHTRANGE_INDEX].Value; UpdateUnitSightRange(unit); MapMarkUnitSight(unit); } } for (unsigned int j = 0; j < UnitTypeVar.GetNumberVariable(); j++) { unit.Variable[j].Enable |= um->Modifier.Variables[j].Enable; if (um->ModifyPercent[j]) { unit.Variable[j].Value = unit.Variable[j].Value * 100 / (100 + um->ModifyPercent[j]); unit.Variable[j].Max = unit.Variable[j].Max * 100 / (100 + um->ModifyPercent[j]); } else { unit.Variable[j].Value -= um->Modifier.Variables[j].Value; unit.Variable[j].Increase -= um->Modifier.Variables[j].Increase; } unit.Variable[j].Max -= um->Modifier.Variables[j].Max; unit.Variable[j].Max = std::max(unit.Variable[j].Max, 0); if (unit.Variable[j].Max > 0) { clamp(&unit.Variable[j].Value, 0, unit.Variable[j].Max); } } }
/** ** Shared vision changed. ** ** @param player Player which changes his state. ** @param state New shared vision state. ** @param opponent Opponent. */ void CommandSharedVision(int player, bool state, int opponent) { int before; int after; int x; int y; int i; // // Do a real hardcore seen recount. First we unmark EVERYTHING. // for (i = 0; i < NumUnits; ++i) { if (!Units[i]->Destroyed) { MapUnmarkUnitSight(Units[i]); } } // // Compute Before and after. // before = Players[player].IsBothSharedVision(&Players[opponent]); if (state == false) { Players[player].SharedVision &= ~(1 << opponent); } else { Players[player].SharedVision |= (1 << opponent); } after = Players[player].IsBothSharedVision(&Players[opponent]); if (before && !after) { // // Don't share vision anymore. Give each other explored terrain for good-bye. // for (x = 0; x < Map.Info.MapWidth; ++x) { for (y = 0; y < Map.Info.MapHeight; ++y) { i = x + y * Map.Info.MapWidth; if (Map.Fields[i].Visible[player] && !Map.Fields[i].Visible[opponent]) { Map.Fields[i].Visible[opponent] = 1; if (opponent == ThisPlayer->Index) { Map.MarkSeenTile(x, y); } } if (Map.Fields[i].Visible[opponent] && !Map.Fields[i].Visible[player]) { Map.Fields[i].Visible[player] = 1; if (player == ThisPlayer->Index) { Map.MarkSeenTile(x, y); } } } } } // // Do a real hardcore seen recount. Now we remark EVERYTHING // for (i = 0; i < NumUnits; ++i) { if (!Units[i]->Destroyed) { MapMarkUnitSight(Units[i]); } } }
/** ** Apply the modifiers of an individual upgrade. ** ** @param unit Unit that will get the modifier applied ** @param um Upgrade modifier that does the effects */ void ApplyIndividualUpgradeModifier(CUnit &unit, const CUpgradeModifier *um) { Assert(um); if (um->Modifier.Variables[SIGHTRANGE_INDEX].Value) { if (!unit.Removed) { MapUnmarkUnitSight(unit); unit.CurrentSightRange = unit.Variable[SIGHTRANGE_INDEX].Value + um->Modifier.Variables[SIGHTRANGE_INDEX].Value; UpdateUnitSightRange(unit); MapMarkUnitSight(unit); } } for (unsigned int j = 0; j < UnitTypeVar.GetNumberVariable(); j++) { unit.Variable[j].Enable |= um->Modifier.Variables[j].Enable; if (um->ModifyPercent[j]) { unit.Variable[j].Value += unit.Variable[j].Value * um->ModifyPercent[j] / 100; unit.Variable[j].Max += unit.Variable[j].Max * um->ModifyPercent[j] / 100; } else { unit.Variable[j].Value += um->Modifier.Variables[j].Value; unit.Variable[j].Increase += um->Modifier.Variables[j].Increase; } unit.Variable[j].Max += um->Modifier.Variables[j].Max; unit.Variable[j].Max = std::max(unit.Variable[j].Max, 0); if (unit.Variable[j].Max > 0) { clamp(&unit.Variable[j].Value, 0, unit.Variable[j].Max); } } if (um->ConvertTo) { CommandTransformIntoType(unit, *um->ConvertTo); } }
/** ** Shared vision changed. ** ** @param player Player which changes his state. ** @param state New shared vision state. ** @param opponent Opponent. */ void CommandSharedVision(int player, bool state, int opponent) { // Do a real hardcore seen recount. First we unmark EVERYTHING. for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &unit = **it; if (!unit.Destroyed) { MapUnmarkUnitSight(unit); } } // Compute Before and after. const int before = Players[player].IsBothSharedVision(Players[opponent]); if (state == false) { Players[player].UnshareVisionWith(Players[opponent]); } else { Players[player].ShareVisionWith(Players[opponent]); } const int after = Players[player].IsBothSharedVision(Players[opponent]); if (before && !after) { // Don't share vision anymore. Give each other explored terrain for good-bye. for (int i = 0; i != Map.Info.MapWidth * Map.Info.MapHeight; ++i) { CMapField &mf = *Map.Field(i); CMapFieldPlayerInfo &mfp = mf.playerInfo; //Wyrmgus start // if (mfp.Visible[player] && !mfp.Visible[opponent]) { if (mfp.Visible[player] && !mfp.Visible[opponent] && !Players[player].Revealed) { //Wyrmgus end mfp.Visible[opponent] = 1; if (opponent == ThisPlayer->Index) { Map.MarkSeenTile(mf); } } //Wyrmgus start // if (mfp.Visible[opponent] && !mfp.Visible[player]) { if (mfp.Visible[opponent] && !mfp.Visible[player] && !Players[opponent].Revealed) { //Wyrmgus end mfp.Visible[player] = 1; if (player == ThisPlayer->Index) { Map.MarkSeenTile(mf); } } } } // Do a real hardcore seen recount. Now we remark EVERYTHING for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &unit = **it; if (!unit.Destroyed) { MapMarkUnitSight(unit); } } }
/* virtual */ void COrder_Die::Execute(CUnit &unit) { // Show death animation if (AnimateActionDie(unit) == false) { // some units has no death animation unit.Anim.Unbreakable = 0; } if (unit.Anim.Unbreakable) { return ; } const CUnitType &type = *unit.Type; // Die sequence terminated, generate corpse. if (type.CorpseType == NULL) { unit.Remove(NULL); //Wyrmgus start UnitClearOrders(unit); //Wyrmgus end unit.Release(); return ; } const CUnitType &corpseType = *type.CorpseType; Assert(type.TileWidth >= corpseType.TileWidth && type.TileHeight >= corpseType.TileHeight); // Update sight for new corpse // We have to unmark BEFORE changing the type. // Always do that, since types can have different vision properties. //Wyrmgus start // unit.Remove(NULL); MapUnmarkUnitSight(unit); //Wyrmgus end unit.Type = &corpseType; unit.Stats = &corpseType.Stats[unit.Player->Index]; //Wyrmgus start const unsigned int var_size = UnitTypeVar.GetNumberVariable(); std::copy(corpseType.Stats[unit.Player->Index].Variables, corpseType.Stats[unit.Player->Index].Variables + var_size, unit.Variable); //Wyrmgus end UpdateUnitSightRange(unit); //Wyrmgus start // unit.Place(unit.tilePos); MapMarkUnitSight(unit); //Wyrmgus end unit.Frame = 0; UnitUpdateHeading(unit); AnimateActionDie(unit); // with new corpse. }
//Wyrmgus start //static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) //Wyrmgus end { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; //Wyrmgus start /* if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } */ if (!SaveGameLoading) { if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } } //Wyrmgus end CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; if (unit.Active) { player.UnitTypesAiActiveCount[oldtype.Slot]--; player.UnitTypesAiActiveCount[newtype.Slot]++; } //Wyrmgus start if (unit.Character == NULL) { player.UnitTypesNonHeroCount[oldtype.Slot]--; player.UnitTypesNonHeroCount[newtype.Slot]++; } //Wyrmgus end player.Demand += newtype.Stats[player.Index].Variables[DEMAND_INDEX].Value - oldtype.Stats[player.Index].Variables[DEMAND_INDEX].Value; player.Supply += newtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value - oldtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; //Wyrmgus start const CUnitStats &oldstats = oldtype.Stats[player.Index]; //Wyrmgus end for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { //Wyrmgus start /* if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } */ if (i == LEVEL_INDEX) { //if the unit's level changed in accordance to the difference between the levels of the two unit types, then its level change would be duplicated when leveling up, so let's skip the level variable here continue; } if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value += newstats.Variables[i].Max - oldstats.Variables[i].Max; } else { unit.Variable[i].Value += newstats.Variables[i].Value - oldstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max += newstats.Variables[i].Max - oldstats.Variables[i].Max; unit.Variable[i].Increase += newstats.Variables[i].Increase - oldstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } //Wyrmgus end } //Wyrmgus start for (size_t i = 0; i < UnitTypes.size(); ++i) { if (newstats.UnitStock[i] != oldstats.UnitStock[i]) { unit.UnitStock[i] += newstats.UnitStock[i] - oldstats.UnitStock[i]; unit.UnitStock[i] = std::max(unit.UnitStock[i], 0); } } //Wyrmgus end //Wyrmgus start //change variation if upgrading (new unit type may have different variations) unit.ChooseVariation(&newtype); for (int i = 0; i < MaxImageLayers; ++i) { unit.ChooseVariation(&newtype, false, i); } //Wyrmgus end unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; //Wyrmgus start //deequip the current equipment if they are incompatible with the new unit type for (int i = 0; i < MaxItemSlots; ++i) { for (size_t j = 0; j < unit.EquippedItems[i].size(); ++j) { if (!unit.CanEquipItemClass(unit.EquippedItems[i][j]->Type->ItemClass)) { unit.DeequipItem(*unit.EquippedItems[i][j]); } } } //Wyrmgus end //Wyrmgus start //change personal name if new unit type's civilization is different from old unit type's civilization if (unit.Character == NULL && !oldtype.Civilization.empty() && !newtype.Civilization.empty() && oldtype.Civilization != newtype.Civilization) { unit.UpdatePersonalName(); } //Wyrmgus end if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; unit.SpellCoolDownTimers = new int[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); memset(unit.SpellCoolDownTimers, 0, SpellTypeTable.size() * sizeof(int)); } UpdateForNewUnit(unit, 1); //Wyrmgus start /* // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } */ if (!SaveGameLoading) { // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); //Wyrmgus start //if unit has a container, update the container's attack range, as the unit's range may have been changed with the upgrade container->UpdateContainerAttackRange(); //Wyrmgus end } } //Wyrmgus end //Wyrmgus start //update the unit's XP required, as its level or points may have changed unit.UpdateXPRequired(); //Wyrmgus end //Wyrmgus start /* // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } */ if (!SaveGameLoading) { // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } } //Wyrmgus end return 1; }
/** ** Apply the modifiers of an upgrade. ** ** This function will mark upgrade done and do all required modifications ** to unit types and will modify allow/forbid maps ** ** @param player Player that get all the upgrades. ** @param um Upgrade modifier that do the effects */ static void ApplyUpgradeModifier(CPlayer *player, const CUpgradeModifier *um) { int z; // iterator on upgrade or unittype. int j; // iterator on cost or variable. int pn; // player number. int varModified; // 0 if variable is not modified. int numunits; // number of unit of the current type. CUnit *unitupgrade[UnitMax]; // array of unit of the current type CUnit *unit; // current unit. Assert(player); Assert(um); pn = player->Index; for (z = 0; z < UpgradeMax; ++z) { // allow/forbid upgrades for player. only if upgrade is not acquired // FIXME: check if modify is allowed if (player->Allow.Upgrades[z] != 'R') { if (um->ChangeUpgrades[z] == 'A') { player->Allow.Upgrades[z] = 'A'; } if (um->ChangeUpgrades[z] == 'F') { player->Allow.Upgrades[z] = 'F'; } // we can even have upgrade acquired w/o costs if (um->ChangeUpgrades[z] == 'R') { player->Allow.Upgrades[z] = 'R'; } } } for (z = 0; z < UnitTypeMax; ++z) { // add/remove allowed units // FIXME: check if modify is allowed player->Allow.Units[z] += um->ChangeUnits[z]; Assert(um->ApplyTo[z] == '?' || um->ApplyTo[z] == 'X'); // this modifier should be applied to unittype id == z if (um->ApplyTo[z] == 'X') { // If Sight range is upgraded, we need to change EVERY unit // to the new range, otherwise the counters get confused. if (um->Modifier.Variables[SIGHTRANGE_INDEX].Value) { numunits = FindUnitsByType(UnitTypes[z], unitupgrade); for (numunits--; numunits >= 0; --numunits) { unit = unitupgrade[numunits]; if (unit->Player->Index == pn && !unit->Removed) { MapUnmarkUnitSight(unit); unit->CurrentSightRange = UnitTypes[z]->Stats[pn].Variables[SIGHTRANGE_INDEX].Max + um->Modifier.Variables[SIGHTRANGE_INDEX].Value; MapMarkUnitSight(unit); } } } // upgrade costs :) for (j = 0; j < MaxCosts; ++j) { UnitTypes[z]->Stats[pn].Costs[j] += um->Modifier.Costs[j]; } varModified = 0; for (j = 0; j < UnitTypeVar.NumberVariable; j++) { varModified |= um->Modifier.Variables[j].Value | um->Modifier.Variables[j].Max | um->Modifier.Variables[j].Increase; UnitTypes[z]->Stats[pn].Variables[j].Value += um->Modifier.Variables[j].Value; if (UnitTypes[z]->Stats[pn].Variables[j].Value < 0) { UnitTypes[z]->Stats[pn].Variables[j].Value = 0; } UnitTypes[z]->Stats[pn].Variables[j].Max += um->Modifier.Variables[j].Max; if (UnitTypes[z]->Stats[pn].Variables[j].Max < 0) { UnitTypes[z]->Stats[pn].Variables[j].Max = 0; } if (UnitTypes[z]->Stats[pn].Variables[j].Value > UnitTypes[z]->Stats[pn].Variables[j].Max) { UnitTypes[z]->Stats[pn].Variables[j].Value = UnitTypes[z]->Stats[pn].Variables[j].Max; } UnitTypes[z]->Stats[pn].Variables[j].Increase += um->Modifier.Variables[j].Increase; } // And now modify ingame units if (varModified) { numunits = FindUnitsByType(UnitTypes[z], unitupgrade); numunits--; // Change to 0 Start not 1 start for (; numunits >= 0; --numunits) { unit = unitupgrade[numunits]; if (unit->Player->Index != player->Index) { continue; } for (j = 0; j < UnitTypeVar.NumberVariable; j++) { unit->Variable[j].Value += um->Modifier.Variables[j].Value; if (unit->Variable[j].Value < 0) { unit->Variable[j].Value = 0; } unit->Variable[j].Max += um->Modifier.Variables[j].Max; if (unit->Variable[j].Max < 0) { unit->Variable[j].Max = 0; } if (unit->Variable[j].Value > unit->Variable[j].Max) { unit->Variable[j].Value = unit->Variable[j].Max; } unit->Variable[j].Increase += um->Modifier.Variables[j].Increase; } } } if (um->ConvertTo) { ConvertUnitTypeTo(player,UnitTypes[z], um->ConvertTo); } } } }
static void Finish(COrder_Built &order, CUnit &unit) { const CUnitType &type = *unit.Type; CPlayer &player = *unit.Player; DebugPrint("%d: Building %s(%s) ready.\n" _C_ player.Index _C_ type.Ident.c_str() _C_ type.Name.c_str()); // HACK: the building is ready now player.UnitTypesCount[type.Slot]++; if (unit.Active) { player.UnitTypesAiActiveCount[type.Slot]++; } unit.Constructed = 0; if (unit.Frame < 0) { unit.Frame = -1; } else { unit.Frame = 0; } CUnit *worker = order.GetWorkerPtr(); if (worker != NULL) { if (type.BoolFlag[BUILDERLOST_INDEX].value) { // Bye bye worker. LetUnitDie(*worker); worker = NULL; } else { // Drop out the worker. worker->ClearAction(); DropOutOnSide(*worker, LookingW, &unit); // If we can harvest from the new building, do it. if (worker->Type->ResInfo[type.GivesResource]) { CommandResource(*worker, unit, 0); } // If we can reurn goods to a new depot, do it. if (worker->CurrentResource && worker->ResourcesHeld > 0 && type.CanStore[worker->CurrentResource]) { CommandReturnGoods(*worker, &unit, 0); } } } if (type.GivesResource && type.StartingResources != 0) { // Has StartingResources, Use those unit.ResourcesHeld = type.StartingResources; } player.Notify(NotifyGreen, unit.tilePos, _("New %s done"), type.Name.c_str()); if (&player == ThisPlayer) { if (type.MapSound.Ready.Sound) { PlayUnitSound(unit, VoiceReady); } else if (worker) { PlayUnitSound(*worker, VoiceWorkCompleted); } else { PlayUnitSound(unit, VoiceBuilding); } } if (player.AiEnabled) { /* Worker can be NULL */ AiWorkComplete(worker, unit); } // FIXME: Vladi: this is just a hack to test wall fixing, // FIXME: also not sure if the right place... // FIXME: Johns: hardcoded unit-type wall / more races! if (&type == UnitTypeOrcWall || &type == UnitTypeHumanWall) { Map.SetWall(unit.tilePos, &type == UnitTypeHumanWall); unit.Remove(NULL); UnitLost(unit); UnitClearOrders(unit); unit.Release(); return ; } UpdateForNewUnit(unit, 0); // Set the direction of the building if it supports them if (type.NumDirections > 1 && type.BoolFlag[NORANDOMPLACING_INDEX].value == false) { if (type.BoolFlag[WALL_INDEX].value) { // Special logic for walls CorrectWallDirections(unit); CorrectWallNeighBours(unit); } else { unit.Direction = (MyRand() >> 8) & 0xFF; // random heading } UnitUpdateHeading(unit); } if (IsOnlySelected(unit) || &player == ThisPlayer) { SelectedUnitChanged(); } MapUnmarkUnitSight(unit); unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max; MapMarkUnitSight(unit); order.Finished = true; }
static void GameLogicLoop() { // Can't find a better place. // FIXME: We need find better place! SaveGameLoading = false; // // Game logic part // if (!GamePaused && NetworkInSync && !SkipGameCycle) { SinglePlayerReplayEachCycle(); ++GameCycle; MultiPlayerReplayEachCycle(); NetworkCommands(); // Get network commands TriggersEachCycle();// handle triggers UnitActions(); // handle units MissileActions(); // handle missiles PlayersEachCycle(); // handle players UpdateTimer(); // update game timer // // Work todo each second. // Split into different frames, to reduce cpu time. // Increment mana of magic units. // Update mini-map. // Update map fog of war. // Call AI. // Check game goals. // Check rescue of units. // switch (GameCycle % CYCLES_PER_SECOND) { case 0: // At cycle 0, start all ai players... if (GameCycle == 0) { for (int player = 0; player < NumPlayers; ++player) { PlayersEachSecond(player); } } break; case 1: break; case 2: break; case 3: // minimap update UI.Minimap.UpdateCache = true; break; case 4: break; case 5: // forest grow Map.RegenerateForest(); break; case 6: // overtaking units RescueUnits(); break; default: { // FIXME: assume that NumPlayers < (CYCLES_PER_SECOND - 7) int player = (GameCycle % CYCLES_PER_SECOND) - 7; Assert(player >= 0); if (player < NumPlayers) { PlayersEachSecond(player); } } } //Wyrmgus start if (GameCycle > 0 && GameCycle % (CYCLES_PER_SECOND * 10 * 3) == 0) { // every 10 seconds of gameplay = 1 hour for time of day calculations, change time of day every three hours if (!GameSettings.Inside) { // only change the time of the day if outdoors GameTimeOfDay += 1; if (GameTimeOfDay == MaxTimesOfDay) { GameTimeOfDay = 1; } } else { // indoors it is always dark (maybe would be better to allow a special setting to have bright indoor places? GameTimeOfDay = NoTimeOfDay; // make indoors have no time of day setting until it is possible to make light sources change their surrounding "time of day" } //update the sight of all units for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &unit = **it; if (!unit.Destroyed) { MapUnmarkUnitSight(unit); UpdateUnitSightRange(unit); MapMarkUnitSight(unit); } } } //Wyrmgus end //Wyrmgus start // if (Preference.AutosaveMinutes != 0 && !IsNetworkGame() && GameCycle > 0 && (GameCycle % (CYCLES_PER_SECOND * 60 * Preference.AutosaveMinutes)) == 0) { // autosave every X minutes, if the option is enabled if (Preference.AutosaveMinutes != 0 && !IsNetworkGame() && !GrandStrategy && GameCycle > 0 && (GameCycle % (CYCLES_PER_SECOND * 60 * Preference.AutosaveMinutes)) == 0) { // autosave every X minutes, if the option is enabled //Wyrmgus end UI.StatusLine.Set(_("Autosave")); //Wyrmgus start // SaveGame("autosave.sav"); CclCommand("if (RunSaveGame ~= nil) then RunSaveGame(\"autosave.sav\") end;"); //Wyrmgus end } } UpdateMessages(); // update messages ParticleManager.update(); // handle particles CheckMusicFinished(); // Check for next song if (FastForwardCycle <= GameCycle || !(GameCycle & 0x3f)) { WaitEventsOneFrame(); } if (!NetworkInSync) { NetworkRecover(); // recover network } }
/** ** Transform a unit in another. ** ** @param unit unit to transform. ** @param newtype new type of the unit. ** ** @return 0 on error, 1 if nothing happens, 2 else. */ static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; player.Demand += newtype.Demand - oldtype.Demand; player.Supply += newtype.Supply - oldtype.Supply; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); } UpdateForNewUnit(unit, 1); // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } return 1; }
/** ** Parse unit ** ** @param l Lua state. ** ** @todo Verify that vision table is always correct (transporter) ** @todo (PlaceUnit() and host-info). */ static int CclUnit(lua_State *l) { const int slot = LuaToNumber(l, 1); if (!lua_istable(l, 2)) { LuaError(l, "incorrect argument"); } CUnit *unit = NULL; CUnitType *type = NULL; CUnitType *seentype = NULL; CPlayer *player = NULL; // Parse the list: const int args = lua_rawlen(l, 2); for (int j = 0; j < args; ++j) { const char *value = LuaToString(l, 2, j + 1); ++j; if (!strcmp(value, "type")) { type = UnitTypeByIdent(LuaToString(l, 2, j + 1)); } else if (!strcmp(value, "seen-type")) { seentype = UnitTypeByIdent(LuaToString(l, 2, j + 1)); } else if (!strcmp(value, "player")) { player = &Players[LuaToNumber(l, 2, j + 1)]; // During a unit's death animation (when action is "die" but the // unit still has its original type, i.e. it's still not a corpse) // the unit is already removed from map and from player's // unit list (=the unit went through LetUnitDie() which // calls RemoveUnit() and UnitLost()). Such a unit should not // be put on player's unit list! However, this state is not // easily detected from this place. It seems that it is // characterized by // unit->CurrentAction()==UnitActionDie so we have to wait // until we parsed at least Unit::Orders[]. Assert(type); unit = &UnitManager.GetSlotUnit(slot); unit->Init(*type); unit->Seen.Type = seentype; unit->Active = 0; unit->Removed = 0; Assert(UnitNumber(*unit) == slot); } else if (!strcmp(value, "current-sight-range")) { unit->CurrentSightRange = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "refs")) { unit->Refs = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "host-info")) { lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1) || lua_rawlen(l, -1) != 4) { LuaError(l, "incorrect argument"); } Vec2i pos; int w; int h; pos.x = LuaToNumber(l, -1, 1); pos.y = LuaToNumber(l, -1, 2); w = LuaToNumber(l, -1, 3); h = LuaToNumber(l, -1, 4); MapSight(*player, pos, w, h, unit->CurrentSightRange, MapMarkTileSight); // Detectcloak works in container if (unit->Type->DetectCloak) { MapSight(*player, pos, w, h, unit->CurrentSightRange, MapMarkTileDetectCloak); } // Radar(Jammer) not. lua_pop(l, 1); } else if (!strcmp(value, "tile")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->tilePos.x , &unit->tilePos.y, -1); lua_pop(l, 1); unit->Offset = Map.getIndex(unit->tilePos); } else if (!strcmp(value, "seen-tile")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->Seen.tilePos.x , &unit->Seen.tilePos.y, -1); lua_pop(l, 1); } else if (!strcmp(value, "stats")) { unit->Stats = &type->Stats[LuaToNumber(l, 2, j + 1)]; } else if (!strcmp(value, "pixel")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->IX , &unit->IY, -1); lua_pop(l, 1); } else if (!strcmp(value, "seen-pixel")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->Seen.IX , &unit->Seen.IY, -1); lua_pop(l, 1); } else if (!strcmp(value, "frame")) { unit->Frame = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "seen")) { unit->Seen.Frame = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "not-seen")) { unit->Seen.Frame = UnitNotSeen; --j; } else if (!strcmp(value, "direction")) { unit->Direction = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "damage-type")) { unit->DamagedType = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "attacked")) { // FIXME : unsigned long should be better handled unit->Attacked = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "auto-repair")) { unit->AutoRepair = 1; --j; } else if (!strcmp(value, "burning")) { unit->Burning = 1; --j; } else if (!strcmp(value, "destroyed")) { unit->Destroyed = 1; --j; } else if (!strcmp(value, "removed")) { unit->Removed = 1; --j; } else if (!strcmp(value, "selected")) { unit->Selected = 1; --j; } else if (!strcmp(value, "summoned")) { unit->Summoned = 1; --j; } else if (!strcmp(value, "waiting")) { unit->Waiting = 1; --j; } else if (!strcmp(value, "mine-low")) { unit->MineLow = 1; --j; } else if (!strcmp(value, "rescued-from")) { unit->RescuedFrom = &Players[LuaToNumber(l, 2, j + 1)]; } else if (!strcmp(value, "seen-by-player")) { const char *s = LuaToString(l, 2, j + 1); unit->Seen.ByPlayer = 0; for (int i = 0; i < PlayerMax && *s; ++i, ++s) { if (*s == '-' || *s == '_' || *s == ' ') { unit->Seen.ByPlayer &= ~(1 << i); } else { unit->Seen.ByPlayer |= (1 << i); } } } else if (!strcmp(value, "seen-destroyed")) { const char *s = LuaToString(l, 2, j + 1); unit->Seen.Destroyed = 0; for (int i = 0; i < PlayerMax && *s; ++i, ++s) { if (*s == '-' || *s == '_' || *s == ' ') { unit->Seen.Destroyed &= ~(1 << i); } else { unit->Seen.Destroyed |= (1 << i); } } } else if (!strcmp(value, "constructed")) { unit->Constructed = 1; --j; } else if (!strcmp(value, "seen-constructed")) { unit->Seen.Constructed = 1; --j; } else if (!strcmp(value, "seen-state")) { unit->Seen.State = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "active")) { unit->Active = 1; --j; } else if (!strcmp(value, "ttl")) { // FIXME : unsigned long should be better handled unit->TTL = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "threshold")) { // FIXME : unsigned long should be better handled unit->Threshold = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "group-id")) { unit->GroupId = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "last-group")) { unit->LastGroup = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "resources-held")) { unit->ResourcesHeld = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "current-resource")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->CurrentResource = CclGetResourceByName(l); lua_pop(l, 1); } else if (!strcmp(value, "pathfinder-input")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->pathFinderData->input.Load(l); lua_pop(l, 1); } else if (!strcmp(value, "pathfinder-output")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->pathFinderData->output.Load(l); lua_pop(l, 1); } else if (!strcmp(value, "wait")) { unit->Wait = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "anim-data")) { lua_rawgeti(l, 2, j + 1); CAnimations::LoadUnitAnim(l, *unit, -1); lua_pop(l, 1); } else if (!strcmp(value, "wait-anim-data")) { lua_rawgeti(l, 2, j + 1); CAnimations::LoadWaitUnitAnim(l, *unit, -1); lua_pop(l, 1); } else if (!strcmp(value, "blink")) { unit->Blink = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "moving")) { unit->Moving = 1; --j; } else if (!strcmp(value, "re-cast")) { unit->ReCast = 1; --j; } else if (!strcmp(value, "boarded")) { unit->Boarded = 1; --j; } else if (!strcmp(value, "next-worker")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->NextWorker = CclGetUnitFromRef(l); lua_pop(l, 1); } else if (!strcmp(value, "resource-workers")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Workers = CclGetUnitFromRef(l); lua_pop(l, 1); } else if (!strcmp(value, "resource-assigned")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Assigned = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(value, "resource-active")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Active = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(value, "units-boarded-count")) { unit->BoardCount = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "units-contained")) { int subargs; int k; lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1)) { LuaError(l, "incorrect argument"); } subargs = lua_rawlen(l, -1); for (k = 0; k < subargs; ++k) { lua_rawgeti(l, -1, k + 1); CUnit *u = CclGetUnitFromRef(l); lua_pop(l, 1); u->AddInContainer(*unit); } lua_pop(l, 1); } else if (!strcmp(value, "orders")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrders(l, *unit); lua_pop(l, 1); // now we know unit's action so we can assign it to a player Assert(player != NULL); unit->AssignToPlayer(*player); if (unit->CurrentAction() == UnitActionBuilt) { DebugPrint("HACK: the building is not ready yet\n"); // HACK: the building is not ready yet unit->Player->UnitTypesCount[type->Slot]--; if (unit->Active) { unit->Player->UnitTypesAiActiveCount[type->Slot]--; } } } else if (!strcmp(value, "critical-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit , &unit->CriticalOrder); lua_pop(l, 1); } else if (!strcmp(value, "saved-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit, &unit->SavedOrder); lua_pop(l, 1); } else if (!strcmp(value, "new-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit, &unit->NewOrder); lua_pop(l, 1); } else if (!strcmp(value, "goal")) { unit->Goal = &UnitManager.GetSlotUnit(LuaToNumber(l, 2, j + 1)); } else if (!strcmp(value, "auto-cast")) { const char *s = LuaToString(l, 2, j + 1); Assert(SpellTypeByIdent(s)); if (!unit->AutoCastSpell) { unit->AutoCastSpell = new char[SpellTypeTable.size()]; memset(unit->AutoCastSpell, 0, SpellTypeTable.size()); } unit->AutoCastSpell[SpellTypeByIdent(s)->Slot] = 1; } else if (!strcmp(value, "spell-cooldown")) { lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1) || lua_rawlen(l, -1) != SpellTypeTable.size()) { LuaError(l, "incorrect argument"); } if (!unit->SpellCoolDownTimers) { unit->SpellCoolDownTimers = new int[SpellTypeTable.size()]; memset(unit->SpellCoolDownTimers, 0, SpellTypeTable.size() * sizeof(int)); } for (size_t k = 0; k < SpellTypeTable.size(); ++k) { unit->SpellCoolDownTimers[k] = LuaToNumber(l, -1, k + 1); } lua_pop(l, 1); } else { const int index = UnitTypeVar.VariableNameLookup[value];// User variables if (index != -1) { // Valid index lua_rawgeti(l, 2, j + 1); DefineVariableField(l, unit->Variable + index, -1); lua_pop(l, 1); continue; } LuaError(l, "Unit: Unsupported tag: %s" _C_ value); } } // Unit may not have been assigned to a player before now. If not, // do so now. It is only assigned earlier if we have orders. // for loading of units from a MAP, and not a savegame, we won't // have orders for those units. They should appear here as if // they were just created. if (!unit->Player) { Assert(player); unit->AssignToPlayer(*player); UpdateForNewUnit(*unit, 0); } // Revealers are units that can see while removed if (unit->Removed && unit->Type->Revealer) { MapMarkUnitSight(*unit); } // Fix Colors for rescued units. if (unit->RescuedFrom) { unit->Colors = &unit->RescuedFrom->UnitColors; } return 0; }
static void GameLogicLoop() { // Can't find a better place. // FIXME: We need find better place! SaveGameLoading = false; #ifdef USE_OAML if (enableOAML && oaml) { // Time of day can change our main music loop, if the current playing track is set for this SetMusicCondition(OAML_CONDID_MAIN_LOOP, Map.TimeOfDay[CurrentMapLayer]); } #endif // // Game logic part // if (!GamePaused && NetworkInSync && !SkipGameCycle) { SinglePlayerReplayEachCycle(); ++GameCycle; MultiPlayerReplayEachCycle(); NetworkCommands(); // Get network commands TriggersEachCycle();// handle triggers UnitActions(); // handle units MissileActions(); // handle missiles PlayersEachCycle(); // handle players UpdateTimer(); // update game timer // // Work todo each second. // Split into different frames, to reduce cpu time. // Increment mana of magic units. // Update mini-map. // Update map fog of war. // Call AI. // Check game goals. // Check rescue of units. // switch (GameCycle % CYCLES_PER_SECOND) { case 0: // At cycle 0, start all ai players... if (GameCycle == 0) { for (int player = 0; player < NumPlayers; ++player) { PlayersEachSecond(player); } } break; case 1: break; case 2: break; case 3: // minimap update UI.Minimap.UpdateCache = true; break; case 4: break; case 5: // forest grow Map.RegenerateForest(); break; case 6: // overtaking units RescueUnits(); break; //Wyrmgus start /* default: { // FIXME: assume that NumPlayers < (CYCLES_PER_SECOND - 7) int player = (GameCycle % CYCLES_PER_SECOND) - 7; Assert(player >= 0); if (player < NumPlayers) { PlayersEachSecond(player); } } */ //Wyrmgus end } //Wyrmgus start int player = (GameCycle - 1) % CYCLES_PER_SECOND; Assert(player >= 0); if (player < NumPlayers) { PlayersEachSecond(player); if ((player + CYCLES_PER_SECOND) < NumPlayers) { PlayersEachSecond(player + CYCLES_PER_SECOND); } } player = (GameCycle - 1) % CYCLES_PER_MINUTE; Assert(player >= 0); if (player < NumPlayers) { PlayersEachMinute(player); } //Wyrmgus end //Wyrmgus start for (size_t z = 0; z < Map.Fields.size(); ++z) { if (GameSettings.Inside || GameSettings.NoTimeOfDay || !Map.TimeOfDaySeconds[z]) { // indoors it is always dark (maybe would be better to allow a special setting to have bright indoor places? Map.TimeOfDay[z] = NoTimeOfDay; // make indoors have no time of day setting until it is possible to make light sources change their surrounding "time of day" continue; } if (GameCycle > 0 && GameCycle % (CYCLES_PER_SECOND * Map.TimeOfDaySeconds[z]) == 0) { Map.TimeOfDay[z] += 1; if (Map.TimeOfDay[z] == MaxTimesOfDay) { Map.TimeOfDay[z] = 1; } #ifdef USE_OAML if (enableOAML && oaml && z == CurrentMapLayer) { // Time of day can change our main music loop, if the current playing track is set for this SetMusicCondition(OAML_CONDID_MAIN_LOOP, Map.TimeOfDay[z]); } #endif //update the sight of all units for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit *unit = *it; if ( unit && unit->IsAlive() && unit->MapLayer == z && ( ((Map.TimeOfDay[z] == MorningTimeOfDay || Map.TimeOfDay[z] == DuskTimeOfDay) && unit->Variable[DAYSIGHTRANGEBONUS_INDEX].Value != 0) // if has day sight bonus and is entering or exiting day || ((Map.TimeOfDay[z] == FirstWatchTimeOfDay || Map.TimeOfDay[z] == DawnTimeOfDay) && unit->Variable[NIGHTSIGHTRANGEBONUS_INDEX].Value != 0) // if has night sight bonus and is entering or exiting night ) ) { MapUnmarkUnitSight(*unit); UpdateUnitSightRange(*unit); MapMarkUnitSight(*unit); } } } } //Wyrmgus end //Wyrmgus start // if (Preference.AutosaveMinutes != 0 && !IsNetworkGame() && GameCycle > 0 && (GameCycle % (CYCLES_PER_SECOND * 60 * Preference.AutosaveMinutes)) == 0) { // autosave every X minutes, if the option is enabled if (Preference.AutosaveMinutes != 0 && !IsNetworkGame() && !GrandStrategy && GameCycle > 0 && (GameCycle % (CYCLES_PER_MINUTE * Preference.AutosaveMinutes)) == 0) { // autosave every X minutes, if the option is enabled //Wyrmgus end UI.StatusLine.Set(_("Autosave")); //Wyrmgus start // SaveGame("autosave.sav"); CclCommand("if (RunSaveGame ~= nil) then RunSaveGame(\"autosave.sav\") end;"); //Wyrmgus end } } UpdateMessages(); // update messages ParticleManager.update(); // handle particles CheckMusicFinished(); // Check for next song if (FastForwardCycle <= GameCycle || !(GameCycle & 0x3f)) { WaitEventsOneFrame(); } if (!NetworkInSync) { NetworkRecover(); // recover network } }
/** ** Remove the modifiers of an upgrade. ** ** This function will unmark upgrade as done and undo all required modifications ** to unit types and will modify allow/forbid maps back ** ** @param player Player that get all the upgrades. ** @param um Upgrade modifier that do the effects */ static void RemoveUpgradeModifier(CPlayer &player, const CUpgradeModifier *um) { Assert(um); int pn = player.Index; if (um->SpeedResearch != 0) { player.SpeedResearch -= um->SpeedResearch; } for (int z = 0; z < UpgradeMax; ++z) { // allow/forbid upgrades for player. only if upgrade is not acquired // FIXME: check if modify is allowed if (player.Allow.Upgrades[z] != 'R') { if (um->ChangeUpgrades[z] == 'A') { player.Allow.Upgrades[z] = 'F'; } if (um->ChangeUpgrades[z] == 'F') { player.Allow.Upgrades[z] = 'A'; } // we can even have upgrade acquired w/o costs if (um->ChangeUpgrades[z] == 'R') { player.Allow.Upgrades[z] = 'A'; } } } for (size_t z = 0; z < UnitTypes.size(); ++z) { CUnitStats &stat = UnitTypes[z]->Stats[pn]; // add/remove allowed units // FIXME: check if modify is allowed player.Allow.Units[z] -= um->ChangeUnits[z]; Assert(um->ApplyTo[z] == '?' || um->ApplyTo[z] == 'X'); // this modifier should be applied to unittype id == z if (um->ApplyTo[z] == 'X') { // If Sight range is upgraded, we need to change EVERY unit // to the new range, otherwise the counters get confused. if (um->Modifier.Variables[SIGHTRANGE_INDEX].Value) { std::vector<CUnit *> unitupgrade; FindUnitsByType(*UnitTypes[z], unitupgrade); for (size_t j = 0; j != unitupgrade.size(); ++j) { CUnit &unit = *unitupgrade[j]; if (unit.Player->Index == pn && !unit.Removed) { MapUnmarkUnitSight(unit); unit.CurrentSightRange = stat.Variables[SIGHTRANGE_INDEX].Max - um->Modifier.Variables[SIGHTRANGE_INDEX].Value; MapMarkUnitSight(unit); } } } // upgrade costs :) for (unsigned int j = 0; j < MaxCosts; ++j) { stat.Costs[j] -= um->Modifier.Costs[j]; stat.Storing[j] -= um->Modifier.Storing[j]; } int varModified = 0; for (unsigned int j = 0; j < UnitTypeVar.GetNumberVariable(); j++) { varModified |= um->Modifier.Variables[j].Value | um->Modifier.Variables[j].Max | um->Modifier.Variables[j].Increase | um->Modifier.Variables[j].Enable | um->ModifyPercent[j]; stat.Variables[j].Enable |= um->Modifier.Variables[j].Enable; if (um->ModifyPercent[j]) { stat.Variables[j].Value = stat.Variables[j].Value * 100 / (100 + um->ModifyPercent[j]); stat.Variables[j].Max = stat.Variables[j].Max * 100 / (100 + um->ModifyPercent[j]); } else { stat.Variables[j].Value -= um->Modifier.Variables[j].Value; stat.Variables[j].Max -= um->Modifier.Variables[j].Max; stat.Variables[j].Increase -= um->Modifier.Variables[j].Increase; } stat.Variables[j].Max = std::max(stat.Variables[j].Max, 0); clamp(&stat.Variables[j].Value, 0, stat.Variables[j].Max); } // And now modify ingame units if (varModified) { std::vector<CUnit *> unitupgrade; FindUnitsByType(*UnitTypes[z], unitupgrade, true); for (size_t j = 0; j != unitupgrade.size(); ++j) { CUnit &unit = *unitupgrade[j]; if (unit.Player->Index != player.Index) { continue; } for (unsigned int j = 0; j < UnitTypeVar.GetNumberVariable(); j++) { unit.Variable[j].Enable |= um->Modifier.Variables[j].Enable; if (um->ModifyPercent[j]) { unit.Variable[j].Value = unit.Variable[j].Value * 100 / (100 + um->ModifyPercent[j]); unit.Variable[j].Max = unit.Variable[j].Max * 100 / (100 + um->ModifyPercent[j]); } else { unit.Variable[j].Value -= um->Modifier.Variables[j].Value; unit.Variable[j].Increase -= um->Modifier.Variables[j].Increase; } unit.Variable[j].Max -= um->Modifier.Variables[j].Max; unit.Variable[j].Max = std::max(unit.Variable[j].Max, 0); clamp(&unit.Variable[j].Value, 0, unit.Variable[j].Max); } } } if (um->ConvertTo) { ConvertUnitTypeTo(player, *um->ConvertTo, *UnitTypes[z]); } } } }