Add COrder_ {Board, Unload, Repair}

Move CUnit::SubAction into corresponding COrder_
Merge COrder_StandGround into COrder_Still
This commit is contained in:
joris 2012-02-23 13:44:40 +01:00
parent 373616150a
commit d52a301924
24 changed files with 742 additions and 621 deletions

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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 &order;
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);
}

View file

@ -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.

View file

@ -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:

View file

@ -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?

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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

View file

@ -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.
}
//@}

View file

@ -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;

View file

@ -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!
**

View file

@ -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;
}
//@}

View file

@ -48,7 +48,6 @@
#include "missile.h"
#include "sound.h"
#include "ai.h"
#include "interface.h"
#include "ui.h"
#include "iolib.h"

View file

@ -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();
}
}
//@}

View file

@ -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;
}
}

View file

@ -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());
}

View file

@ -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;
}
}
}
}

View file

@ -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 &currentPos, 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

View file

@ -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

View file

@ -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")) {

View file

@ -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();

View file

@ -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;

View file

@ -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);