diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bc967e91..c2e02150b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,7 @@ source_group(map FILES ${map_SRCS}) set(missile_SRCS src/missile/missile.cpp src/missile/missile_cliptotarget.cpp + src/missile/missile_continuous.cpp src/missile/missile_cycleonce.cpp src/missile/missile_deathcoil.cpp src/missile/missile_fire.cpp diff --git a/src/action/action_built.cpp b/src/action/action_built.cpp index 061b207ff..937d23e2a 100644 --- a/src/action/action_built.cpp +++ b/src/action/action_built.cpp @@ -259,7 +259,6 @@ static void Finish(COrder_Built &order, CUnit &unit) DebugPrint("%d: %s canceled.\n" _C_ unit.Player->Index _C_ unit.Type->Name.c_str()); CancelBuilt(*this, unit); - this->Finished = true; return ; } diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp index 19be3ed5d..14d7936fc 100644 --- a/src/ai/ai_force.cpp +++ b/src/ai/ai_force.cpp @@ -285,8 +285,58 @@ void AiForce::RemoveDeadUnit() } } +class AiForceRallyPointFinder +{ +public: + AiForceRallyPointFinder(const CUnit &startUnit, const int distance, const Vec2i &startPos, + Vec2i *resultPos) : + startUnit(startUnit), distance(distance), startPos(startPos), resultPos(resultPos), + movemask(startUnit.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit | MapFieldBuilding)) {} + VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from); +private: + const CUnit &startUnit; + const int distance; + const Vec2i startPos; + const int movemask; + Vec2i *resultPos; +}; + +VisitResult AiForceRallyPointFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from) +{ + if (AiEnemyUnitsInDistance(*startUnit.Player, NULL, pos, 20) == false + && Distance(pos, startPos) <= distance) { + *resultPos = pos; + return VisitResult_Finished; + } + if (CanMoveToMask(pos, movemask)) { // reachable + return VisitResult_Ok; + } else { // unreachable + return VisitResult_DeadEnd; + } +} + +bool AiForce::NewRallyPoint(const Vec2i &startPos, Vec2i *resultPos) +{ + Assert(this->Units.size() > 0); + const CUnit &leader = *(this->Units[0]); + const int distance = leader.MapDistanceTo(startPos); + + TerrainTraversal terrainTraversal; + + terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight); + terrainTraversal.Init(); + + Assert(Map.Info.IsPointOnMap(startPos)); + terrainTraversal.PushPos(startPos); + + AiForceRallyPointFinder aiForceRallyPointFinder(leader, distance, startPos, resultPos); + + return terrainTraversal.Run(aiForceRallyPointFinder); +} + void AiForce::Attack(const Vec2i &pos) { + bool isDefenceForce = false; RemoveDeadUnit(); if (Units.size() == 0) { @@ -311,8 +361,9 @@ void AiForce::Attack(const Vec2i &pos) if (enemy) { goalPos = enemy->tilePos; } + } else { + isDefenceForce = true; } - 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()) { @@ -321,8 +372,17 @@ void AiForce::Attack(const Vec2i &pos) } 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. - this->State = AiForceAttackingState_Attacking; + for (size_t i = 0; i != this->Units.size(); ++i) { CUnit *const unit = this->Units[i]; @@ -331,9 +391,9 @@ void AiForce::Attack(const Vec2i &pos) unit->Wait = delay; if (unit->Type->CanAttack) { - CommandAttack(*unit, goalPos, NULL, FlushCommands); + CommandAttack(*unit, this->GoalPos, NULL, FlushCommands); } else { - CommandMove(*unit, goalPos, FlushCommands); + CommandMove(*unit, this->GoalPos, FlushCommands); } } } @@ -679,6 +739,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce) */ void AiForce::Update() { + Assert(Defending == false); if (Size() == 0) { Attacking = false; if (!Defending && State > AiForceAttackingState_Waiting) { @@ -722,33 +783,66 @@ void AiForce::Update() return ; } - Assert(Map.Info.IsPointOnMap(GoalPos)); std::vector 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(aiunit.CurrentOrder()); - - if (order.HasGoal() && order.IsValid()) { - leader = &aiunit; - } + if (aiunit.IsIdle() && aiunit.IsAliveOnMap()) { + idleUnits.push_back(&aiunit); } } + if (idleUnits.empty()) { - return ; + return; } - if (leader == NULL) { + + Assert(Map.Info.IsPointOnMap(GoalPos)); + if (State == AiForceAttackingState_GoingToRallyPoint) { + // Check if we are near the goalpos + if (Units[0]->MapDistanceTo(GoalPos) <= 20) { + const CUnit *unit = NULL; + + AiForceEnemyFinder(*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 != 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, this->GoalPos, NULL, FlushCommands); + } else if (aiunit.Type->CanTransport()) { + if (aiunit.BoardCount != 0) { + CommandUnload(aiunit, this->GoalPos, NULL, FlushCommands); + } else { + // FIXME : Retrieve unit blocked (transport previously full) + CommandMove(aiunit, aiunit.Player->StartPos, FlushCommands); + this->Remove(aiunit); + } + } else { + CommandMove(aiunit, this->GoalPos, FlushCommands); + } + } + + if (State == AiForceAttackingState_Attacking) { 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)); + for (size_t i = 0; i != Size(); ++i) { + maxDist = std::max(maxDist, Units[i]->MapDistanceTo(this->GoalPos)); } if (maxDist < thresholdDist) { const CUnit *unit = NULL; @@ -762,28 +856,12 @@ void AiForce::Update() Attacking = false; State = AiForceAttackingState_Waiting; 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); + Vec2i resultPos; + NewRallyPoint(unit->tilePos, &resultPos); + this->GoalPos = resultPos; + this->State = AiForceAttackingState_GoingToRallyPoint; } - } else { - CommandMove(aiunit, pos, FlushCommands); } } } diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h index bfa2b2c74..9fca431f8 100644 --- a/src/ai/ai_local.h +++ b/src/ai/ai_local.h @@ -102,6 +102,7 @@ enum AiForceAttackingState { AiForceAttackingState_Free = -1, AiForceAttackingState_Waiting = 0, AiForceAttackingState_Boarding, + AiForceAttackingState_GoingToRallyPoint, AiForceAttackingState_AttackingWithTransporter, AiForceAttackingState_Attacking, }; @@ -153,6 +154,7 @@ public: int PlanAttack(); void ReturnToHome(); + bool NewRallyPoint(const Vec2i &startPos, Vec2i *resultPos); private: void CountTypes(unsigned int *counter, const size_t len); diff --git a/src/missile/missile_continuous.cpp b/src/missile/missile_continuous.cpp new file mode 100644 index 000000000..8b96e44fb --- /dev/null +++ b/src/missile/missile_continuous.cpp @@ -0,0 +1,52 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name missile_continuous.cpp - The missile Continuous. */ +// +// (c) Copyright 2012 by cybermind +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; only version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +//@{ + +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ + +#include "stratagus.h" + +#include "missile.h" + +/** +** Missile don't move, than disappears when TTL is 0 +*/ +void MissileContinious::Action() +{ + this->Wait = this->Type->Sleep; + this->MissileHit(); + if (this->NextMissileFrame(1, 0)) { + this->SpriteFrame = 0; + } +} + +//@} \ No newline at end of file diff --git a/src/missile/missile_tracer.cpp b/src/missile/missile_tracer.cpp index ce8b10044..ea0c52990 100644 --- a/src/missile/missile_tracer.cpp +++ b/src/missile/missile_tracer.cpp @@ -60,7 +60,7 @@ static int TracerMissile(Missile &missile) Assert(missile.Type != NULL); Assert(missile.TotalStep != 0); if (missile.TargetUnit) { - missile.destination = missile.TargetUnit->GetMapPixelPosCenter(); + missile.destination = missile.TargetUnit->GetMapPixelPosTopLeft(); } const PixelPos diff = (missile.destination - missile.source); diff --git a/src/stratagus/script_player.cpp b/src/stratagus/script_player.cpp index b60c2a2a7..bdb08195f 100644 --- a/src/stratagus/script_player.cpp +++ b/src/stratagus/script_player.cpp @@ -876,13 +876,13 @@ static int CclSetPlayerData(lua_State *l) } else if (!strcmp(data, "TotalKills")) { p->TotalKills = LuaToNumber(l, 3); } else if (!strcmp(data, "SpeedResourcesHarvest")) { - LuaCheckArgs(l, 3); + LuaCheckArgs(l, 4); const std::string res = LuaToString(l, 3); const int resId = GetResourceIdByName(l, res.c_str()); p->SpeedResourcesHarvest[resId] = LuaToNumber(l, 4); } else if (!strcmp(data, "SpeedResourcesReturn")) { - LuaCheckArgs(l, 3); + LuaCheckArgs(l, 4); const std::string res = LuaToString(l, 3); const int resId = GetResourceIdByName(l, res.c_str());