1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name action_resource.cpp - The generic resource action. */
12 //
13 // (c) Copyright 2001-2005 by Crestez Leonard and Jimmy Salmon
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #include "action/action_resource.h"
39
40 #include "ai.h"
41 #include "animation.h"
42 #include "interface.h"
43 #include "iolib.h"
44 #include "map.h"
45 #include "pathfinder.h"
46 #include "player.h"
47 #include "script.h"
48 #include "sound.h"
49 #include "tileset.h"
50 #include "translate.h"
51 #include "ui.h"
52 #include "unit.h"
53 #include "unit_find.h"
54 #include "unittype.h"
55 #include "video.h"
56
57 #include "../ai/ai_local.h"
58
59 /*----------------------------------------------------------------------------
60 -- Declarations
61 ----------------------------------------------------------------------------*/
62
63 #define SUB_START_RESOURCE 0
64 #define SUB_MOVE_TO_RESOURCE 5
65 #define SUB_UNREACHABLE_RESOURCE 31
66 #define SUB_START_GATHERING 55
67 #define SUB_GATHER_RESOURCE 60
68 #define SUB_STOP_GATHERING 65
69 #define SUB_MOVE_TO_DEPOT 70
70 #define SUB_UNREACHABLE_DEPOT 100
71 #define SUB_RETURN_RESOURCE 120
72
73 /*----------------------------------------------------------------------------
74 -- Functions
75 ----------------------------------------------------------------------------*/
76
77 class NearReachableTerrainFinder
78 {
79 public:
NearReachableTerrainFinder(const CPlayer & player,int maxDist,int movemask,int resmask,Vec2i * resPos)80 NearReachableTerrainFinder(const CPlayer &player, int maxDist, int movemask, int resmask, Vec2i *resPos) :
81 player(player), maxDist(maxDist), movemask(movemask), resmask(resmask), resPos(resPos) {}
82 VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
83 private:
84 const CPlayer &player;
85 int maxDist;
86 int movemask;
87 int resmask;
88 Vec2i *resPos;
89 };
90
Visit(TerrainTraversal & terrainTraversal,const Vec2i & pos,const Vec2i & from)91 VisitResult NearReachableTerrainFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
92 {
93 if (!player.AiEnabled && !Map.Field(pos)->playerInfo.IsExplored(player)) {
94 return VisitResult_DeadEnd;
95 }
96 // Look if found what was required.
97 if (CanMoveToMask(pos, movemask)) {
98 if (resPos) {
99 *resPos = from;
100 }
101 return VisitResult_Finished;
102 }
103 if (Map.Field(pos)->CheckMask(resmask)) { // reachable
104 if (terrainTraversal.Get(pos) <= maxDist) {
105 return VisitResult_Ok;
106 } else {
107 return VisitResult_DeadEnd;
108 }
109 } else { // unreachable
110 return VisitResult_DeadEnd;
111 }
112 }
113
FindNearestReachableTerrainType(int movemask,int resmask,int range,const CPlayer & player,const Vec2i & startPos,Vec2i * terrainPos)114 static bool FindNearestReachableTerrainType(int movemask, int resmask, int range,
115 const CPlayer &player, const Vec2i &startPos, Vec2i *terrainPos)
116 {
117 TerrainTraversal terrainTraversal;
118
119 terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
120 terrainTraversal.Init();
121
122 terrainTraversal.PushPos(startPos);
123
124 NearReachableTerrainFinder nearReachableTerrainFinder(player, range, movemask, resmask, terrainPos);
125
126 return terrainTraversal.Run(nearReachableTerrainFinder);
127 }
128
129
130
131
NewActionResource(CUnit & harvester,const Vec2i & pos)132 /* static */ COrder *COrder::NewActionResource(CUnit &harvester, const Vec2i &pos)
133 {
134 COrder_Resource *order = new COrder_Resource(harvester);
135 Vec2i ressourceLoc;
136
137 // Find the closest piece of wood next to a tile where the unit can move
138 if (!FindNearestReachableTerrainType(harvester.Type->MovementMask, MapFieldForest, 20, *harvester.Player, pos, &ressourceLoc)) {
139 DebugPrint("FIXME: Give up???\n");
140 ressourceLoc = pos;
141 }
142 order->goalPos = ressourceLoc;
143 order->CurrentResource = WoodCost; // Hard-coded resource.
144 return order;
145 }
146
NewActionResource(CUnit & harvester,CUnit & mine)147 /* static */ COrder *COrder::NewActionResource(CUnit &harvester, CUnit &mine)
148 {
149 COrder_Resource *order = new COrder_Resource(harvester);
150
151 order->SetGoal(&mine);
152 order->Resource.Mine = &mine;
153 order->Resource.Pos = mine.tilePos + mine.Type->GetHalfTileSize();
154 order->CurrentResource = mine.Type->GivesResource;
155 return order;
156 }
157
NewActionReturnGoods(CUnit & harvester,CUnit * depot)158 /* static */ COrder *COrder::NewActionReturnGoods(CUnit &harvester, CUnit *depot)
159 {
160 COrder_Resource *order = new COrder_Resource(harvester);
161
162 // Destination could be killed. NETWORK!
163 if (depot && depot->Destroyed) {
164 depot = NULL;
165 }
166 order->CurrentResource = harvester.CurrentResource;
167 order->DoneHarvesting = true;
168
169 if (harvester.CurrentResource) {
170 const ResourceInfo &resinfo = *harvester.Type->ResInfo[harvester.CurrentResource];
171 if (resinfo.TerrainHarvester && harvester.ResourcesHeld < resinfo.ResourceCapacity) {
172 // Need to finish harvesting first!
173 delete order;
174 return NewActionResource(harvester, harvester.tilePos);
175 }
176 }
177
178 if (depot == NULL) {
179 depot = FindDeposit(harvester, 1000, harvester.CurrentResource);
180 }
181 if (depot) {
182 order->Depot = depot;
183 order->UnitGotoGoal(harvester, depot, SUB_MOVE_TO_DEPOT);
184 } else {
185 order->State = SUB_UNREACHABLE_DEPOT;
186 order->goalPos = harvester.tilePos;
187 }
188 return order;
189 }
190
191
GetHarvestLocation() const192 Vec2i COrder_Resource::GetHarvestLocation() const
193 {
194 if (this->Resource.Mine != NULL) {
195 return this->Resource.Mine->tilePos;
196 } else {
197 return this->Resource.Pos;
198 }
199 }
200
IsGatheringStarted() const201 bool COrder_Resource::IsGatheringStarted() const
202 {
203 return this->State > SUB_START_GATHERING;
204 }
205
IsGatheringFinished() const206 bool COrder_Resource::IsGatheringFinished() const
207 {
208 return this->State >= SUB_STOP_GATHERING;
209 }
210
IsGatheringWaiting() const211 bool COrder_Resource::IsGatheringWaiting() const
212 {
213 return this->State == SUB_START_GATHERING && this->worker->Wait != 0;
214 }
215
~COrder_Resource()216 COrder_Resource::~COrder_Resource()
217 {
218 CUnit *mine = this->Resource.Mine;
219
220 if (mine && mine->IsAlive()) {
221 worker->DeAssignWorkerFromMine(*mine);
222 }
223
224 CUnit *goal = this->GetGoal();
225 if (goal) {
226 // If mining decrease the active count on the resource.
227 if (this->State == SUB_GATHER_RESOURCE) {
228
229 goal->Resource.Active--;
230 Assert(goal->Resource.Active >= 0);
231 }
232 }
233 }
234
Save(CFile & file,const CUnit & unit) const235 /* virtual */ void COrder_Resource::Save(CFile &file, const CUnit &unit) const
236 {
237 file.printf("{\"action-resource\",");
238 if (this->Finished) {
239 file.printf(" \"finished\",");
240 }
241 if (this->HasGoal()) {
242 file.printf(" \"goal\", \"%s\",", UnitReference(this->GetGoal()).c_str());
243 }
244 file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y);
245
246 Assert(this->worker != NULL && worker->IsAlive());
247 file.printf(" \"worker\", \"%s\",", UnitReference(worker).c_str());
248 file.printf(" \"current-res\", %d,", this->CurrentResource);
249
250 file.printf(" \"res-pos\", {%d, %d},", this->Resource.Pos.x, this->Resource.Pos.y);
251 if (this->Resource.Mine != NULL) {
252 file.printf(" \"res-mine\", \"%s\",", UnitReference(this->Resource.Mine).c_str());
253 }
254 if (this->Depot != NULL) {
255 file.printf(" \"res-depot\", \"%s\",", UnitReference(this->Depot).c_str());
256 }
257 if (this->DoneHarvesting) {
258 file.printf(" \"done-harvesting\",");
259 }
260 file.printf(" \"timetoharvest\", %d,", this->TimeToHarvest);
261 file.printf(" \"state\", %d", this->State);
262 file.printf("}");
263 }
264
ParseSpecificData(lua_State * l,int & j,const char * value,const CUnit & unit)265 /* virtual */ bool COrder_Resource::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
266 {
267 if (!strcmp(value, "current-res")) {
268 ++j;
269 this->CurrentResource = LuaToNumber(l, -1, j + 1);
270 } else if (!strcmp(value, "done-harvesting")) {
271 this->DoneHarvesting = true;
272 } else if (!strcmp(value, "res-depot")) {
273 ++j;
274 lua_rawgeti(l, -1, j + 1);
275 this->Depot = CclGetUnitFromRef(l);
276 lua_pop(l, 1);
277 } else if (!strcmp(value, "res-mine")) {
278 ++j;
279 lua_rawgeti(l, -1, j + 1);
280 this->Resource.Mine = CclGetUnitFromRef(l);
281 lua_pop(l, 1);
282 } else if (!strcmp(value, "res-pos")) {
283 ++j;
284 lua_rawgeti(l, -1, j + 1);
285 CclGetPos(l, &this->Resource.Pos.x , &this->Resource.Pos.y);
286 lua_pop(l, 1);
287 } else if (!strcmp(value, "state")) {
288 ++j;
289 this->State = LuaToNumber(l, -1, j + 1);
290 } else if (!strcmp(value, "timetoharvest")) {
291 ++j;
292 this->TimeToHarvest = LuaToNumber(l, -1, j + 1);
293 } else if (!strcmp(value, "worker")) {
294 ++j;
295 lua_rawgeti(l, -1, j + 1);
296 this->worker = CclGetUnitFromRef(l);
297 lua_pop(l, 1);
298 } else if (!strcmp(value, "tile")) {
299 ++j;
300 lua_rawgeti(l, -1, j + 1);
301 CclGetPos(l, &this->goalPos.x , &this->goalPos.y);
302 lua_pop(l, 1);
303 } else {
304 return false;
305 }
306 return true;
307 }
308
IsValid() const309 /* virtual */ bool COrder_Resource::IsValid() const
310 {
311 return true;
312 }
313
Show(const CViewport & vp,const PixelPos & lastScreenPos) const314 /* virtual */ PixelPos COrder_Resource::Show(const CViewport &vp, const PixelPos &lastScreenPos) const
315 {
316 PixelPos targetPos;
317
318 if (this->HasGoal()) {
319 targetPos = vp.MapToScreenPixelPos(this->GetGoal()->GetMapPixelPosCenter());
320 } else {
321 targetPos = vp.TilePosToScreen_Center(this->goalPos);
322 }
323 Video.FillCircleClip(ColorYellow, lastScreenPos, 2);
324 Video.DrawLineClip(ColorYellow, lastScreenPos, targetPos);
325 Video.FillCircleClip(ColorYellow, targetPos, 3);
326 return targetPos;
327 }
328
UpdatePathFinderData(PathFinderInput & input)329 /* virtual */ void COrder_Resource::UpdatePathFinderData(PathFinderInput &input)
330 {
331 input.SetMinRange(0);
332 input.SetMaxRange(1);
333
334 Vec2i tileSize;
335 if (this->HasGoal()) {
336 CUnit *goal = this->GetGoal();
337 tileSize.x = goal->Type->TileWidth;
338 tileSize.y = goal->Type->TileHeight;
339 input.SetGoal(goal->tilePos, tileSize);
340 } else {
341 tileSize.x = 0;
342 tileSize.y = 0;
343 input.SetGoal(this->goalPos, tileSize);
344 }
345 }
346
347
OnAiHitUnit(CUnit & unit,CUnit * attacker,int)348 /* virtual */ bool COrder_Resource::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /* damage*/)
349 {
350 if (this->IsGatheringFinished()) {
351 // Normal return to depot
352 return true;
353 }
354 if (this->IsGatheringStarted() && unit.ResourcesHeld > 0) {
355 // escape to Depot with what you have
356 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
357 if (resinfo.TerrainHarvester && unit.ResourcesHeld < resinfo.ResourceCapacity) {
358 // We don't have anything yet.
359 return false;
360 }
361 this->DoneHarvesting = true;
362 return true;
363 }
364 return false;
365 }
366
367
368
369 /**
370 ** Move unit to terrain.
371 **
372 ** @return 1 if reached, -1 if unreacheable, 0 if on the way.
373 */
MoveToResource_Terrain(CUnit & unit)374 int COrder_Resource::MoveToResource_Terrain(CUnit &unit)
375 {
376 Vec2i pos = this->goalPos;
377
378 // Wood gone, look somewhere else.
379 if ((Map.Info.IsPointOnMap(pos) == false || Map.Field(pos)->IsTerrainResourceOnMap(CurrentResource) == false)
380 && (!unit.IX) && (!unit.IY)) {
381 if (!FindTerrainType(unit.Type->MovementMask, MapFieldForest, 16, *unit.Player, this->goalPos, &pos)) {
382 // no wood in range
383 return -1;
384 } else {
385 this->goalPos = pos;
386 }
387 }
388 switch (DoActionMove(unit)) {
389 case PF_UNREACHABLE:
390 unit.Wait = 10;
391 if (unit.Player->AiEnabled) {
392 this->Range++;
393 if (this->Range >= 5) {
394 this->Range = 0;
395 AiCanNotMove(unit);
396 }
397 }
398 if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 9999, *unit.Player, unit.tilePos, &pos)) {
399 this->goalPos = pos;
400 DebugPrint("Found a better place to harvest %d,%d\n" _C_ pos.x _C_ pos.y);
401 // FIXME: can't this overflow? It really shouldn't, since
402 // x and y are really supossed to be reachable, checked thorugh a flood fill.
403 // I don't know, sometimes stuff happens.
404 return 0;
405 }
406 return -1;
407 case PF_REACHED:
408 return 1;
409 case PF_WAIT:
410 if (unit.Player->AiEnabled) {
411 this->Range++;
412 if (this->Range >= 5) {
413 this->Range = 0;
414 AiCanNotMove(unit);
415 }
416 }
417 default:
418 return 0;
419 }
420 }
421
422 /**
423 ** Move unit to unit resource.
424 **
425 ** @return 1 if reached, -1 if unreacheable, 0 if on the way.
426 */
MoveToResource_Unit(CUnit & unit)427 int COrder_Resource::MoveToResource_Unit(CUnit &unit)
428 {
429 const CUnit *goal = this->GetGoal();
430 Assert(goal);
431
432 switch (DoActionMove(unit)) { // reached end-point?
433 case PF_UNREACHABLE:
434 return -1;
435 case PF_REACHED:
436 break;
437 case PF_WAIT:
438 if (unit.Player->AiEnabled) {
439 this->Range++;
440 if (this->Range >= 5) {
441 this->Range = 0;
442 AiCanNotMove(unit);
443 }
444 }
445 default:
446 // Goal gone or something.
447 if (unit.Anim.Unbreakable || goal->IsVisibleAsGoal(*unit.Player)) {
448 return 0;
449 }
450 break;
451 }
452 return 1;
453 }
454
455 /**
456 ** Move unit to resource.
457 **
458 ** @param unit Pointer to unit.
459 **
460 ** @return 1 if reached, -1 if unreacheable, 0 if on the way.
461 */
MoveToResource(CUnit & unit)462 int COrder_Resource::MoveToResource(CUnit &unit)
463 {
464 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
465
466 if (resinfo.TerrainHarvester) {
467 return MoveToResource_Terrain(unit);
468 } else {
469 return MoveToResource_Unit(unit);
470 }
471 }
472
UnitGotoGoal(CUnit & unit,CUnit * const goal,int state)473 void COrder_Resource::UnitGotoGoal(CUnit &unit, CUnit *const goal, int state)
474 {
475 if (this->GetGoal() != goal) {
476 this->SetGoal(goal);
477 if (goal) {
478 this->goalPos.x = this->goalPos.y = -1;
479 }
480 }
481 this->State = state;
482 if (state == SUB_MOVE_TO_DEPOT || state == SUB_MOVE_TO_RESOURCE) {
483 unit.pathFinderData->output.Cycles = 0; //moving counter
484 }
485 }
486
487 /**
488 ** Start harvesting the resource.
489 **
490 ** @param unit Pointer to unit.
491 **
492 ** @return TRUE if ready, otherwise FALSE.
493 */
StartGathering(CUnit & unit)494 int COrder_Resource::StartGathering(CUnit &unit)
495 {
496 CUnit *goal;
497 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
498 Assert(!unit.IX);
499 Assert(!unit.IY);
500
501 if (resinfo.TerrainHarvester) {
502 // This shouldn't happened?
503 #if 0
504 if (!Map.IsTerrainResourceOnMap(unit.Orders->goalPos, this->CurrentResource)) {
505 DebugPrint("Wood gone, just like that?\n");
506 return 0;
507 }
508 #endif
509 UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos);
510 if (resinfo.WaitAtResource) {
511 this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]);
512 } else {
513 this->TimeToHarvest = 1;
514 }
515 this->DoneHarvesting = 0;
516 if (this->CurrentResource != unit.CurrentResource) {
517 DropResource(unit);
518 unit.CurrentResource = this->CurrentResource;
519 }
520 return 1;
521 }
522
523 goal = this->GetGoal();
524
525 // Target is dead, stop getting resources.
526 if (!goal || goal->IsVisibleAsGoal(*unit.Player) == false) {
527 // Find an alternative, but don't look too far.
528 this->goalPos.x = -1;
529 this->goalPos.y = -1;
530 if ((goal = UnitFindResource(unit, unit, 15, this->CurrentResource, unit.Player->AiEnabled))) {
531 this->State = SUB_START_RESOURCE;
532 this->SetGoal(goal);
533 } else {
534 this->ClearGoal();
535 this->Finished = true;
536 }
537 return 0;
538 }
539
540 // FIXME: 0 can happen, if to near placed by map designer.
541 Assert(unit.MapDistanceTo(*goal) <= 1);
542
543 // Update the heading of a harvesting unit to looks straight at the resource.
544 UnitHeadingFromDeltaXY(unit, goal->tilePos - unit.tilePos + goal->Type->GetHalfTileSize());
545
546 // If resource is still under construction, wait!
547 if ((goal->Type->MaxOnBoard && goal->Resource.Active >= goal->Type->MaxOnBoard)
548 || goal->CurrentAction() == UnitActionBuilt) {
549 // FIXME: Determine somehow when the resource will be free to use
550 // FIXME: Could we somehow find another resource? Think minerals
551 // FIXME: We should add a flag for that, and a limited range.
552 // FIXME: Think minerals in st*rcr*ft!!
553 // However the CPU usage is really low (no pathfinding stuff).
554 unit.Wait = 10;
555 return 0;
556 }
557
558 // Place unit inside the resource
559 if (!resinfo.HarvestFromOutside) {
560 if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->InsideCount) {
561 this->ClearGoal();
562 int selected = unit.Selected;
563 unit.Remove(goal);
564 if (selected && !Preference.DeselectInMine) {
565 unit.Removed = 0;
566 SelectUnit(unit);
567 SelectionChanged();
568 unit.Removed = 1;
569 }
570 } else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->InsideCount) {
571 //Resource is full, wait
572 unit.Wait = 10;
573 return 0;
574 }
575 }
576
577 if (this->CurrentResource != unit.CurrentResource) {
578 DropResource(unit);
579 unit.CurrentResource = this->CurrentResource;
580 }
581
582 // Activate the resource
583 goal->Resource.Active++;
584
585 if (resinfo.WaitAtResource) {
586 this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]);
587 } else {
588 this->TimeToHarvest = 1;
589 }
590 this->DoneHarvesting = 0;
591 return 1;
592 }
593
594 /**
595 ** Animate a unit that is harvesting
596 **
597 ** @param unit Unit to animate
598 */
AnimateActionHarvest(CUnit & unit)599 static void AnimateActionHarvest(CUnit &unit)
600 {
601 Assert(unit.Type->Animations->Harvest[unit.CurrentResource]);
602 UnitShowAnimation(unit, unit.Type->Animations->Harvest[unit.CurrentResource]);
603 }
604
605 /**
606 ** Find something else to do when the resource is exhausted.
607 ** This is called from GatherResource when the resource is empty.
608 **
609 ** @param unit pointer to harvester unit.
610 ** @param source pointer to resource unit.
611 */
LoseResource(CUnit & unit,CUnit & source)612 void COrder_Resource::LoseResource(CUnit &unit, CUnit &source)
613 {
614 CUnit *depot;
615 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
616
617 Assert((unit.Container == &source && !resinfo.HarvestFromOutside)
618 || (!unit.Container && resinfo.HarvestFromOutside));
619
620 if (resinfo.HarvestFromOutside) {
621 this->ClearGoal();
622 --source.Resource.Active;
623 }
624
625 // Continue to harvest if we aren't fully loaded
626 if (resinfo.HarvestFromOutside && unit.ResourcesHeld < resinfo.ResourceCapacity) {
627 CUnit *goal = UnitFindResource(unit, unit, 15, this->CurrentResource, 1);
628
629 if (goal) {
630 this->goalPos.x = -1;
631 this->goalPos.y = -1;
632 this->State = SUB_START_RESOURCE;
633 this->SetGoal(goal);
634 return;
635 }
636 }
637
638 // If we are fully loaded first search for a depot.
639 if (unit.ResourcesHeld && (depot = FindDeposit(unit, 1000, unit.CurrentResource))) {
640 if (unit.Container) {
641 DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), &source);
642 }
643 // Remember where it mined, so it can look around for another resource.
644 //
645 //FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
646 //unit.CurrentOrder()->Arg1.ResourcePos = (unit.X << 16) | unit.Y;
647 this->DoneHarvesting = true;
648 UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
649 DebugPrint("%d: Worker %d report: Resource is exhausted, Going to depot\n"
650 _C_ unit.Player->Index _C_ UnitNumber(unit));
651 return;
652 }
653 // No depot found, or harvester empty
654 // Dump the unit outside and look for something to do.
655 if (unit.Container) {
656 Assert(!resinfo.HarvestFromOutside);
657 DropOutOnSide(unit, LookingW, &source);
658 }
659 this->goalPos.x = -1;
660 this->goalPos.y = -1;
661 //use depot as goal
662 depot = UnitFindResource(unit, unit, 15, this->CurrentResource, unit.Player->AiEnabled);
663 if (depot) {
664 DebugPrint("%d: Worker %d report: Resource is exhausted, Found another resource.\n"
665 _C_ unit.Player->Index _C_ UnitNumber(unit));
666 this->State = SUB_START_RESOURCE;
667 this->SetGoal(depot);
668 } else {
669 DebugPrint("%d: Worker %d report: Resource is exhausted, Just sits around confused.\n"
670 _C_ unit.Player->Index _C_ UnitNumber(unit));
671 this->Finished = true;
672 }
673 }
674
675
676
677 /**
678 ** Gather the resource
679 **
680 ** @param unit Pointer to unit.
681 **
682 ** @return non-zero if ready, otherwise zero.
683 */
GatherResource(CUnit & unit)684 int COrder_Resource::GatherResource(CUnit &unit)
685 {
686 CUnit *source = 0;
687 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
688 int addload;
689
690 if (resinfo.HarvestFromOutside || resinfo.TerrainHarvester) {
691 AnimateActionHarvest(unit);
692 } else {
693 unit.Anim.CurrAnim = NULL;
694 }
695
696 this->TimeToHarvest--;
697
698 if (this->DoneHarvesting) {
699 Assert(resinfo.HarvestFromOutside || resinfo.TerrainHarvester);
700 return !unit.Anim.Unbreakable;
701 }
702
703 // Target gone?
704 if (resinfo.TerrainHarvester && !Map.Field(this->goalPos)->IsTerrainResourceOnMap(this->CurrentResource)) {
705 if (!unit.Anim.Unbreakable) {
706 // Action now breakable, move to resource again.
707 this->State = SUB_MOVE_TO_RESOURCE;
708 // Give it some reasonable look while searching.
709 // FIXME: which frame?
710 unit.Frame = 0;
711 }
712 return 0;
713 // No wood? Freeze!!!
714 }
715
716 while (!this->DoneHarvesting && this->TimeToHarvest < 0) {
717 //FIXME: rb - how should it look for WaitAtResource == 0
718 if (resinfo.WaitAtResource) {
719 this->TimeToHarvest += std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]);
720 } else {
721 this->TimeToHarvest += 1;
722 }
723
724 // Calculate how much we can load.
725 if (resinfo.ResourceStep) {
726 addload = resinfo.ResourceStep;
727 } else {
728 addload = resinfo.ResourceCapacity;
729 }
730 // Make sure we don't bite more than we can chew.
731 if (unit.ResourcesHeld + addload > resinfo.ResourceCapacity) {
732 addload = resinfo.ResourceCapacity - unit.ResourcesHeld;
733 }
734
735 if (resinfo.TerrainHarvester) {
736 unit.ResourcesHeld += addload;
737
738 if (addload && unit.ResourcesHeld == resinfo.ResourceCapacity) {
739 Map.ClearTile(this->goalPos);
740 }
741 } else {
742 if (resinfo.HarvestFromOutside) {
743 source = this->GetGoal();
744 } else {
745 source = unit.Container;
746 }
747
748 Assert(source);
749 Assert(source->ResourcesHeld <= 655350);
750 bool is_visible = source->IsVisibleAsGoal(*unit.Player);
751 // Target is not dead, getting resources.
752 if (is_visible) {
753 // Don't load more that there is.
754 addload = std::min(source->ResourcesHeld, addload);
755 unit.ResourcesHeld += addload;
756 source->ResourcesHeld -= addload;
757 }
758
759 // End of resource: destroy the resource.
760 // FIXME: implement depleted resources.
761 if ((!is_visible) || (source->ResourcesHeld == 0)) {
762 if (unit.Anim.Unbreakable) {
763 return 0;
764 }
765 DebugPrint("%d: Worker %d report: Resource is destroyed\n" _C_ unit.Player->Index _C_ UnitNumber(unit));
766 bool dead = source->IsAlive() == false;
767
768 // Improved version of DropOutAll that makes workers go to the depot.
769 LoseResource(unit, *source);
770 for (CUnit *uins = source->Resource.Workers;
771 uins; uins = uins->NextWorker) {
772 if (uins != &unit && uins->CurrentOrder()->Action == UnitActionResource) {
773 COrder_Resource &order = *static_cast<COrder_Resource *>(uins->CurrentOrder());
774 if (!uins->Anim.Unbreakable && order.State == SUB_GATHER_RESOURCE) {
775 order.LoseResource(*uins, *source);
776 }
777 }
778 }
779 // Don't destroy the resource twice.
780 // This only happens when it's empty.
781 if (!dead) {
782 if (Preference.MineNotifications
783 && unit.Player->Index == ThisPlayer->Index
784 && source->Variable[GIVERESOURCE_INDEX].Max > DefaultIncomes[this->CurrentResource]) {
785 unit.Player->Notify(NotifyYellow, source->tilePos, _("%s has collapsed!"), source->Type->Name.c_str());
786 }
787 LetUnitDie(*source);
788 // FIXME: make the workers inside look for a new resource.
789 }
790 source = NULL;
791 return 0;
792 }
793 }
794 if (resinfo.TerrainHarvester) {
795 if (unit.ResourcesHeld == resinfo.ResourceCapacity) {
796 // Mark as complete.
797 this->DoneHarvesting = true;
798 }
799 return 0;
800 } else {
801 if (resinfo.HarvestFromOutside) {
802 if ((unit.ResourcesHeld == resinfo.ResourceCapacity) || (source == NULL)) {
803 // Mark as complete.
804 this->DoneHarvesting = true;
805 }
806 return 0;
807 } else {
808 return unit.ResourcesHeld == resinfo.ResourceCapacity && source;
809 }
810 }
811 }
812 return 0;
813 }
814
GetNumWaitingWorkers(const CUnit & mine)815 int GetNumWaitingWorkers(const CUnit &mine)
816 {
817 int ret = 0;
818 CUnit *worker = mine.Resource.Workers;
819
820 for (int i = 0; NULL != worker; worker = worker->NextWorker, ++i) {
821 Assert(worker->CurrentAction() == UnitActionResource);
822 COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder());
823
824 if (order.IsGatheringWaiting()) {
825 ret++;
826 }
827 Assert(i <= mine.Resource.Assigned);
828 }
829 return ret;
830 }
831
832 /**
833 ** Stop gathering from the resource, go home.
834 **
835 ** @param unit Poiner to unit.
836 **
837 ** @return TRUE if ready, otherwise FALSE.
838 */
StopGathering(CUnit & unit)839 int COrder_Resource::StopGathering(CUnit &unit)
840 {
841 CUnit *source = 0;
842 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
843
844 if (!resinfo.TerrainHarvester) {
845 if (resinfo.HarvestFromOutside) {
846 source = this->GetGoal();
847 this->ClearGoal();
848 } else {
849 source = unit.Container;
850 }
851 source->Resource.Active--;
852 Assert(source->Resource.Active >= 0);
853 //Store resource position.
854 this->Resource.Mine = source;
855
856 if (Preference.MineNotifications && unit.Player->Index == ThisPlayer->Index
857 && source->IsAlive()
858 && !source->MineLow
859 && source->ResourcesHeld * 100 / source->Variable[GIVERESOURCE_INDEX].Max <= 10
860 && source->Variable[GIVERESOURCE_INDEX].Max > DefaultIncomes[this->CurrentResource]) {
861 unit.Player->Notify(NotifyYellow, source->tilePos, _("%s is running low!"), source->Type->Name.c_str());
862 source->MineLow = 1;
863 }
864
865 if (source->Type->MaxOnBoard) {
866 int count = 0;
867 CUnit *worker = source->Resource.Workers;
868 CUnit *next = NULL;
869 for (; NULL != worker; worker = worker->NextWorker) {
870 Assert(worker->CurrentAction() == UnitActionResource);
871 COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder());
872 if (worker != &unit && order.IsGatheringWaiting()) {
873 count++;
874 if (next) {
875 if (next->Wait > worker->Wait) {
876 next = worker;
877 }
878 } else {
879 next = worker;
880 }
881 }
882 }
883 if (next) {
884 if (!unit.Player->AiEnabled) {
885 DebugPrint("%d: Worker %d report: Unfreez resource gathering of %d <Wait %d> on %d [Assigned: %d Waiting %d].\n"
886 _C_ unit.Player->Index _C_ UnitNumber(unit)
887 _C_ UnitNumber(*next) _C_ next->Wait
888 _C_ UnitNumber(*source) _C_ source->Resource.Assigned
889 _C_ count);
890 }
891 next->Wait = 0;
892 //source->Data.Resource.Waiting = count - 1;
893 //Assert(source->Data.Resource.Assigned >= source->Data.Resource.Waiting);
894 //StartGathering(next);
895 }
896 }
897 } else {
898 // Store resource position.
899 this->Resource.Pos = unit.tilePos;
900 Assert(this->Resource.Mine == NULL);
901 }
902
903 #ifdef DEBUG
904 if (!unit.ResourcesHeld) {
905 DebugPrint("Unit %d is empty???\n" _C_ UnitNumber(unit));
906 }
907 #endif
908
909 // Find and send to resource deposit.
910 CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
911 if (!depot || !unit.ResourcesHeld || this->Finished) {
912 if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
913 Assert(unit.Container);
914 DropOutOnSide(unit, LookingW, source);
915 }
916 CUnit *mine = this->Resource.Mine;
917
918 if (mine) {
919 unit.DeAssignWorkerFromMine(*mine);
920 this->Resource.Mine = NULL;
921 }
922
923 DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n"
924 _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.CurrentResource);
925 this->Finished = true;
926 return 0;
927 } else {
928 if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
929 Assert(unit.Container);
930 DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source);
931 }
932 UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
933 }
934 if (IsOnlySelected(unit)) {
935 SelectedUnitChanged();
936 }
937 #if 1
938 return 1;
939 #endif
940 }
941
942 /**
943 ** Move to resource depot
944 **
945 ** @param unit Pointer to unit.
946 **
947 ** @return TRUE if reached, otherwise FALSE.
948 */
MoveToDepot(CUnit & unit)949 int COrder_Resource::MoveToDepot(CUnit &unit)
950 {
951 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
952 CUnit &goal = *this->GetGoal();
953 CPlayer &player = *unit.Player;
954 Assert(&goal);
955
956 switch (DoActionMove(unit)) { // reached end-point?
957 case PF_UNREACHABLE:
958 return -1;
959 case PF_REACHED:
960 break;
961 case PF_WAIT:
962 if (unit.Player->AiEnabled) {
963 this->Range++;
964 if (this->Range >= 5) {
965 this->Range = 0;
966 AiCanNotMove(unit);
967 }
968 }
969 default:
970 if (unit.Anim.Unbreakable || goal.IsVisibleAsGoal(player)) {
971 return 0;
972 }
973 break;
974 }
975
976 //
977 // Target is dead, stop getting resources.
978 //
979 if (!goal.IsVisibleAsGoal(player)) {
980 DebugPrint("%d: Worker %d report: Destroyed depot\n" _C_ player.Index _C_ UnitNumber(unit));
981
982 unit.CurrentOrder()->ClearGoal();
983
984 CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
985
986 if (depot) {
987 UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
988 DebugPrint("%d: Worker %d report: Going to new deposit.\n" _C_ player.Index _C_ UnitNumber(unit));
989 } else {
990 DebugPrint("%d: Worker %d report: Can't find a new resource deposit.\n"
991 _C_ player.Index _C_ UnitNumber(unit));
992
993 // FIXME: perhaps we should choose an alternative
994 this->Finished = true;
995 }
996 return 0;
997 }
998
999 // If resource depot is still under construction, wait!
1000 if (goal.CurrentAction() == UnitActionBuilt) {
1001 unit.Wait = 10;
1002 return 0;
1003 }
1004
1005 this->ClearGoal();
1006 unit.Wait = resinfo.WaitAtDepot;
1007
1008 // Place unit inside the depot
1009 if (unit.Wait) {
1010 int selected = unit.Selected;
1011 unit.Remove(&goal);
1012 if (selected && !Preference.DeselectInMine) {
1013 unit.Removed = 0;
1014 SelectUnit(unit);
1015 SelectionChanged();
1016 unit.Removed = 1;
1017 }
1018 unit.Anim.CurrAnim = NULL;
1019 }
1020
1021 // Update resource.
1022 const int rindex = resinfo.FinalResource;
1023 player.ChangeResource(rindex, (unit.ResourcesHeld * player.Incomes[rindex]) / 100, true);
1024 player.TotalResources[rindex] += (unit.ResourcesHeld * player.Incomes[rindex]) / 100;
1025 unit.ResourcesHeld = 0;
1026 unit.CurrentResource = 0;
1027
1028 if (unit.Wait) {
1029 unit.Wait /= std::max(1, unit.Player->SpeedResourcesReturn[resinfo.ResourceId] / SPEEDUP_FACTOR);
1030 if (unit.Wait) {
1031 unit.Wait--;
1032 }
1033 }
1034 return 1;
1035 }
1036
1037 /**
1038 ** Wait in depot, for the resources stored.
1039 **
1040 ** @param unit Pointer to unit.
1041 **
1042 ** @return TRUE if ready, otherwise FALSE.
1043 */
WaitInDepot(CUnit & unit)1044 bool COrder_Resource::WaitInDepot(CUnit &unit)
1045 {
1046 const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
1047 const CUnit *depot = ResourceDepositOnMap(unit.tilePos, resinfo.ResourceId);
1048
1049 //Assert(depot);
1050
1051 // Range hardcoded. don't stray too far though
1052 if (resinfo.TerrainHarvester) {
1053 Vec2i pos = this->Resource.Pos;
1054
1055 if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 10, *unit.Player, pos, &pos)) {
1056 if (depot) {
1057 DropOutNearest(unit, pos, depot);
1058 }
1059 this->goalPos = pos;
1060 } else {
1061 if (depot) {
1062 DropOutOnSide(unit, LookingW, depot);
1063 }
1064 this->Finished = true;
1065 return false;
1066 }
1067 } else {
1068 /// FIXME: Make it customizable
1069 const unsigned int tooManyWorkers = 15;
1070
1071 CUnit *mine = this->Resource.Mine;
1072 const int range = 15;
1073 CUnit *newdepot = nullptr;
1074 CUnit *goal = nullptr;
1075 const bool longWay = unit.pathFinderData->output.Cycles > 500;
1076
1077 if (unit.Player->AiEnabled && AiPlayer && AiPlayer->BuildDepots) {
1078 // If the depot is overused, we need first to try to switch into another depot
1079 // Use depot's ref counter for that
1080 if (longWay || !mine || (depot->Refs > tooManyWorkers)) {
1081 newdepot = AiGetSuitableDepot(unit, *depot, &goal);
1082 if (newdepot == NULL && longWay) {
1083 // We need a new depot
1084 AiNewDepotRequest(unit);
1085 }
1086 }
1087 }
1088
1089 // If goal is not NULL, then we got it in AiGetSuitableDepot
1090 if (!goal) {
1091 if (mine != nullptr && mine->IsAlive()) {
1092 goal = mine;
1093 } else {
1094 const CUnit *start_unit = nullptr;
1095 if (newdepot != nullptr) {
1096 start_unit = newdepot;
1097 } else if (depot != nullptr) {
1098 start_unit = depot;
1099 }
1100 goal = UnitFindResource(unit, (start_unit ? *start_unit : unit), 1000, this->CurrentResource,
1101 unit.Player->AiEnabled, (newdepot ? newdepot : depot));
1102 }
1103
1104
1105 }
1106
1107 if (goal) {
1108 if (depot) {
1109 DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot);
1110 }
1111
1112 if (goal != mine) {
1113 if (mine) {
1114 unit.DeAssignWorkerFromMine(*mine);
1115 }
1116 unit.AssignWorkerToMine(*goal);
1117 this->Resource.Mine = goal;
1118 }
1119 this->SetGoal(goal);
1120 this->goalPos.x = this->goalPos.y = -1;
1121 } else {
1122 #ifdef DEBUG
1123 const Vec2i &pos = mine ? mine->tilePos : unit.tilePos;
1124 DebugPrint("%d: Worker %d report: [%d,%d] Resource gone near [%d,%d] in range %d. Sit and play dumb.\n"
1125 _C_ unit.Player->Index _C_ UnitNumber(unit)
1126 _C_ unit.tilePos.x _C_ unit.tilePos.y
1127 _C_ pos.x _C_ pos.y _C_ range);
1128 #endif // DEBUG
1129 if (depot) {
1130 DropOutOnSide(unit, LookingW, depot);
1131 }
1132 if (mine) {
1133 unit.DeAssignWorkerFromMine(*mine);
1134 this->Resource.Mine = NULL;
1135 }
1136 this->Finished = true;
1137 return false;
1138 }
1139 }
1140 return true;
1141 }
1142
DropResource(CUnit & unit)1143 void COrder_Resource::DropResource(CUnit &unit)
1144 {
1145 if (unit.CurrentResource) {
1146 const ResourceInfo &resinfo = *unit.Type->ResInfo[unit.CurrentResource];
1147
1148 if (!resinfo.TerrainHarvester) {
1149 CUnit *mine = this->Resource.Mine;
1150 if (mine) {
1151 unit.DeAssignWorkerFromMine(*mine);
1152 }
1153 }
1154 //fast clean both resource data: pos and mine
1155 this->Resource.Mine = NULL;
1156 unit.CurrentResource = 0;
1157 unit.ResourcesHeld = 0;
1158 }
1159 }
1160
1161 /**
1162 ** Give up on gathering.
1163 **
1164 ** @param unit Pointer to unit.
1165 */
ResourceGiveUp(CUnit & unit)1166 void COrder_Resource::ResourceGiveUp(CUnit &unit)
1167 {
1168 DebugPrint("%d: Worker %d report: Gave up on resource gathering.\n" _C_ unit.Player->Index _C_ UnitNumber(unit));
1169 if (this->HasGoal()) {
1170 DropResource(unit);
1171 this->ClearGoal();
1172 }
1173 this->Finished = true;
1174 }
1175
1176 /**
1177 ** Try to find another resource before give up
1178 **
1179 ** return false if failed, true otherwise.
1180 */
1181
FindAnotherResource(CUnit & unit)1182 bool COrder_Resource::FindAnotherResource(CUnit &unit)
1183 {
1184 if (this->CurrentResource) {
1185 const ResourceInfo *resinfo = unit.Type->ResInfo[this->CurrentResource];
1186 if (resinfo) {
1187 if (!resinfo->TerrainHarvester) {
1188 CUnit *newGoal = UnitFindResource(unit, this->Resource.Mine ? *this->Resource.Mine : unit, 8, this->CurrentResource, 1);
1189
1190 if (newGoal) {
1191 CUnit *mine = this->Resource.Mine;
1192 if (mine) {
1193 unit.DeAssignWorkerFromMine(*mine);
1194 }
1195 unit.AssignWorkerToMine(*newGoal);
1196 this->Resource.Mine = newGoal;
1197 this->goalPos.x = -1;
1198 this->goalPos.y = -1;
1199 this->State = SUB_MOVE_TO_RESOURCE;
1200 this->SetGoal(newGoal);
1201 return true;
1202 }
1203 } else {
1204 Vec2i resPos;
1205 if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 8, *unit.Player, unit.tilePos, &resPos)) {
1206 this->goalPos = resPos;
1207 this->State = SUB_MOVE_TO_RESOURCE;
1208 DebugPrint("Found a better place to harvest %d,%d\n" _C_ resPos.x _C_ resPos.y);
1209 return true;
1210 }
1211 }
1212 }
1213 }
1214 return false;
1215 }
1216
1217
1218 /**
1219 ** Initialize
1220 **
1221 ** return false if action is canceled, true otherwise.
1222 */
ActionResourceInit(CUnit & unit)1223 bool COrder_Resource::ActionResourceInit(CUnit &unit)
1224 {
1225 Assert(this->State == SUB_START_RESOURCE);
1226
1227 this->Range = 0;
1228 CUnit *const goal = this->GetGoal();
1229 CUnit *mine = this->Resource.Mine;
1230
1231 if (mine) {
1232 unit.DeAssignWorkerFromMine(*mine);
1233 this->Resource.Mine = NULL;
1234 }
1235 if (goal && goal->IsAlive() == false) {
1236 return false;
1237 }
1238 if (goal && goal->CurrentAction() != UnitActionBuilt) {
1239 unit.AssignWorkerToMine(*goal);
1240 this->Resource.Mine = goal;
1241 }
1242
1243 UnitGotoGoal(unit, goal, SUB_MOVE_TO_RESOURCE);
1244 return true;
1245 }
1246
1247 /**
1248 ** Control the unit action: getting a resource.
1249 **
1250 ** This the generic function for oil, gold, ...
1251 **
1252 ** @param unit Pointer to unit.
1253 */
Execute(CUnit & unit)1254 void COrder_Resource::Execute(CUnit &unit)
1255 {
1256 // can be different by Cloning (trained unit)...
1257 this->worker = &unit;
1258
1259 if (unit.Wait) {
1260 if (!unit.Waiting) {
1261 unit.Waiting = 1;
1262 unit.WaitBackup = unit.Anim;
1263 }
1264 UnitShowAnimation(unit, unit.Type->Animations->Still);
1265 unit.Wait--;
1266 return;
1267 }
1268 if (unit.Waiting) {
1269 unit.Anim = unit.WaitBackup;
1270 unit.Waiting = 0;
1271 }
1272
1273 // Let's start mining.
1274 if (this->State == SUB_START_RESOURCE) {
1275 if (ActionResourceInit(unit) == false) {
1276 ResourceGiveUp(unit);
1277 return;
1278 }
1279 }
1280
1281 // Move to the resource location.
1282 if (SUB_MOVE_TO_RESOURCE <= this->State && this->State < SUB_UNREACHABLE_RESOURCE) {
1283 const int ret = MoveToResource(unit);
1284
1285 switch (ret) {
1286 case -1: { // Can't Reach
1287 this->State++;
1288 unit.Wait = 5;
1289 return;
1290 }
1291 case 1: { // Reached
1292 this->State = SUB_START_GATHERING;
1293 break;
1294 }
1295 case 0: // Move along.
1296 return;
1297 default: {
1298 Assert(0);
1299 break;
1300 }
1301 }
1302 }
1303
1304 // Resource seems to be unreachable
1305 if (this->State == SUB_UNREACHABLE_RESOURCE) {
1306 if (this->FindAnotherResource(unit) == false) {
1307 ResourceGiveUp(unit);
1308 return;
1309 }
1310 }
1311
1312 // Start gathering the resource
1313 if (this->State == SUB_START_GATHERING) {
1314 if (StartGathering(unit)) {
1315 this->State = SUB_GATHER_RESOURCE;
1316 } else {
1317 return;
1318 }
1319 }
1320
1321 // Gather the resource.
1322 if (this->State == SUB_GATHER_RESOURCE) {
1323 if (GatherResource(unit)) {
1324 this->State = SUB_STOP_GATHERING;
1325 } else {
1326 return;
1327 }
1328 }
1329
1330 // Stop gathering the resource.
1331 if (this->State == SUB_STOP_GATHERING) {
1332 if (StopGathering(unit)) {
1333 this->State = SUB_MOVE_TO_DEPOT;
1334 unit.pathFinderData->output.Cycles = 0; //moving counter
1335 } else {
1336 return;
1337 }
1338 }
1339
1340 // Move back home.
1341 if (SUB_MOVE_TO_DEPOT <= this->State && this->State < SUB_UNREACHABLE_DEPOT) {
1342 const int ret = MoveToDepot(unit);
1343
1344 switch (ret) {
1345 case -1: { // Can't Reach
1346 this->State++;
1347 unit.Wait = 5;
1348 return;
1349 }
1350 case 1: { // Reached
1351 this->State = SUB_RETURN_RESOURCE;
1352 return;
1353 }
1354 case 0: // Move along.
1355 return;
1356 default: {
1357 Assert(0);
1358 return;
1359 }
1360 }
1361 }
1362
1363 // Depot seems to be unreachable
1364 if (this->State == SUB_UNREACHABLE_DEPOT) {
1365 ResourceGiveUp(unit);
1366 return;
1367 }
1368
1369 // Unload resources at the depot.
1370 if (this->State == SUB_RETURN_RESOURCE) {
1371 if (WaitInDepot(unit)) {
1372 this->State = SUB_START_RESOURCE;
1373
1374 // It's posible, though very rare that the unit's goal blows up
1375 // this cycle, but after this unit. Thus, next frame the unit
1376 // will start mining a destroyed site. If, on the otherhand we
1377 // are already in SUB_MOVE_TO_RESOURCE then we can handle it.
1378 // So, we pass through SUB_START_RESOURCE the very instant it
1379 // goes out of the depot.
1380 //HandleActionResource(order, unit);
1381 }
1382 }
1383 }
1384
1385 /**
1386 ** Get goal position
1387 */
GetGoalPos() const1388 /* virtual */ const Vec2i COrder_Resource::GetGoalPos() const
1389 {
1390 const Vec2i invalidPos(-1, -1);
1391 if (goalPos != invalidPos) {
1392 return goalPos;
1393 }
1394 if (this->HasGoal()) {
1395 return this->GetGoal()->tilePos;
1396 }
1397 return invalidPos;
1398 }
1399
1400 //@}
1401