/** ** Toggle units from a particular type and belonging to the local player. ** ** The base is included in the selection and defines ** the type of the other units to be selected. ** ** @param base Toggle all units of same type. ** ** @return Number of units found, 0 means selection unchanged ** ** FIXME: toggle not written ** FIXME: should always select the nearest 9 units to the base! */ int ToggleUnitsByType(CUnit &base) { const CUnitType &type = *base.Type; // if unit is a cadaver or hidden (not on map) // no unit can be selected. if (base.Removed || base.IsAlive() == false) { return 0; } // if unit isn't belonging to the player, or is a static unit // (like a building), only 1 unit can be selected at the same time. if (!CanSelectMultipleUnits(*base.Player) || !type.SelectableByRectangle) { return 0; } if (!SelectUnit(base)) { // Add base to selection return 0; } // // Search for other visible units of the same type // // select all visible units. // StephanR: should be (MapX,MapY,MapX+MapWidth-1,MapY+MapHeight-1) ??? // FIXME: this should probably be cleaner implemented if SelectUnitsByType() // took parameters of the selection rectangle as arguments */ const CViewport *vp = UI.MouseViewport; const Vec2i offset(1, 1); const Vec2i minPos = vp->MapPos - offset; const Vec2i vpSize(vp->MapWidth, vp->MapHeight); const Vec2i maxPos = vp->MapPos + vpSize + offset; std::vector<CUnit *> table; Select(minPos, maxPos, table, HasSameTypeAs(type)); // FIXME: peon/peasant with gold/wood & co are considered from // different type... idem for tankers for (size_t i = 0; i < table.size(); ++i) { CUnit &unit = *table[i]; if (!CanSelectMultipleUnits(*unit.Player)) { continue; } if (unit.IsUnusable()) { // guess SelectUnits doesn't check this continue; } if (&unit == &base) { // no need to have the same unit twice continue; } if (unit.TeamSelected) { // Somebody else onteam has this unit continue; } if (!SelectUnit(unit)) { // add unit to selection return Selected.size(); } } NetworkSendSelection(&Selected[0], Selected.size()); return Selected.size(); }
COrder_Resource::~COrder_Resource() { CUnit *mine = this->Resource.Mine; if (mine && mine->IsAlive()) { worker->DeAssignWorkerFromMine(*mine); } CUnit *goal = this->GetGoal(); if (goal) { // If mining decrease the active count on the resource. if (this->State == SUB_GATHER_RESOURCE) { goal->Resource.Active--; Assert(goal->Resource.Active >= 0); } } }
/* virtual */ bool COrder_Attack::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/) { CUnit *goal = this->GetGoal(); if (goal) { if (goal->IsAlive() == false) { this->ClearGoal(); this->goalPos = goal->tilePos; return false; } if (goal == attacker) { return true; } if (goal->CurrentAction() == UnitActionAttack) { const COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder()); if (order.GetGoal() == &unit) { //we already fight with one of attackers; return true; } } } return false; }
/** ** Add a unit to the other selected units. ** ** @param unit Pointer to unit to add. ** ** @return true if added to selection, false otherwise ** (if Selected.size() == MaxSelectable or ** unit is already selected or unselectable) */ int SelectUnit(CUnit &unit) { if (unit.Type->BoolFlag[REVEALER_INDEX].value) { // Revealers cannot be selected DebugPrint("Selecting revealer?\n"); return 0; } if (unit.Removed) { // Removed cannot be selected DebugPrint("Selecting removed?\n"); return 0; } if (Selected.size() == MaxSelectable) { return 0; } if (unit.Selected) { return 0; } if (unit.Type->BoolFlag[ISNOTSELECTABLE_INDEX].value && GameRunning) { return 0; } //Wyrmgus start if (!unit.IsAlive()) { // don't select dead units return 0; } //Wyrmgus end Selected.push_back(&unit); unit.Selected = 1; if (Selected.size() > 1) { Selected[0]->LastGroup = unit.LastGroup = GroupId; } return 1; }
/** ** Select units from a particular type and belonging to the local player. ** ** The base is included in the selection and defines ** the type of the other units to be selected. ** ** @param base Select all units of same type. ** ** @return Number of units found, 0 means selection unchanged ** ** FIXME: 0 can't happen. Maybe when scripting will use it? ** ** FIXME: should always select the nearest 9 units to the base! */ int SelectUnitsByType(CUnit &base) { const CUnitType &type = *base.Type; const CViewport *vp = UI.MouseViewport; Assert(UI.MouseViewport); if (type.ClicksToExplode) { HandleSuicideClick(base); } // if unit is a cadaver or hidden (not on map) // no unit can be selected. if (base.Removed || base.IsAlive() == false) { return 0; } if (type.IsNotSelectable && GameRunning) { return 0; } if (base.TeamSelected) { // Somebody else onteam has this unit return 0; } UnSelectAll(); Selected.push_back(&base); base.Selected = 1; // if unit isn't belonging to the player or allied player, or is a static unit // (like a building), only 1 unit can be selected at the same time. if (!CanSelectMultipleUnits(*base.Player) || !type.SelectableByRectangle) { return Selected.size(); } // // Search for other visible units of the same type // std::vector<CUnit *> table; // select all visible units. // StephanR: should be (MapX,MapY,MapX+MapWidth-1,MapY+MapHeight-1) ??? /* FIXME: this should probably be cleaner implemented if SelectUnitsByType() * took parameters of the selection rectangle as arguments */ const Vec2i offset(1, 1); const Vec2i minPos = vp->MapPos - offset; const Vec2i vpSize(vp->MapWidth, vp->MapHeight); const Vec2i maxPos = vp->MapPos + vpSize + offset; Select(minPos, maxPos, table, HasSameTypeAs(type)); // FIXME: peon/peasant with gold/wood & co are considered from // different type... idem for tankers for (size_t i = 0; i != table.size(); ++i) { CUnit &unit = *table[i]; if (!CanSelectMultipleUnits(*unit.Player)) { continue; } if (unit.IsUnusable()) { // guess SelectUnits doesn't check this continue; } if (&unit == &base) { // no need to have the same unit twice :) continue; } if (unit.TeamSelected) { // Somebody else onteam has this unit continue; } Selected.push_back(&unit); unit.Selected = 1; if (Selected.size() == MaxSelectable) { break; } } if (Selected.size() > 1) { for (size_t i = 0; i != Selected.size(); ++i) { Selected[i]->LastGroup = GroupId; } } NetworkSendSelection(&Selected[0], Selected.size()); return Selected.size(); }
/** ** Handle attacking the target. ** ** @param unit Unit, for that the attack is handled. */ void COrder_Attack::AttackTarget(CUnit &unit) { Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos)); AnimateActionAttack(unit, *this); if (unit.Anim.Unbreakable) { return; } if (!this->HasGoal() && (this->Action == UnitActionAttackGround || Map.WallOnMap(this->goalPos))) { return; } // Target is dead ? Change order ? if (CheckForDeadGoal(unit)) { return; } CUnit *goal = this->GetGoal(); bool dead = !goal || goal->IsAlive() == false; // No target choose one. if (!goal) { goal = AttackUnitsInReactRange(unit); // No new goal, continue way to destination. if (!goal) { // Return to old task ? if (unit.RestoreOrder()) { return; } this->State = MOVE_TO_TARGET; return; } // Save current command to come back. COrder *savedOrder = COrder::NewActionAttack(unit, this->goalPos); if (unit.CanStoreOrder(savedOrder) == false) { delete savedOrder; savedOrder = NULL; } else { unit.SavedOrder = savedOrder; } this->SetGoal(goal); this->goalPos = goal->tilePos; this->MinRange = unit.Type->MinAttackRange; this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max; this->State |= WEAK_TARGET; // Have a weak target, try a better target. // FIXME: if out of range also try another target quick } else { if ((this->State & WEAK_TARGET)) { CUnit *newTarget = AttackUnitsInReactRange(unit); if (newTarget && ThreatCalculate(unit, *newTarget) < ThreatCalculate(unit, *goal)) { if (unit.CanStoreOrder(this)) { unit.SavedOrder = this->Clone(); } goal = newTarget; this->SetGoal(newTarget); this->goalPos = newTarget->tilePos; this->MinRange = unit.Type->MinAttackRange; this->State = MOVE_TO_TARGET; } } } // Still near to target, if not goto target. const int dist = unit.MapDistanceTo(*goal); if (dist > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { // towers don't chase after goal if (unit.CanMove()) { if (unit.CanStoreOrder(this)) { if (dead) { unit.SavedOrder = COrder::NewActionAttack(unit, this->goalPos); } else { unit.SavedOrder = this->Clone(); } } } unit.Frame = 0; this->State &= WEAK_TARGET; this->State |= MOVE_TO_TARGET; } if (dist < unit.Type->MinAttackRange) { this->State = MOVE_TO_TARGET; } // Turn always to target if (goal) { const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } }
/** ** Stop gathering from the resource, go home. ** ** @param unit Poiner to unit. ** ** @return TRUE if ready, otherwise FALSE. */ int COrder_Resource::StopGathering(CUnit &unit) { CUnit *source = 0; const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; //Wyrmgus start // if (!resinfo.TerrainHarvester) { if (!Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end //Wyrmgus start // if (resinfo.HarvestFromOutside) { if (this->GetGoal() && this->GetGoal()->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { //Wyrmgus end source = this->GetGoal(); this->ClearGoal(); } else { source = unit.Container; } source->Resource.Active--; Assert(source->Resource.Active >= 0); //Store resource position. this->Resource.Mine = source; if (Preference.MineNotifications && unit.Player->Index == ThisPlayer->Index && source->IsAlive() && !source->MineLow && source->ResourcesHeld * 100 / source->Variable[GIVERESOURCE_INDEX].Max <= 10 //Wyrmgus start // && source->Variable[GIVERESOURCE_INDEX].Max > DefaultIncomes[this->CurrentResource]) { && source->Variable[GIVERESOURCE_INDEX].Max > (DefaultIncomes[this->CurrentResource] * 10)) { //Wyrmgus end //Wyrmgus start // unit.Player->Notify(NotifyYellow, source->tilePos, _("%s is running low!"), source->Type->Name.c_str()); unit.Player->Notify(NotifyYellow, source->tilePos, _("Our %s is nearing depletion!"), source->Type->Name.c_str()); //Wyrmgus end source->MineLow = 1; } if (source->Type->MaxOnBoard) { int count = 0; CUnit *worker = source->Resource.Workers; CUnit *next = NULL; for (; NULL != worker; worker = worker->NextWorker) { Assert(worker->CurrentAction() == UnitActionResource); COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder()); if (worker != &unit && order.IsGatheringWaiting()) { count++; if (next) { if (next->Wait > worker->Wait) { next = worker; } } else { next = worker; } } } if (next) { if (!unit.Player->AiEnabled) { DebugPrint("%d: Worker %d report: Unfreez resource gathering of %d <Wait %d> on %d [Assigned: %d Waiting %d].\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ UnitNumber(*next) _C_ next->Wait _C_ UnitNumber(*source) _C_ source->Resource.Assigned _C_ count); } next->Wait = 0; //source->Data.Resource.Waiting = count - 1; //Assert(source->Data.Resource.Assigned >= source->Data.Resource.Waiting); //StartGathering(next); } } } else { // Store resource position. this->Resource.Pos = unit.tilePos; Assert(this->Resource.Mine == NULL); } #ifdef DEBUG if (!unit.ResourcesHeld) { DebugPrint("Unit %d is empty???\n" _C_ UnitNumber(unit)); } #endif // Find and send to resource deposit. CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource); if (!depot || !unit.ResourcesHeld || this->Finished) { //Wyrmgus start // if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) { if (!((source && source->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) || Map.Info.IsPointOnMap(this->goalPos))) { //Wyrmgus end Assert(unit.Container); DropOutOnSide(unit, LookingW, source); } CUnit *mine = this->Resource.Mine; if (mine) { unit.DeAssignWorkerFromMine(*mine); this->Resource.Mine = NULL; } DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.CurrentResource); this->Finished = true; return 0; } else { //Wyrmgus start // if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) { if (!((source && source->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) || Map.Info.IsPointOnMap(this->goalPos))) { //Wyrmgus end Assert(unit.Container); DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source); } UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); } if (IsOnlySelected(unit)) { SelectedUnitChanged(); } #if 1 return 1; #endif }
/** ** Gather the resource ** ** @param unit Pointer to unit. ** ** @return non-zero if ready, otherwise zero. */ int COrder_Resource::GatherResource(CUnit &unit) { CUnit *source = 0; const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; int addload; //Wyrmgus start bool harvest_from_outside = (this->GetGoal() && this->GetGoal()->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value); // if (resinfo.HarvestFromOutside || resinfo.TerrainHarvester) { if (harvest_from_outside || Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end AnimateActionHarvest(unit); } else { unit.Anim.CurrAnim = NULL; } this->TimeToHarvest--; if (this->DoneHarvesting) { //Wyrmgus start // Assert(resinfo.HarvestFromOutside || resinfo.TerrainHarvester); Assert(harvest_from_outside || Map.Info.IsPointOnMap(this->goalPos)); //Wyrmgus end return !unit.Anim.Unbreakable; } // Target gone? //Wyrmgus start // if (resinfo.TerrainHarvester && !Map.Field(this->goalPos)->IsTerrainResourceOnMap(this->CurrentResource)) { if (Map.Info.IsPointOnMap(this->goalPos) && !Map.Field(this->goalPos)->IsTerrainResourceOnMap(this->CurrentResource)) { //Wyrmgus end if (!unit.Anim.Unbreakable) { // Action now breakable, move to resource again. this->State = SUB_MOVE_TO_RESOURCE; // Give it some reasonable look while searching. // FIXME: which frame? unit.Frame = 0; } return 0; // No wood? Freeze!!! } while (!this->DoneHarvesting && this->TimeToHarvest < 0) { //FIXME: rb - how should it look for WaitAtResource == 0 if (resinfo.WaitAtResource) { // Wyrmgus start // this->TimeToHarvest += std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); int wait_at_resource = resinfo.WaitAtResource; int resource_harvest_speed = unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]; if (!Map.Info.IsPointOnMap(this->goalPos) && !harvest_from_outside) { wait_at_resource = resinfo.WaitAtResource * 100 / resinfo.ResourceStep; } if (this->GetGoal()) { resource_harvest_speed += this->GetGoal()->Variable[TIMEEFFICIENCYBONUS_INDEX].Value; } this->TimeToHarvest += std::max<int>(1, wait_at_resource * SPEEDUP_FACTOR / resource_harvest_speed); //Wyrmgus end } else { this->TimeToHarvest += 1; } // Calculate how much we can load. //Wyrmgus start // if (resinfo.ResourceStep) { if (resinfo.ResourceStep && (harvest_from_outside || Map.Info.IsPointOnMap(this->goalPos))) { //Wyrmgus end addload = resinfo.ResourceStep; } else { addload = resinfo.ResourceCapacity; } // Make sure we don't bite more than we can chew. if (unit.ResourcesHeld + addload > resinfo.ResourceCapacity) { addload = resinfo.ResourceCapacity - unit.ResourcesHeld; } //Wyrmgus start // if (resinfo.TerrainHarvester) { if (Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end //Wyrmgus start CMapField &mf = *Map.Field(this->goalPos); if (addload > mf.Value) { addload = mf.Value; } mf.Value -= addload; //Wyrmgus end unit.ResourcesHeld += addload; //Wyrmgus start // if (addload && unit.ResourcesHeld == resinfo.ResourceCapacity) { if (mf.Value <= 0) { //Wyrmgus end //Wyrmgus start // Map.ClearWoodTile(this->goalPos); if (this->CurrentResource == WoodCost) { Map.ClearWoodTile(this->goalPos); } else if (this->CurrentResource == StoneCost) { Map.ClearRockTile(this->goalPos); } //Wyrmgus end } } else { //Wyrmgus start // if (resinfo.HarvestFromOutside) { if (harvest_from_outside) { //Wyrmgus end source = this->GetGoal(); } else { source = unit.Container; } Assert(source); Assert(source->ResourcesHeld <= 655350); //Wyrmgus start UpdateUnitVariables(*source); //update resource source's variables //Wyrmgus end bool is_visible = source->IsVisibleAsGoal(*unit.Player); // Target is not dead, getting resources. if (is_visible) { // Don't load more that there is. addload = std::min(source->ResourcesHeld, addload); unit.ResourcesHeld += addload; source->ResourcesHeld -= addload; } // End of resource: destroy the resource. // FIXME: implement depleted resources. if ((!is_visible) || (source->ResourcesHeld == 0)) { if (unit.Anim.Unbreakable) { return 0; } DebugPrint("%d: Worker %d report: Resource is destroyed\n" _C_ unit.Player->Index _C_ UnitNumber(unit)); bool dead = source->IsAlive() == false; // Improved version of DropOutAll that makes workers go to the depot. LoseResource(unit, *source); for (CUnit *uins = source->Resource.Workers; uins; uins = uins->NextWorker) { if (uins != &unit && uins->CurrentOrder()->Action == UnitActionResource) { COrder_Resource &order = *static_cast<COrder_Resource *>(uins->CurrentOrder()); if (!uins->Anim.Unbreakable && order.State == SUB_GATHER_RESOURCE) { order.LoseResource(*uins, *source); } } } // Don't destroy the resource twice. // This only happens when it's empty. if (!dead) { if (Preference.MineNotifications && unit.Player->Index == ThisPlayer->Index //Wyrmgus start // && source->Variable[GIVERESOURCE_INDEX].Max > DefaultIncomes[this->CurrentResource]) { && source->Variable[GIVERESOURCE_INDEX].Max > (DefaultIncomes[this->CurrentResource] * 10)) { //Wyrmgus end //Wyrmgus start // unit.Player->Notify(NotifyYellow, source->tilePos, _("%s has collapsed!"), source->Type->Name.c_str()); unit.Player->Notify(NotifyYellow, source->tilePos, _("Our %s has been depleted!"), source->Type->Name.c_str()); //Wyrmgus end } LetUnitDie(*source); // FIXME: make the workers inside look for a new resource. } source = NULL; return 0; } } //Wyrmgus start // if (resinfo.TerrainHarvester) { if (Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end if (unit.ResourcesHeld == resinfo.ResourceCapacity) { // Mark as complete. this->DoneHarvesting = true; } return 0; } else { //Wyrmgus start // if (resinfo.HarvestFromOutside) { if (harvest_from_outside) { //Wyrmgus end if ((unit.ResourcesHeld == resinfo.ResourceCapacity) || (source == NULL)) { // Mark as complete. this->DoneHarvesting = true; } return 0; } else { return unit.ResourcesHeld == resinfo.ResourceCapacity && source; } } } return 0; }
/* virtual */ void CAnimation_SpawnMissile::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); const int startx = ParseAnimInt(unit, this->startXStr.c_str()); const int starty = ParseAnimInt(unit, this->startYStr.c_str()); const int destx = ParseAnimInt(unit, this->destXStr.c_str()); const int desty = ParseAnimInt(unit, this->destYStr.c_str()); const SpawnMissile_Flags flags = (SpawnMissile_Flags)(ParseAnimFlags(unit, this->flagsStr.c_str())); const int offsetnum = ParseAnimInt(unit, this->offsetNumStr.c_str()); const CUnit *goal = flags & SM_RelTarget ? unit.CurrentOrder()->GetGoal() : &unit; const int dir = ((goal->Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos moff = goal->Type->MissileOffsets[dir][!offsetnum ? 0 : offsetnum - 1]; PixelPos start; PixelPos dest; MissileType *mtype = MissileTypeByIdent(this->missileTypeStr); if (mtype == NULL) { return; } if (!goal || goal->Destroyed) { return; } if ((flags & SM_Pixel)) { start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + moff.x + startx; start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + moff.y + starty; } else { start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2 + moff.x; start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2 + moff.y; } if ((flags & SM_ToTarget)) { CUnit *target = goal->CurrentOrder()->GetGoal(); if (!target || target->Destroyed) { Assert(!mtype->AlwaysFire || mtype->Range); if (!target && mtype->AlwaysFire == false) { return; } } if (!target) { if (goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionAttackGround) { COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } else if (goal->CurrentAction() == UnitActionSpellCast) { COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } if (flags & SM_Pixel) { dest.x += destx; dest.y += desty; } else { dest.x += destx * PixelTileSize.x; dest.y += desty * PixelTileSize.y; } } else if (flags & SM_Pixel) { dest.x = target->GetMapPixelPosCenter().x + destx; dest.y = target->GetMapPixelPosCenter().y + desty; } else { dest.x = (target->tilePos.x + destx) * PixelTileSize.x; dest.y = (target->tilePos.y + desty) * PixelTileSize.y; dest += target->Type->GetPixelSize() / 2; } } else { if ((flags & SM_Pixel)) { dest.x = goal->GetMapPixelPosCenter().x + destx; dest.y = goal->GetMapPixelPosCenter().y + desty; } else { dest.x = (goal->tilePos.x + destx) * PixelTileSize.x; dest.y = (goal->tilePos.y + desty) * PixelTileSize.y; dest += goal->Type->GetPixelSize() / 2; } } Vec2i destTilePos = Map.MapPixelPosToTilePos(dest); const int dist = goal->MapDistanceTo(destTilePos); if ((flags & SM_Ranged) && !(flags & SM_Pixel) && dist > goal->Stats->Variables[ATTACKRANGE_INDEX].Max && dist < goal->Type->MinAttackRange) { } else { Missile *missile = MakeMissile(*mtype, start, dest); if (flags & SM_SetDirection) { PixelPos posd; posd.x = Heading2X[goal->Direction / NextDirection]; posd.y = Heading2Y[goal->Direction / NextDirection]; missile->MissileNewHeadingFromXY(posd); } if (flags & SM_Damage) { missile->SourceUnit = &unit; } CUnit *target = goal->CurrentOrder()->GetGoal(); if (flags & SM_ToTarget && target && target->IsAlive()) { missile->TargetUnit = target; } } }
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 } }