/** ** Move in a random direction ** ** @return true if the unit moves, false otherwise */ static bool MoveRandomly(CUnit &unit) { if (unit.Type->RandomMovementProbability == false || ((SyncRand() % 100) > unit.Type->RandomMovementProbability)) { return false; } // pick random location Vec2i pos = unit.tilePos; pos.x += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; pos.y += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; // restrict to map Map.Clamp(pos); // move if possible if (pos != unit.tilePos) { UnmarkUnitFieldFlags(unit); if (UnitCanBeAt(unit, pos)) { MarkUnitFieldFlags(unit); CommandMove(unit, pos, FlushCommands); return true; } MarkUnitFieldFlags(unit); } return false; }
/** ** Send command: Move unit to position. ** ** @param unit pointer to unit. ** @param pos map tile position to move to. ** @param flush Flag flush all pending commands. */ void SendCommandMove(CUnit &unit, const Vec2i &pos, int flush) { if (!IsNetworkGame()) { CommandLog("move", &unit, flush, pos.x, pos.y, NoUnitP, NULL, -1); CommandMove(unit, pos, flush); } else { NetworkSendCommand(MessageCommandMove, unit, pos.x, pos.y, NoUnitP, 0, flush); } }
void AiForceManager::Update() { for (unsigned int f = 0; f < forces.size(); ++f) { AiForce &force = forces[f]; // Look if our defenders still have enemies in range. if (force.Defending) { force.RemoveDeadUnit(); if (force.Size() == 0) { force.Attacking = false; force.Defending = false; continue; } const int nearDist = 5; if (Map.Info.IsPointOnMap(force.GoalPos) == false) { force.ReturnToHome(); // Check if some unit from force reached goal point } else if (force.Units[0]->MapDistanceTo(force.GoalPos) <= nearDist) { // Look if still enemies in attack range. const CUnit *dummy = NULL; if (!AiForceEnemyFinder<AIATTACK_RANGE>(force, &dummy).found()) { force.ReturnToHome(); } } else { // Find idle units and order them to defend std::vector<CUnit *> idleUnits; for (unsigned int i = 0; i != force.Size(); ++i) { CUnit &aiunit = *force.Units[i]; if (aiunit.IsIdle() && aiunit.IsAliveOnMap()) { idleUnits.push_back(&aiunit); } } for (unsigned int i = 0; i != idleUnits.size(); ++i) { CUnit *const unit = idleUnits[i]; if (unit->Container == NULL) { const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. unit->Wait = delay; if (unit->Type->CanAttack) { CommandAttack(*unit, force.GoalPos, NULL, FlushCommands); } else { CommandMove(*unit, force.GoalPos, FlushCommands); } } } } } else if (force.Attacking) { force.RemoveDeadUnit(); force.Update(); } } }
void AiForce::Attack(const Vec2i &pos) { RemoveDeadUnit(); if (Units.size() == 0) { this->Attacking = false; return; } if (!this->Attacking) { // Remember the original force position so we can return there after attack if (this->Role == AiForceRoleDefend || (this->Role == AiForceRoleAttack && this->State == AiForceAttackingState_Waiting)) { this->HomePos = this->Units[this->Units.size() - 1]->tilePos; } this->Attacking = true; } Vec2i goalPos(pos); if (Map.Info.IsPointOnMap(goalPos) == false) { /* Search in entire map */ const CUnit *enemy = NULL; AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &enemy); if (enemy) { goalPos = enemy->tilePos; } } this->GoalPos = goalPos; if (Map.Info.IsPointOnMap(goalPos) == false) { DebugPrint("%d: Need to plan an attack with transporter\n" _C_ AiPlayer->Player->Index); if (State == AiForceAttackingState_Waiting && !PlanAttack()) { DebugPrint("%d: Can't transport\n" _C_ AiPlayer->Player->Index); Attacking = false; } return; } // Send all units in the force to enemy. this->State = AiForceAttackingState_Attacking; for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->Container == NULL) { const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. unit->Wait = delay; if (unit->Type->CanAttack) { CommandAttack(*unit, goalPos, NULL, FlushCommands); } else { CommandMove(*unit, goalPos, FlushCommands); } } } }
/** ** Attack opponent with force. ** ** @param force Force number to attack with. */ global void AiAttackWithForce(int force) { const AiUnit* aiunit; const Unit* enemy; int x; int y; AiCleanForce(force); AiPlayer->Force[force].Attacking=0; if( (aiunit=AiPlayer->Force[force].Units) ) { AiPlayer->Force[force].Attacking=1; enemy=NoUnitP; while( aiunit && !enemy ) { // Use an unit that can attack if( aiunit->Unit->Type->CanAttack ) { enemy = AttackUnitsInDistance(aiunit->Unit, MaxMapWidth); } aiunit=aiunit->Next; } if (!enemy) { DebugLevel0Fn("Need to plan an attack with transporter\n"); if( !AiPlayer->Force[force].State && !AiPlanAttack(&AiPlayer->Force[force]) ) { DebugLevel0Fn("Can't transport, look for walls\n"); if( !AiFindWall(&AiPlayer->Force[force]) ) { AiPlayer->Force[force].Attacking=0; } } return; } AiPlayer->Force[force].State=0; x = enemy->X; y = enemy->Y; // // Send all units in the force to enemy. // aiunit=AiPlayer->Force[force].Units; while( aiunit ) { if( aiunit->Unit->Type->CanAttack ) { CommandAttack(aiunit->Unit, x, y, NULL,FlushCommands); } else { CommandMove(aiunit->Unit, x, y, FlushCommands); } aiunit=aiunit->Next; } } }
void AiForce::ReturnToHome() { if (Map.Info.IsPointOnMap(this->HomePos)) { for (size_t i = 0; i != this->Units.size(); ++i) { CUnit &unit = *this->Units[i]; CommandMove(unit, this->HomePos, FlushCommands); } } const Vec2i invalidPos(-1, -1); this->HomePos = invalidPos; this->GoalPos = invalidPos; this->Defending = false; this->Attacking = false; }
/** ** Move unit to unit resource. ** ** @return 1 if reached, -1 if unreacheable, 0 if on the way. */ int COrder_Resource::MoveToResource_Unit(CUnit &unit) { const CUnit *goal = this->GetGoal(); Assert(goal); switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end return -1; case PF_REACHED: break; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: // Goal gone or something. if (unit.Anim.Unbreakable || goal->IsVisibleAsGoal(*unit.Player)) { return 0; } break; } return 1; }
/** ** Move unit to new position ** ** @param unit pointer to unit. ** @param pos map position to move to. ** @param flush if true, flush command queue. */ void CommandMove(CUnit &unit, const Vec2i &pos, int flush) { Assert(Map.Info.IsPointOnMap(pos)); if (IsUnitValidForNetwork(unit) == false) { return ; } //Wyrmgus start CMapField &mf = *Map.Field(unit.tilePos); CMapField &new_mf = *Map.Field(pos); //if the unit is a land unit over a raft, move the raft instead of the unit if ((mf.Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { CommandStopUnit(*table[i]); //always stop the raft if a new command is issued if ((new_mf.Flags & MapFieldWaterAllowed) || (new_mf.Flags & MapFieldCoastAllowed) || (mf.Flags & MapFieldWaterAllowed)) { // if is standing on water, tell the raft to go to the nearest coast, even if the ultimate goal is on land CommandStopUnit(unit); CommandMove(*table[i], pos, flush); return; } } } } //Wyrmgus end COrderPtr *order; if (!unit.CanMove()) { ClearNewAction(unit); order = &unit.NewOrder; } else { order = GetNextOrder(unit, flush); if (order == NULL) { return; } } *order = COrder::NewActionMove(pos); ClearSavedAction(unit); }
/** ** Attack at position with force. ** ** @param force Force number to attack with. ** @param x X tile map position to be attacked. ** @param y Y tile map position to be attacked. */ global void AiAttackWithForceAt(int force,int x,int y) { const AiUnit* aiunit; AiCleanForce(force); if( (aiunit=AiPlayer->Force[force].Units) ) { AiPlayer->Force[force].Attacking=1; // // Send all units in the force to enemy. // while( aiunit ) { if( aiunit->Unit->Type->CanAttack ) { CommandAttack(aiunit->Unit, x, y, NULL,FlushCommands); } else { CommandMove(aiunit->Unit, x, y, FlushCommands); } aiunit=aiunit->Next; } } }
/** ** Force on attack ride. We attack until there is no unit or enemy left. ** ** @param force Force pointer. */ void AiForce::Update() { Assert(Defending == false); if (Size() == 0) { Attacking = false; if (!Defending && State > AiForceAttackingState_Waiting) { DebugPrint("%d: Attack force #%lu was destroyed, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Reset(true); } return; } Attacking = false; for (unsigned int i = 0; i < Size(); ++i) { CUnit *aiunit = Units[i]; if (aiunit->Type->CanAttack) { Attacking = true; break; } } if (Attacking == false) { if (!Defending && State > AiForceAttackingState_Waiting) { DebugPrint("%d: Attack force #%lu has lost all agresive units, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Reset(true); } return ; } #if 0 if (State == AiForceAttackingState_Waiting) { if (!this->PlanAttack()) { DebugPrint("Can't transport, look for walls\n"); if (!AiFindWall(this)) { Attacking = false; return ; } } State = AiForceAttackingState_Boarding; } #endif if (State == AiForceAttackingState_Boarding) { AiGroupAttackerForTransport(*this); return ; } if (State == AiForceAttackingState_AttackingWithTransporter) { // Move transporters to goalpos std::vector<CUnit *> transporters; bool emptyTrans = true; for (unsigned int i = 0; i != Size(); ++i) { CUnit &aiunit = *Units[i]; if (aiunit.CanMove() && aiunit.Type->MaxOnBoard) { transporters.push_back(&aiunit); if (aiunit.BoardCount > 0) { emptyTrans = false; } } } if (transporters.empty()) { // Our transporters have been destroyed DebugPrint("%d: Attack force #%lu has lost all agresive units, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Reset(true); } else if (emptyTrans) { // We have emptied our transporters, go go go State = AiForceAttackingState_GoingToRallyPoint; } else { for (size_t i = 0; i != transporters.size(); ++i) { CUnit &trans = *transporters[i]; const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. trans.Wait = delay; CommandUnload(trans, this->GoalPos, NULL, FlushCommands); } } return; } CUnit *leader = NULL; for (unsigned int i = 0; i != Size(); ++i) { CUnit &aiunit = *Units[i]; if (aiunit.IsAgressive()) { leader = &aiunit; break; } } const int thresholdDist = 5; // Hard coded value Assert(Map.Info.IsPointOnMap(GoalPos)); if (State == AiForceAttackingState_GoingToRallyPoint) { // Check if we are near the goalpos int minDist = Units[0]->MapDistanceTo(this->GoalPos); int maxDist = minDist; for (size_t i = 0; i != Size(); ++i) { const int distance = Units[i]->MapDistanceTo(this->GoalPos); minDist = std::min(minDist, distance); maxDist = std::max(maxDist, distance); } if (WaitOnRallyPoint > 0 && minDist <= thresholdDist) { --WaitOnRallyPoint; } if (maxDist <= thresholdDist || !WaitOnRallyPoint) { const CUnit *unit = NULL; AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &unit); if (!unit) { AiForceEnemyFinder<AIATTACK_ALLMAP>(*this, &unit); if (!unit) { // No enemy found, give up // FIXME: should the force go home or keep trying to attack? DebugPrint("%d: Attack force #%lu can't find a target, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Attacking = false; State = AiForceAttackingState_Waiting; return; } } this->GoalPos = unit->tilePos; State = AiForceAttackingState_Attacking; for (size_t i = 0; i != this->Size(); ++i) { CUnit &aiunit = *this->Units[i]; const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. aiunit.Wait = delay; if (aiunit.IsAgressive()) { CommandAttack(aiunit, this->GoalPos, NULL, FlushCommands); } else { if (leader) { CommandDefend(aiunit, *leader, FlushCommands); } else { CommandMove(aiunit, this->GoalPos, FlushCommands); } } } } } std::vector<CUnit *> idleUnits; for (unsigned int i = 0; i != Size(); ++i) { CUnit &aiunit = *Units[i]; if (aiunit.IsIdle()) { idleUnits.push_back(&aiunit); } } if (idleUnits.empty()) { return; } if (State == AiForceAttackingState_Attacking && idleUnits.size() == this->Size()) { const CUnit *unit = NULL; bool isNaval = false; for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->Type->UnitType == UnitTypeNaval && unit->Type->CanAttack) { isNaval = true; break; } } if (isNaval) { AiForceEnemyFinder<AIATTACK_ALLMAP>(*this, &unit); } else { AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &unit); } if (!unit) { // No enemy found, give up // FIXME: should the force go home or keep trying to attack? DebugPrint("%d: Attack force #%lu can't find a target, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Attacking = false; State = AiForceAttackingState_Waiting; return; } else { Vec2i resultPos; NewRallyPoint(unit->tilePos, &resultPos); this->GoalPos = resultPos; this->State = AiForceAttackingState_GoingToRallyPoint; } } for (size_t i = 0; i != idleUnits.size(); ++i) { CUnit &aiunit = *idleUnits[i]; const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. aiunit.Wait = delay; if (leader) { if (aiunit.IsAgressive()) { if (State == AiForceAttackingState_Attacking) { CommandAttack(aiunit, leader->tilePos, NULL, FlushCommands); } else { CommandAttack(aiunit, this->GoalPos, NULL, FlushCommands); } } else { CommandDefend(aiunit, *leader, FlushCommands); } } else { if (aiunit.IsAgressive()) { CommandAttack(aiunit, this->GoalPos, NULL, FlushCommands); } else { CommandMove(aiunit, this->GoalPos, FlushCommands); } } } }
void AiForce::Attack(const Vec2i &pos) { bool isDefenceForce = false; RemoveDeadUnit(); if (Units.size() == 0) { this->Attacking = false; this->State = AiForceAttackingState_Waiting; return; } if (!this->Attacking) { // Remember the original force position so we can return there after attack if (this->Role == AiForceRoleDefend || (this->Role == AiForceRoleAttack && this->State == AiForceAttackingState_Waiting)) { this->HomePos = this->Units[this->Units.size() - 1]->tilePos; } this->Attacking = true; } Vec2i goalPos(pos); bool isNaval = false; for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->Type->UnitType == UnitTypeNaval && unit->Type->CanAttack) { isNaval = true; break; } } bool isTransporter = false; for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->Type->CanTransport() && unit->IsAgressive() == false) { isTransporter = true; break; } } if (Map.Info.IsPointOnMap(goalPos) == false) { /* Search in entire map */ const CUnit *enemy = NULL; if (isTransporter) { AiForceEnemyFinder<AIATTACK_AGRESSIVE>(*this, &enemy); } else if (isNaval) { AiForceEnemyFinder<AIATTACK_ALLMAP>(*this, &enemy); } else { AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &enemy); } if (enemy) { goalPos = enemy->tilePos; } } else { isDefenceForce = true; } if (Map.Info.IsPointOnMap(goalPos) == false || isTransporter) { DebugPrint("%d: Need to plan an attack with transporter\n" _C_ AiPlayer->Player->Index); if (State == AiForceAttackingState_Waiting && !PlanAttack()) { DebugPrint("%d: Can't transport\n" _C_ AiPlayer->Player->Index); Attacking = false; } return; } if (this->State == AiForceAttackingState_Waiting && isDefenceForce == false) { Vec2i resultPos; NewRallyPoint(goalPos, &resultPos); this->GoalPos = resultPos; this->State = AiForceAttackingState_GoingToRallyPoint; } else { this->GoalPos = goalPos; this->State = AiForceAttackingState_Attacking; } // Send all units in the force to enemy. CUnit *leader = NULL; for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->IsAgressive()) { leader = unit; break; } } for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; if (unit->Container == NULL) { const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. unit->Wait = delay; if (unit->IsAgressive()) { CommandAttack(*unit, this->GoalPos, NULL, FlushCommands); } else { if (leader) { CommandDefend(*unit, *leader, FlushCommands); } else { CommandMove(*unit, this->GoalPos, FlushCommands); } } } } }
/** ** Try to move a unit that's in the way */ static void AiMoveUnitInTheWay(CUnit *unit) { static int dirs[8][2] = {{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}}; int ux0; int uy0; int ux1; int uy1; int bx0; int by0; int bx1; int by1; int x; int y; int trycount,i; CUnit *blocker; CUnitType *unittype; CUnitType *blockertype; CUnit *movableunits[16]; int movablepos[16][2]; int movablenb; AiPlayer = unit->Player->Ai; unittype = unit->Type; ux0 = unit->X; uy0 = unit->Y; ux1 = ux0 + unittype->TileWidth - 1; uy1 = uy0 + unittype->TileHeight - 1; movablenb = 0; // Try to make some unit moves around it for (i = 0; i < NumUnits; ++i) { blocker = Units[i]; if (blocker->IsUnusable()) { continue; } if (!blocker->IsIdle()) { continue; } if (blocker->Player != unit->Player) { // Not allied if (!(blocker->Player->Allied & (1 << unit->Player->Index))) { continue; } } blockertype = blocker->Type; if (blockertype->UnitType != unittype->UnitType) { continue; } if (!CanMove(blocker)) { continue; } bx0 = blocker->X; by0 = blocker->Y; bx1 = bx0 + blocker->Type->TileWidth - 1; by1 = by0 + blocker->Type->TileHeight - 1;; // Check for collision if (!((ux0 == bx1 + 1 || ux1 == bx0 - 1) && (std::max(by0, uy0) <= std::min(by1, uy1))) && !((uy0 == by1 + 1 || uy1 == by0 - 1) && (std::max(bx0, ux0) <= std::min(bx1, ux1)))) { continue; } if (unit == blocker) { continue; } // Move blocker in a rand dir i = SyncRand() & 7; trycount = 8; while (trycount > 0) { i = (i + 1) & 7; --trycount; x = blocker->X + dirs[i][0]; y = blocker->Y + dirs[i][1]; // Out of the map => no ! if (x < 0 || y < 0 || x >= Map.Info.MapWidth || y >= Map.Info.MapHeight) { continue; } // move to blocker ? => no ! if (x == ux0 && y == uy0) { continue; } movableunits[movablenb] = blocker; movablepos[movablenb][0] = x; movablepos[movablenb][1] = y; ++movablenb; trycount = 0; } if (movablenb >= 16) { break; } } // Don't move more than 1 unit. if (movablenb) { i = SyncRand() % movablenb; CommandMove(movableunits[i], movablepos[i][0], movablepos[i][1], FlushCommands); } }
/** ** Force on attack ride. We attack until there is no unit or enemy left. ** ** @param force Force pointer. */ void AiForce::Update() { if (Size() == 0) { Attacking = false; if (!Defending && State > AiForceAttackingState_Waiting) { DebugPrint("%d: Attack force #%lu was destroyed, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Reset(true); } return; } Attacking = false; for (unsigned int i = 0; i < Size(); ++i) { CUnit *aiunit = Units[i]; if (aiunit->Type->CanAttack) { Attacking = true; break; } } if (Attacking == false) { if (!Defending && State > AiForceAttackingState_Waiting) { DebugPrint("%d: Attack force #%lu has lost all agresive units, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Reset(true); } return ; } #if 0 if (State == AiForceAttackingState_Waiting) { if (!this->PlanAttack()) { DebugPrint("Can't transport, look for walls\n"); if (!AiFindWall(this)) { Attacking = false; return ; } } State = AiForceAttackingState_Boarding; } #endif if (State == AiForceAttackingState_Boarding) { AiGroupAttackerForTransport(*this); return ; } Assert(Map.Info.IsPointOnMap(GoalPos)); std::vector<CUnit *> idleUnits; const CUnit *leader = NULL; for (unsigned int i = 0; i != Size(); ++i) { CUnit &aiunit = *Units[i]; if (aiunit.IsIdle()) { if (aiunit.IsAliveOnMap()) { idleUnits.push_back(&aiunit); } } else if (leader == NULL && aiunit.CurrentAction() == UnitActionAttack) { const COrder_Attack &order = *static_cast<COrder_Attack *>(aiunit.CurrentOrder()); if (order.HasGoal() && order.IsValid()) { leader = &aiunit; } } } if (idleUnits.empty()) { return ; } if (leader == NULL) { const int thresholdDist = 5; // Hard coded value int maxDist = 0; for (size_t i = 0; i != idleUnits.size(); ++i) { maxDist = std::max(maxDist, idleUnits[i]->MapDistanceTo(this->GoalPos)); } if (maxDist < thresholdDist) { const CUnit *unit = NULL; AiForceEnemyFinder<AIATTACK_BUILDING>(*this, &unit); if (!unit) { // No enemy found, give up // FIXME: should the force go home or keep trying to attack? DebugPrint("%d: Attack force #%lu can't find a target, giving up\n" _C_ AiPlayer->Player->Index _C_(long unsigned int)(this - & (AiPlayer->Force[0]))); Attacking = false; return; } GoalPos = unit->tilePos; } } const Vec2i pos = leader != NULL ? leader->tilePos : this->GoalPos; for (size_t i = 0; i != idleUnits.size(); ++i) { CUnit &aiunit = *idleUnits[i]; const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference. aiunit.Wait = delay; if (aiunit.Type->CanAttack) { CommandAttack(aiunit, pos, NULL, FlushCommands); } else if (aiunit.Type->CanTransport()) { if (aiunit.BoardCount != 0) { CommandUnload(aiunit, pos, NULL, FlushCommands); } else { // FIXME : Retrieve unit blocked (transport previously full) CommandMove(aiunit, aiunit.Player->StartPos, FlushCommands); this->Remove(aiunit); } } else { CommandMove(aiunit, pos, FlushCommands); } } }
/** ** Move to resource depot ** ** @param unit Pointer to unit. ** ** @return TRUE if reached, otherwise FALSE. */ int COrder_Resource::MoveToDepot(CUnit &unit) { const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; CUnit &goal = *this->GetGoal(); CPlayer &player = *unit.Player; Assert(&goal); switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end return -1; case PF_REACHED: break; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: if (unit.Anim.Unbreakable || goal.IsVisibleAsGoal(player)) { return 0; } break; } // // Target is dead, stop getting resources. // if (!goal.IsVisibleAsGoal(player)) { DebugPrint("%d: Worker %d report: Destroyed depot\n" _C_ player.Index _C_ UnitNumber(unit)); unit.CurrentOrder()->ClearGoal(); CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource); if (depot) { UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); DebugPrint("%d: Worker %d report: Going to new deposit.\n" _C_ player.Index _C_ UnitNumber(unit)); } else { DebugPrint("%d: Worker %d report: Can't find a new resource deposit.\n" _C_ player.Index _C_ UnitNumber(unit)); // FIXME: perhaps we should choose an alternative this->Finished = true; } return 0; } // If resource depot is still under construction, wait! if (goal.CurrentAction() == UnitActionBuilt) { unit.Wait = 10; return 0; } this->ClearGoal(); unit.Wait = resinfo.WaitAtDepot; // Place unit inside the depot if (unit.Wait) { int selected = unit.Selected; unit.Remove(&goal); if (selected && !Preference.DeselectInMine) { unit.Removed = 0; SelectUnit(unit); SelectionChanged(); unit.Removed = 1; } unit.Anim.CurrAnim = NULL; } // Update resource. const int rindex = resinfo.FinalResource; //Wyrmgus start // player.ChangeResource(rindex, (unit.ResourcesHeld * player.Incomes[rindex]) / 100, true); // player.TotalResources[rindex] += (unit.ResourcesHeld * player.Incomes[rindex]) / 100; player.ChangeResource(rindex, (unit.ResourcesHeld * resinfo.FinalResourceConversionRate / 100 * player.Incomes[rindex]) / 100, true); player.TotalResources[rindex] += (unit.ResourcesHeld * resinfo.FinalResourceConversionRate / 100 * player.Incomes[rindex]) / 100; //Wyrmgus end unit.ResourcesHeld = 0; unit.CurrentResource = 0; if (unit.Wait) { //Wyrmgus start // unit.Wait /= std::max(1, unit.Player->SpeedResourcesReturn[resinfo.ResourceId] / SPEEDUP_FACTOR); unit.Wait /= std::max(1, (unit.Player->SpeedResourcesReturn[resinfo.ResourceId] + goal.Variable[TIMEEFFICIENCYBONUS_INDEX].Value) / SPEEDUP_FACTOR); //Wyrmgus end if (unit.Wait) { unit.Wait--; } } return 1; }
/* virtual */ void COrder_Follow::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); UnitShowAnimation(unit, unit.GetAnimations()->Still); //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); // Reached target if (this->State == State_TargetReached) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->Finished = true; return ; } // Don't follow after immobile units if (goal && goal->CanMove() == false) { this->Finished = true; return; } //Wyrmgus start // if (goal->tilePos == this->goalPos) { if (goal->tilePos == this->goalPos && goal->MapLayer == this->MapLayer) { //Wyrmgus end // Move to the next order if (unit.Orders.size() > 1) { this->Finished = true; return ; } unit.Wait = 10; if (this->Range > 1) { this->Range = 1; this->State = State_Init; } return ; } this->State = State_Init; } if (this->State == State_Init) { // first entry this->State = State_Initialized; } switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start if ((Map.Field(unit.tilePos, unit.MapLayer)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table, unit.MapLayer); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands, this->HasGoal() ? this->GetGoal()->MapLayer : this->MapLayer); } return; } } } //Wyrmgus end // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal) { // goal has died this->Finished = true; return ; } // Handle Teleporter Units // FIXME: BAD HACK // goal shouldn't be busy and portal should be alive if (goal->Type->BoolFlag[TELEPORTER_INDEX].value && goal->Goal && goal->Goal->IsAlive() && unit.MapDistanceTo(*goal) <= 1) { if (!goal->IsIdle()) { // wait unit.Wait = 10; return; } // Check if we have enough mana if (goal->Goal->Type->TeleportCost > goal->Variable[MANA_INDEX].Value) { this->Finished = true; return; } else { goal->Variable[MANA_INDEX].Value -= goal->Goal->Type->TeleportCost; } // Everything is OK, now teleport the unit unit.Remove(NULL); if (goal->Type->TeleportEffectIn) { goal->Type->TeleportEffectIn->pushPreamble(); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(unit)); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(*goal)); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().x); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().y); goal->Type->TeleportEffectIn->run(); } unit.tilePos = goal->Goal->tilePos; //Wyrmgus start unit.MapLayer = goal->Goal->MapLayer; //Wyrmgus end DropOutOnSide(unit, unit.Direction, NULL); // FIXME: we must check if the units supports the new order. CUnit &dest = *goal->Goal; if (dest.Type->TeleportEffectOut) { dest.Type->TeleportEffectOut->pushPreamble(); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(unit)); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(dest)); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().x); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().y); dest.Type->TeleportEffectOut->run(); } if (dest.NewOrder == NULL || (dest.NewOrder->Action == UnitActionResource && !unit.Type->BoolFlag[HARVESTER_INDEX].value) //Wyrmgus start // || (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack) || (dest.NewOrder->Action == UnitActionAttack && !unit.CanAttack(true)) //Wyrmgus end || (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) { this->Finished = true; return ; } else { if (dest.NewOrder->HasGoal()) { if (dest.NewOrder->GetGoal()->Destroyed) { delete dest.NewOrder; dest.NewOrder = NULL; this->Finished = true; return ; } unit.Orders.insert(unit.Orders.begin() + 1, dest.NewOrder->Clone()); this->Finished = true; return ; } } } this->goalPos = goal->tilePos; //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->State = State_TargetReached; } // FALL THROUGH default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->ClearGoal(); goal = NULL; } if (unit.Anim.Unbreakable) { return ; } // If our leader is dead or stops or attacks: // Attack any enemy in reaction range. // If don't set the goal, the unit can than choose a // better goal if moving nearer to enemy. //Wyrmgus start // if (unit.Type->CanAttack if (unit.CanAttack() //Wyrmgus end && (!goal || goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionStill)) { CUnit *target = AttackUnitsInReactRange(unit); if (target) { // Save current command to come back. COrder *savedOrder = NULL; if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = this->Clone(); } this->Finished = true; //Wyrmgus start // unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos)); unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos, target->MapLayer)); //Wyrmgus end if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } } } }
/** ** Order a unit ** ** @param l Lua state. ** ** OrderUnit(player, unit-type, sloc, dloc, order) */ static int CclOrderUnit(lua_State *l) { LuaCheckArgs(l, 5); lua_pushvalue(l, 1); const int plynr = TriggerGetPlayer(l); lua_pop(l, 1); lua_pushvalue(l, 2); const CUnitType *unittype = TriggerGetUnitType(l); lua_pop(l, 1); if (!lua_istable(l, 3)) { LuaError(l, "incorrect argument"); } Vec2i pos1; pos1.x = LuaToNumber(l, 3, 1); pos1.y = LuaToNumber(l, 3, 2); Vec2i pos2; if (lua_rawlen(l, 3) == 4) { pos2.x = LuaToNumber(l, 3, 3); pos2.y = LuaToNumber(l, 3, 4); } else { pos2 = pos1; } if (!lua_istable(l, 4)) { LuaError(l, "incorrect argument"); } Vec2i dpos1; Vec2i dpos2; dpos1.x = LuaToNumber(l, 4, 1); dpos1.y = LuaToNumber(l, 4, 2); if (lua_rawlen(l, 4) == 4) { dpos2.x = LuaToNumber(l, 4, 3); dpos2.y = LuaToNumber(l, 4, 4); } else { dpos2 = dpos1; } const char *order = LuaToString(l, 5); std::vector<CUnit *> table; Select(pos1, pos2, table); for (size_t i = 0; i != table.size(); ++i) { CUnit &unit = *table[i]; if (unittype == ANY_UNIT || (unittype == ALL_FOODUNITS && !unit.Type->Building) || (unittype == ALL_BUILDINGS && unit.Type->Building) || unittype == unit.Type) { if (plynr == -1 || plynr == unit.Player->Index) { if (!strcmp(order, "move")) { CommandMove(unit, (dpos1 + dpos2) / 2, 1); } else if (!strcmp(order, "attack")) { CUnit *attack = TargetOnMap(unit, dpos1, dpos2); CommandAttack(unit, (dpos1 + dpos2) / 2, attack, 1); } else if (!strcmp(order, "patrol")) { CommandPatrolUnit(unit, (dpos1 + dpos2) / 2, 1); } else { LuaError(l, "Unsupported order: %s" _C_ order); } } } } return 0; }
/** ** Try to move a unit that's in the way */ static void AiMoveUnitInTheWay(CUnit &unit) { static Vec2i dirs[8] = {Vec2i(-1, -1), Vec2i(-1, 0), Vec2i(-1, 1), Vec2i(0, 1), Vec2i(1, 1), Vec2i(1, 0), Vec2i(1, -1), Vec2i(0, -1)}; CUnit *movableunits[16]; Vec2i movablepos[16]; int movablenb; AiPlayer = unit.Player->Ai; // No more than 1 move per 10 cycle ( avoid stressing the pathfinder ) if (GameCycle <= AiPlayer->LastCanNotMoveGameCycle + 10) { return; } const CUnitType &unittype = *unit.Type; const Vec2i u0 = unit.tilePos; const Vec2i u1(u0.x + unittype.TileWidth - 1, u0.y + unittype.TileHeight - 1); movablenb = 0; // Try to make some unit moves around it for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &blocker = **it; if (blocker.IsUnusable()) { continue; } if (!blocker.CanMove() || blocker.Moving) { continue; } if (blocker.Player != unit.Player && blocker.Player->IsAllied(*unit.Player) == false) { continue; } const CUnitType &blockertype = *blocker.Type; if (blockertype.UnitType != unittype.UnitType) { continue; } const Vec2i b0 = blocker.tilePos; const Vec2i b1(b0.x + blockertype.TileWidth - 1, b0.y + blockertype.TileHeight - 1); if (&unit == &blocker) { continue; } // Check for collision if (unit.MapDistanceTo(blocker) >= unit.Type->TileWidth + 1) { continue; } // Move blocker in a rand dir int r = SyncRand() & 7; int trycount = 8; while (trycount > 0) { r = (r + 1) & 7; --trycount; const Vec2i pos = blocker.tilePos + blocker.Type->TileWidth * dirs[r]; // Out of the map => no ! if (!Map.Info.IsPointOnMap(pos)) { continue; } // move to blocker ? => no ! if (pos == u0) { continue; } if (Map.Field(pos)->UnitCache.size() > 0) { continue; } movableunits[movablenb] = &blocker; movablepos[movablenb] = pos; ++movablenb; trycount = 0; } if (movablenb >= 16) { break; } } // Don't move more than 1 unit. if (movablenb) { const int index = SyncRand() % movablenb; COrder *savedOrder = NULL; if (movableunits[index]->IsIdle() == false) { if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } } CommandMove(*movableunits[index], movablepos[index], FlushCommands); if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } AiPlayer->LastCanNotMoveGameCycle = GameCycle; } }
/* virtual */ void COrder_Defend::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); UnitShowAnimation(unit, unit.GetAnimations()->Still); //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); if (this->State == State_Init) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } this->State = State_MovingToTarget; } else if (this->State == State_Defending) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } } if (!unit.Anim.Unbreakable) { if (AutoCast(unit) || AutoAttack(unit) || AutoRepair(unit)) { return; } } switch (DoActionMove(unit)) { case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer to the enemy if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return; } } } //Wyrmgus end // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { // goal has died this->Finished = true; return; } // Now defend the goal this->goalPos = goal->tilePos; this->State = State_Defending; } default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); this->ClearGoal(); goal = NULL; if (this->State == State_Defending) { this->Finished = true; return; } } }
/** ** Move unit to terrain. ** ** @return 1 if reached, -1 if unreacheable, 0 if on the way. */ int COrder_Resource::MoveToResource_Terrain(CUnit &unit) { Vec2i pos = this->goalPos; // Wood gone, look somewhere else. if ((Map.Info.IsPointOnMap(pos) == false || Map.Field(pos)->IsTerrainResourceOnMap(CurrentResource) == false) && (!unit.IX) && (!unit.IY)) { //Wyrmgus start // if (!FindTerrainType(unit.Type->MovementMask, MapFieldForest, 16, *unit.Player, this->goalPos, &pos)) { if ((this->CurrentResource == WoodCost && !FindTerrainType(unit.Type->MovementMask, MapFieldForest, 16, *unit.Player, this->goalPos, &pos)) || (this->CurrentResource == StoneCost && !FindTerrainType(unit.Type->MovementMask, MapFieldRocks, 16, *unit.Player, this->goalPos, &pos))) { //Wyrmgus end // no wood in range return -1; } else { this->goalPos = pos; } } switch (DoActionMove(unit)) { case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end unit.Wait = 10; if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } //Wyrmgus start // if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 9999, *unit.Player, unit.tilePos, &pos)) { if ((this->CurrentResource == WoodCost && FindTerrainType(unit.Type->MovementMask, MapFieldForest, 9999, *unit.Player, unit.tilePos, &pos)) || (this->CurrentResource == StoneCost && FindTerrainType(unit.Type->MovementMask, MapFieldRocks, 9999, *unit.Player, unit.tilePos, &pos))) { //Wyrmgus end this->goalPos = pos; DebugPrint("Found a better place to harvest %d,%d\n" _C_ pos.x _C_ pos.y); // FIXME: can't this overflow? It really shouldn't, since // x and y are really supossed to be reachable, checked thorugh a flood fill. // I don't know, sometimes stuff happens. return 0; } return -1; case PF_REACHED: return 1; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: return 0; } }
/** ** Execute a command (from network). ** ** @param msgnr Network message type ** @param unum Unit number (slot) that receive the command. ** @param x optional X map position. ** @param y optional y map position. ** @param dstnr optional destination unit. */ void ExecCommand(unsigned char msgnr, UnitRef unum, unsigned short x, unsigned short y, UnitRef dstnr) { CUnit &unit = UnitManager.GetSlotUnit(unum); const Vec2i pos(x, y); const int arg1 = x; const int arg2 = y; // // Check if unit is already killed? // if (unit.Destroyed) { DebugPrint(" destroyed unit skipping %d\n" _C_ UnitNumber(unit)); return; } Assert(unit.Type); const int status = (msgnr & 0x80) >> 7; // Note: destroyed destination unit is handled by the action routines. switch (msgnr & 0x7F) { case MessageSync: return; case MessageQuit: return; case MessageChat: return; case MessageCommandStop: CommandLog("stop", &unit, FlushCommands, -1, -1, NoUnitP, NULL, -1); CommandStopUnit(unit); break; case MessageCommandStand: CommandLog("stand-ground", &unit, status, -1, -1, NoUnitP, NULL, -1); CommandStandGround(unit, status); break; case MessageCommandDefend: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("defend", &unit, status, -1, -1, &dest, NULL, -1); CommandDefend(unit, dest, status); } break; } case MessageCommandFollow: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("follow", &unit, status, -1, -1, &dest, NULL, -1); CommandFollow(unit, dest, status); } break; } case MessageCommandMove: //Wyrmgus start // CommandLog("move", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); // CommandMove(unit, pos, status); if (!unit.CanMove()) { //FIXME: find better way to identify whether the unit should move or set a rally point CommandLog("rally-point", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); CommandRallyPoint(unit, pos); } else { CommandLog("move", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); CommandMove(unit, pos, status); } //Wyrmgus end break; //Wyrmgus start case MessageCommandPickUp: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("pick-up", &unit, status, -1, -1, &dest, NULL, -1); CommandPickUp(unit, dest, status); } break; } //Wyrmgus end case MessageCommandRepair: { CUnit *dest = NoUnitP; if (dstnr != (unsigned short)0xFFFF) { dest = &UnitManager.GetSlotUnit(dstnr); Assert(dest && dest->Type); } CommandLog("repair", &unit, status, pos.x, pos.y, dest, NULL, -1); CommandRepair(unit, pos, dest, status); break; } case MessageCommandAutoRepair: CommandLog("auto-repair", &unit, status, arg1, arg2, NoUnitP, NULL, 0); CommandAutoRepair(unit, arg1); break; case MessageCommandAttack: { CUnit *dest = NoUnitP; if (dstnr != (unsigned short)0xFFFF) { dest = &UnitManager.GetSlotUnit(dstnr); Assert(dest && dest->Type); } CommandLog("attack", &unit, status, pos.x, pos.y, dest, NULL, -1); CommandAttack(unit, pos, dest, status); break; } case MessageCommandGround: CommandLog("attack-ground", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); CommandAttackGround(unit, pos, status); break; //Wyrmgus start case MessageCommandUse: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("use", &unit, status, -1, -1, &dest, NULL, -1); CommandUse(unit, dest, status); } break; } //Wyrmgus end case MessageCommandPatrol: CommandLog("patrol", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); CommandPatrolUnit(unit, pos, status); break; case MessageCommandBoard: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("board", &unit, status, arg1, arg2, &dest, NULL, -1); CommandBoard(unit, dest, status); } break; } case MessageCommandUnload: { CUnit *dest = NULL; if (dstnr != (unsigned short)0xFFFF) { dest = &UnitManager.GetSlotUnit(dstnr); Assert(dest && dest->Type); } CommandLog("unload", &unit, status, pos.x, pos.y, dest, NULL, -1); CommandUnload(unit, pos, dest, status); break; } case MessageCommandBuild: CommandLog("build", &unit, status, pos.x, pos.y, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), -1); CommandBuildBuilding(unit, pos, *UnitTypes[dstnr], status); break; case MessageCommandDismiss: CommandLog("dismiss", &unit, FlushCommands, -1, -1, NULL, NULL, -1); CommandDismiss(unit); break; case MessageCommandResourceLoc: CommandLog("resource-loc", &unit, status, pos.x, pos.y, NoUnitP, NULL, -1); CommandResourceLoc(unit, pos, status); break; case MessageCommandResource: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("resource", &unit, status, -1, -1, &dest, NULL, -1); CommandResource(unit, dest, status); } break; } case MessageCommandReturn: { CUnit *dest = (dstnr != (unsigned short)0xFFFF) ? &UnitManager.GetSlotUnit(dstnr) : NULL; CommandLog("return", &unit, status, -1, -1, dest, NULL, -1); CommandReturnGoods(unit, dest, status); break; } case MessageCommandTrain: //Wyrmgus start // CommandLog("train", &unit, status, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), -1); // CommandTrainUnit(unit, *UnitTypes[dstnr], status); CommandLog("train", &unit, status, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), arg1); // use X as a way to mark the player CommandTrainUnit(unit, *UnitTypes[dstnr], arg1, status); //Wyrmgus end break; case MessageCommandCancelTrain: // We need (short)x for the last slot -1 if (dstnr != (unsigned short)0xFFFF) { CommandLog("cancel-train", &unit, FlushCommands, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), (short)x); CommandCancelTraining(unit, (short)x, UnitTypes[dstnr]); } else { CommandLog("cancel-train", &unit, FlushCommands, -1, -1, NoUnitP, NULL, (short)x); CommandCancelTraining(unit, (short)x, NULL); } break; case MessageCommandUpgrade: //Wyrmgus start /* CommandLog("upgrade-to", &unit, status, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), -1); CommandUpgradeTo(unit, *UnitTypes[dstnr], status); break; */ if (arg1 == 2) { //use X as a way to mark whether this is an upgrade or a transformation CommandLog("transform-into", &unit, status, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), -1); CommandTransformIntoType(unit, *UnitTypes[dstnr]); } else { CommandLog("upgrade-to", &unit, status, -1, -1, NoUnitP, UnitTypes[dstnr]->Ident.c_str(), -1); CommandUpgradeTo(unit, *UnitTypes[dstnr], status); } break; //Wyrmgus end case MessageCommandCancelUpgrade: CommandLog("cancel-upgrade-to", &unit, FlushCommands, -1, -1, NoUnitP, NULL, -1); CommandCancelUpgradeTo(unit); break; case MessageCommandResearch: CommandLog("research", &unit, status, -1, -1, NoUnitP, AllUpgrades[arg1]->Ident.c_str(), -1); CommandResearch(unit, *AllUpgrades[arg1], status); break; case MessageCommandCancelResearch: CommandLog("cancel-research", &unit, FlushCommands, -1, -1, NoUnitP, NULL, -1); CommandCancelResearch(unit); break; //Wyrmgus start case MessageCommandQuest: { CommandLog("quest", &unit, 0, 0, 0, NoUnitP, Quests[arg1]->Ident.c_str(), -1); CommandQuest(unit, Quests[arg1]); break; } case MessageCommandBuy: { if (dstnr != (unsigned short)0xFFFF) { CUnit &dest = UnitManager.GetSlotUnit(dstnr); Assert(dest.Type); CommandLog("buy", &unit, 0, -1, -1, &dest, NULL, arg1); CommandBuy(unit, &dest, arg1); } break; } //Wyrmgus end default: { int id = (msgnr & 0x7f) - MessageCommandSpellCast; if (arg2 != (unsigned short)0xFFFF) { CUnit *dest = NULL; if (dstnr != (unsigned short)0xFFFF) { dest = &UnitManager.GetSlotUnit(dstnr); Assert(dest && dest->Type); } CommandLog("spell-cast", &unit, status, pos.x, pos.y, dest, NULL, id); CommandSpellCast(unit, pos, dest, *SpellTypeTable[id], status); } else { CommandLog("auto-spell-cast", &unit, status, arg1, -1, NoUnitP, NULL, id); CommandAutoSpellCast(unit, id, arg1); } break; } } }
/** ** Move in a random direction ** ** @return true if the unit moves, false otherwise */ static bool MoveRandomly(CUnit &unit) { if (!unit.Type->RandomMovementProbability || SyncRand(100) > unit.Type->RandomMovementProbability) { return false; } // pick random location Vec2i pos = unit.tilePos; pos.x += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; pos.y += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance; // restrict to map Map.Clamp(pos, unit.MapLayer->ID); // move if possible if (pos != unit.tilePos) { UnmarkUnitFieldFlags(unit); if (UnitCanBeAt(unit, pos, unit.MapLayer->ID)) { MarkUnitFieldFlags(unit); //Wyrmgus start //prefer terrains which this unit's species is native to; only go to other ones if is already in a non-native terrain type if (unit.Type->Species && std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(unit.tilePos, false, unit.MapLayer->ID)) != unit.Type->Species->Terrains.end()) { if (std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(pos, false, unit.MapLayer->ID)) == unit.Type->Species->Terrains.end()) { return false; } } if (unit.Type->BoolFlag[PEOPLEAVERSION_INDEX].value) { std::vector<CUnit *> table; SelectAroundUnit(unit, std::max(6, unit.Type->RandomMovementDistance), table, HasNotSamePlayerAs(*unit.Player)); if (!table.size()) { //only avoid going near a settled area if isn't already surrounded by civilizations' units //don't go near settled areas Vec2i minpos = pos; Vec2i maxpos = pos; minpos.x = pos.x - std::max(6, unit.Type->RandomMovementDistance); minpos.y = pos.y - std::max(6, unit.Type->RandomMovementDistance); maxpos.x = pos.x + std::max(6, unit.Type->RandomMovementDistance); maxpos.y = pos.y + std::max(6, unit.Type->RandomMovementDistance); std::vector<CUnit *> second_table; Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player)); if (second_table.size() > 0) { return false; } } else { //even if is already in a settled area, don't go to places adjacent to units owned by players other than the neutral player Vec2i minpos = pos; Vec2i maxpos = pos; minpos.x = pos.x - 1; minpos.y = pos.y - 1; maxpos.x = pos.x + 1; maxpos.y = pos.y + 1; std::vector<CUnit *> second_table; Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player)); if (second_table.size() > 0) { return false; } } } CommandMove(unit, pos, FlushCommands, unit.MapLayer->ID); return true; } MarkUnitFieldFlags(unit); } return false; }