1 /*
2  * Seven Kingdoms: Ancient Adversaries
3  *
4  * Copyright 1997,1998 Enlight Software Ltd.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 //Filename:    OUNIT2.CPP
22 //Description: Unit functions
23 
24 #include <OWORLD.h>
25 #include <ONATION.h>
26 #include <OFIRM.h>
27 #include <OTOWN.h>
28 #include <OUNIT.h>
29 
30 
31 //------- Begin of function Unit::think_aggressive_action --------//
32 //
33 // This function is called when the unit is in aggressive mode.
34 //
think_aggressive_action()35 int Unit::think_aggressive_action()
36 {
37 	//------ think about resuming the original action -----//
38 
39 	if( original_action_mode && cur_action!=SPRITE_ATTACK )	// if it is currently attacking somebody, don't think about resuming the original action
40 	{
41 		return think_resume_original_action();
42 	}
43 
44 	//---- think about attacking nearby units if this unit is attacking a town or a firm ---//
45 
46 	if( aggressive_mode && unit_mode==0 && cur_action==SPRITE_ATTACK )	// only in attack mode, as if the unit is still moving the target may be far away from the current position
47 	{
48 		//--- only when the unit is currently attacking a firm or a town ---//
49 
50 		if( action_mode2 == ACTION_ATTACK_FIRM ||
51 			 action_mode2 == ACTION_ATTACK_TOWN )
52 		{
53 			if( info.game_date%5==0 )				// check once every 5 days
54 				return think_change_attack_target();
55 		}
56 	}
57 
58 	return 0;
59 }
60 //-------- End of function Unit::think_aggressive_action --------//
61 
62 
63 //--------- Begin of function Unit::think_resume_original_action --------//
64 //
65 // If this unit is chasing a target to attack. Stop the chase
66 // if the target is being far away from its original location.
67 //
think_resume_original_action()68 int Unit::think_resume_original_action()
69 {
70 	if( !is_visible() )		// if the unit is no longer visible, cancel the saved orignal action
71 	{
72 		original_action_mode = 0;
73 		return 0;
74 	}
75 
76 	//---- if the unit is in defense mode now, don't do anything ----//
77 
78 	if( in_any_defense_mode() )
79 		return 0;
80 
81 	//----------------------------------------------------//
82 	//
83 	// If the action has been changed or the target unit has been deleted,
84 	// stop the chase right and move back to the original position
85 	// before the auto guard attack.
86 	//
87 	//----------------------------------------------------//
88 
89 	if(action_mode2!=ACTION_ATTACK_UNIT || unit_array.is_deleted(action_para2))
90 	{
91 		resume_original_action();
92 		return 1;
93 	}
94 
95 	//-----------------------------------------------------//
96 	//
97 	// Stop the chase if the target is being far away from
98 	// its original location and move back to its original
99 	// position before the auto guard attack.
100 	//
101 	//-----------------------------------------------------//
102 
103 	#define AUTO_GUARD_CHASE_ATTACK_DISTANCE	5
104 
105 	Unit* targetUnit = unit_array[action_para2];
106 
107 	int curDistance = misc.points_distance( targetUnit->next_x_loc(), targetUnit->next_y_loc(),
108 							original_target_x_loc, original_target_y_loc );
109 
110 	if( curDistance > AUTO_GUARD_CHASE_ATTACK_DISTANCE )
111 	{
112 		resume_original_action();
113 		return 1;
114 	}
115 
116 	return 0;
117 }
118 //---------- End of function Unit::think_resume_original_action --------//
119 
120 
121 //------- Begin of function Unit::think_change_attack_target -------//
122 //
123 // When the unit is attacking a firm or town, look out for enemy units
124 // to attack. Enemy units should be attacked first.
125 //
think_change_attack_target()126 int Unit::think_change_attack_target()
127 {
128 	err_when( !nation_recno );		// only for nation units
129 
130 	//----------------------------------------------//
131 
132 	int attackRange	  = MAX(attack_range, 8);
133 	int attackScanRange = attackRange*2+1;
134 
135 	int		 xOffset, yOffset;
136 	int		 xLoc, yLoc;
137 	Location* locPtr;
138 	int		 curXLoc = next_x_loc(), curYLoc = next_y_loc();
139 	int	 	 regionId = world.get_region_id(curXLoc, curYLoc);
140 
141 	for( int i=2 ; i<attackScanRange*attackScanRange ; i++ )
142 	{
143 		misc.cal_move_around_a_point(i, attackScanRange, attackScanRange, xOffset, yOffset);
144 
145 		xLoc = curXLoc + xOffset;
146 		yLoc = curYLoc + yOffset;
147 
148 		xLoc = MAX(0, xLoc);
149 		xLoc = MIN(MAX_WORLD_X_LOC-1, xLoc);
150 
151 		yLoc = MAX(0, yLoc);
152 		yLoc = MIN(MAX_WORLD_Y_LOC-1, yLoc);
153 
154 		locPtr = world.get_loc(xLoc, yLoc);
155 
156 		if( locPtr->region_id != regionId )
157 			continue;
158 
159 		//----- if there is a unit on the location ------//
160 
161 		if( locPtr->has_unit(UNIT_LAND) )
162 		{
163 			int unitRecno = locPtr->unit_recno(UNIT_LAND);
164 
165 			if( unit_array.is_deleted(unitRecno) )
166 				continue;
167 
168 			if( unit_array[unitRecno]->nation_recno != nation_recno &&
169 				 idle_detect_unit_checking(unitRecno) )
170 			{
171 				save_original_action();
172 
173 				original_target_x_loc = xLoc;
174 				original_target_y_loc = yLoc;
175 
176 				attack_unit(xLoc, yLoc);
177 				return 1;
178 			}
179 		}
180 	}
181 
182 	return 0;
183 }
184 //---------- End of function Unit::think_change_attack_target ------//
185 
186 
187 //----------- Begin of function Unit::save_original_action -------//
save_original_action()188 void Unit::save_original_action()
189 {
190 	if( original_action_mode==0 )
191 	{
192 		original_action_mode  = action_mode2;
193 		original_action_para  = action_para2;
194 		original_action_x_loc = action_x_loc2;
195 		original_action_y_loc = action_y_loc2;
196 	}
197 }
198 //----------- End of function Unit::save_original_action -------//
199 
200 
201 //----------- Begin of function Unit::resume_original_action -------//
202 
resume_original_action()203 void Unit::resume_original_action()
204 {
205 	if( !original_action_mode )
206 		return;
207 
208 	//--------- If it is an attack action ---------//
209 
210 	if( original_action_mode == ACTION_ATTACK_UNIT ||
211 		 original_action_mode == ACTION_ATTACK_FIRM ||
212 		 original_action_mode == ACTION_ATTACK_TOWN )
213 	{
214 		resume_original_attack_action();
215 		return;
216 	}
217 
218 	//--------------------------------------------//
219 
220 	if( original_action_x_loc<0 || original_action_x_loc>=MAX_WORLD_X_LOC ||
221 		 original_action_y_loc<0 || original_action_y_loc>=MAX_WORLD_Y_LOC )
222 	{
223 		original_action_mode = 0;
224 		return;
225 	}
226 
227 	short selectedArray[1];
228 	selectedArray[0] = sprite_recno;		// use unit_array.attack() instead of unit.attack_???() as we are unsure about what type of object the target is.
229 
230 	Location* locPtr = world.get_loc(original_action_x_loc, original_action_y_loc);
231 
232 	//--------- resume assign to town -----------//
233 
234 	if( original_action_mode == ACTION_ASSIGN_TO_TOWN && locPtr->is_town() )
235 	{
236 		if( locPtr->town_recno() == original_action_para &&
237 			 town_array[original_action_para]->nation_recno == nation_recno )
238 		{
239 			unit_array.assign( original_action_x_loc, original_action_y_loc, 0,
240 									 COMMAND_AUTO, selectedArray, 1 );
241 		}
242 	}
243 
244 	//--------- resume assign to firm ----------//
245 
246 	else if( original_action_mode == ACTION_ASSIGN_TO_FIRM && locPtr->is_firm() )
247 	{
248 		if( locPtr->firm_recno() == original_action_para &&
249 			 firm_array[original_action_para]->nation_recno == nation_recno )
250 		{
251 			unit_array.assign( original_action_x_loc, original_action_y_loc, 0,
252 									 COMMAND_AUTO, selectedArray, 1 );
253 		}
254 	}
255 
256 	//--------- resume build firm ---------//
257 
258 	else if( original_action_mode == ACTION_BUILD_FIRM )
259 	{
260 		if( world.can_build_firm( original_action_x_loc, original_action_y_loc,
261 										  original_action_para, sprite_recno ) )
262 		{
263 			build_firm( original_action_x_loc, original_action_y_loc,
264 							original_action_para, COMMAND_AUTO );
265 		}
266 	}
267 
268 	//--------- resume settle ---------//
269 
270 	else if( original_action_mode == ACTION_SETTLE )
271 	{
272 		if( world.can_build_town( original_action_x_loc, original_action_y_loc, sprite_recno ) )
273 		{
274 			unit_array.settle( original_action_x_loc, original_action_y_loc,
275 									 0, COMMAND_AUTO, selectedArray, 1 );
276 		}
277 	}
278 
279 	//--------- resume move ----------//
280 
281 	else if( original_action_mode == ACTION_MOVE )
282 	{
283 		unit_array.move_to( original_action_x_loc, original_action_y_loc, 0,
284 								  selectedArray, 1, COMMAND_AUTO );
285 	}
286 
287 	original_action_mode = 0;
288 }
289 //----------- End of function Unit::resume_original_action -------//
290 
291 
292 //----------- Begin of function Unit::resume_original_attack_action -------//
293 //
resume_original_attack_action()294 void Unit::resume_original_attack_action()
295 {
296 	if( !original_action_mode )
297 		return;
298 
299 	if( original_action_mode != ACTION_ATTACK_UNIT &&
300 		 original_action_mode != ACTION_ATTACK_FIRM &&
301 		 original_action_mode != ACTION_ATTACK_TOWN )
302 	{
303 		original_action_mode = 0;
304 		return;
305 	}
306 
307 	//--------------------------------------------//
308 
309 	err_when( original_action_x_loc<0 || original_action_x_loc>=MAX_WORLD_X_LOC );
310 	err_when( original_action_y_loc<0 || original_action_y_loc>=MAX_WORLD_Y_LOC );
311 
312 	Location* locPtr = world.get_loc(original_action_x_loc, original_action_y_loc);
313 	int		 targetNationRecno = -1;
314 
315 	if( original_action_mode == ACTION_ATTACK_UNIT && locPtr->has_unit(UNIT_LAND) )
316 	{
317 		int unitRecno = locPtr->unit_recno(UNIT_LAND);
318 
319 		if( unitRecno == original_action_para )
320 			targetNationRecno = unit_array[unitRecno]->nation_recno;
321 	}
322 	else if( original_action_mode == ACTION_ATTACK_FIRM && locPtr->is_firm() )
323 	{
324 		int firmRecno = locPtr->firm_recno();
325 
326 		if( firmRecno == original_action_para )
327 			targetNationRecno = firm_array[firmRecno]->nation_recno;
328 	}
329 	else if( original_action_mode == ACTION_ATTACK_TOWN && locPtr->is_town() )
330 	{
331 		int townRecno = locPtr->town_recno();
332 
333 		if( townRecno == original_action_para )
334 			targetNationRecno = town_array[townRecno]->nation_recno;
335 	}
336 
337 	//----- the original target is no longer valid ----//
338 
339 	if( targetNationRecno == -1 )
340 	{
341 		original_action_mode = 0;
342 		return;
343 	}
344 
345 	//---- only resume attacking the target if the target nation is at war with us currently ---//
346 
347 	if( !targetNationRecno || (targetNationRecno &&
348 		 targetNationRecno != nation_recno &&
349 		 nation_array[nation_recno]->get_relation_status(targetNationRecno) == NATION_HOSTILE ))
350 	{
351 		short selectedArray[1];
352 		selectedArray[0] = sprite_recno;		// use unit_array.attack() instead of unit.attack_???() as we are unsure about what type of object the target is.
353 
354 		// #### begin Gilbert 5/8 ########//
355 		unit_array.attack(original_action_x_loc, original_action_y_loc, 0, selectedArray, 1, COMMAND_AI, 0 );
356 		// #### end Gilbert 5/8 ########//
357 	}
358 
359 	original_action_mode = 0;
360 }
361 //----------- End of function Unit::resume_original_attack_action -------//
362 
363 //------- Begin of function Unit::ask_team_help_attack --------//
364 //
365 // It returns whether any of the co-member of this unit in a troop
366 // is under attack.
367 //
368 // <Unit*> attackerUnit - the unit pointer of the attacker
369 //
ask_team_help_attack(Unit * attackerUnit)370 void Unit::ask_team_help_attack(Unit* attackerUnit)
371 {
372 	//--- if the attacking unit is our unit (this can happen if the unit is porcupine) ---//
373 
374 	if( attackerUnit->nation_recno == nation_recno )
375 		return;
376 
377 	//-----------------------------------------//
378 
379 	int leaderUnitRecno=0;
380 
381 	if( leader_unit_recno )		// if the current unit is a soldier, get its leader's recno
382 		leaderUnitRecno = leader_unit_recno;
383 
384 	else if( team_info )			// this unit is the commander
385 		leaderUnitRecno = sprite_recno;
386 
387 	if( leaderUnitRecno )
388 	{
389 		TeamInfo* teamInfo = unit_array[leaderUnitRecno]->team_info;
390 
391 		err_when( !teamInfo );
392 
393 		for( int i=teamInfo->member_count-1 ; i>=0 ; i-- )
394 		{
395 			int unitRecno = teamInfo->member_unit_array[i];
396 
397 			if( unit_array.is_deleted(unitRecno) )
398 				continue;
399 
400 			Unit* unitPtr = unit_array[ unitRecno ];
401 
402 			if( unitPtr->cur_action==SPRITE_IDLE && unitPtr->is_visible() )
403 			{
404 				unitPtr->attack_unit(attackerUnit->sprite_recno);
405 				return;
406 			}
407 		}
408 	}
409 }
410 //-------- End of function Unit::ask_team_help_attack --------//
411 
412