From 58f777c425738ccee1094a47e026e8401a26fab0 Mon Sep 17 00:00:00 2001 From: joris <joris.dauphin@gmail.com> Date: Tue, 21 Feb 2012 15:39:36 +0100 Subject: [PATCH] Add COrder_Built --- src/action/action_build.cpp | 575 +++++++++++++++++++--------------- src/action/action_repair.cpp | 88 ++---- src/action/actions.cpp | 38 ++- src/action/command.cpp | 2 +- src/include/actions.h | 52 ++- src/include/script.h | 5 + src/include/unit.h | 7 +- src/map/script_map.cpp | 1 - src/pathfinder/pathfinder.cpp | 1 + src/stratagus/missile.cpp | 1 + src/stratagus/selection.cpp | 8 +- src/ui/botpanel.cpp | 1 + src/ui/button_checks.cpp | 1 + src/ui/interface.cpp | 1 + src/ui/mainscr.cpp | 7 +- src/ui/mouse.cpp | 1 + src/unit/build.cpp | 1 + src/unit/script_unit.cpp | 52 --- src/unit/script_unittype.cpp | 16 +- src/unit/unit.cpp | 15 +- src/unit/unit_draw.cpp | 24 +- src/unit/unit_save.cpp | 40 +-- 22 files changed, 482 insertions(+), 455 deletions(-) diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp index 72aa22856..29ad1d712 100644 --- a/src/action/action_build.cpp +++ b/src/action/action_build.cpp @@ -49,42 +49,13 @@ #include "interface.h" #include "pathfinder.h" #include "construct.h" +#include "iolib.h" +#include "script.h" /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ -/** -** Update construction frame -** -** @param unit The building under construction. -*/ -static void UpdateConstructionFrame(CUnit &unit) -{ - CConstructionFrame *cframe; - CConstructionFrame *tmp; - int percent; - - percent = unit.CurrentOrder()->Data.Built.Progress / - (unit.Type->Stats[unit.Player->Index].Costs[TimeCost] * 6); - cframe = tmp = unit.Type->Construction->Frames; - while (tmp) { - if (percent < tmp->Percent) { - break; - } - cframe = tmp; - tmp = tmp->Next; - } - if (cframe != unit.CurrentOrder()->Data.Built.Frame) { - unit.CurrentOrder()->Data.Built.Frame = cframe; - if (unit.Frame < 0) { - unit.Frame = -cframe->Frame - 1; - } else { - unit.Frame = cframe->Frame; - } - } -} - /** ** Move to build location ** @@ -287,7 +258,8 @@ static void StartBuilding(CUnit &unit, CUnit &ontop) } // Must set action before placing, otherwise it will incorrectly mark radar - build->CurrentOrder()->Action = UnitActionBuilt; + delete build->CurrentOrder(); + build->Orders[0] = COrder::NewActionBuilt(unit, *build); // Must place after previous for map flags build->Place(pos); @@ -298,18 +270,10 @@ static void StartBuilding(CUnit &unit, CUnit &ontop) // HACK: the building is not ready yet build->Player->UnitTypesCount[type.Slot]--; - // Make sure the bulding doesn't cancel itself out right away. - build->CurrentOrder()->Data.Built.Progress = 0;//FIXME ? 100 : 0 - build->Variable[HP_INDEX].Value = 1; - if (build->Variable[SHIELD_INDEX].Max) - build->Variable[SHIELD_INDEX].Value = 1; - UpdateConstructionFrame(*build); - // We need somebody to work on it. if (!type.BuilderOutside) { //FIXME: cancel buld gen crash // Place the builder inside the building - build->CurrentOrder()->Data.Built.Worker = &unit; // HACK: allows the unit to be removed build->CurrentSightRange = 1; //HACK: reset anim @@ -337,7 +301,6 @@ static void StartBuilding(CUnit &unit, CUnit &ontop) // Mark the new building seen. MapMarkUnitSight(*build); } - UpdateConstructionFrame(*build); } /** @@ -359,48 +322,6 @@ static void BuildBuilding(CUnit &unit) // //int animlength = unit.Data.Build.Cycles; unit.CurrentOrder()->Data.Build.Cycles = 0; -#if 0 - CUnit *goal; - int hp; - - //goal hp are mod by HandleActionBuilt - //and outsid builder use repair now. - goal = unit.CurrentOrder()->GetGoal(); - //Assert(goal); - if(!goal) { - return; - } - - if (goal->CurrentAction() == UnitActionDie) { - unit.CurrentOrder()->ClearGoal(); - unit.State = 0; - unit.ClearAction(); - return; - } - - // hp is the current damage taken by the unit. - hp = (goal->CurrentOrder()->Data.Built.Progress * goal->Variable[HP_INDEX].Max) / - (goal->Stats->Costs[TimeCost] * 600) - goal->Variable[HP_INDEX].Value; - - // FIXME: implement this below: - // unit.CurrentOrder()->Data.Built.Worker->Type->BuilderSpeedFactor; - goal->CurrentOrder()->Data.Built.Progress += 100 * animlength * SpeedBuild; - // Keep the same level of damage while increasing HP. - goal->Variable[HP_INDEX].Value = (goal->Data.Built.Progress * goal->Variable[HP_INDEX].Max) / - (goal->Stats->Costs[TimeCost] * 600) - hp; - if (goal->Variable[HP_INDEX].Value > goal->Variable[HP_INDEX].Max) { - goal->Variable[HP_INDEX].Value = goal->Variable[HP_INDEX].Max; - } - - // - // Building is gone or finished - // - if (goal->Variable[HP_INDEX].Value == goal->Variable[HP_INDEX].Max) { - unit.CurrentOrder()->ClearGoal(); - unit.State = 0; - unit.ClearAction(); - } -#endif } /** @@ -425,6 +346,317 @@ void HandleActionBuild(COrder& /*order*/, CUnit &unit) } } +/////////////////////////// +// Action_built +////////////////////////// + +/* virtual */ COrder_Built *COrder_Built::Clone() const +{ + return new COrder_Built(*this); +} +/* virtual */ void COrder_Built::Save(CFile &file, const CUnit &unit) const +{ + file.printf("{\"action-built\", "); + + CConstructionFrame *cframe = unit.Type->Construction->Frames; + int frame = 0; + while (cframe != this->Data.Frame) { + cframe = cframe->Next; + ++frame; + } + if (this->Data.Worker == NULL) { + file.printf("\"worker\", \"%s\", ", UnitReference(this->Data.Worker).c_str()); + } + file.printf("\"progress\", %d, \"frame\", %d", this->Data.Progress, frame); + if (this->Data.Cancel) { + file.printf(", \"cancel\""); + } + file.printf("}"); +} + +/* virtual */ bool COrder_Built::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit) +{ + if (!strcmp(value, "worker")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->Data.Worker = CclGetUnitFromRef(l); + lua_pop(l, 1); + } else if (!strcmp(value, "progress")) { + ++j; + lua_rawgeti(l, -1, j + 1); + this->Data.Progress = LuaToNumber(l, -1); + lua_pop(l, 1); + } else if (!strcmp(value, "cancel")) { + this->Data.Cancel = 1; + } else if (!strcmp(value, "frame")) { + ++j; + lua_rawgeti(l, -1, j + 1); + int frame = LuaToNumber(l, -1); + lua_pop(l, 1); + CConstructionFrame *cframe = unit.Type->Construction->Frames; + while (frame--) { + cframe = cframe->Next; + } + this->Data.Frame = cframe; + } else { + return false; + } + return true; +} + + +static void CancelBuilt(COrder_Built &order, CUnit &unit) +{ + Assert(unit.CurrentOrder() == &order); + CUnit *worker = order.GetWorkerPtr(); + + // Drop out unit + if (worker != NULL) { + + + + worker->ClearAction(); + + // HACK: make sure the sight is updated correctly +// unit.CurrentSightRange = 1; + DropOutOnSide(*worker, LookingW, &unit); +// unit.CurrentSightRange = 0; + } + + // Player gets back 75% of the original cost for a building. + unit.Player->AddCostsFactor(unit.Stats->Costs, CancelBuildingCostsFactor); + // Cancel building + LetUnitDie(unit); +} + +static bool Finish(COrder_Built &order, CUnit& unit) +{ + const CUnitType &type = *unit.Type; + CPlayer &player = *unit.Player; + + DebugPrint("%d: Building %s(%s) ready.\n" _C_ player.Index _C_ type.Ident.c_str() _C_ type.Name.c_str() ); + + // HACK: the building is ready now + player.UnitTypesCount[type.Slot]++; + unit.Constructed = 0; + if (unit.Frame < 0) { + unit.Frame = -1; + } else { + unit.Frame = 0; + } + CUnit *worker = order.GetWorkerPtr(); + + if (worker != NULL) { + if (type.BuilderLost) { + // Bye bye worker. + LetUnitDie(*worker); + worker = NULL; + } else { // Drop out the worker. + worker->ClearAction(); +#if 0 + // HACK: make sure the sight is updated correctly + // unit.CurrentSightRange = 1; +#endif + DropOutOnSide(*worker, LookingW, &unit); + + // If we can harvest from the new building, do it. + if (worker->Type->ResInfo[type.GivesResource]) { + CommandResource(*worker, unit, 0); + } + } + } + + if (type.GivesResource && type.StartingResources != 0) { + // Has StartingResources, Use those + unit.ResourcesHeld = type.StartingResources; + } + + player.Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y, _("New %s done"), type.Name.c_str()); + if (&player == ThisPlayer) { + if (type.Sound.Ready.Sound) { + PlayUnitSound(unit, VoiceReady); + } else if (worker) { + PlayUnitSound(*worker, VoiceWorkCompleted); + } else { + PlayUnitSound(unit, VoiceBuilding); + } + } + + if (player.AiEnabled) { + /* Worker can be NULL */ + AiWorkComplete(worker, unit); + } + + // FIXME: Vladi: this is just a hack to test wall fixing, + // FIXME: also not sure if the right place... + // FIXME: Johns: hardcoded unit-type wall / more races! + if (&type == UnitTypeOrcWall || &type == UnitTypeHumanWall) { + Map.SetWall(unit.tilePos, &type == UnitTypeHumanWall); + unit.Remove(NULL); + UnitLost(unit); + UnitClearOrders(unit); + unit.Release(); + return false; + } + + UpdateForNewUnit(unit, 0); + + // Set the direction of the building if it supports them + if (type.NumDirections > 1) { + if (type.Wall) { // Special logic for walls + CorrectWallDirections(unit); + CorrectWallNeighBours(unit); + } else { + unit.Direction = (MyRand() >> 8) & 0xFF; // random heading + } + UnitUpdateHeading(unit); + } + + if (IsOnlySelected(unit) || &player == ThisPlayer) { + SelectedUnitChanged(); + } + MapUnmarkUnitSight(unit); + unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max; + MapMarkUnitSight(unit); + return true; +} + + +/* virtual */ bool COrder_Built::Execute(CUnit &unit) +{ + const CUnitType &type = *unit.Type; + + int amount; + if (type.BuilderOutside) { + amount = type.AutoBuildRate; + } else { + // FIXME: implement this below: + // this->Data.Worker->Type->BuilderSpeedFactor; + amount = 100; + } + this->Progress(unit, amount); + + // Check if construction should be canceled... + if (this->Data.Cancel || this->Data.Progress < 0) { + DebugPrint("%d: %s canceled.\n" _C_ unit.Player->Index _C_ unit.Type->Name.c_str()); + + CancelBuilt(*this, unit); + return false; + } + + const int maxProgress = type.Stats[unit.Player->Index].Costs[TimeCost] * 600; + + // Check if building ready. Note we can both build and repair. + if (!unit.Anim.Unbreakable && this->Data.Progress >= maxProgress) { + return Finish(*this, unit); + } + return false; +} + +/* virtual */ void COrder_Built::Cancel(CUnit &unit) +{ + this->Data.Cancel = 1; +} + +/* virtual */ void COrder_Built::UpdateUnitVariables(CUnit &unit) const +{ + Assert(unit.CurrentOrder() == this); + + unit.Variable[BUILD_INDEX].Value = this->Data.Progress; + unit.Variable[BUILD_INDEX].Max = unit.Type->Stats[unit.Player->Index].Costs[TimeCost] * 600; + + // This should happen when building unit with several peons + // Maybe also with only one. + // FIXME : Should be better to fix it in action_{build,repair}.c ? + if (unit.Variable[BUILD_INDEX].Value > unit.Variable[BUILD_INDEX].Max) { + // assume value is wrong. + unit.Variable[BUILD_INDEX].Value = unit.Variable[BUILD_INDEX].Max; + } +} + +/* virtual */ void COrder_Built::FillSeenValues(CUnit &unit) const +{ + unit.Seen.State = 1; + unit.Seen.CFrame = this->Data.Frame; +} + + +static const CConstructionFrame *FindCFramePercent(const CConstructionFrame &cframe, int percent) +{ + const CConstructionFrame *prev = &cframe; + + for (const CConstructionFrame *it = cframe.Next; it; it = it->Next) { + if (percent < it->Percent) { + return prev; + } + prev = it; + } + return prev; +} + +/** +** Update construction frame +** +** @param unit The building under construction. +*/ +void COrder_Built::UpdateConstructionFrame(CUnit &unit) +{ + const CUnitType &type = *unit.Type; + const int percent = this->Data.Progress / (type.Stats[unit.Player->Index].Costs[TimeCost] * 6); + const CConstructionFrame *cframe = FindCFramePercent(*type.Construction->Frames, percent); + + Assert(cframe != NULL); + + if (cframe != this->Data.Frame) { + this->Data.Frame = cframe; + if (unit.Frame < 0) { + unit.Frame = -cframe->Frame - 1; + } else { + unit.Frame = cframe->Frame; + } + } +} + + +void COrder_Built::Progress(CUnit &unit, int amount) +{ + Boost(unit, amount, HP_INDEX); + Boost(unit, amount, SHIELD_INDEX); + + this->Data.Progress += amount * SpeedBuild; + UpdateConstructionFrame(unit); +} + +void COrder_Built::ProgressHp(CUnit &unit, int amount) +{ + Boost(unit, amount, HP_INDEX); + + this->Data.Progress += amount * SpeedBuild; + UpdateConstructionFrame(unit); +} + + +void COrder_Built::Boost(CUnit &building, int amount, int varIndex) const +{ + Assert(building.CurrentOrder() == this); + + const int costs = building.Stats->Costs[TimeCost] * 600; + const int progress = this->Data.Progress; + const int newProgress = progress + amount * SpeedBuild; + const int maxValue = building.Variable[varIndex].Max; + + int ¤tValue = building.Variable[varIndex].Value; + + // damageValue is the current damage taken by the unit. + const int damageValue = (progress * maxValue) / costs - currentValue; + + // Keep the same level of damage while increasing Value. + currentValue = (newProgress * maxValue) / costs - damageValue; + currentValue = std::min(currentValue, maxValue); +} + + + /** ** Unit under Construction ** @@ -432,183 +664,12 @@ void HandleActionBuild(COrder& /*order*/, CUnit &unit) */ void HandleActionBuilt(COrder& order, CUnit &unit) { - CUnit *worker; - CUnitType *type; - int n, mod; - int progress, oldprogress; - //For shields - int sn, smod; + Assert(order.Action == UnitActionBuilt); - type = unit.Type; - - // mod is use for round to upper - mod = (unit.Stats->Costs[TimeCost] * 600) - unit.Variable[HP_INDEX].Value; - smod = (unit.Stats->Costs[TimeCost] * 600) - unit.Variable[SHIELD_INDEX].Value; - - // n is the current damage taken by the unit. - n = (order.Data.Built.Progress * unit.Variable[HP_INDEX].Max + (mod - 1)) / mod; - sn = (order.Data.Built.Progress * unit.Variable[SHIELD_INDEX].Max + (smod - 1)) / smod; - - // This below is most often 0 - if (type->BuilderOutside) { - progress = unit.Type->AutoBuildRate; - } else { - progress = 100; - // FIXME: implement this below: - // unit.Data.Built.Worker->Type->BuilderSpeedFactor; - } - // Building speeds increase or decrease. - progress *= SpeedBuild; - oldprogress = order.Data.Built.Progress; - order.Data.Built.Progress += progress; - // mod is use for round to upper and use it as cache - mod = type->Stats[unit.Player->Index].Costs[TimeCost] * 600; - - // Keep the same level of damage while increasing HP. - unit.Variable[HP_INDEX].Value += - (order.Data.Built.Progress * unit.Variable[HP_INDEX].Max + (mod - n - 1)) / (mod - n) - - (oldprogress * unit.Variable[HP_INDEX].Max + (mod - n - 1)) / (mod - n); - if (unit.Variable[HP_INDEX].Value > unit.Stats->Variables[HP_INDEX].Max) { - unit.Variable[HP_INDEX].Value = unit.Stats->Variables[HP_INDEX].Max; - } - if (unit.Variable[SHIELD_INDEX].Max > 0) - { - unit.Variable[SHIELD_INDEX].Value += - (order.Data.Built.Progress * unit.Variable[SHIELD_INDEX].Max + (mod - sn - 1)) / (mod - sn) - - (oldprogress * unit.Variable[SHIELD_INDEX].Max + (mod - sn - 1)) / (mod - sn); - if (unit.Variable[SHIELD_INDEX].Value > unit.Stats->Variables[SHIELD_INDEX].Max) { - unit.Variable[SHIELD_INDEX].Value = unit.Stats->Variables[SHIELD_INDEX].Max; - } - } - - // Check if construction should be canceled... - if (order.Data.Built.Cancel || order.Data.Built.Progress < 0) { - DebugPrint("%d: %s canceled.\n" _C_ unit.Player->Index _C_ unit.Type->Name.c_str()); - // Drop out unit - if ((worker = order.Data.Built.Worker)) { - - worker->CurrentOrder()->ClearGoal(); - worker->ClearAction(); - //worker->State = 0; - - unit.CurrentOrder()->Data.Built.Worker = NoUnitP; - // HACK: make sure the sight is updated correctly - unit.CurrentSightRange = 1; - DropOutOnSide(*worker, LookingW, &unit); - unit.CurrentSightRange = 0; - } - - // Player gets back 75% of the original cost for a building. - unit.Player->AddCostsFactor(unit.Stats->Costs, CancelBuildingCostsFactor); - // Cancel building - LetUnitDie(unit); - return; - } - - // - // Check if building ready. Note we can both build and repair. - // - //if (unit.CurrentOrder()->Data.Built.Progress >= unit.Stats->Costs[TimeCost] * 600 || - if (!order.Data.Built.Worker->Anim.Unbreakable && (order.Data.Built.Progress >= mod || - unit.Variable[HP_INDEX].Value >= unit.Stats->Variables[HP_INDEX].Max)) { - DebugPrint("%d: Building %s(%s) ready.\n" _C_ unit.Player->Index - _C_ unit.Type->Ident.c_str() _C_ unit.Type->Name.c_str() ); - if (unit.Variable[HP_INDEX].Value > unit.Stats->Variables[HP_INDEX].Max) { - unit.Variable[HP_INDEX].Value = unit.Stats->Variables[HP_INDEX].Max; - } + if (order.Execute(unit)) { + order.ClearGoal(); unit.ClearAction(); - // HACK: the building is ready now - unit.Player->UnitTypesCount[type->Slot]++; - unit.Constructed = 0; - if (unit.Frame < 0) { - unit.Frame = -1; - } else { - unit.Frame = 0; - } - - if ((worker = unit.CurrentOrder()->Data.Built.Worker)) { - // Bye bye worker. - if (type->BuilderLost) { - // FIXME: enough? - LetUnitDie(*worker); - worker = NULL; - // Drop out the worker. - } else { - worker->ClearAction(); - worker->SubAction = 0;//may be 40 - // HACK: make sure the sight is updated correctly - unit.CurrentSightRange = 1; - DropOutOnSide(*worker, LookingW, &unit); - - worker->CurrentOrder()->ClearGoal(); - - // - // If we can harvest from the new building, do it. - // - if (worker->Type->ResInfo[type->GivesResource]) { - CommandResource(*worker, unit, 0); - } - } - } - - if (type->GivesResource) { - // Set to Zero as it's part of a union - memset(&unit.CurrentOrder()->Data, 0, sizeof(unit.CurrentOrder()->Data)); - // Has StartingResources, Use those - unit.ResourcesHeld = type->StartingResources; - } - - unit.Player->Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y, - _("New %s done"), type->Name.c_str()); - if (unit.Player == ThisPlayer) { - if (unit.Type->Sound.Ready.Sound) { - PlayUnitSound(unit, VoiceReady); - } else if (worker) { - PlayUnitSound(*worker, VoiceWorkCompleted); - } else { - PlayUnitSound(unit, VoiceBuilding); - } - } - - if (unit.Player->AiEnabled) { - /* Worker can be NULL */ - AiWorkComplete(worker, unit); - } - - // FIXME: Vladi: this is just a hack to test wall fixing, - // FIXME: also not sure if the right place... - // FIXME: Johns: hardcoded unit-type wall / more races! - if (unit.Type == UnitTypeOrcWall || unit.Type == UnitTypeHumanWall) { - Map.SetWall(unit.tilePos, unit.Type == UnitTypeHumanWall); - unit.Remove(NULL); - UnitLost(unit); - UnitClearOrders(unit); - unit.Release(); - return; - } - - UpdateForNewUnit(unit, 0); - - // Set the direction of the building if it supports them - if (unit.Type->NumDirections > 1) { - if (unit.Type->Wall) { // Special logic for walls - CorrectWallDirections(unit); - CorrectWallNeighBours(unit); - } else { - unit.Direction = (MyRand() >> 8) & 0xFF; // random heading - } - UnitUpdateHeading(unit); - } - - if (IsOnlySelected(unit) || unit.Player == ThisPlayer) { - SelectedUnitChanged(); - } - unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max; - MapMarkUnitSight(unit); - return; } - - UpdateConstructionFrame(unit); } //@} diff --git a/src/action/action_repair.cpp b/src/action/action_repair.cpp index 63ba2d25a..a4b983d3d 100644 --- a/src/action/action_repair.cpp +++ b/src/action/action_repair.cpp @@ -62,69 +62,45 @@ */ static void RepairUnit(CUnit &unit, CUnit &goal) { - CPlayer *player; - int animlength; - int hp; + 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 ; + } + CPlayer *player = unit.Player; char buf[100]; - player = unit.Player; + // Calculate the repair costs. + Assert(goal.Stats->Variables[HP_INDEX].Max); - if (goal.CurrentAction() != UnitActionBuilt) { - // - // 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; - } + // 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; } + // FIXME: We shouldn't animate if no resources are available. + return; } - // - // Subtract the resources - // - player->SubCosts(goal.Type->RepairCosts); + } + // + // Subtract the resources + // + player->SubCosts(goal.Type->RepairCosts); - goal.Variable[HP_INDEX].Value += goal.Type->RepairHP; - if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { - goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; - } - } else { - int costs = goal.Stats->Costs[TimeCost] * 600; - // hp is the current damage taken by the unit. - hp = (goal.CurrentOrder()->Data.Built.Progress * goal.Variable[HP_INDEX].Max) / - costs - goal.Variable[HP_INDEX].Value; - // - // Calculate the length of the attack (repair) anim. - // - animlength = unit.CurrentOrder()->Data.Repair.Cycles; - unit.CurrentOrder()->Data.Repair.Cycles = 0; - - // FIXME: implement this below: - //unit.Data.Built.Worker->Type->BuilderSpeedFactor; - goal.CurrentOrder()->Data.Built.Progress += 100 * animlength * SpeedBuild; - // Keep the same level of damage while increasing HP. - goal.Variable[HP_INDEX].Value = - (goal.CurrentOrder()->Data.Built.Progress * goal.Stats->Variables[HP_INDEX].Max) / - costs - hp; - if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { - goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; - } + goal.Variable[HP_INDEX].Value += goal.Type->RepairHP; + if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { + goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; } } diff --git a/src/action/actions.cpp b/src/action/actions.cpp index 319f2d2c4..9cf0349db 100644 --- a/src/action/actions.cpp +++ b/src/action/actions.cpp @@ -176,14 +176,28 @@ extern void AiReduceMadeInBuilt(PlayerAi &pai, const CUnitType &type); return order; } -/* static */ COrder* COrder::NewActionBuilt() +/* static */ COrder* COrder::NewActionBuilt(CUnit &builder, CUnit &unit) { - COrder *order = new COrder; + COrder_Built* order = new COrder_Built(); order->Action = UnitActionBuilt; + + // Make sure the bulding doesn't cancel itself out right away. + + order->Data.Progress = 0;//FIXME ? 100 : 0 + unit.Variable[HP_INDEX].Value = 1; + if (unit.Variable[SHIELD_INDEX].Max) { + unit.Variable[SHIELD_INDEX].Value = 1; + } + order->UpdateConstructionFrame(unit); + + if (unit.Type->BuilderOutside == false) { + order->Data.Worker = &builder; + } return order; } + /* static */ COrder* COrder::NewActionDie() { COrder *order = new COrder; @@ -460,6 +474,14 @@ extern void AiReduceMadeInBuilt(PlayerAi &pai, const CUnitType &type); return order; } +/* static */ COrder* COrder::NewActionBuilt() +{ + COrder *order = new COrder_Built; + + order->Action = UnitActionBuilt; + return order; +} + /* static */ COrder* COrder::NewActionFollow() { COrder *order = new COrder; @@ -518,7 +540,7 @@ extern void AiReduceMadeInBuilt(PlayerAi &pai, const CUnitType &type); /* static */ COrder* COrder::NewActionSpellCast() { - COrder *order = new COrder; + COrder *order = new COrder_SpellCast; order->Action = UnitActionSpellCast; return order; @@ -655,17 +677,13 @@ bool COrder::CheckRange() const return (Range <= Map.Info.MapWidth || Range <= Map.Info.MapHeight); } -void COrder::FillSeenValues(CUnit &unit) const +/* virtual */ void COrder::FillSeenValues(CUnit &unit) const { - unit.Seen.State = (Action == UnitActionBuilt) | ((Action == UnitActionUpgradeTo) << 1); + unit.Seen.State = ((Action == UnitActionUpgradeTo) << 1); if (unit.CurrentAction() == UnitActionDie) { unit.Seen.State = 3; } - if (Action == UnitActionBuilt) { - unit.Seen.CFrame = Data.Built.Frame; - } else { - unit.Seen.CFrame = NULL; - } + unit.Seen.CFrame = NULL; } bool COrder::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/) diff --git a/src/action/command.cpp b/src/action/command.cpp index 8960c7087..22f481245 100644 --- a/src/action/command.cpp +++ b/src/action/command.cpp @@ -471,7 +471,7 @@ void CommandDismiss(CUnit &unit) { // Check if building is still under construction? (NETWORK!) if (unit.CurrentAction() == UnitActionBuilt) { - unit.CurrentOrder()->Data.Built.Cancel = 1; + unit.CurrentOrder()->Cancel(unit); } else { DebugPrint("Suicide unit ... \n"); LetUnitDie(unit); diff --git a/src/include/actions.h b/src/include/actions.h index a5ef7e54e..93c4e87f4 100644 --- a/src/include/actions.h +++ b/src/include/actions.h @@ -31,16 +31,14 @@ #define __ACTIONS_H__ //@{ -#ifndef __UNIT_CACHE_H__ -#include "unit_cache.h" +#ifndef __UNIT_H__ +#include "unit.h" #endif #ifndef __VEC2I_H__ #include "vec2i.h" #endif -//#include "vec2i.h" - /*---------------------------------------------------------------------------- -- Declarations ----------------------------------------------------------------------------*/ @@ -124,6 +122,7 @@ public: virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit); virtual void UpdateUnitVariables(CUnit &unit) const; + virtual void FillSeenValues(CUnit &unit) const; void ReleaseRefs(CUnit &owner); @@ -156,7 +155,6 @@ public: void NewResetPath() { Data.Move.Fast = 1; Data.Move.Length = 0; } void SaveDataMove(CFile &file) const; - void FillSeenValues(CUnit &unit) const; bool OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/); void AiUnitKilled(CUnit &unit); @@ -168,6 +166,7 @@ public: static COrder* NewActionBoard(CUnit &unit); static COrder* NewActionBuild(const CUnit &builder, const Vec2i &pos, CUnitType &building); static COrder* NewActionBuilt(); + static COrder* NewActionBuilt(CUnit &builder, CUnit &unit); static COrder* NewActionDie(); static COrder* NewActionFollow(CUnit &dest); static COrder* NewActionMove(const Vec2i &pos); @@ -235,12 +234,6 @@ public: #define MAX_PATH_LENGTH 28 /// max length of precalculated path char Path[MAX_PATH_LENGTH]; /// directions of stored path } Move; /// ActionMove,... - struct _order_built_ { - CUnit *Worker; /// Worker building this unit - int Progress; /// Progress counter, in 1/100 cycles. - int Cancel; /// Cancel construction - CConstructionFrame *Frame; /// Construction frame - } Built; /// ActionBuilt,... struct _order_build_ { int Cycles; /// Cycles unit has been building for } Build; /// ActionBuild @@ -260,6 +253,43 @@ public: } Data; /// Storage room for different commands }; +class COrder_Built : public COrder +{ + friend COrder* COrder::NewActionBuilt(CUnit &builder, CUnit &unit); +public: + virtual COrder_Built *Clone() const; + + 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); + virtual void Cancel(CUnit &unit); + + virtual void UpdateUnitVariables(CUnit &unit) const; + virtual void FillSeenValues(CUnit &unit) const; + + void Progress(CUnit & unit, int amount); + void ProgressHp(CUnit &unit, int amount); + + const CConstructionFrame& GetFrame() const { return *Data.Frame; } + const CUnitPtr &GetWorker() const { return Data.Worker; } + CUnit *GetWorkerPtr() { return Data.Worker; } + +private: + void Boost(CUnit &building, int amount, int varIndex) const; + void UpdateConstructionFrame(CUnit &unit); + +private: + struct { + CUnitPtr Worker; /// Worker building this unit + int Progress; /// Progress counter, in 1/100 cycles. + int Cancel; /// Cancel construction + const CConstructionFrame *Frame; /// Construction frame + } Data; /// ActionBuilt,... +}; + + + class COrder_Research : public COrder { public: diff --git a/src/include/script.h b/src/include/script.h index 46efb910e..d241a44a4 100644 --- a/src/include/script.h +++ b/src/include/script.h @@ -284,6 +284,11 @@ extern void SaveCcl(CFile *file); /// Save CCL module extern void SavePreferences(); /// Save user preferences extern int CclCommand(const std::string &command, bool exitOnError = true); + +CUnit *CclGetUnitFromRef(lua_State *l); + + + extern NumberDesc *Damage; /// Damage calculation for missile. /// transform string in corresponding index. diff --git a/src/include/unit.h b/src/include/unit.h index fe9179b8c..650d6aa5c 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -354,8 +354,6 @@ #include "player.h" #endif -#include "actions.h" - #include "vec2i.h" /*---------------------------------------------------------------------------- @@ -546,7 +544,7 @@ public: /* Seen stuff. */ int VisCount[PlayerMax]; /// Unit visibility counts struct _seen_stuff_ { - CConstructionFrame *CFrame; /// Seen construction frame + const CConstructionFrame *CFrame; /// Seen construction frame int Frame; /// last seen frame/stage of buildings CUnitType *Type; /// Pointer to last seen unit-type int X; /// Last unit->X Seen @@ -1105,6 +1103,9 @@ extern int CanTransport(const CUnit &transporter, const CUnit &unit); /// Generate a unit reference, a printable unique string for unit extern std::string UnitReference(const CUnit &unit); + /// Generate a unit reference, a printable unique string for unit +extern std::string UnitReference(const CUnitPtr &unit); + /// Save an order extern void SaveOrder(const COrder &order, const CUnit &unit, CFile *file); /// save unit-structure diff --git a/src/map/script_map.cpp b/src/map/script_map.cpp index 32eca5223..cc46123d4 100644 --- a/src/map/script_map.cpp +++ b/src/map/script_map.cpp @@ -44,7 +44,6 @@ #include "map.h" #include "tileset.h" #include "minimap.h" -#include "actions.h" #include "results.h" #include "ui.h" #include "player.h" diff --git a/src/pathfinder/pathfinder.cpp b/src/pathfinder/pathfinder.cpp index 495344071..f98777824 100644 --- a/src/pathfinder/pathfinder.cpp +++ b/src/pathfinder/pathfinder.cpp @@ -40,6 +40,7 @@ #include "unittype.h" #include "unit.h" #include "pathfinder.h" +#include "actions.h" //astar.cpp diff --git a/src/stratagus/missile.cpp b/src/stratagus/missile.cpp index ec86cca82..2b9c51a5f 100644 --- a/src/stratagus/missile.cpp +++ b/src/stratagus/missile.cpp @@ -56,6 +56,7 @@ #include "missile.h" #include "sound.h" #include "ui.h" +#include "actions.h" #include "iolib.h" #include "util.h" diff --git a/src/stratagus/selection.cpp b/src/stratagus/selection.cpp index 0865fa808..d3d005e27 100644 --- a/src/stratagus/selection.cpp +++ b/src/stratagus/selection.cpp @@ -165,7 +165,7 @@ void ChangeSelectedUnits(CUnit **units, int count) if (count == 1 && units[0]->Type->ClicksToExplode && !units[0]->Type->IsNotSelectable) { HandleSuicideClick(*units[0]); - if (units[0]->CurrentAction() == UnitActionDie) { + if (!units[0]->IsAlive()) { NetworkSendSelection(units, count); return ; } @@ -379,7 +379,7 @@ int SelectUnitsByType(CUnit &base) // if unit is a cadaver or hidden (not on map) // no unit can be selected. - if (base.Removed || base.CurrentAction() == UnitActionDie) { + if (base.Removed || base.IsAlive() == false) { return 0; } @@ -464,7 +464,7 @@ int ToggleUnitsByType(CUnit &base) // if unit is a cadaver or hidden (not on map) // no unit can be selected. - if (base.Removed || base.CurrentAction() == UnitActionDie) { + if (base.Removed || base.IsAlive() == false) { return 0; } // if unit isn't belonging to the player, or is a static unit @@ -709,7 +709,7 @@ CUnit**table, int num_units = UnitMax) continue; } // FIXME: Can we get this? - if (!unit.Removed && unit.CurrentAction() != UnitActionDie) { + if (!unit.Removed && unit.IsAlive()) { SelectSingleUnit(unit); return 1; } diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index a178f83aa..16ef88b08 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -56,6 +56,7 @@ #include "commands.h" #include "video.h" #include "font.h" +#include "actions.h" #include "guichan/key.h" #include "guichan/sdl/sdlinput.h" diff --git a/src/ui/button_checks.cpp b/src/ui/button_checks.cpp index 58632a816..c5bc67146 100644 --- a/src/ui/button_checks.cpp +++ b/src/ui/button_checks.cpp @@ -44,6 +44,7 @@ #include "interface.h" #include "network.h" #include "player.h" +#include "actions.h" /*---------------------------------------------------------------------------- -- Functions diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 8fd0fd269..cecc041b6 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -60,6 +60,7 @@ #include "ai.h" #include "widgets.h" #include "replay.h" +#include "actions.h" /*---------------------------------------------------------------------------- -- Defines diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index c9054ec3f..4b60fae06 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -59,6 +59,7 @@ #include "network.h" #include "menus.h" #include "spells.h" +#include "actions.h" #ifdef DEBUG #include "../ai/ai_local.h" #endif @@ -285,7 +286,7 @@ UStrInt GetComponent(const CUnit &unit, int index, EnumVariable e, int t) ** ** @return The desired unit. */ -const CUnit *GetUnitRef(const CUnit &unit, EnumUnit e) +static const CUnit *GetUnitRef(const CUnit &unit, EnumUnit e) { switch (e) { case UnitRefItSelf: @@ -296,7 +297,9 @@ const CUnit *GetUnitRef(const CUnit &unit, EnumUnit e) return unit.Container; case UnitRefWorker : if (unit.CurrentAction() == UnitActionBuilt) { - return unit.CurrentOrder()->Data.Built.Worker; + COrder_Built &order = *static_cast<COrder_Built*>(unit.CurrentOrder()); + + return order.GetWorkerPtr(); } else { return NoUnitP; } diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index 7c8add4d3..6c761e27a 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -61,6 +61,7 @@ #include "network.h" #include "spells.h" #include "widgets.h" +#include "actions.h" /*---------------------------------------------------------------------------- -- Variables diff --git a/src/unit/build.cpp b/src/unit/build.cpp index c0d1d0acb..48d4ba0fe 100644 --- a/src/unit/build.cpp +++ b/src/unit/build.cpp @@ -38,6 +38,7 @@ #include "unittype.h" #include "map.h" #include "player.h" +#include "actions.h" /*---------------------------------------------------------------------------- -- Functions diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index 0126043fd..a913f54bd 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -155,53 +155,6 @@ static void CclGetPos(lua_State *l, T *x , T *y, const int offset = -1) lua_pop(l, 1); } -/** -** Parse built -** -** @param l Lua state. -** @param order Unit pointer which should be filled with the data. -*/ -static void CclParseBuilt(lua_State *l, const CUnit &unit, COrderPtr order) -{ - const char *value; - int args; - int j; - - if (!lua_istable(l, -1)) { - LuaError(l, "incorrect argument"); - } - args = lua_objlen(l, -1); - for (j = 0; j < args; ++j) { - lua_rawgeti(l, -1, j + 1); - value = LuaToString(l, -1); - lua_pop(l, 1); - ++j; - if (!strcmp(value, "worker")) { - lua_rawgeti(l, -1, j + 1); - order->Data.Built.Worker = CclGetUnitFromRef(l); - lua_pop(l, 1); - } else if (!strcmp(value, "progress")) { - lua_rawgeti(l, -1, j + 1); - order->Data.Built.Progress = LuaToNumber(l, -1); - lua_pop(l, 1); - } else if (!strcmp(value, "cancel")) { - order->Data.Built.Cancel = 1; - --j; - } else if (!strcmp(value, "frame")) { - lua_rawgeti(l, -1, j + 1); - int frame = LuaToNumber(l, -1); - lua_pop(l, 1); - CConstructionFrame *cframe = unit.Type->Construction->Frames; - while (frame--) { - cframe = cframe->Next; - } - order->Data.Built.Frame = cframe; - } else { - LuaError(l, "ParseBuilt: Unsupported tag: %s" _C_ value); - } - } -} - /** ** Parse res worker data ** @@ -419,11 +372,6 @@ bool COrder::ParseSpecificData(lua_State *l, int &j, const char *value, const CU this->Arg1.Resource.Pos = invalidPos; this->Arg1.Resource.Mine = CclGetUnitFromRef(l); lua_pop(l, 1); - } else if (!strcmp(value, "data-built")) { - ++j; - lua_rawgeti(l, -1, j + 1); - CclParseBuilt(l, unit, this); - lua_pop(l, 1); } else if (!strcmp(value, "data-res-worker")) { ++j; lua_rawgeti(l, -1, j + 1); diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp index e7a9be242..6c7defc10 100644 --- a/src/unit/script_unittype.cpp +++ b/src/unit/script_unittype.cpp @@ -53,6 +53,7 @@ #include "unit.h" #include "unit_manager.h" #include "player.h" +#include "actions.h" #include "luacallback.h" /*---------------------------------------------------------------------------- @@ -2158,22 +2159,7 @@ static int CclDefineDecorations(lua_State *l) /* virtual */ void COrder::UpdateUnitVariables(CUnit &unit) const { - const CUnitType *type = unit.Type; - switch (unit.CurrentAction()) { - // Build - case UnitActionBuilt: - unit.Variable[BUILD_INDEX].Value = unit.CurrentOrder()->Data.Built.Progress; - unit.Variable[BUILD_INDEX].Max = type->Stats[unit.Player->Index].Costs[TimeCost] * 600; - - // This should happen when building unit with several peons - // Maybe also with only one. - // FIXME : Should be better to fix it in action_{build,repair}.c ? - if (unit.Variable[BUILD_INDEX].Value > unit.Variable[BUILD_INDEX].Max) { - // assume value is wrong. - unit.Variable[BUILD_INDEX].Value = unit.Variable[BUILD_INDEX].Max; - } - break; // Training case UnitActionTrain: unit.Variable[TRAINING_INDEX].Value = unit.CurrentOrder()->Data.Train.Ticks; diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 873ddb397..ed87baf44 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -266,7 +266,9 @@ unsigned int CUnit::CurrentAction() const void CUnit::ClearAction() { - CurrentOrder()->Action = UnitActionStill; + delete CurrentOrder(); + Orders[0] = COrder::NewActionStill(); + SubAction = 0; if (Selected) { SelectedUnitChanged(); @@ -2821,16 +2823,6 @@ void LetUnitDie(CUnit &unit) unit.Goal = NULL; } - // During resource build, the worker holds the resource amount, - // but if canceling building the platform, the worker is already - // outside. - if (type->GivesResource && - unit.CurrentAction() == UnitActionBuilt && - unit.CurrentOrder()->Data.Built.Worker) { - // Restore value for oil-patch - unit.ResourcesHeld = unit.CurrentOrder()->Data.Built.Worker->ResourcesHeld; - } - // Transporters lose or save their units and building their workers if (unit.UnitInside && unit.Type->SaveCargo) DropOutAll(unit); @@ -2842,7 +2834,6 @@ void LetUnitDie(CUnit &unit) UnitClearOrders(unit); - // Unit has death animation. // Not good: UnitUpdateHeading(unit); diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp index c21d6a05a..9fdf3e5f2 100644 --- a/src/unit/unit_draw.cpp +++ b/src/unit/unit_draw.cpp @@ -55,6 +55,7 @@ #include "interface.h" #include "font.h" #include "ui.h" +#include "actions.h" #include "script.h" /*---------------------------------------------------------------------------- @@ -173,7 +174,7 @@ void DrawUnitSelection(const CViewport *vp, const CUnit &unit) const CUnitType &type = *unit.Type; const PixelPos screenPos = vp->TilePosToScreen_TopLeft(unit.tilePos); - const int x = screenPos.x + unit.IX +type.TileWidth * PixelTileSize.x / 2 + const int x = screenPos.x + unit.IX + type.TileWidth * PixelTileSize.x / 2 - type.BoxWidth / 2 - (type.Width - type.Sprite->Width) / 2; const int y = screenPos.y + unit.IY + type.TileHeight * PixelTileSize.y / 2 - type.BoxHeight / 2 - (type.Height - type.Sprite->Height) / 2; @@ -1080,7 +1081,7 @@ void CUnit::Draw(const CViewport *vp) const int constructed; CPlayerColorGraphic *sprite; ResourceInfo *resinfo; - CConstructionFrame *cframe; + const CConstructionFrame *cframe; CUnitType *type; /* @@ -1113,8 +1114,14 @@ void CUnit::Draw(const CViewport *vp) const if (state == 2) { type = this->CurrentOrder()->Arg1.Type; } - // This is trash unless the unit is being built, and that's when we use it. - cframe = this->CurrentOrder()->Data.Built.Frame; + + if (this->CurrentAction() == UnitActionBuilt) { + COrder_Built &order = *static_cast<COrder_Built*>(this->CurrentOrder()); + + cframe = &order.GetFrame(); + } else { + cframe = NULL; + } } else { const Vec2i seenTilePos = {this->Seen.X, this->Seen.Y}; const PixelPos &screenPos = vp->TilePosToScreen_TopLeft(seenTilePos); @@ -1257,8 +1264,13 @@ void CUnitDrawProxy::operator=(const CUnit *unit) } if (unit->Constructed) { - // This is trash unless the unit is being built, and that's when we use it. - cframe = unit->CurrentOrder()->Data.Built.Frame; + if (unit->CurrentAction() == UnitActionBuilt) { + COrder_Built &order = *static_cast<COrder_Built*>(unit->CurrentOrder()); + + cframe = &order.GetFrame(); + } else { + cframe = NULL; + } } } else { IY = unit->Seen.IY; diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp index 4443516c4..fed0b6265 100644 --- a/src/unit/unit_save.cpp +++ b/src/unit/unit_save.cpp @@ -45,6 +45,7 @@ #include "spells.h" #include "construct.h" #include "iolib.h" +#include "actions.h" /*---------------------------------------------------------------------------- -- Functions @@ -61,6 +62,20 @@ std::string UnitReference(const CUnit &unit) return ss.str(); } +/** +** Generate a unit reference, a printable unique string for unit. +*/ +std::string UnitReference(const CUnitPtr &unit) +{ + Assert(unit != NULL); + + std::ostringstream ss; + ss << "U" << std::setfill('0') << std::setw(4) << std::uppercase << + std::hex << unit->Slot; + return ss.str(); +} + + /** ** Save an order. ** @@ -108,9 +123,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file) case UnitActionUpgradeTo: file.printf("\"action-upgrade-to\","); break; - case UnitActionBuilt: - file.printf("\"action-built\","); - break; case UnitActionBoard: file.printf("\"action-board\","); break; @@ -203,28 +215,6 @@ void SaveOrder(const COrder &order, const CUnit &unit, CFile *file) file.printf("}"); break; case UnitActionBuilt: - { - CConstructionFrame *cframe; - int frame; - - cframe = unit.Type->Construction->Frames; - frame = 0; - while (cframe != order.Data.Built.Frame) { - cframe = cframe->Next; - ++frame; - } - file.printf(",\n \"data-built\", {"); - - if (order.Data.Built.Worker) { - file.printf("\"worker\", \"%s\", ", UnitReference(*order.Data.Built.Worker).c_str()); - } - file.printf("\"progress\", %d, \"frame\", %d", order.Data.Built.Progress, frame); - if (order.Data.Built.Cancel) { - file.printf(", \"cancel\""); - } - file.printf("}"); - break; - } case UnitActionResearch: break; case UnitActionUpgradeTo: