diff --git a/src/action/action_attack.cpp b/src/action/action_attack.cpp index 15178795c..28f7617ae 100644 --- a/src/action/action_attack.cpp +++ b/src/action/action_attack.cpp @@ -157,11 +157,11 @@ static int CheckForTargetInRange(CUnit &unit) order->MinRange = unit.Type->MinAttackRange; order->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max; order->goalPos.x = order->goalPos.y = -1; - unit.SubAction |= WEAK_TARGET; // weak target + order->SubAction.Attack |= WEAK_TARGET; // weak target order->NewResetPath(); } // Have a weak target, try a better target. - } else if (order->HasGoal() && (unit.SubAction & WEAK_TARGET)) { + } else if (order->HasGoal() && (order->SubAction.Attack & WEAK_TARGET)) { CUnit *goal = order->GetGoal(); CUnit *temp = AttackUnitsInReactRange(unit); @@ -231,7 +231,7 @@ static void MoveToTarget(CUnit &unit) unit.State = 0; const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); - unit.SubAction++; + order->SubAction.Attack++; return; } // @@ -244,8 +244,8 @@ static void MoveToTarget(CUnit &unit) // Reached wall or ground, now attacking it unit.State = 0; UnitHeadingFromDeltaXY(unit, order->goalPos - unit.tilePos); - unit.SubAction &= WEAK_TARGET; - unit.SubAction |= ATTACK_TARGET; + order->SubAction.Attack &= WEAK_TARGET; + order->SubAction.Attack |= ATTACK_TARGET; return; } } @@ -320,7 +320,7 @@ static void AttackTarget(CUnit &unit) if (unit.RestoreOrder()) { return; } - unit.SubAction = MOVE_TO_TARGET; + order->SubAction.Attack = MOVE_TO_TARGET; return; } // Save current command to come back. @@ -335,14 +335,14 @@ static void AttackTarget(CUnit &unit) order->MinRange = unit.Type->MinAttackRange; order->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max; order->NewResetPath(); - unit.SubAction |= WEAK_TARGET; + order->SubAction.Attack |= WEAK_TARGET; // // Have a weak target, try a better target. // FIXME: if out of range also try another target quick // } else { - if ((unit.SubAction & WEAK_TARGET)) { + if ((order->SubAction.Attack & WEAK_TARGET)) { CUnit *temp = AttackUnitsInReactRange(unit); if (temp && temp->Type->Priority > goal->Type->Priority) { COrder *savedOrder = order->Clone(); @@ -355,7 +355,7 @@ static void AttackTarget(CUnit &unit) order->SetGoal(temp); order->goalPos.x = order->goalPos.y = -1; order->MinRange = unit.Type->MinAttackRange; - unit.SubAction = MOVE_TO_TARGET; + order->SubAction.Attack = MOVE_TO_TARGET; order->NewResetPath(); } } @@ -375,11 +375,11 @@ static void AttackTarget(CUnit &unit) order->NewResetPath(); unit.Frame = 0; unit.State = 0; - unit.SubAction &= WEAK_TARGET; - unit.SubAction |= MOVE_TO_TARGET; + order->SubAction.Attack &= WEAK_TARGET; + order->SubAction.Attack |= MOVE_TO_TARGET; } if (dist < unit.Type->MinAttackRange) { - unit.SubAction = MOVE_TO_TARGET; + order->SubAction.Attack = MOVE_TO_TARGET; } // @@ -414,7 +414,7 @@ void HandleActionAttack(COrder& order, CUnit &unit) return; } - switch (unit.SubAction) { + switch (order.SubAction.Attack) { case 0: // First entry { // did Order change ? @@ -431,12 +431,12 @@ void HandleActionAttack(COrder& order, CUnit &unit) const Vec2i dir = goal.tilePos + goal.Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); - unit.SubAction = ATTACK_TARGET; + order.SubAction.Attack = ATTACK_TARGET; AttackTarget(unit); return; } } - unit.SubAction = MOVE_TO_TARGET; + order.SubAction.Attack = MOVE_TO_TARGET; order.NewResetPath(); // FIXME: should use a reachable place to reduce pathfinder time. Assert(unit.State == 0); diff --git a/src/action/action_board.cpp b/src/action/action_board.cpp index 219167d8b..0b9120e2a 100644 --- a/src/action/action_board.cpp +++ b/src/action/action_board.cpp @@ -42,14 +42,51 @@ #include "player.h" #include "unit.h" #include "actions.h" -#include "interface.h" #include "pathfinder.h" #include "map.h" +#include "iolib.h" +#include "script.h" /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ +/* virtual */ void COrder_Board::Save(CFile &file, const CUnit &unit) const +{ + file.printf("{\"action-board\","); + + file.printf(" \"range\", %d,", this->Range); + if (this->HasGoal()) { + CUnit &goal = *this->GetGoal(); + if (goal.Destroyed) { + /* this unit is destroyed so it's not in the global unit + * array - this means it won't be saved!!! */ + printf ("FIXME: storing destroyed Goal - loading will fail.\n"); + } + file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str()); + } + file.printf(" \"state\", %d,\n ", this->State); + + SaveDataMove(file); + + file.printf("}"); +} + +/* virtual */ bool COrder_Board::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) +{ + if (this->ParseMoveData(l, j, value)) { + return true; + } else if (!strcmp("state", value)) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); + } else { + return false; + } + return true; +} + /** ** Move to transporter. ** @@ -80,49 +117,47 @@ static int MoveToTransporter(CUnit &unit) ** ** @return True if ship arrived/present, False otherwise. */ -static int WaitForTransporter(CUnit &unit) +bool COrder_Board::WaitForTransporter(CUnit &unit) { if (unit.Wait) { unit.Wait--; - return 0; + return false; } - CUnit *trans = unit.CurrentOrder()->GetGoal(); + const CUnit *trans = this->GetGoal(); if (!trans || !CanTransport(*trans, unit)) { // FIXME: destination destroyed?? unit.Wait = 6; - return 0; + return false; } if (!trans->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Transporter Gone\n"); - unit.CurrentOrder()->ClearGoal(); + this->ClearGoal(); unit.Wait = 6; - return 0; + return false; } if (unit.MapDistanceTo(*trans) == 1) { // enter transporter - return 1; + return true; } - // // FIXME: any enemies in range attack them, while waiting. - // // n0b0dy: This means we have to search with a smaller range. // It happens only when you reach the shore,and the transporter // is not there. The unit searches with a big range, so it thinks // it's there. This is why we reset the search. The transporter // should be a lot closer now, so it's not as bad as it seems. - unit.SubAction = 0; - unit.CurrentOrder()->Range = 1; + this->State = 0; + this->Range = 1; // Uhh wait a bit. unit.Wait = 10; - return 0; + return false; } /** @@ -130,26 +165,19 @@ static int WaitForTransporter(CUnit &unit) ** ** @param unit Pointer to unit. */ -static void EnterTransporter(CUnit &unit) +static void EnterTransporter(CUnit &unit, COrder_Board &order) { - CUnit *transporter; + CUnit *transporter = order.GetGoal(); - unit.ClearAction(); + Assert(transporter != NULL); - transporter = unit.CurrentOrder()->GetGoal(); if (!transporter->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Transporter gone\n"); - unit.CurrentOrder()->ClearGoal(); return; } - unit.CurrentOrder()->ClearGoal(); - - // - // Place the unit inside the transporter. - // - if (transporter->BoardCount < transporter->Type->MaxOnBoard) { + // Place the unit inside the transporter. unit.Remove(transporter); transporter->BoardCount++; unit.Boarded = 1; @@ -166,6 +194,56 @@ static void EnterTransporter(CUnit &unit) DebugPrint("No free slot in transporter\n"); } +/* virtual */ bool COrder_Board::Execute(CUnit &unit) +{ + switch (this->State) { + // Wait for transporter + case 201: + if (this->WaitForTransporter(unit)) { + this->State = 202; + } else { + UnitShowAnimation(unit, unit.Type->Animations->Still); + } + break; + // Enter transporter + case 202: + EnterTransporter(unit, *this); + return true; + break; + // Move to transporter + case 0: + if (unit.Wait) { + unit.Wait--; + return false; + } + this->NewResetPath(); + this->State = 1; + // FALL THROUGH + default: + if (this->State <= 200) { + const int pathRet = MoveToTransporter(unit); + // FIXME: if near transporter wait for enter + if (pathRet) { + if (pathRet == PF_UNREACHABLE) { + if (++this->State == 200) { + return true; + } else { + // Try with a bigger range. + if (this->CheckRange()) { + this->Range++; + this->State--; + } + } + } else if (pathRet == PF_REACHED) { + this->State = 201; + } + } + } + break; + } + return false; +} + /** ** The unit boards a transporter. ** @@ -175,52 +253,8 @@ static void EnterTransporter(CUnit &unit) */ void HandleActionBoard(COrder& order, CUnit &unit) { - switch (unit.SubAction) { - // Wait for transporter - case 201: - if (WaitForTransporter(unit)) { - unit.SubAction = 202; - } else { - UnitShowAnimation(unit, unit.Type->Animations->Still); - } - break; - // Enter transporter - case 202: - EnterTransporter(unit); - break; - // Move to transporter - case 0: - if (unit.Wait) { - unit.Wait--; - return; - } - order.NewResetPath(); - unit.SubAction = 1; - // FALL THROUGH - default: - if (unit.SubAction <= 200) { - int i; - // FIXME: if near transporter wait for enter - if ((i = MoveToTransporter(unit))) { - if (i == PF_UNREACHABLE) { - if (++unit.SubAction == 200) { - unit.ClearAction(); - order.ClearGoal(); - } else { - // - // Try with a bigger range. - // - if (order.CheckRange()) { - order.Range++; - unit.SubAction--; - } - } - } else if (i == PF_REACHED) { - unit.SubAction = 201; - } - } - } - break; + if (order.Execute(unit)) { + unit.ClearAction(); } } diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp index 47a2a266c..084afe6b1 100644 --- a/src/action/action_build.cpp +++ b/src/action/action_build.cpp @@ -56,6 +56,7 @@ extern void AiReduceMadeInBuilt(PlayerAi &pai, const CUnitType &type); enum { + State_Start = 0, State_MoveToLocationMax = 10, // Range from prev State_NearOfLocation = 11, // Range to next State_StartBuilding_Failed = 20, @@ -89,8 +90,8 @@ enum } file.printf(" \"building\", \"%s\",", UnitReference(this->BuildingUnit).c_str()); } - file.printf(" \"type\", \"%s\",\n ", this->Type->Ident.c_str()); - + file.printf(" \"type\", \"%s\",", this->Type->Ident.c_str()); + file.printf(" \"state\", %d,\n ", this->State); SaveDataMove(file); file.printf("}"); } @@ -104,6 +105,11 @@ enum lua_rawgeti(l, -1, j + 1); this->BuildingUnit = CclGetUnitFromRef(l); lua_pop(l, 1); + } else if (!strcmp(value, "state")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); } else if (!strcmp(value, "type")) { ++j; lua_rawgeti(l, -1, j + 1); @@ -135,18 +141,18 @@ void COrder_Build::AiUnitKilled(CUnit& unit) ** ** @param unit Unit to move */ -static bool MoveToLocation(CUnit &unit, COrder_Build &order) +bool COrder_Build::MoveToLocation(CUnit &unit) { // First entry - if (!unit.SubAction) { - order.Data.Move.Cycles = 0; //moving counter - unit.SubAction = 1; - order.NewResetPath(); + if (this->State == 0) { + this->Data.Move.Cycles = 0; //moving counter + this->State = 1; + this->NewResetPath(); } switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: { // Some tries to reach the goal - if (unit.SubAction++ < 10) { + if (this->State++ < 10) { // To keep the load low, retry each 1/4 second. // NOTE: we can already inform the AI about this problem? unit.Wait = CYCLES_PER_SECOND / 4; @@ -156,12 +162,12 @@ static bool MoveToLocation(CUnit &unit, COrder_Build &order) unit.Player->Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, _("You cannot reach building place")); if (unit.Player->AiEnabled) { - AiCanNotReach(unit, order.GetUnitType()); + AiCanNotReach(unit, this->GetUnitType()); } return true; } case PF_REACHED: - unit.SubAction = State_NearOfLocation; + this->State = State_NearOfLocation; return false; default: @@ -219,10 +225,10 @@ private: ** ** @param unit Unit to check */ -static CUnit *CheckCanBuild(CUnit &unit, COrder_Build &order) +CUnit *COrder_Build::CheckCanBuild(CUnit &unit) { - const Vec2i pos = order.goalPos; - const CUnitType &type = order.GetUnitType(); + const Vec2i pos = this->goalPos; + const CUnitType &type = this->GetUnitType(); // Check if the building could be built there. @@ -231,28 +237,27 @@ static CUnit *CheckCanBuild(CUnit &unit, COrder_Build &order) if (ontop != NULL) { return ontop; } - +#if 0 /* * FIXME: rb - CheckAlreadyBuilding should be somehow * enabled/disable via game lua scripting */ CUnit *building = AlreadyBuildingFinder(unit, type).Find(Map.Field(pos)); if (building != NULL) { - if (unit.CurrentOrder() == &order) { + if (unit.CurrentOrder() == this) { DebugPrint("%d: Worker [%d] is helping build: %s [%d]\n" _C_ unit.Player->Index _C_ unit.Slot _C_ building->Type->Name.c_str() _C_ building->Slot); - unit.SubAction = 0; - delete ℴ - unit.Orders[0] = COrder::NewActionRepair(unit, *building); - return NULL; + delete this; // Bad + unit.Orders[0] = COrder::NewActionRepair(unit, *building); + return NULL; } } - +#endif // Some tries to build the building. - unit.SubAction++; + this->State++; // To keep the load low, retry each 10 cycles // NOTE: we can already inform the AI about this problem? unit.Wait = 10; @@ -311,12 +316,12 @@ bool COrder_Build::StartBuilding(CUnit &unit, CUnit &ontop) if (!type.BuilderOutside) { UnitShowAnimation(unit, unit.Type->Animations->Still); unit.Remove(build); - unit.SubAction = State_BuildFromInside; + this->State = State_BuildFromInside; if (unit.Selected) { SelectedUnitChanged(); } } else { - unit.SubAction = State_BuildFromOutside; + this->State = State_BuildFromOutside; this->BuildingUnit = build; unit.Direction = DirectionToHeading(build->tilePos - unit.tilePos); UnitUpdateHeading(unit); @@ -372,24 +377,24 @@ bool COrder_Build::BuildFromOutside(CUnit &unit) const unit.Wait--; return false; } - if (unit.SubAction <= State_MoveToLocationMax) { - if (MoveToLocation(unit, *this)) { + if (this->State <= State_MoveToLocationMax) { + if (this->MoveToLocation(unit)) { return true; } } const CUnitType &type = this->GetUnitType(); - if (State_NearOfLocation <= unit.SubAction && unit.SubAction < State_StartBuilding_Failed) { + if (State_NearOfLocation <= this->State && this->State < State_StartBuilding_Failed) { if (CheckLimit(unit, type) == false) { return true; } - CUnit *ontop = CheckCanBuild(unit, *this); + CUnit *ontop = this->CheckCanBuild(unit); if (ontop != NULL) { this->StartBuilding(unit, *ontop); } } - if (unit.SubAction == State_StartBuilding_Failed) { + if (this->State == State_StartBuilding_Failed) { unit.Player->Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, _("You cannot build %s here"), type.Name.c_str()); if (unit.Player->AiEnabled) { @@ -397,7 +402,7 @@ bool COrder_Build::BuildFromOutside(CUnit &unit) const } return true; } - if (unit.SubAction == State_BuildFromOutside) { + if (this->State == State_BuildFromOutside) { this->BuildFromOutside(unit); } diff --git a/src/action/action_die.cpp b/src/action/action_die.cpp index 3646c1cca..389fc9a73 100644 --- a/src/action/action_die.cpp +++ b/src/action/action_die.cpp @@ -106,7 +106,6 @@ static bool AnimateActionDie(CUnit &unit) unit.Stats = &type.Stats[unit.Player->Index]; unit.Place(unit.tilePos); - unit.SubAction = 0; unit.Frame = 0; UnitUpdateHeading(unit); AnimateActionDie(unit); // with new corpse. diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp index ab821e0de..21408a57c 100644 --- a/src/action/action_follow.cpp +++ b/src/action/action_follow.cpp @@ -42,7 +42,6 @@ #include "unittype.h" #include "pathfinder.h" #include "map.h" -#include "interface.h" #include "actions.h" /*---------------------------------------------------------------------------- @@ -67,7 +66,7 @@ void HandleActionFollow(COrder& order, CUnit &unit) CUnit *goal = order.GetGoal(); // Reached target - if (unit.SubAction == 128) { + if (order.SubAction.Follow == 128) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); @@ -91,14 +90,14 @@ void HandleActionFollow(COrder& order, CUnit &unit) unit.Wait = 10; if (order.Range > 1) { order.Range = 1; - unit.SubAction = 0; + order.SubAction.Follow = 0; } return; } - unit.SubAction = 0; + order.SubAction.Follow = 0; } - if (!unit.SubAction) { // first entry - unit.SubAction = 1; + if (!order.SubAction.Follow) { // first entry + order.SubAction .Follow= 1; order.NewResetPath(); Assert(unit.State == 0); } @@ -161,7 +160,7 @@ void HandleActionFollow(COrder& order, CUnit &unit) return; } order.goalPos = goal->tilePos; - unit.SubAction = 128; + order.SubAction.Follow = 128; } // FALL THROUGH default: diff --git a/src/action/action_move.cpp b/src/action/action_move.cpp index 3c35c2edb..864fd5901 100644 --- a/src/action/action_move.cpp +++ b/src/action/action_move.cpp @@ -182,13 +182,6 @@ void HandleActionMove(COrder& order, CUnit &unit) return; } - if (!unit.SubAction) { // first entry - unit.SubAction = 1; - unit.CurrentOrder()->NewResetPath(); - order.Data.Move.Cycles = 0; - Assert(unit.State == 0); - } - // FIXME: (mr-russ) Make a reachable goal here with GoalReachable ... switch (DoActionMove(unit)) { // reached end-point? diff --git a/src/action/action_patrol.cpp b/src/action/action_patrol.cpp index 9b4b1cfc2..43ea097cc 100644 --- a/src/action/action_patrol.cpp +++ b/src/action/action_patrol.cpp @@ -57,6 +57,9 @@ file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y); file.printf(" \"range\", %d,", this->Range); + if (this->WaitingCycle != 0) { + file.printf(" \"waiting-cycle\", %d,", this->WaitingCycle); + } file.printf(" \"patrol\", {%d, %d},\n ", this->WayPoint.x, this->WayPoint.y); SaveDataMove(file); file.printf("}"); @@ -71,6 +74,11 @@ lua_rawgeti(l, -1, j + 1); CclGetPos(l, &this->WayPoint.x , &this->WayPoint.y); lua_pop(l, 1); + } else if (!strcmp(value, "waiting-cycle")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->WaitingCycle = LuaToNumber(l, -1); + lua_pop(l, 1); } else { return false; } @@ -84,37 +92,30 @@ return false; } - if (!unit.SubAction) { // first entry. - this->Data.Move.Cycles = 0; //moving counter - this->NewResetPath(); - unit.SubAction = 1; - } - switch (DoActionMove(unit)) { case PF_FAILED: - unit.SubAction = 1; + this->WaitingCycle = 0; break; case PF_UNREACHABLE: // Increase range and try again - unit.SubAction = 1; + this->WaitingCycle = 1; if (this->CheckRange()) { this->Range++; break; } // FALL THROUGH case PF_REACHED: - unit.SubAction = 1; + this->WaitingCycle = 1; this->Range = 0; std::swap(this->WayPoint, this->goalPos); - this->Data.Move.Cycles = 0; //moving counter this->NewResetPath(); break; case PF_WAIT: // Wait for a while then give up - unit.SubAction++; - if (unit.SubAction == 5) { - unit.SubAction = 1; + this->WaitingCycle++; + if (this->WaitingCycle == 5) { + this->WaitingCycle = 0; this->Range = 0; std::swap(this->WayPoint, this->goalPos); @@ -123,12 +124,12 @@ } break; default: // moving - unit.SubAction = 1; + this->WaitingCycle = 0; break; } if (!unit.Anim.Unbreakable) { - if (AutoAttack(unit, false) || AutoRepair(unit) || AutoCast(unit)) { + if (AutoAttack(unit) || AutoRepair(unit) || AutoCast(unit)) { return true; } } diff --git a/src/action/action_repair.cpp b/src/action/action_repair.cpp index a4b983d3d..170581031 100644 --- a/src/action/action_repair.cpp +++ b/src/action/action_repair.cpp @@ -48,60 +48,103 @@ #include "map.h" #include "pathfinder.h" #include "interface.h" +#include "iolib.h" +#include "script.h" /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ +/* virtual */ void COrder_Repair::Save(CFile &file, const CUnit &unit) const +{ + file.printf("{\"action-repair\","); + + file.printf(" \"range\", %d,", this->Range); + if (this->HasGoal()) { + CUnit &goal = *this->GetGoal(); + if (goal.Destroyed) { + /* this unit is destroyed so it's not in the global unit + * array - this means it won't be saved!!! */ + printf ("FIXME: storing destroyed Goal - loading will fail.\n"); + } + file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str()); + } + file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y); + + file.printf(" \"repaircycle\", %d,", this->RepairCycle); + file.printf(" \"state\", %d,\n ", this->State); + + SaveDataMove(file); + + file.printf("}"); +} + +/* virtual */ bool COrder_Repair::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) +{ + if (this->ParseMoveData(l, j, value)) { + return true; + } else if (!strcmp("repaircycle", value)) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->RepairCycle = LuaToNumber(l, -1); + lua_pop(l, 1); + } else if (!strcmp("state", value)) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); + } else { + return false; + } + return true; +} + + /** ** Repair a unit. ** ** @param unit unit repairing ** @param goal unit being repaired +** +** @return true when action is finished/canceled. */ -static void RepairUnit(CUnit &unit, CUnit &goal) +bool COrder_Repair::RepairUnit(const CUnit &unit, CUnit &goal) { if (goal.CurrentAction() == UnitActionBuilt) { COrder_Built &order = *static_cast<COrder_Built *>(goal.CurrentOrder()); - order.ProgressHp(goal, 100 * unit.CurrentOrder()->Data.Repair.Cycles); - unit.CurrentOrder()->Data.Repair.Cycles = 0; - return ; + order.ProgressHp(goal, 100 * this->RepairCycle); + this->RepairCycle = 0; + return false; } - CPlayer *player = unit.Player; - char buf[100]; + if (goal.Variable[HP_INDEX].Value >= goal.Variable[HP_INDEX].Max) { + return true; + } + CPlayer &player = *unit.Player; // Calculate the repair costs. Assert(goal.Stats->Variables[HP_INDEX].Max); // Check if enough resources are available for (int i = 1; i < MaxCosts; ++i) { - if (player->Resources[i] < goal.Type->RepairCosts[i]) { - snprintf(buf, 100, _("We need more %s for repair!"), - DefaultResourceNames[i].c_str()); - player->Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, buf); - if (player->AiEnabled) { - // FIXME: call back to AI? - unit.CurrentOrder()->ClearGoal(); - if (!unit.RestoreOrder()) { - unit.ClearAction(); - unit.State = 0; - } - } - // FIXME: We shouldn't animate if no resources are available. - return; + if (player.Resources[i] < goal.Type->RepairCosts[i]) { + char buf[100]; + + snprintf(buf, 100, _("We need more %s for repair!"), DefaultResourceNames[i].c_str()); + player.Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, buf); + return true; } } - // // Subtract the resources - // - player->SubCosts(goal.Type->RepairCosts); + player.SubCosts(goal.Type->RepairCosts); goal.Variable[HP_INDEX].Value += goal.Type->RepairHP; - if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { + if (goal.Variable[HP_INDEX].Value >= goal.Variable[HP_INDEX].Max) { goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; + return true; } + return false; } /** @@ -109,46 +152,33 @@ static void RepairUnit(CUnit &unit, CUnit &goal) ** ** @param unit Unit, for that the repair animation is played. */ -static int AnimateActionRepair(CUnit &unit) +static void AnimateActionRepair(CUnit &unit) { UnitShowAnimation(unit, unit.Type->Animations->Repair); - return 0; } -/** -** Unit repairs -** -** @param unit Unit, for that the attack is handled. -*/ -void HandleActionRepair(COrder& order, CUnit &unit) +/* virtual */ bool COrder_Repair::Execute(CUnit &unit) { - CUnit *goal; - int err; - - switch (unit.SubAction) { + switch (this->State) { case 0: - order.NewResetPath(); - unit.SubAction = 1; + this->NewResetPath(); + this->State = 1; // FALL THROUGH - case 1:// Move near to target. + case 1: { // Move near to target. // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. - err = DoActionMove(unit); + int err = DoActionMove(unit); if (!unit.Anim.Unbreakable) { // No goal: if meeting damaged building repair it. - goal = order.GetGoal(); + CUnit *goal = this->GetGoal(); - // Target is dead, choose new one. - // - // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("repair target gone.\n"); - order.goalPos = goal->tilePos; - // FIXME: should I clear this here? - order.ClearGoal(); + this->goalPos = goal->tilePos; + this->ClearGoal(); goal = NULL; - order.NewResetPath(); + this->NewResetPath(); } } else if (unit.Player->AiEnabled) { // Ai players workers should stop if target is killed @@ -156,70 +186,65 @@ void HandleActionRepair(COrder& order, CUnit &unit) } // Have reached target? FIXME: could use return value - if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange && - goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { + if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange + && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit.State = 0; - unit.SubAction = 2; - order.Data.Repair.Cycles = 0; + this->State = 2; + this->RepairCycle = 0; const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } else if (err < 0) { - order.ClearGoal(); - if (!unit.RestoreOrder()) { - unit.ClearAction(); - unit.State = 0; - } - return; + return true; } - - // FIXME: Should be it already? - Assert(unit.CurrentAction() == UnitActionRepair); } break; - - case 2:// Repair the target. + } + case 2: {// Repair the target. AnimateActionRepair(unit); - order.Data.Repair.Cycles++; - if (!unit.Anim.Unbreakable) { - goal = unit.CurrentOrder()->GetGoal(); - - // Target is dead, choose new one. - // - // Check if goal is correct unit. - // FIXME: should I do a function for this? - if (goal) { - if (!goal->IsVisibleAsGoal(*unit.Player)) { - DebugPrint("repair goal is gone\n"); - order.goalPos = goal->tilePos; - // FIXME: should I clear this here? - order.ClearGoal(); - goal = NULL; - order.NewResetPath(); - } else { - int dist = unit.MapDistanceTo(*goal); - if (dist <= unit.Type->RepairRange) { - RepairUnit(unit, *goal); - goal = order.GetGoal(); - } else if (dist > unit.Type->RepairRange) { - // If goal has move, chase after it - unit.State = 0; - unit.SubAction = 0; - } - } - } - - // Target is fine, choose new one. - if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { - order.ClearGoal(); - if (!unit.RestoreOrder()) { - unit.ClearAction(); - unit.State = 0; - } - return; - } - // FIXME: automatic repair + this->RepairCycle++; + if (unit.Anim.Unbreakable) { + return false; } - break; + CUnit *goal = this->GetGoal(); + + if (goal) { + if (!goal->IsVisibleAsGoal(*unit.Player)) { + DebugPrint("repair goal is gone\n"); + this->goalPos = goal->tilePos; + // FIXME: should I clear this here? + this->ClearGoal(); + goal = NULL; + this->NewResetPath(); + } else { + const int dist = unit.MapDistanceTo(*goal); + + if (dist <= unit.Type->RepairRange) { + if (RepairUnit(unit, *goal)) { + return true; + } + } else if (dist > unit.Type->RepairRange) { + // If goal has move, chase after it + unit.State = 0; + this->State = 0; + } + } + } + // Target is fine, choose new one. + if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { + return true; + } + // FIXME: automatic repair + } + break; + } + return false; +} + + +void HandleActionRepair(COrder& order, CUnit &unit) +{ + if (order.Execute(unit)) { + unit.ClearAction(); } } diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp index ef71ebc70..9d4752f2e 100644 --- a/src/action/action_resource.cpp +++ b/src/action/action_resource.cpp @@ -161,7 +161,7 @@ static void UnitGotoGoal(CUnit &unit, CUnit *const goal, int mode) } } order->Range = 1; - unit.SubAction = mode; + order->SubAction.Res = mode; unit.State = 0; if (mode == SUB_MOVE_TO_DEPOT || mode == SUB_MOVE_TO_RESOURCE) { unit.CurrentOrder()->Data.Move.Cycles = 0; //moving counter @@ -209,7 +209,7 @@ static int StartGathering(CUnit &unit) // Find an alternative, but don't look too far. unit.CurrentOrder()->goalPos.x = unit.CurrentOrder()->goalPos.y = -1; if ((goal = UnitFindResource(unit, unit.tilePos, 15, unit.CurrentResource, unit.Player->AiEnabled))) { - unit.SubAction = SUB_START_RESOURCE; + unit.CurrentOrder()->SubAction.Res = SUB_START_RESOURCE; unit.CurrentOrder()->SetGoal(goal); } else { unit.CurrentOrder()->ClearGoal(); @@ -324,7 +324,7 @@ static void LoseResource(CUnit &unit, const CUnit &source) unit.CurrentOrder()->Arg1.Resource.Mine = goal; unit.CurrentOrder()->Range = 1; unit.CurrentOrder()->goalPos = goal->tilePos; - unit.SubAction = SUB_MOVE_TO_RESOURCE; + unit.CurrentOrder()->SubAction.Res = SUB_MOVE_TO_RESOURCE; unit.State = 0; return; } @@ -361,7 +361,7 @@ static void LoseResource(CUnit &unit, const CUnit &source) if (depot) { DebugPrint("%d: Worker %d report: Resource is exhausted, Found another resource.\n" _C_ unit.Player->Index _C_ unit.Slot); - unit.SubAction = SUB_START_RESOURCE; + unit.CurrentOrder()->SubAction.Res = SUB_START_RESOURCE; unit.State = 0; unit.CurrentOrder()->SetGoal(depot); } else { @@ -391,7 +391,9 @@ static int GatherResource(CUnit &unit) int addload; if (!resinfo.HarvestFromOutside && unit.Container != NULL) { - unit.Container->SubAction = SUB_GATHER_RESOURCE; +#if 0 +// unit.Container->SubAction = SUB_GATHER_RESOURCE; +#endif UnitShowAnimation(*unit.Container, unit.Container->Type->Animations->Harvest[unit.CurrentResource]); } @@ -412,7 +414,7 @@ static int GatherResource(CUnit &unit) if (resinfo.TerrainHarvester && !Map.ForestOnMap(unit.CurrentOrder()->goalPos)) { if (!unit.Anim.Unbreakable) { // Action now breakable, move to resource again. - unit.SubAction = SUB_MOVE_TO_RESOURCE; + unit.CurrentOrder()->SubAction.Res = SUB_MOVE_TO_RESOURCE; // Give it some reasonable look while searching. // FIXME: which frame? unit.Frame = 0; @@ -529,7 +531,7 @@ int GetNumWaitingWorkers(const CUnit &mine) for (int i = 0; NULL != worker; worker = worker->NextWorker, ++i) { - if (worker->SubAction == SUB_START_GATHERING && worker->Wait) { + if (worker->CurrentOrder()->SubAction.Res == SUB_START_GATHERING && worker->Wait) { ret++; } Assert(i <= mine.Resource.Assigned); @@ -558,10 +560,11 @@ static int StopGathering(CUnit &unit) } source->Resource.Active--; Assert(source->Resource.Active >= 0); - +#if 0 if (!resinfo.HarvestFromOutside && source->Resource.Active == 0) { source->SubAction = 1; } +#endif //Store resource position. if (unit.Orders[0]->Arg1.Resource.Mine) { unit.Orders[0]->Arg1.Resource.Mine->RefsDecrease(); @@ -575,7 +578,7 @@ static int StopGathering(CUnit &unit) CUnit *next = NULL; for(; NULL != worker; worker = worker->NextWorker) { - if (worker != &unit && worker->SubAction == SUB_START_GATHERING && worker->Wait) { + if (worker != &unit && worker->CurrentOrder()->SubAction.Res == SUB_START_GATHERING && worker->Wait) { count++; if (next) { if (next->Wait > worker->Wait) @@ -849,7 +852,7 @@ void ResourceGiveUp(CUnit &unit) */ static bool ActionResourceInit(CUnit &unit) { - Assert(unit.SubAction == SUB_START_RESOURCE); + Assert(unit.CurrentOrder()->SubAction.Res == SUB_START_RESOURCE); CUnit *const goal = unit.CurrentOrder()->GetGoal(); int newres; @@ -905,27 +908,27 @@ void HandleActionResource(COrder& order, CUnit &unit) } // Let's start mining. - if (unit.SubAction == SUB_START_RESOURCE) { + if (order.SubAction.Res == SUB_START_RESOURCE) { if (ActionResourceInit(unit) == false) { return; } } // Move to the resource location. - if (SUB_MOVE_TO_RESOURCE <= unit.SubAction && unit.SubAction < SUB_UNREACHABLE_RESOURCE) { + if (SUB_MOVE_TO_RESOURCE <= order.SubAction.Res && order.SubAction.Res < SUB_UNREACHABLE_RESOURCE) { const int ret = MoveToResource(unit); switch (ret) { case -1: // Can't Reach { - unit.SubAction++; + order.SubAction.Res++; unit.Wait = 5; return; } case 1: // Reached { - unit.SubAction = SUB_START_GATHERING; + order.SubAction.Res = SUB_START_GATHERING; break; } case 0: // Move along. @@ -939,53 +942,53 @@ void HandleActionResource(COrder& order, CUnit &unit) } // Resource seems to be unreachable - if (unit.SubAction == SUB_UNREACHABLE_RESOURCE) { + if (order.SubAction.Res == SUB_UNREACHABLE_RESOURCE) { ResourceGiveUp(unit); return; } // Start gathering the resource - if (unit.SubAction == SUB_START_GATHERING) { + if (order.SubAction.Res == SUB_START_GATHERING) { if (StartGathering(unit)) { - unit.SubAction = SUB_GATHER_RESOURCE; + order.SubAction.Res = SUB_GATHER_RESOURCE; } else { return; } } // Gather the resource. - if (unit.SubAction == SUB_GATHER_RESOURCE) { + if (order.SubAction.Res == SUB_GATHER_RESOURCE) { if (GatherResource(unit)) { - unit.SubAction = SUB_STOP_GATHERING; + order.SubAction.Res = SUB_STOP_GATHERING; } else { return; } } // Stop gathering the resource. - if (unit.SubAction == SUB_STOP_GATHERING) { + if (order.SubAction.Res == SUB_STOP_GATHERING) { if (StopGathering(unit)) { - unit.SubAction = SUB_MOVE_TO_DEPOT; - unit.CurrentOrder()->Data.Move.Cycles = 0; //moving counter + order.SubAction.Res = SUB_MOVE_TO_DEPOT; + order.Data.Move.Cycles = 0; //moving counter } else return; } // Move back home. - if (SUB_MOVE_TO_DEPOT <= unit.SubAction && unit.SubAction < SUB_UNREACHABLE_DEPOT) { + if (SUB_MOVE_TO_DEPOT <= order.SubAction.Res && order.SubAction.Res < SUB_UNREACHABLE_DEPOT) { const int ret = MoveToDepot(unit); switch (ret) { case -1: // Can't Reach { - unit.SubAction++; + order.SubAction.Res++; unit.Wait = 5; return; } case 1: // Reached { - unit.SubAction = SUB_RETURN_RESOURCE; + order.SubAction.Res = SUB_RETURN_RESOURCE; return; } case 0: // Move along. @@ -999,15 +1002,15 @@ void HandleActionResource(COrder& order, CUnit &unit) } // Depot seems to be unreachable - if (unit.SubAction == SUB_UNREACHABLE_DEPOT) { + if (order.SubAction.Res == SUB_UNREACHABLE_DEPOT) { ResourceGiveUp(unit); return; } // Unload resources at the depot. - if (unit.SubAction == SUB_RETURN_RESOURCE) { + if (order.SubAction.Res == SUB_RETURN_RESOURCE) { if (WaitInDepot(unit)) { - unit.SubAction = SUB_START_RESOURCE; + order.SubAction.Res = SUB_START_RESOURCE; // It's posible, though very rare that the unit's goal blows up // this cycle, but after this unit. Thus, next frame the unit diff --git a/src/action/action_returngoods.cpp b/src/action/action_returngoods.cpp index 981c58071..846ada886 100644 --- a/src/action/action_returngoods.cpp +++ b/src/action/action_returngoods.cpp @@ -88,7 +88,7 @@ void HandleActionReturnGoods(COrder& order, CUnit &unit) //unit.CurrentOrder()->Arg1.ResourcePos = -1; order.NewResetPath(); - unit.SubAction = /* SUB_MOVE_TO_DEPOT */ 70; // FIXME : Define value. + order.SubAction.Res = /* SUB_MOVE_TO_DEPOT */ 70; // FIXME : Define value. } //@} diff --git a/src/action/action_spellcast.cpp b/src/action/action_spellcast.cpp index 6276a4da2..2349cd0bb 100644 --- a/src/action/action_spellcast.cpp +++ b/src/action/action_spellcast.cpp @@ -54,7 +54,6 @@ #include "tileset.h" #include "map.h" #include "spells.h" -#include "interface.h" #include "iolib.h" #include "script.h" @@ -81,6 +80,7 @@ } file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y); + file.printf("\"state\", %d", this->State); file.printf(" \"spell\", \"%s\",\n ", this->Spell->Ident.c_str()); SaveDataMove(file); @@ -96,6 +96,11 @@ lua_rawgeti(l, -1, j + 1); this->Spell = SpellTypeByIdent(LuaToString(l, -1)); lua_pop(l, 1); + } else if (!strcmp(value, "state")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); } else { return false; } @@ -145,45 +150,38 @@ static void AnimateActionSpellCast(CUnit &unit, COrder_SpellCast &order) ** ** @param unit Unit, for that the spell cast is handled. */ -static void SpellMoveToTarget(CUnit &unit) +bool COrder_SpellCast::SpellMoveToTarget(CUnit &unit) { - CUnit *goal; - int err; - // Unit can't move - err = 1; + int err = 1; if (unit.CanMove()) { err = DoActionMove(unit); if (unit.Anim.Unbreakable) { - return; + return false; } } // when reached DoActionMove changes unit action // FIXME: use return codes from pathfinder - COrderPtr order = unit.CurrentOrder(); - goal = order->GetGoal(); + CUnit *goal = this->GetGoal(); - if (goal && unit.MapDistanceTo(*goal) <= order->Range) { + if (goal && unit.MapDistanceTo(*goal) <= this->Range) { // there is goal and it is in range unit.State = 0; UnitHeadingFromDeltaXY(unit, goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos); - unit.SubAction++; // cast the spell - return; - } else if (!goal && unit.MapDistanceTo(order->goalPos.x, order->goalPos.y) <= order->Range) { + this->State++; // cast the spell + return false; + } else if (!goal && unit.MapDistanceTo(this->goalPos.x, this->goalPos.y) <= this->Range) { // there is no goal and target spot is in range - UnitHeadingFromDeltaXY(unit, order->goalPos - unit.tilePos); - unit.SubAction++; // cast the spell - return; + UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos); + this->State++; // cast the spell + return false; } else if (err == PF_UNREACHABLE) { - // // goal/spot unreachable and out of range -- give up - // - unit.ClearAction(); unit.State = 0; - order->ClearGoal(); // Release references + return true; } - Assert(!unit.Type->Vanishes && !unit.Destroyed); + return false; } @@ -196,7 +194,7 @@ static void SpellMoveToTarget(CUnit &unit) return false; } const SpellType &spell = order.GetSpell(); - switch (unit.SubAction) { + switch (this->State) { case 0: // Check if we can cast the spell. if (!CanCastSpell(unit, &spell, order.GetGoal(), order.goalPos.x, order.goalPos.y)) { @@ -221,14 +219,16 @@ static void SpellMoveToTarget(CUnit &unit) unit.CurrentOrder()->NewResetPath(); } unit.ReCast = 0; // repeat spell on next pass? (defaults to `no') - unit.SubAction = 1; + this->State = 1; // FALL THROUGH case 1: // Move to the target. if (spell.Range && spell.Range != INFINITE_RANGE) { - SpellMoveToTarget(unit); - break; + if (SpellMoveToTarget(unit) == true) { + return true; + } + return false; } else { - unit.SubAction = 2; + this->State = 2; } // FALL THROUGH case 2: // Cast spell on the target. @@ -252,7 +252,7 @@ static void SpellMoveToTarget(CUnit &unit) break; default: - unit.SubAction = 0; // Reset path, than move to target + this->State = 0; // Reset path, than move to target break; } return false; diff --git a/src/action/action_stand.cpp b/src/action/action_stand.cpp index 22c830f95..cb41d2071 100644 --- a/src/action/action_stand.cpp +++ b/src/action/action_stand.cpp @@ -38,41 +38,11 @@ #include "stratagus.h" #include "unit.h" #include "actions.h" -#include "iolib.h" /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ - -/* virtual */ void COrder_StandGround::Save(CFile &file, const CUnit &unit) const -{ - file.printf("{\"action-stand-ground\""); - if (this->HasGoal()) { - CUnit &goal = *this->GetGoal(); - if (goal.Destroyed) { - /* this unit is destroyed so it's not in the global unit - * array - this means it won't be saved!!! */ - printf ("FIXME: storing destroyed Goal - loading will fail.\n"); - } - file.printf(", \"goal\", \"%s\"", UnitReference(goal).c_str()); - } - file.printf("}"); -} - -/* virtual */ bool COrder_StandGround::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) -{ - return false; -} - -/* virtual */ bool COrder_StandGround::Execute(CUnit &unit) -{ - ActionStillGeneric(unit, true); - - return false; -} - - /** ** Unit stands ground! ** diff --git a/src/action/action_still.cpp b/src/action/action_still.cpp index 491498b36..889535c24 100644 --- a/src/action/action_still.cpp +++ b/src/action/action_still.cpp @@ -48,16 +48,21 @@ #include "spells.h" #include "player.h" #include "iolib.h" +#include "script.h" -#define SUB_STILL_INIT 0 -#define SUB_STILL_STANDBY 1 -#define SUB_STILL_ATTACK 2 - - +enum { + SUB_STILL_INIT = 0, + SUB_STILL_STANDBY = 1, + SUB_STILL_ATTACK = 2 +}; /* virtual */ void COrder_Still::Save(CFile &file, const CUnit &unit) const { - file.printf("{\"action-still\""); + if (this->Action == UnitActionStill) { + file.printf("{\"action-still\""); + } else { + file.printf("{\"action-stand-ground\","); + } if (this->HasGoal()) { CUnit &goal = *this->GetGoal(); if (goal.Destroyed) { @@ -65,14 +70,24 @@ * array - this means it won't be saved!!! */ printf ("FIXME: storing destroyed Goal - loading will fail.\n"); } - file.printf(", \"goal\", \"%s\"", UnitReference(goal).c_str()); + file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str()); } + + file.printf(", \"state\", %d", this->State); file.printf("}"); } /* virtual */ bool COrder_Still::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) { - return false; + if (!strcmp("state", value)) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); + } else { + return false; + } + return true; } @@ -92,53 +107,47 @@ void UnHideUnit(CUnit &unit) */ static bool MoveRandomly(CUnit &unit) { - if (unit.Type->RandomMovementProbability && - ((SyncRand() % 100) <= unit.Type->RandomMovementProbability)) { - // pick random location - Vec2i pos = unit.tilePos; + if (unit.Type->RandomMovementProbability == false + || ((SyncRand() % 100) > unit.Type->RandomMovementProbability)) { + return false; + } + // pick random location + Vec2i pos = unit.tilePos; - switch ((SyncRand() >> 12) & 15) { - case 0: pos.x++; break; - case 1: pos.y++; break; - case 2: pos.x--; break; - case 3: pos.y--; break; - case 4: pos.x++; pos.y++; break; - case 5: pos.x--; pos.y++; break; - case 6: pos.y--; pos.x++; break; - case 7: pos.x--; pos.y--; break; - default: - break; - } + switch ((SyncRand() >> 12) & 15) { + case 0: pos.x++; break; + case 1: pos.y++; break; + case 2: pos.x--; break; + case 3: pos.y--; break; + case 4: pos.x++; pos.y++; break; + case 5: pos.x--; pos.y++; break; + case 6: pos.y--; pos.x++; break; + case 7: pos.x--; pos.y--; break; + default: + break; + } - // restrict to map - if (pos.x < 0) { - pos.x = 0; - } else if (pos.x >= Map.Info.MapWidth) { - pos.x = Map.Info.MapWidth - 1; - } - if (pos.y < 0) { - pos.y = 0; - } else if (pos.y >= Map.Info.MapHeight) { - pos.y = Map.Info.MapHeight - 1; - } + // restrict to map + if (pos.x < 0) { + pos.x = 0; + } else if (pos.x >= Map.Info.MapWidth) { + pos.x = Map.Info.MapWidth - 1; + } + if (pos.y < 0) { + pos.y = 0; + } else if (pos.y >= Map.Info.MapHeight) { + pos.y = Map.Info.MapHeight - 1; + } - // move if possible - if (pos != unit.tilePos) { - UnmarkUnitFieldFlags(unit); - if (UnitCanBeAt(unit, pos)) { - COrderPtr order = unit.CurrentOrder(); - // FIXME: Don't use pathfinder for this, costs too much cpu. - order->Action = UnitActionMove; - Assert(!order->HasGoal()); - order->ClearGoal(); - order->Range = 0; - order->goalPos = pos; - unit.State = 0; - //return true;//TESTME: new localization - } + // move if possible + if (pos != unit.tilePos) { + UnmarkUnitFieldFlags(unit); + if (UnitCanBeAt(unit, pos)) { MarkUnitFieldFlags(unit); + CommandMove(unit, pos, FlushCommands); + return true; } - return true;//TESTME: old localization + MarkUnitFieldFlags(unit); } return false; } @@ -152,9 +161,9 @@ bool AutoCast(CUnit &unit) { if (unit.AutoCastSpell && !unit.Removed) { // Removed units can't cast any spells, from bunker) for (unsigned int i = 0; i < SpellTypeTable.size(); ++i) { - if (unit.AutoCastSpell[i] && - (SpellTypeTable[i]->AutoCast || SpellTypeTable[i]->AICast) && - AutoCastSpell(unit, SpellTypeTable[i])) { + if (unit.AutoCastSpell[i] + && (SpellTypeTable[i]->AutoCast || SpellTypeTable[i]->AICast) + && AutoCastSpell(unit, SpellTypeTable[i])) { return true; } } @@ -189,7 +198,7 @@ static CUnit *UnitToRepairInRange(const CUnit &unit, int range) return &candidate; } } - return NoUnitP; + return NULL; } /** @@ -206,7 +215,7 @@ bool AutoRepair(CUnit &unit) } CUnit *repairedUnit = UnitToRepairInRange(unit, repairRange); - if (repairedUnit == NoUnitP) { + if (repairedUnit == NULL) { return false; } const Vec2i invalidPos = {-1, -1}; @@ -221,36 +230,41 @@ bool AutoRepair(CUnit &unit) return true; } -/** -** Auto attack nearby units if possible -*/ -bool AutoAttack(CUnit &unit, bool stand_ground) +bool COrder_Still::AutoAttackStand(CUnit &unit) { if (unit.Type->CanAttack == false) { return false; } + // Removed units can only attack in AttackRange, from bunker + CUnit *goal = AttackUnitsInRange(unit); - if (stand_ground || unit.Removed || unit.CanMove() == false) { - // Removed units can only attack in AttackRange, from bunker - CUnit *goal = AttackUnitsInRange(unit); + if (goal == NULL) { + return false; + } - if (goal == NULL) { - return false; - } + CUnit *oldGoal =this->GetGoal(); + if (oldGoal && oldGoal->CurrentAction() == UnitActionDie) { + this->ClearGoal(); + oldGoal = NULL; + } + if (this->State < SUB_STILL_ATTACK || oldGoal != goal) { + // New target. + this->SetGoal(goal); + unit.State = 0; + this->State = SUB_STILL_ATTACK; // Mark attacking. + UnitHeadingFromDeltaXY(unit, goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos); + } + return true; +} - CUnit *temp = unit.CurrentOrder()->GetGoal(); - if (temp && temp->CurrentAction() == UnitActionDie) { - unit.CurrentOrder()->ClearGoal(); - temp = NoUnitP; - } - if (unit.SubAction < SUB_STILL_ATTACK || temp != goal) { - // New target. - unit.CurrentOrder()->SetGoal(goal); - unit.State = 0; - unit.SubAction = SUB_STILL_ATTACK; // Mark attacking. - UnitHeadingFromDeltaXY(unit, goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos); - } - return true; + +/** +** Auto attack nearby units if possible +*/ +bool AutoAttack(CUnit &unit) +{ + if (unit.Type->CanAttack == false) { + return false; } // Normal units react in reaction range. CUnit *goal = AttackUnitsInReactRange(unit); @@ -258,7 +272,6 @@ bool AutoAttack(CUnit &unit, bool stand_ground) if (goal == NULL) { return false; } - COrder *savedOrder; if (unit.SavedOrder != NULL) { @@ -268,7 +281,6 @@ bool AutoAttack(CUnit &unit, bool stand_ground) } else { savedOrder = unit.CurrentOrder()->Clone(); } - // Weak goal, can choose other unit, come back after attack CommandAttack(unit, goal->tilePos, NULL, FlushCommands); @@ -279,28 +291,17 @@ bool AutoAttack(CUnit &unit, bool stand_ground) } - -/** -** Unit stands still or stand ground. -** -** @param unit Unit pointer for action. -** @param stand_ground true if unit is standing ground. -*/ -void ActionStillGeneric(CUnit &unit, bool stand_ground) +/* virtual */ bool COrder_Still::Execute(CUnit &unit) { // If unit is not bunkered and removed, wait if (unit.Removed - && (!unit.Container - || !unit.Container->Type->CanTransport() - || !unit.Container->Type->AttackFromTransporter - || unit.Type->Missile.Missile->Class == MissileClassNone)) { - // If unit is in building or transporter it is removed. - return; + && (unit.Container == NULL || unit.Container->Type->AttackFromTransporter == false)) { + return false; } - switch (unit.SubAction) { + switch (this->State) { case SUB_STILL_INIT: //first entry - unit.SubAction = SUB_STILL_STANDBY; + this->State = SUB_STILL_STANDBY; // no break : follow case SUB_STILL_STANDBY: UnitShowAnimation(unit, unit.Type->Animations->Still); @@ -309,17 +310,23 @@ void ActionStillGeneric(CUnit &unit, bool stand_ground) AnimateActionAttack(unit); break; } - if (unit.Anim.Unbreakable) { // animation can't be aborted here - return; + return false; } - - if ((unit.IsAgressive() && AutoAttack(unit, stand_ground)) - || AutoCast(unit) - || AutoRepair(unit) - || MoveRandomly(unit)) { - return; + if (this->Action == UnitActionStandGround || unit.Removed || unit.CanMove() == false) { + if (unit.IsAgressive()) { + this->AutoAttackStand(unit); + return false; + } + } else { + if ((unit.IsAgressive() && AutoAttack(unit)) + || AutoCast(unit) + || AutoRepair(unit) + || MoveRandomly(unit)) { + return true; + } } + return false; } /** @@ -331,14 +338,11 @@ void HandleActionStill(COrder& order, CUnit &unit) { Assert(order.Action == UnitActionStill); - order.Execute(unit); + if (order.Execute(unit)) { + unit.ClearAction(); + } } -/* virtual */ bool COrder_Still::Execute(CUnit &unit) -{ - ActionStillGeneric(unit, false); - return false; -} //@} diff --git a/src/action/action_train.cpp b/src/action/action_train.cpp index d748a4835..822ea8cd7 100644 --- a/src/action/action_train.cpp +++ b/src/action/action_train.cpp @@ -48,7 +48,6 @@ #include "missile.h" #include "sound.h" #include "ai.h" -#include "interface.h" #include "ui.h" #include "iolib.h" diff --git a/src/action/action_unload.cpp b/src/action/action_unload.cpp index c7d925414..cd52b1f84 100644 --- a/src/action/action_unload.cpp +++ b/src/action/action_unload.cpp @@ -42,13 +42,50 @@ #include "unit.h" #include "actions.h" #include "map.h" -#include "interface.h" #include "pathfinder.h" +#include "script.h" +#include "iolib.h" /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ +/* virtual */ void COrder_Unload::Save(CFile &file, const CUnit &unit) const +{ + file.printf("{\"action-unload\","); + if (this->HasGoal()) { + CUnit &goal = *this->GetGoal(); + if (goal.Destroyed) { + /* this unit is destroyed so it's not in the global unit + * array - this means it won't be saved!!! */ + printf ("FIXME: storing destroyed Goal - loading will fail.\n"); + } + file.printf(" \"goal\", \"%s\",", UnitReference(goal).c_str()); + } + file.printf(" \"tile\", {%d, %d}, ", this->goalPos.x, this->goalPos.y); + file.printf("\"state\", %d,\n "); + SaveDataMove(file); + file.printf("}"); +} + +/* virtual */ bool COrder_Unload::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) +{ + if (this->ParseMoveData(l, j, value) == true) { + return true; + } else if (!strcmp("state", value)) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->State = LuaToNumber(l, -1); + lua_pop(l, 1); + } else { + return false; + } + return true; +} + + + + /** ** Find a free position close to startPos ** @@ -114,7 +151,7 @@ static bool FindUnloadPosition(const CUnit &transporter, const CUnit &unit, cons ** ** @param unit Unit to drop out. ** -** @return True if unit can be unloaded. +** @return True if unit is unloaded. ** ** @bug FIXME: Place unit only on fields reachable from the transporter */ @@ -125,11 +162,12 @@ static int UnloadUnit(CUnit &transporter, CUnit &unit) Assert(unit.Removed); if (!FindUnloadPosition(transporter, unit, transporter.tilePos, maxRange, &pos)) { - return 0; + return false; } unit.Boarded = 0; unit.Place(pos); - return 1; + transporter.BoardCount--; + return true; } /** @@ -265,38 +303,36 @@ static int MoveToDropZone(CUnit &unit) /** ** Make one or more unit leave the transporter. ** -** @param unit Pointer to unit. +** @return false if action should continue */ -static void LeaveTransporter(CUnit &transporter) +bool COrder_Unload::LeaveTransporter(CUnit &transporter) { int stillonboard = 0; - CUnit *goal = transporter.CurrentOrder()->GetGoal(); - // + // Goal is the specific unit unit that you want to unload. // This can be NULL, in case you want to unload everything. - // - if (goal) { - if (goal->Destroyed) { + if (this->HasGoal()) { + CUnit &goal = *this->GetGoal(); + + if (goal.Destroyed) { DebugPrint("destroyed unit unloading?\n"); - transporter.CurrentOrder()->ClearGoal(); - return; + this->ClearGoal(); + return true; } transporter.CurrentOrder()->ClearGoal(); - goal->tilePos = transporter.tilePos; // Try to unload the unit. If it doesn't work there is no problem. - if (UnloadUnit(transporter, *goal)) { - transporter.BoardCount--; + if (UnloadUnit(transporter, goal)) { + this->ClearGoal(); + } else { + ++stillonboard; } } else { // Unload all units. - goal = transporter.UnitInside; + CUnit *goal = transporter.UnitInside; for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) { if (goal->Boarded) { - goal->tilePos = transporter.tilePos; if (!UnloadUnit(transporter, *goal)) { ++stillonboard; - } else { - transporter.BoardCount--; } } } @@ -309,15 +345,64 @@ static void LeaveTransporter(CUnit &transporter) if (stillonboard) { // We tell it to unload at it's current position. This can't be done, // so it will search for a piece of free coast nearby. - transporter.CurrentOrder()->Action = UnitActionUnload; - transporter.CurrentOrder()->ClearGoal(); - transporter.CurrentOrder()->goalPos = transporter.tilePos; - transporter.SubAction = 0; + this->State = 0; + return false; } else { - transporter.ClearAction(); + return true; } } +/* virtual */ bool COrder_Unload::Execute(CUnit &unit) +{ + const int maxSearchRange = 20; + + if (!unit.CanMove()) { + this->State = 2; + } + switch (this->State) { + case 0: // Choose destination + if (!this->HasGoal()) { + Vec2i pos; + + if (!ClosestFreeDropZone(unit, this->goalPos, maxSearchRange, &pos)) { + return true; + } + this->goalPos = pos; + } + + this->NewResetPath(); + this->State = 1; + // follow on next case + case 1: // Move unit to destination + // The Goal is the unit that we have to unload. + if (!this->HasGoal()) { + const int moveResult = MoveToDropZone(unit); + + // We have to unload everything + if (moveResult) { + if (moveResult == PF_REACHED) { + if (++this->State == 1) { + return true; + } + } else { + this->State = 2; + } + } + return false; + } + case 2: { // Leave the transporter + // FIXME: show still animations ? + if (LeaveTransporter(unit)) { + return true; + } + return false; + } + default: + return false; + } +} + + /** ** The transporter unloads a unit. ** @@ -325,52 +410,12 @@ static void LeaveTransporter(CUnit &transporter) */ void HandleActionUnload(COrder& order, CUnit &unit) { - const int maxSearchRange = 20; + Assert(order.Action == UnitActionUnload); - if (!unit.CanMove()) { - unit.SubAction = 2; - } - switch (unit.SubAction) { - case 0: // Choose destination - if (!order.HasGoal()) { - Vec2i pos; - - if (!ClosestFreeDropZone(unit, order.goalPos, maxSearchRange, &pos)) { - // Sorry... I give up. - unit.ClearAction(); - return; - } - order.goalPos = pos; - } - - unit.CurrentOrder()->NewResetPath(); - unit.SubAction = 1; - // follow on next case - case 1: // Move unit to destination - // The Goal is the unit that we have to unload. - if (!unit.CurrentOrder()->HasGoal()) { - const int moveResult = MoveToDropZone(unit); - - // We have to unload everything - if (moveResult) { - if (moveResult == PF_REACHED) { - if (++unit.SubAction == 1) { - unit.ClearAction(); - } - } else { - unit.SubAction = 2; - } - } - break; - } - case 2: // Leave the transporter - // FIXME: show still animations ? - LeaveTransporter(unit); - if (unit.CanMove() && unit.CurrentAction() != UnitActionStill) { - HandleActionUnload(*unit.CurrentOrder() , unit); - } - break; + if (order.Execute(unit)) { + unit.ClearAction(); } } + //@} diff --git a/src/action/actions.cpp b/src/action/actions.cpp index 0d9dd4b52..15159991a 100644 --- a/src/action/actions.cpp +++ b/src/action/actions.cpp @@ -47,7 +47,6 @@ #include "player.h" #include "unit.h" #include "missile.h" -#include "interface.h" #include "map.h" #include "sound.h" #include "spells.h" @@ -133,7 +132,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures /* static */ COrder* COrder::NewActionBoard(CUnit &unit) { - COrder *order = new COrder(UnitActionBoard); + COrder_Board *order = new COrder_Board; order->SetGoal(&unit); order->Range = 1; @@ -234,7 +233,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures /* static */ COrder* COrder::NewActionRepair(CUnit &unit, CUnit &target) { - COrder *order = new COrder(UnitActionRepair); + COrder_Repair *order = new COrder_Repair(); if (target.Destroyed) { order->goalPos = target.tilePos + target.Type->GetHalfTileSize(); @@ -249,14 +248,13 @@ unsigned SyncHash; /// Hash calculated to find sync failures { Assert(Map.Info.IsPointOnMap(pos)); - COrder *order = new COrder(UnitActionRepair); + COrder_Repair *order = new COrder_Repair; order->goalPos = pos; return order; } - /* static */ COrder* COrder::NewActionResearch(CUnit &unit, CUpgrade &upgrade) { COrder_Research *order = new COrder_Research(); @@ -350,17 +348,14 @@ unsigned SyncHash; /// Hash calculated to find sync failures /* static */ COrder* COrder::NewActionStandGround() { - return new COrder_StandGround; + return new COrder_Still(true); } /* static */ COrder* COrder::NewActionStill() { - return new COrder_Still; + return new COrder_Still(false); } - - - /* static */ COrder* COrder::NewActionTrain(CUnit &trainer, CUnitType &type) { COrder_Train *order = new COrder_Train; @@ -383,7 +378,7 @@ unsigned SyncHash; /// Hash calculated to find sync failures /* static */ COrder* COrder::NewActionUnload(const Vec2i &pos, CUnit *what) { - COrder *order = new COrder(UnitActionUnload); + COrder *order = new COrder_Unload; order->goalPos = pos; if (what && !what->Destroyed) { @@ -452,7 +447,7 @@ void COrder::ReleaseRefs(CUnit &unit) if (this->HasGoal()) { // If mining decrease the active count on the resource. if (this->Action == UnitActionResource) { - if (unit.SubAction == 60 /* SUB_GATHER_RESOURCE */ ) { + if (this->SubAction.Res == 60 /* SUB_GATHER_RESOURCE */ ) { CUnit *goal = this->GetGoal(); goal->Resource.Active--; @@ -460,7 +455,7 @@ void COrder::ReleaseRefs(CUnit &unit) } } // Still shouldn't have a reference unless attacking - Assert(!(this->Action == UnitActionStill && !unit.SubAction)); + Assert(!(this->Action == UnitActionStill && !SubAction.Attack)); this->ClearGoal(); } #ifdef DEBUG @@ -525,11 +520,11 @@ bool COrder::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/) // Maybe AI should cancel action and save resources ??? return true; case UnitActionResource: - if (unit.SubAction >= 65) { + if (SubAction.Res >= 65) { //Normal return to depot return true; } - if (unit.SubAction > 55 && + if (SubAction.Res > 55 && unit.ResourcesHeld > 0) { //escape to Depot with this what you have; Data.ResWorker.DoneHarvesting = 1; @@ -1564,10 +1559,7 @@ static void HandleUnitAction(CUnit &unit) delete unit.Orders[0]; unit.Orders.erase(unit.Orders.begin()); - // - // Note subaction 0 should reset. - // - unit.SubAction = unit.State = 0; + unit.State = 0; unit.Wait = 0; if (IsOnlySelected(unit)) { // update display for new action @@ -1721,7 +1713,7 @@ void UnitActions() SyncHash = (SyncHash << 5) | (SyncHash >> 27); SyncHash ^= unit.Orders.size() > 0 ? unit.CurrentAction() << 18 : 0; SyncHash ^= unit.State << 12; - SyncHash ^= unit.SubAction << 6; +// SyncHash ^= unit.SubAction << 6; SyncHash ^= unit.Refs << 3; } } diff --git a/src/action/command.cpp b/src/action/command.cpp index dfb4f465f..18c28e678 100644 --- a/src/action/command.cpp +++ b/src/action/command.cpp @@ -47,7 +47,6 @@ #include "upgrade.h" #include "pathfinder.h" #include "spells.h" -#include "interface.h" #include "ui.h" /*---------------------------------------------------------------------------- @@ -107,9 +106,6 @@ static void RemoveOrder(CUnit &unit, unsigned int order) delete unit.Orders[order]; unit.Orders.erase(unit.Orders.begin() + order); - if (order == 0) { - unit.SubAction = 0; - } if (unit.Orders.empty()) { unit.Orders.push_back(COrder::NewActionStill()); } diff --git a/src/ai/ai_resource.cpp b/src/ai/ai_resource.cpp index 579a6fd83..c4d7f3d62 100644 --- a/src/ai/ai_resource.cpp +++ b/src/ai/ai_resource.cpp @@ -238,10 +238,15 @@ static int AiBuildBuilding(const CUnitType &type, CUnitType &building, int near_ int action = unit.Orders[j]->Action; if (action == UnitActionBuild || action == UnitActionRepair || - action == UnitActionReturnGoods || - (action == UnitActionResource && - unit.SubAction > 55) /* SUB_START_GATHERING */) { - break; + action == UnitActionReturnGoods) { + break; + } + if (action == UnitActionResource) { + const COrder &order = *unit.Orders[j]; + + if (order.SubAction.Res > 55 /* SUB_START_GATHERING */) { + break; + } } } if (j == unit.Orders.size()) { @@ -1093,7 +1098,9 @@ static void AiCollectResources() for (int k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) { unit = units_assigned[src_c][k]; - if (unit->SubAction >= 65 /* SUB_STOP_GATHERING */ ) { + COrder &order = *unit->CurrentOrder(); + + if (order.SubAction.Res >= 65 /* SUB_STOP_GATHERING */ ) { //worker returning with resource continue; } @@ -1162,10 +1169,16 @@ static int AiRepairBuilding(const CUnitType &type, CUnit &building) for (int i = 0; i < nunits; ++i) { CUnit &unit = *table[i]; - if (unit.Type->RepairRange && unit.Orders.size() == 1 && - ((unit.CurrentAction() == UnitActionResource && unit.SubAction <= 55) /* SUB_START_GATHERING */ || - unit.CurrentAction() == UnitActionStill)) { - table[num++] = &unit; + if (unit.Type->RepairRange && unit.Orders.size() == 1) { + if (unit.CurrentAction() == UnitActionStill) { + table[num++] = &unit; + } else if (unit.CurrentAction() == UnitActionResource) { + COrder &order = *unit.CurrentOrder(); + + if (order.SubAction.Res <= 55 /* SUB_START_GATHERING */) { + table[num++] = &unit; + } + } } } diff --git a/src/include/actions.h b/src/include/actions.h index e30c5cf46..f6abac63f 100644 --- a/src/include/actions.h +++ b/src/include/actions.h @@ -108,6 +108,7 @@ public: goalPos.x = -1; goalPos.y = -1; memset(&Arg1, 0, sizeof (Arg1)); + memset(&SubAction, 0, sizeof (SubAction)); memset(&Data, 0, sizeof (Data)); } virtual ~COrder(); @@ -170,10 +171,8 @@ public: #if 1 // currently needed for parsing static COrder* NewActionAttack() { return new COrder(UnitActionAttack); } static COrder* NewActionAttackGround() { return new COrder(UnitActionAttackGround); } - static COrder* NewActionBoard() { return new COrder(UnitActionBoard); } static COrder* NewActionFollow() { return new COrder(UnitActionFollow); } static COrder* NewActionMove() { return new COrder(UnitActionMove); } - static COrder* NewActionRepair() { return new COrder(UnitActionRepair); } static COrder* NewActionResource() { return new COrder(UnitActionResource); } static COrder* NewActionReturnGoods() { return new COrder(UnitActionReturnGoods); } static COrder* NewActionUnload() { return new COrder(UnitActionUnload); } @@ -199,6 +198,13 @@ public: } Resource; } Arg1; /// Extra command argument. + union { + int Attack; + int Follow; + int Res; + } SubAction; + + union _order_data_ { struct _order_move_ { unsigned short int Cycles; /// how much Cycles we move. @@ -211,17 +217,34 @@ public: int TimeToHarvest; /// how much time until we harvest some more. unsigned DoneHarvesting:1; /// Harvesting done, wait for action to break. } ResWorker; /// Worker harvesting - struct _order_repair_ { - int Cycles; /// Cycles unit has been repairing for - } Repair; /// Repairing unit } Data; /// Storage room for different commands }; +class COrder_Board : public COrder +{ + friend COrder* COrder::NewActionBoard(CUnit &unit); +public: + COrder_Board() : COrder(UnitActionBoard), State(0) {} + + virtual COrder_Board *Clone() const { return new COrder_Board(*this); } + + virtual void Save(CFile &file, const CUnit &unit) const; + virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); + + virtual bool Execute(CUnit &unit); + +private: + bool WaitForTransporter(CUnit &unit); + +private: + int State; +}; + class COrder_Build : public COrder { friend COrder* COrder::NewActionBuild(const CUnit &builder, const Vec2i &pos, CUnitType &building); public: - COrder_Build() : COrder(UnitActionBuild), Type(NULL) {} + COrder_Build() : COrder(UnitActionBuild), Type(NULL), State(0) {} virtual COrder_Build *Clone() const { return new COrder_Build(*this); } @@ -235,11 +258,14 @@ public: const CUnitType& GetUnitType() const { return *Type; } private: + bool MoveToLocation(CUnit &unit); + CUnit *CheckCanBuild(CUnit &unit); bool StartBuilding(CUnit &unit, CUnit &ontop); bool BuildFromOutside(CUnit &unit) const; private: CUnitType *Type; /// build a unit of this unit-type CUnitPtr BuildingUnit; /// unit builded. + int State; }; @@ -298,7 +324,7 @@ class COrder_Patrol : public COrder { friend COrder* COrder::NewActionPatrol(const Vec2i ¤tPos, const Vec2i &dest); public: - COrder_Patrol() : COrder(UnitActionPatrol) {} + COrder_Patrol() : COrder(UnitActionPatrol), WaitingCycle(0) {} virtual COrder_Patrol *Clone() const { return new COrder_Patrol(*this); } @@ -310,8 +336,30 @@ public: const Vec2i& GetWayPoint() const { return WayPoint; } private: Vec2i WayPoint; /// position for patroling. + unsigned int WaitingCycle; /// number of cycle pathfinder wait. }; +class COrder_Repair : public COrder +{ + friend COrder* COrder::NewActionRepair(CUnit &unit, CUnit &target); + friend COrder* COrder::NewActionRepair(const Vec2i &pos); +public: + COrder_Repair() : COrder(UnitActionRepair), State(0), RepairCycle(0) {} + + virtual COrder_Repair *Clone() const { return new COrder_Repair(*this); } + + virtual void Save(CFile &file, const CUnit &unit) const; + virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); + + virtual bool Execute(CUnit &unit); +private: + bool RepairUnit(const CUnit &unit, CUnit &goal); +private: + unsigned int State; + unsigned int RepairCycle; +}; + + class COrder_Research : public COrder { @@ -337,7 +385,7 @@ private: class COrder_SpellCast : public COrder { public: - COrder_SpellCast() : COrder(UnitActionSpellCast), Spell(NULL) {} + COrder_SpellCast() : COrder(UnitActionSpellCast), Spell(NULL), State(0) {} virtual COrder_SpellCast *Clone() const { return new COrder_SpellCast(*this); } @@ -350,30 +398,17 @@ public: const SpellType& GetSpell() const { return *Spell; } void SetSpell(SpellType &spell) { Spell = &spell; } +private: + bool SpellMoveToTarget(CUnit &unit); private: SpellType *Spell; -}; - - - - -class COrder_StandGround : public COrder -{ -public: - COrder_StandGround() : COrder(UnitActionStandGround) {} - - virtual COrder_StandGround *Clone() const { return new COrder_StandGround(*this); } - - virtual void Save(CFile &file, const CUnit &unit) const; - virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); - - virtual bool Execute(CUnit &unit); + int State; }; class COrder_Still : public COrder { public: - COrder_Still() : COrder(UnitActionStill) {} + COrder_Still(bool stand) : COrder(stand ? UnitActionStandGround : UnitActionStill), State(0) {} virtual COrder_Still *Clone() const { return new COrder_Still(*this); } @@ -381,12 +416,13 @@ public: virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); virtual bool Execute(CUnit &unit); +private: + bool AutoAttackStand(CUnit &unit); +private: + int State; }; - - - class COrder_Train : public COrder { friend COrder* COrder::NewActionTrain(CUnit &trainer, CUnitType &type); @@ -429,6 +465,27 @@ private: }; + +class COrder_Unload : public COrder +{ + friend COrder* COrder::NewActionUnload(const Vec2i &pos, CUnit *what); +public: + COrder_Unload() : COrder(UnitActionUnload), State(0) {} + + virtual COrder_Unload *Clone() const { return new COrder_Unload(*this); } + + virtual void Save(CFile &file, const CUnit &unit) const; + virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); + + virtual bool Execute(CUnit &unit); + +private: + bool LeaveTransporter(CUnit &transporter); +private: + int State; +}; + + class COrder_UpgradeTo : public COrder { friend COrder* COrder::NewActionUpgradeTo(CUnit &unit, CUnitType &type); @@ -451,7 +508,6 @@ private: int Ticks; /// Ticks to complete }; - /*---------------------------------------------------------------------------- -- Variables ----------------------------------------------------------------------------*/ @@ -535,7 +591,7 @@ extern void CommandSharedVision(int player, bool state, int opponent); extern void DropResource(CUnit &unit); extern void ResourceGiveUp(CUnit &unit); extern int GetNumWaitingWorkers(const CUnit &mine); -extern bool AutoAttack(CUnit &unit, bool stand_ground); +extern bool AutoAttack(CUnit &unit); extern bool AutoRepair(CUnit &unit); extern bool AutoCast(CUnit &unit); @@ -543,8 +599,6 @@ extern void UnHideUnit(CUnit &unit); typedef void HandleActionFunc(COrder& order, CUnit &unit); - /// Generic still action -extern void ActionStillGeneric(CUnit &unit, bool stand_ground); /// Handle command still extern HandleActionFunc HandleActionStill; /// Handle command stand ground diff --git a/src/include/unit.h b/src/include/unit.h index 48eecbf47..1cf8597c2 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -269,12 +269,6 @@ ** in a harvester. ** @todo continue documentation ** -** CUnit::SubAction -** -** This is an action private variable, it is zero on the first -** entry of an action. Must be set to zero, if an action finishes. -** It should only be used inside of actions. -** ** CUnit::Wait ** ** The unit is forced too wait for that many cycles. Be carefull, @@ -514,7 +508,6 @@ public: unsigned char DamagedType; /// Index of damage type of unit which damaged this unit unsigned long Attacked; /// gamecycle unit was last attacked - unsigned SubAction : 8; /// sub-action of unit unsigned State : 8; /// action state unsigned Blink : 3; /// Let selection rectangle blink unsigned Moving : 1; /// The unit is moving diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index 8c087697b..fea37faaf 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -252,7 +252,14 @@ bool COrder::ParseMoveData(lua_State *l, int &j, const char *value) bool COrder::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) { - if (!strcmp(value, "current-resource")) { + if (this->ParseMoveData(l, j, value)) { + return true; + } else if (!strcmp(value, "subaction")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->SubAction.Attack = this->SubAction.Follow = this->SubAction.Res = LuaToNumber(l, -1); + lua_pop(l, 1); + } else if (!strcmp(value, "current-resource")) { ++j; lua_rawgeti(l, -1, j + 1); this->CurrentResource = CclGetResourceByName(l); @@ -324,9 +331,9 @@ void CclParseOrder(lua_State *l, const CUnit &unit, COrderPtr *orderPtr) lua_pop(l, 1); if (!strcmp(actiontype, "action-still")) { - *orderPtr = new COrder_Still; + *orderPtr = new COrder_Still(false); } else if (!strcmp(actiontype, "action-stand-ground")) { - *orderPtr = new COrder_StandGround; + *orderPtr = new COrder_Still(true); } else if (!strcmp(actiontype, "action-follow")) { *orderPtr = COrder::NewActionFollow(); } else if (!strcmp(actiontype, "action-move")) { @@ -348,7 +355,7 @@ void CclParseOrder(lua_State *l, const CUnit &unit, COrderPtr *orderPtr) } else if (!strcmp(actiontype, "action-built")) { *orderPtr = new COrder_Built; } else if (!strcmp(actiontype, "action-board")) { - *orderPtr = COrder::NewActionBoard(); + *orderPtr = new COrder_Board; } else if (!strcmp(actiontype, "action-unload")) { *orderPtr = COrder::NewActionUnload(); } else if (!strcmp(actiontype, "action-patrol")) { @@ -356,13 +363,13 @@ void CclParseOrder(lua_State *l, const CUnit &unit, COrderPtr *orderPtr) } else if (!strcmp(actiontype, "action-build")) { *orderPtr = new COrder_Build; } else if (!strcmp(actiontype, "action-repair")) { - *orderPtr = COrder::NewActionRepair(); + *orderPtr = new COrder_Repair; } else if (!strcmp(actiontype, "action-resource")) { *orderPtr = COrder::NewActionResource(); } else if (!strcmp(actiontype, "action-return-goods")) { *orderPtr = COrder::NewActionReturnGoods(); } else if (!strcmp(actiontype, "action-transform-into")) { - *orderPtr = new COrder_TransformInto(); + *orderPtr = new COrder_TransformInto; } else { LuaError(l, "ParseOrder: Unsupported type: %s" _C_ actiontype); } @@ -591,8 +598,6 @@ static int CclUnit(lua_State *l) lua_pushvalue(l, j + 1); unit->CurrentResource = CclGetResourceByName(l); lua_pop(l, 1); - } else if (!strcmp(value, "sub-action")) { - unit->SubAction = LuaToNumber(l, j + 1); } else if (!strcmp(value, "wait")) { unit->Wait = LuaToNumber(l, j + 1); } else if (!strcmp(value, "state")) { diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 6808bdcf8..01a9f92ac 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -170,7 +170,6 @@ void CUnit::Init() GroupId = 0; LastGroup = 0; ResourcesHeld = 0; - SubAction = 0; Wait = 0; State = 0; Blink = 0; @@ -268,7 +267,6 @@ void CUnit::ClearAction() delete CurrentOrder(); Orders[0] = COrder::NewActionStill(); - SubAction = 0; if (Selected) { SelectedUnitChanged(); } @@ -372,7 +370,6 @@ bool CUnit::RestoreOrder() // Restart order state. this->State = 0; - this->SubAction = 0; // Cannot delete this->Orders[0] since it is generally that order // which call this method. @@ -1064,7 +1061,7 @@ void UnitClearOrders(CUnit &unit) } unit.Orders.clear(); unit.Orders.push_back(COrder::NewActionStill()); - unit.SubAction = unit.State = 0; + unit.State = 0; } /** @@ -2136,7 +2133,6 @@ void DropOutAll(const CUnit &source) DropOutOnSide(*unit, LookingW, &source); Assert(!unit->CurrentOrder()->HasGoal()); unit->CurrentOrder()->Action = UnitActionStill; - unit->SubAction = 0; } } @@ -2821,7 +2817,6 @@ void LetUnitDie(CUnit &unit) // Unit has death animation. // Not good: UnitUpdateHeading(unit); - unit.SubAction = 0; unit.State = 0; delete unit.Orders[0]; unit.Orders[0] = COrder::NewActionDie(); diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp index 6d5d9a2c3..f5e272091 100644 --- a/src/unit/unit_draw.cpp +++ b/src/unit/unit_draw.cpp @@ -757,7 +757,8 @@ static void ShowSingleOrder(const CUnit &unit, const PixelPos &pos, const COrder pos2 = CurrentViewport->TilePosToScreen_Center(order.goalPos); // FALL THROUGH case UnitActionAttack: - if (unit.SubAction & 2) { // Show weak targets. + { + if (order.SubAction.Attack & 2) { // Show weak targets. e_color = ColorBlue; } else { e_color = ColorRed; @@ -765,7 +766,7 @@ static void ShowSingleOrder(const CUnit &unit, const PixelPos &pos, const COrder color = ColorRed; dest = true; break; - + } case UnitActionBoard: e_color = color = ColorGreen; dest = true; diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp index acc3f0852..15eb222aa 100644 --- a/src/unit/unit_save.cpp +++ b/src/unit/unit_save.cpp @@ -108,15 +108,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file) case UnitActionAttackGround: file.printf("\"action-attack-ground\","); break; - case UnitActionBoard: - file.printf("\"action-board\","); - break; - case UnitActionUnload: - file.printf("\"action-unload\","); - break; - case UnitActionRepair: - file.printf("\"action-repair\","); - break; case UnitActionResource: file.printf("\"action-resource\","); break; @@ -142,6 +133,21 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file) } file.printf(" \"tile\", {%d, %d}", order.goalPos.x, order.goalPos.y); + switch (order.Action) { + case UnitActionAttack: + case UnitActionAttackGround: + file.printf(", \"subaction\", %d", order.SubAction.Attack); + break; + case UnitActionFollow: + file.printf(", \"subaction\", %d", order.SubAction.Follow); + break; + case UnitActionResource: + case UnitActionReturnGoods: + file.printf(", \"subaction\", %d", order.SubAction.Res); + break; + } + + // Extra arg. switch (order.Action) { case UnitActionResource : @@ -330,17 +336,6 @@ void SaveUnit(const CUnit &unit, CFile *file) file->printf("\"current-resource\", \"%s\",\n ", DefaultResourceNames[unit.CurrentResource].c_str()); } - if (unit.SubAction && unit.IsAgressive() && - (unit.CurrentAction() == UnitActionStill || - unit.CurrentAction() == UnitActionStandGround)) - { - //Force recalculate Guard points - //if unit atack from StandGround then attac target is recalculate - //When unit first time handle action code. - file->printf("\"sub-action\", 0, "); - } else { - file->printf("\"sub-action\", %d, ", unit.SubAction); - } file->printf("\"wait\", %d, ", unit.Wait); file->printf("\"state\", %d,", unit.State); file->printf("\"anim-wait\", %d,", unit.Anim.Wait);