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_defend.cpp - The defend action. */
12 //
13 //      (c) Copyright 2012 by cybermind
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_defend.h"
39 
40 #include "animation.h"
41 #include "iolib.h"
42 #include "map.h"
43 #include "pathfinder.h"
44 #include "script.h"
45 #include "ui.h"
46 #include "unit.h"
47 #include "unittype.h"
48 #include "video.h"
49 
50 enum {
51 	State_Init = 0,
52 	State_MovingToTarget,
53 	State_Defending
54 };
55 
56 /*----------------------------------------------------------------------------
57 --  Functions
58 ----------------------------------------------------------------------------*/
59 
NewActionDefend(CUnit & dest)60 /* static */ COrder *COrder::NewActionDefend(CUnit &dest)
61 {
62 	COrder_Defend *order = new COrder_Defend();
63 
64 	if (dest.Destroyed) {
65 		order->goalPos = dest.tilePos + dest.Type->GetHalfTileSize();
66 	} else {
67 		order->SetGoal(&dest);
68 		order->Range = 1;
69 	}
70 	return order;
71 }
72 
Save(CFile & file,const CUnit & unit) const73 /* virtual */ void COrder_Defend::Save(CFile &file, const CUnit &unit) const
74 {
75 	file.printf("{\"action-defend\",");
76 
77 	if (this->Finished) {
78 		file.printf(" \"finished\", ");
79 	}
80 	file.printf(" \"range\", %d,", this->Range);
81 	if (this->HasGoal()) {
82 		file.printf(" \"goal\", \"%s\",", UnitReference(this->GetGoal()).c_str());
83 	}
84 	file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y);
85 
86 	file.printf(" \"state\", %d", this->State);
87 
88 	file.printf("}");
89 }
90 
ParseSpecificData(lua_State * l,int & j,const char * value,const CUnit & unit)91 /* virtual */ bool COrder_Defend::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
92 {
93 	if (!strcmp(value, "state")) {
94 		++j;
95 		this->State = LuaToNumber(l, -1, j + 1);
96 	} else if (!strcmp(value, "range")) {
97 		++j;
98 		this->Range = LuaToNumber(l, -1, j + 1);
99 	} else if (!strcmp(value, "tile")) {
100 		++j;
101 		lua_rawgeti(l, -1, j + 1);
102 		CclGetPos(l, &this->goalPos.x , &this->goalPos.y);
103 		lua_pop(l, 1);
104 	} else {
105 		return false;
106 	}
107 	return true;
108 }
109 
IsValid() const110 /* virtual */ bool COrder_Defend::IsValid() const
111 {
112 	return true;
113 }
114 
Show(const CViewport & vp,const PixelPos & lastScreenPos) const115 /* virtual */ PixelPos COrder_Defend::Show(const CViewport &vp, const PixelPos &lastScreenPos) const
116 {
117 	PixelPos targetPos;
118 
119 	if (this->HasGoal()) {
120 		targetPos = vp.MapToScreenPixelPos(this->GetGoal()->GetMapPixelPosCenter());
121 	} else {
122 		targetPos = vp.TilePosToScreen_Center(this->goalPos);
123 	}
124 	Video.FillCircleClip(ColorGreen, lastScreenPos, 2);
125 	Video.DrawLineClip(ColorGreen, lastScreenPos, targetPos);
126 	Video.FillCircleClip(ColorOrange, targetPos, 3);
127 	return targetPos;
128 }
129 
UpdatePathFinderData(PathFinderInput & input)130 /* virtual */ void COrder_Defend::UpdatePathFinderData(PathFinderInput &input)
131 {
132 	input.SetMinRange(0);
133 	input.SetMaxRange(this->Range);
134 
135 	Vec2i tileSize;
136 	if (this->HasGoal()) {
137 		CUnit *goal = this->GetGoal();
138 		tileSize.x = goal->Type->TileWidth;
139 		tileSize.y = goal->Type->TileHeight;
140 		input.SetGoal(goal->tilePos, tileSize);
141 	} else {
142 		tileSize.x = 0;
143 		tileSize.y = 0;
144 		input.SetGoal(this->goalPos, tileSize);
145 	}
146 }
147 
148 
Execute(CUnit & unit)149 /* virtual */ void COrder_Defend::Execute(CUnit &unit)
150 {
151 	if (unit.Wait) {
152 		if (!unit.Waiting) {
153 			unit.Waiting = 1;
154 			unit.WaitBackup = unit.Anim;
155 		}
156 		UnitShowAnimation(unit, unit.Type->Animations->Still);
157 		unit.Wait--;
158 		return;
159 	}
160 	if (unit.Waiting) {
161 		unit.Anim = unit.WaitBackup;
162 		unit.Waiting = 0;
163 	}
164 	CUnit *goal = this->GetGoal();
165 
166 	if (this->State == State_Init) {
167 		if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) {
168 			this->Finished = true;
169 			return;
170 		}
171 		this->State = State_MovingToTarget;
172 	} else if (this->State == State_Defending) {
173 		if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) {
174 			this->Finished = true;
175 			return;
176 		}
177 	}
178 
179 	if (!unit.Anim.Unbreakable) {
180 		if (AutoCast(unit) || AutoAttack(unit) || AutoRepair(unit)) {
181 			return;
182 		}
183 	}
184 
185 	switch (DoActionMove(unit)) {
186 		case PF_UNREACHABLE:
187 			// Some tries to reach the goal
188 			this->Range++;
189 			break;
190 		case PF_REACHED: {
191 			if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { // goal has died
192 				this->Finished = true;
193 				return;
194 			}
195 
196 			// Now defend the goal
197 			this->goalPos = goal->tilePos;
198 			this->State = State_Defending;
199 		}
200 		default:
201 			break;
202 	}
203 
204 	// Target destroyed?
205 	if (goal && !goal->IsVisibleAsGoal(*unit.Player)) {
206 		DebugPrint("Goal gone\n");
207 		this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
208 		this->ClearGoal();
209 		goal = NULL;
210 		if (this->State == State_Defending) {
211 			this->Finished = true;
212 			return;
213 		}
214 	}
215 }
216 
217 /**
218 **  Get goal position
219 */
GetGoalPos() const220 /* virtual */ const Vec2i COrder_Defend::GetGoalPos() const
221 {
222 	const Vec2i invalidPos(-1, -1);
223 	if (goalPos != invalidPos) {
224 		return goalPos;
225 	}
226 	if (this->HasGoal()) {
227 		return this->GetGoal()->tilePos;
228 	}
229 	return invalidPos;
230 }
231