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