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    : OUNITAT3.CPP
22 //Description : Object Unit's decision making functions for attacking same or different type of target and reactivating
23 //					 idle unit that are ordered to attack
24 //Owner		  : Alex
25 
26 #include <ALL.h>
27 #include <OWORLD.h>
28 #include <OUNIT.h>
29 #include <OGAME.h>
30 
31 #ifdef NO_DEBUG_UNIT
32 #undef err_when
33 #undef err_here
34 #undef err_if
35 #undef err_else
36 #undef err_now
37 #define err_when(cond)
38 #define err_here()
39 #define err_if(cond)
40 #define err_else
41 #define err_now(msg)
42 #undef DEBUG
43 #endif
44 
45 //--------- Begin of function Unit::move_try_to_range_attack ---------//
46 // return 1 if it is possible to reach a location to attack the target
47 // return 0 otherwise
48 //
49 // <Unit*>	targetUnit	- pointer to target unit
50 //
move_try_to_range_attack(Unit * targetUnit)51 int Unit::move_try_to_range_attack(Unit* targetUnit)
52 {
53 	int curXLoc = next_x_loc();
54 	int curYLoc = next_y_loc();
55 	int targetXLoc = targetUnit->next_x_loc();
56 	int targetYLoc = targetUnit->next_y_loc();
57 
58 	if(world.get_loc(curXLoc, curYLoc)->region_id==world.get_loc(targetXLoc, targetYLoc)->region_id)
59 	{
60 		//------------ for same region id, search now ---------------//
61 		if(search(targetXLoc, targetYLoc, 1, SEARCH_MODE_TO_ATTACK, action_para))
62 			return 1;
63 		else // search failure,
64 		{
65 			stop2(KEEP_DEFENSE_MODE);
66 			return 0;
67 		}
68 	}
69 	else
70 	{
71 		//--------------- different territory ------------------//
72 		int targetWidth = targetUnit->sprite_info->loc_width;
73 		int targetHeight = targetUnit->sprite_info->loc_height;
74 		int maxRange = max_attack_range();
75 
76 		if(possible_place_for_range_attack(targetXLoc, targetYLoc, targetWidth, targetHeight, maxRange))
77 		{
78 			//---------------------------------------------------------------------------------//
79 			// space is found, attack target now
80 			//---------------------------------------------------------------------------------//
81 			if(move_to_range_attack(targetXLoc, targetYLoc, targetUnit->sprite_id, SEARCH_MODE_ATTACK_UNIT_BY_RANGE, maxRange))
82 				return 1;
83 			else
84 			{
85 				stop2(KEEP_DEFENSE_MODE);
86 				return 0;
87 			}
88 			return 1;
89 		}
90 		else
91 		{
92 			//---------------------------------------------------------------------------------//
93 			// unable to find location to attack the target, stop or move to the target
94 			//---------------------------------------------------------------------------------//
95 			if(action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET && action_mode2!=ACTION_DEFEND_TOWN_ATTACK_TARGET &&
96 				action_mode2!=ACTION_MONSTER_DEFEND_ATTACK_TARGET)
97 				move_to(targetXLoc, targetYLoc, 1); // abort attacking, just call move_to() instead
98 			else
99 				stop2(KEEP_DEFENSE_MODE);
100 			return 0;
101 		}
102 	}
103 
104 	return 0;
105 }
106 //----------- End of function Unit::move_try_to_range_attack -----------//
107 
108 
109 //--------- Begin of function Unit::move_to_range_attack ---------//
110 // search and attack target
111 //
112 // <int>		targetXLoc	-	target x location
113 // <int>		targetYLoc	-	target y location
114 // <short>	miscNo		-	is sprite_id if target is a unit
115 //									is firm_id if target is a firm
116 //									is 0 if target is a town or a wall
117 // <short>	searchMode	-	search mode being used
118 // <short>	maxRange		-	MAX attack range of this unit
119 //
move_to_range_attack(int targetXLoc,int targetYLoc,short miscNo,short searchMode,short maxRange)120 int Unit::move_to_range_attack(int targetXLoc, int targetYLoc, short miscNo, short searchMode, short maxRange)
121 {
122 	//---------------------------------------------------------------------------------//
123 	// part 1, searching
124 	//---------------------------------------------------------------------------------//
125 	seek_path.set_attack_range_para(maxRange);
126 	search(targetXLoc, targetYLoc, 1, searchMode, miscNo);
127 	seek_path.reset_attack_range_para();
128 	//search(targetXLoc, targetYLoc, 1, searchMode, maxRange);
129 
130 	if(result_node_array==NULL || result_node_count==0)
131 		return 0;
132 
133 	//---------------------------------------------------------------------------------//
134 	// part 2, editing result path
135 	//---------------------------------------------------------------------------------//
136 	Location *locPtr = world.get_loc(next_x_loc(), next_y_loc());
137 	err_when(!locPtr);
138 
139 	int regionId = locPtr->region_id;	// the region_id this unit in
140 
141 	//----------------------------------------------------//
142 	err_when(result_node_count<2);
143 	ResultNode* editNode1 = result_node_array + result_node_count - 1;
144 	ResultNode* editNode2 = editNode1-1;
145 	int vecX = editNode1->node_x - editNode2->node_x;
146 	int vecY = editNode1->node_y - editNode2->node_y;
147 
148 	if(vecX)
149 		vecX = ((vecX>0) ? 1 : -1)*move_step_magn();
150 	if(vecY)
151 		vecY = ((vecY>0) ? 1 : -1)*move_step_magn();
152 
153 	int x = editNode1->node_x;
154 	int y = editNode1->node_y;
155 	int i, found=0, removedStep=0, preX, preY;
156 
157 	for(i=result_node_count; i>1; i--)
158 	{
159 		while(x!=editNode2->node_x || y!=editNode2->node_y)
160 		{
161 			locPtr = world.get_loc(x, y);
162 			if(locPtr->region_id == regionId)
163 			{
164 				found = i;
165 				preX = x;
166 				preY = y;
167 				break;
168 			}
169 
170 			x -= vecX;
171 			y -= vecY;
172 			removedStep++;
173 		}
174 
175 		if(found)
176 			break;
177 
178 		editNode1 = editNode2;
179 		editNode2--;
180 
181 		vecX = editNode1->node_x - editNode2->node_x;
182 		vecY = editNode1->node_y - editNode2->node_y;
183 		if(vecX)
184 			vecX = ((vecX>0) ? 1 : -1)*move_step_magn();
185 		if(vecY)
186 			vecY = ((vecY>0) ? 1 : -1)*move_step_magn();
187 
188 		x = editNode1->node_x;
189 		y = editNode1->node_y;
190 	}
191 
192 	//---------------------------------------------------------------------------//
193 	// update unit parameters
194 	//---------------------------------------------------------------------------//
195 	if(found)
196 	{
197 		result_node_count = found;
198 		ResultNode* lastNode = result_node_array + result_node_count - 1;
199 		int goX = go_x>>ZOOM_X_SHIFT_COUNT;
200 		int goY = go_y>>ZOOM_Y_SHIFT_COUNT;
201 
202 		//---------------------------------------------------------------------//
203 		// note: build?Loc-1, build?Loc+width, build?Loc+height may <0 or
204 		//			>MAX_WORLD_?_LOC.  To prevent errors from occuring, goX, goY
205 		//			must not be outside the map boundary
206 		//---------------------------------------------------------------------//
207 		if(goX==editNode1->node_x && goY==editNode1->node_y)
208 		{
209 			go_x = preX*ZOOM_LOC_WIDTH;
210 			go_y = preY*ZOOM_LOC_HEIGHT;
211 		}
212 		else if(result_node_count==2)
213 		{
214 			int magnCG = misc.points_distance(cur_x, cur_y, go_x, go_y);
215 			int magnNG = misc.points_distance(next_x, next_y, go_x, go_y);
216 			err_when(magnCG==0 && magnNG==0);
217 
218 			if(magnCG && magnNG)
219 			{
220 				//---------- lie on the same line -----------//
221 				if( (go_x-cur_x)/magnCG==(go_x-next_x)/magnNG && (go_y-cur_y)/magnCG==(go_y-next_y)/magnNG )
222 				{
223 					go_x = preX*ZOOM_LOC_WIDTH;
224 					go_y = preY*ZOOM_LOC_HEIGHT;
225 				}
226 			}
227 		}
228 
229 		lastNode->node_x = preX;
230 		lastNode->node_y = preY;
231 		move_to_x_loc = lastNode->node_x;
232 		move_to_y_loc = lastNode->node_y;
233 
234 		result_path_dist -= (removedStep)*move_step_magn();
235 
236 		err_when((cur_x!=next_x || cur_y!=next_y) &&	// is not blocked
237 					(check_unit_dir1=get_dir(cur_x, cur_y, next_x, next_y))!=(check_unit_dir2=get_dir(cur_x, cur_y, go_x, go_y)));
238 	}
239 
240 	return found;
241 }
242 //----------- End of function Unit::move_to_range_attack -----------//
243 
244 
245 //--------- Begin of function Unit::can_attack_different_type_target ---------//
246 // return 1 if able to use range_attack
247 // return 0 otherwise
248 //
can_attack_different_target_type()249 int Unit::can_attack_different_target_type()
250 {
251 	int maxRange = max_attack_range();
252 	if(mobile_type==UNIT_LAND && !maxRange)
253 		return 0; // unable to do range attack or cannot attack
254 
255 	if(maxRange>1)
256 		return maxRange;
257 	else
258 		return 0;
259 }
260 //----------- End of function Unit::can_attack_different_type_target -----------//
261 
262 
263 //--------- Begin of function Unit::possible_place_for_range_attack ---------//
264 // check whether there is any place for this unit to attack the target
265 //
266 // <int> targetXLoc		- target x location
267 // <int> targetYLoc		- target y location
268 // <int> targetWidth		- target width
269 // <int> targetHeight	- target height
270 // <int> maxRange			- MAX attack range of this unit
271 //
272 // return 1 if place found
273 // return 0 otherwise
274 //
possible_place_for_range_attack(int targetXLoc,int targetYLoc,int targetWidth,int targetHeight,int maxRange)275 int Unit::possible_place_for_range_attack(int targetXLoc, int targetYLoc, int targetWidth, int targetHeight, int maxRange)
276 {
277 	err_when(targetXLoc<0 || targetXLoc>=MAX_WORLD_X_LOC || targetYLoc<0 || targetYLoc>=MAX_WORLD_Y_LOC);
278 	err_when(maxRange==0);
279 
280 	if(mobile_type==UNIT_AIR)
281 		return 1; // air unit can reach any region
282 
283 	int curXLoc = next_x_loc();
284 	int curYLoc = next_y_loc();
285 
286 	if(abs(curXLoc-targetXLoc)<=maxRange && abs(curYLoc-targetYLoc)<=maxRange) // inside the attack range
287 		return 1;
288 
289 	//----------------- init parameters -----------------//
290 	Location *locPtr = world.get_loc(curXLoc, curYLoc);
291 	int regionId = locPtr->region_id;
292 	int xLoc1 = MAX(targetXLoc-maxRange, 0);
293 	int yLoc1 = MAX(targetYLoc-maxRange, 0);
294 	int xLoc2 = MIN(targetXLoc+targetWidth-1+maxRange, MAX_WORLD_X_LOC-1);
295 	int yLoc2 = MIN(targetYLoc+targetHeight-1+maxRange, MAX_WORLD_Y_LOC-1);
296 	int checkXLoc, checkYLoc;
297 
298 	//--------- do adjustment for UNIT_SEA and UNIT_AIR ---------//
299 	if(mobile_type!=UNIT_LAND)
300 	{
301 		if(xLoc1%2)
302 			xLoc1++;
303 		if(yLoc1%2)
304 			yLoc1++;
305 		if(xLoc2%2)
306 			xLoc2--;
307 		if(yLoc2%2)
308 			yLoc2--;
309 	}
310 
311 	//-------- checking for surrounding location ----------//
312 	switch(mobile_type)
313 	{
314 		case UNIT_LAND:
315 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
316 				{
317 					locPtr = world.get_loc(checkXLoc, yLoc1);
318 					if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
319 						return 1;
320 
321 					locPtr = world.get_loc(checkXLoc, yLoc2);
322 					if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
323 						return 1;
324 				}
325 
326 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
327 				{
328 					locPtr = world.get_loc(xLoc1, checkYLoc);
329 					if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
330 						return 1;
331 
332 					locPtr = world.get_loc(xLoc2, checkYLoc);
333 					if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
334 						return 1;
335 				}
336 				break;
337 
338 		case UNIT_SEA:
339 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
340 				{
341 					if(checkXLoc%2==0 && yLoc1%2==0)
342 					{
343 						locPtr = world.get_loc(checkXLoc, yLoc1);
344 						if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
345 							return 1;
346 					}
347 
348 					if(checkXLoc%2==0 && yLoc2%2==0)
349 					{
350 						locPtr = world.get_loc(checkXLoc, yLoc2);
351 						if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
352 							return 1;
353 					}
354 				}
355 
356 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
357 				{
358 					if(xLoc1%2==0 && checkYLoc%2==0)
359 					{
360 						locPtr = world.get_loc(xLoc1, checkYLoc);
361 						if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
362 							return 1;
363 					}
364 
365 					if(xLoc2%2==0 && checkYLoc%2==0)
366 					{
367 						locPtr = world.get_loc(xLoc2, checkYLoc);
368 						if(locPtr->region_id==regionId && locPtr->is_accessible(mobile_type))
369 							return 1;
370 					}
371 				}
372 				break;
373 
374 		case UNIT_AIR:
375 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
376 				{
377 					if(checkXLoc%2==0 && yLoc1%2==0)
378 					{
379 						locPtr = world.get_loc(checkXLoc, yLoc1);
380 						if(locPtr->is_accessible(mobile_type))
381 							return 1;
382 					}
383 
384 					if(checkXLoc%2==0 && yLoc2%2==0)
385 					{
386 						locPtr = world.get_loc(checkXLoc, yLoc2);
387 						if(locPtr->is_accessible(mobile_type))
388 							return 1;
389 					}
390 				}
391 
392 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
393 				{
394 					if(xLoc1%2==0 && checkYLoc%2==0)
395 					{
396 						locPtr = world.get_loc(xLoc1, checkYLoc);
397 						if(locPtr->is_accessible(mobile_type))
398 							return 1;
399 					}
400 
401 					if(xLoc2%2==0 && checkYLoc%2==0)
402 					{
403 						locPtr = world.get_loc(xLoc2, checkYLoc);
404 						if(locPtr->is_accessible(mobile_type))
405 							return 1;
406 					}
407 				}
408 				break;
409 
410 		default: err_here();
411 					break;
412 	}
413 
414 	return 0;
415 }
416 //----------- End of function Unit::possible_place_for_range_attack -----------//
417 
418 
419 //=====================================================================================//
420 //--------- Begin of function Unit::space_for_attack ---------//
421 // check whether there is any place for the unit to attack target.
422 //
423 // <int>		targetXLoc			-	target x location
424 // <int>		targetYLoc			-	target y location
425 // <char>	targetMobileType	-	target mobile type
426 // <int>		targetWidth			-	target width
427 // <int>		targetHeight		-	target height
428 //
space_for_attack(int targetXLoc,int targetYLoc,char targetMobileType,int targetWidth,int targetHeight)429 int Unit::space_for_attack(int targetXLoc, int targetYLoc, char targetMobileType, int targetWidth, int targetHeight)
430 {
431 	if(mobile_type==UNIT_LAND && targetMobileType==UNIT_LAND)
432 		return space_around_target(targetXLoc, targetYLoc, targetWidth, targetHeight);
433 
434 	if((mobile_type==UNIT_SEA && targetMobileType==UNIT_SEA) ||
435 		(mobile_type==UNIT_AIR && targetMobileType==UNIT_AIR))
436 		return space_around_target_ver2(targetXLoc, targetYLoc, targetWidth, targetHeight);
437 
438 	//-------------------------------------------------------------------------//
439 	// mobile_type is differet from that of target unit
440 	//-------------------------------------------------------------------------//
441 	Location *locPtr = world.get_loc(next_x_loc(), next_y_loc());
442 	if(mobile_type==UNIT_LAND && targetMobileType==UNIT_SEA &&
443 		!can_attack_different_target_type() &&
444 		ship_surr_has_free_land(targetXLoc, targetYLoc, locPtr->region_id))
445 		return 1;
446 
447 	int maxRange = max_attack_range();
448 	if(maxRange==1)
449 		return 0;
450 
451 	if(free_space_for_range_attack(targetXLoc, targetYLoc, targetWidth, targetHeight, targetMobileType, maxRange))
452 		return 1;
453 
454 	return 0;
455 }
456 //----------- End of function Unit::space_for_attack -----------//
457 
458 
459 //--------- Begin of function Unit::space_around_target ---------//
460 // check the surroundung location around a square, and the result is
461 // stored in the blocked_edge[] by bit
462 //
463 // <int>	squareXLoc	-	upper left x location of target
464 // <int>	squareYLoc	-	upper left y location of target
465 // <int> width			-	target width
466 // <int> height		-	target height
467 //
468 // return 1 if the surrounding location that can_move is not equal to
469 //				the result in the blocked_edge stored previously.
470 // return 0 otherwise (i.e. all location situation is same as before)
471 //
space_around_target(int squareXLoc,int squareYLoc,int width,int height)472 int Unit::space_around_target(int squareXLoc, int squareYLoc, int width, int height)
473 {
474 	err_when(width<=0 || height<=0);
475 	//				edge 1
476 	//				1 1 4
477 	// edge 2	2 x 4		edge 4
478 	//				2 3 3
479 	//				edge3
480 
481 	Location *locPtr;
482 	Unit *unitPtr;
483 	char sum, locWeight;
484 	int testXLoc, testYLoc, i, equal=1;
485 
486 	//------------------ top edge ---------------//
487 	sum = 0;
488 	if((testYLoc=squareYLoc-1) >= 0)
489 	{
490 		if(squareXLoc>=1) // have upper left corner
491 		{
492 			i=-1;
493 			locWeight = 1;
494 		}
495 		else
496 		{
497 			i = 0;
498 			locWeight = 2;
499 		}
500 
501 		for(; i<width; i++, locWeight<<=1)
502 		{
503 			locPtr = world.get_loc(squareXLoc+i, testYLoc);
504 			if(locPtr->can_move(mobile_type))
505 				sum ^= locWeight;
506 			else if(locPtr->has_unit(mobile_type))
507 			{
508 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
509 				if(unitPtr->cur_action!=SPRITE_ATTACK)
510 					sum ^= locWeight;
511 			}
512 		}
513 	}
514 
515 	if(blocked_edge[0]!=sum)
516 	{
517 		blocked_edge[0] = sum;
518 		equal = 0;
519 	}
520 
521 	//----------------- left edge -----------------//
522 	sum = 0;
523 	if((testXLoc=squareXLoc-1) >= 0)
524 	{
525 		if(squareYLoc+height<=MAX_WORLD_Y_LOC-1) // have lower left corner
526 		{
527 			i = height;
528 			locWeight = 1;
529 		}
530 		else
531 		{
532 			i = height - 1;
533 			locWeight = 2;
534 		}
535 
536 		for(; i>=0; i--, locWeight<<=1)
537 		{
538 			locPtr = world.get_loc(testXLoc, squareYLoc+i);
539 			if(locPtr->can_move(mobile_type))
540 				sum ^= locWeight;
541 			else if(locPtr->has_unit(mobile_type))
542 			{
543 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
544 				if(unitPtr->cur_action!=SPRITE_ATTACK)
545 					sum ^= locWeight;
546 			}
547 		}
548 	}
549 
550 	if(blocked_edge[1]!=sum)
551 	{
552 		blocked_edge[1] = sum;
553 		equal = 0;
554 	}
555 
556 	//------------------- bottom edge ------------------//
557 	sum = 0;
558 	if((testYLoc=squareYLoc+height) <= MAX_WORLD_Y_LOC-1)
559 	{
560 		if(squareXLoc+width<=MAX_WORLD_X_LOC-1) // have lower right corner
561 		{
562 			i = width;
563 			locWeight = 1;
564 		}
565 		else
566 		{
567 			i = width - 1;
568 			locWeight = 2;
569 		}
570 
571 		for(; i>=0; i--, locWeight<<=1)
572 		{
573 			locPtr = world.get_loc(squareXLoc+i, testYLoc);
574 			if(locPtr->can_move(mobile_type))
575 				sum ^= locWeight;
576 			else if(locPtr->has_unit(mobile_type))
577 			{
578 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
579 				if(unitPtr->cur_action!=SPRITE_ATTACK)
580 					sum ^= locWeight;
581 			}
582 		}
583 	}
584 
585 	if(blocked_edge[2]!=sum)
586 	{
587 		blocked_edge[2] = sum;
588 		equal = 0;
589 	}
590 
591 	//---------------------- right edge ----------------------//
592 	sum = 0;
593 	if((testXLoc=squareXLoc+width) <= MAX_WORLD_X_LOC-1)
594 	{
595 		if(squareYLoc>=1) // have upper right corner
596 		{
597 			i = -1;
598 			locWeight = 1;
599 		}
600 		else
601 		{
602 			i = 0;
603 			locWeight = 2;
604 		}
605 
606 		for(; i<height; i++, locWeight<<=1)
607 		{
608 			locPtr = world.get_loc(testXLoc, squareYLoc+i);
609 			if(locPtr->can_move(mobile_type))
610 				sum ^= locWeight;
611 			else if(locPtr->has_unit(mobile_type))
612 			{
613 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
614 				if(unitPtr->cur_action!=SPRITE_ATTACK)
615 					sum ^= locWeight;
616 			}
617 		}
618 	}
619 
620 	if(blocked_edge[3]!=sum)
621 	{
622 		blocked_edge[3] = sum;
623 		equal = 0;
624 	}
625 
626 	return !equal;
627 }
628 //----------- End of function Unit::space_around_target -----------//
629 
630 
631 //--------- Begin of function Unit::space_around_target_ver2 ---------//
632 // similar function as space_around_target()
633 // This version is for sea unit and air unit only
634 //
space_around_target_ver2(int targetXLoc,int targetYLoc,int targetWidth,int targetHeight)635 int Unit::space_around_target_ver2(int targetXLoc, int targetYLoc, int targetWidth, int targetHeight)
636 {
637 	err_when(targetWidth<=0 || targetHeight<=0);
638 
639 	Location *locPtr;
640 	Unit *unitPtr;
641 	char sum, locWeight;
642 	int xLoc1, yLoc1, xLoc2, yLoc2;
643 	int i, equal=1;
644 	//int testXLoc, testYLoc,
645 
646 	xLoc1 = targetXLoc%2 ? targetXLoc-1 : targetXLoc-2;
647 	yLoc1 = targetYLoc%2 ? targetYLoc-1 : targetYLoc-2;
648 	xLoc2 = (targetXLoc+targetWidth-1)%2 ? targetXLoc+targetWidth : targetXLoc+targetWidth+1;
649 	yLoc2 = (targetYLoc+targetHeight-1)%2 ? targetYLoc+targetHeight : targetYLoc+targetHeight+1;
650 
651 	//------------------------ top edge ------------------------//
652 	sum = 0;
653 	if(yLoc1>=0)
654 	{
655 		if(xLoc1>=0)
656 		{
657 			i = xLoc1;
658 			locWeight = 1;
659 		}
660 		else
661 		{
662 			i = xLoc1 + 2;
663 			err_when(i<0);
664 			locWeight = 2;
665 		}
666 
667 		for(; i<=xLoc2; i+=2, locWeight<<=1)
668 		{
669 			locPtr = world.get_loc(i, yLoc1);
670 			if(locPtr->can_move(mobile_type))
671 				sum ^= locWeight;
672 			else if(locPtr->has_unit(mobile_type))
673 			{
674 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
675 				if(unitPtr->cur_action!=SPRITE_ATTACK)
676 					sum ^= locWeight;
677 			}
678 		}
679 	}
680 
681 	if(blocked_edge[0]!=sum)
682 	{
683 		blocked_edge[0] = sum;
684 		equal = 0;
685 	}
686 
687 	//---------------------- left edge -----------------------//
688 	sum = 0;
689 	if(xLoc1>=0)
690 	{
691 		if(yLoc2<=MAX_WORLD_Y_LOC-1)
692 		{
693 			i = yLoc2;
694 			locWeight = 1;
695 		}
696 		else
697 		{
698 			i = yLoc2-2;
699 			err_when(i>=MAX_WORLD_Y_LOC);
700 			locWeight = 2;
701 		}
702 
703 		for(; i>yLoc1; i-=2, locWeight<<=1)
704 		{
705 			locPtr = world.get_loc(xLoc1, i);
706 			if(locPtr->can_move(mobile_type))
707 				sum ^= locWeight;
708 			else if(locPtr->has_unit(mobile_type))
709 			{
710 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
711 				if(unitPtr->cur_action!=SPRITE_ATTACK)
712 					sum ^= locWeight;
713 			}
714 		}
715 	}
716 
717 	if(blocked_edge[1]!=sum)
718 	{
719 		blocked_edge[1] = sum;
720 		equal = 0;
721 	}
722 
723 	//----------------------- bottom edge ---------------------------//
724 	sum = 0;
725 	if(yLoc2<=MAX_WORLD_Y_LOC-1)
726 	{
727 		if(xLoc2<=MAX_WORLD_X_LOC-1)
728 		{
729 			i = xLoc2;
730 			locWeight = 1;
731 		}
732 		else
733 		{
734 			i = xLoc2-2;
735 			err_when(i>=MAX_WORLD_X_LOC);
736 			locWeight = 2;
737 		}
738 
739 		for(; i>xLoc1; i-=2, locWeight<<=1)
740 		{
741 			locPtr = world.get_loc(i, yLoc2);
742 			if(locPtr->can_move(mobile_type))
743 				sum ^= locWeight;
744 			else if(locPtr->has_unit(mobile_type))
745 			{
746 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
747 				if(unitPtr->cur_action!=SPRITE_ATTACK)
748 					sum ^= locWeight;
749 			}
750 		}
751 	}
752 
753 	if(blocked_edge[2]!=sum)
754 	{
755 		blocked_edge[2] = sum;
756 		equal = 0;
757 	}
758 
759 	//---------------------- right edge ------------------------//
760 	sum = 0;
761 	if(xLoc2<=MAX_WORLD_X_LOC-1)
762 	{
763 		if(yLoc1>=0)
764 		{
765 			i = yLoc1;
766 			locWeight = 1;
767 		}
768 		else
769 		{
770 			i = yLoc1+2;
771 			err_when(i<0);
772 			locWeight = 2;
773 		}
774 
775 		for(; i<yLoc2; i+=2, locWeight<<=1)
776 		{
777 			locPtr = world.get_loc(xLoc2, i);
778 			if(locPtr->can_move(mobile_type))
779 				sum ^= locWeight;
780 			else if(locPtr->has_unit(mobile_type))
781 			{
782 				unitPtr = unit_array[locPtr->unit_recno(mobile_type)];
783 				if(unitPtr->cur_action!=SPRITE_ATTACK)
784 					sum ^= locWeight;
785 			}
786 		}
787 	}
788 
789 	if(blocked_edge[3]!=sum)
790 	{
791 		blocked_edge[3] = sum;
792 		equal = 0;
793 	}
794 
795 	return !equal;
796 }
797 //----------- End of function Unit::space_around_target_ver2 -----------//
798 
799 
800 //--------- Begin of function Unit::ship_surr_has_free_land ---------//
801 // check surrounding place for close attack by land units
802 //
803 // <int>		targetXLoc	- target x loc
804 // <int>		targetYLoc	- target y loc
805 // <uint8_t>	regionId		- region id
806 //
807 // return 1 if there is space for the land unit to move to ship surrounding for close attack
808 // return 0 otherwise
809 //
ship_surr_has_free_land(int targetXLoc,int targetYLoc,uint8_t regionId)810 int Unit::ship_surr_has_free_land(int targetXLoc, int targetYLoc, uint8_t regionId)
811 {
812 	err_when(mobile_type!=UNIT_LAND);
813 	Location *locPtr;
814 	int xShift, yShift, checkXLoc, checkYLoc;
815 
816 	for(int i=2; i<9; i++)
817 	{
818 		misc.cal_move_around_a_point(i, 3, 3, xShift, yShift);
819 		checkXLoc = targetXLoc+xShift;
820 		checkYLoc = targetYLoc+yShift;
821 
822 		if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
823 			continue;
824 
825 		locPtr = world.get_loc(checkXLoc, checkYLoc);
826 		if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
827 			return 1;
828 	}
829 
830 	return 0;
831 }
832 //----------- End of function Unit::ship_surr_has_free_land -----------//
833 
834 
835 //--------- Begin of function Unit::free_space_for_range_attack ---------//
836 // similar to possible_place_for_range_attack() but checking can_move() rather than is_accessible()
837 //
free_space_for_range_attack(int targetXLoc,int targetYLoc,int targetWidth,int targetHeight,int targetMobileType,int maxRange)838 int Unit::free_space_for_range_attack(int targetXLoc, int targetYLoc, int targetWidth, int targetHeight, int targetMobileType, int maxRange)
839 {
840 	err_when(targetXLoc<0 || targetXLoc>=MAX_WORLD_X_LOC || targetYLoc<0 || targetYLoc>=MAX_WORLD_Y_LOC);
841 	err_when(maxRange==0);
842 
843 	//if(mobile_type==UNIT_AIR)
844 	//	return 1; // air unit can reach any region
845 
846 	int curXLoc = next_x_loc();
847 	int curYLoc = next_y_loc();
848 
849 	if(abs(curXLoc-targetXLoc)<=maxRange && abs(curYLoc-targetYLoc)<=maxRange) // inside the attack range
850 		return 1;
851 
852 	Location *locPtr = world.get_loc(curXLoc, curYLoc);
853 	int regionId = locPtr->region_id;
854 	int xLoc1 = MAX(targetXLoc-maxRange, 0);
855 	int yLoc1 = MAX(targetYLoc-maxRange, 0);
856 	int xLoc2 = MIN(targetXLoc+targetWidth-1+maxRange, MAX_WORLD_X_LOC-1);
857 	int yLoc2 = MIN(targetYLoc+targetHeight-1+maxRange, MAX_WORLD_Y_LOC-1);
858 	int checkXLoc, checkYLoc;
859 
860 	//--------- do adjustment for UNIT_SEA and UNIT_AIR ---------//
861 	if(mobile_type!=UNIT_LAND)
862 	{
863 		if(xLoc1%2)
864 			xLoc1++;
865 		if(yLoc1%2)
866 			yLoc1++;
867 		if(xLoc2%2)
868 			xLoc2--;
869 		if(yLoc2%2)
870 			yLoc2--;
871 	}
872 
873 	//-------- checking for surrounding location ----------//
874 	switch(mobile_type)
875 	{
876 		case UNIT_LAND:
877 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
878 				{
879 					locPtr = world.get_loc(checkXLoc, yLoc1);
880 					if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
881 						return 1;
882 
883 					locPtr = world.get_loc(checkXLoc, yLoc2);
884 					if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
885 						return 1;
886 				}
887 
888 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
889 				{
890 					locPtr = world.get_loc(xLoc1, checkYLoc);
891 					if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
892 						return 1;
893 
894 					locPtr = world.get_loc(xLoc2, checkYLoc);
895 					if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
896 						return 1;
897 				}
898 				break;
899 
900 		case UNIT_SEA:
901 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
902 				{
903 					if(checkXLoc%2==0 && yLoc1%2==0)
904 					{
905 						locPtr = world.get_loc(checkXLoc, yLoc1);
906 						if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
907 							return 1;
908 					}
909 
910 					if(checkXLoc%2==0 && yLoc2%2==0)
911 					{
912 						locPtr = world.get_loc(checkXLoc, yLoc2);
913 						if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
914 							return 1;
915 					}
916 				}
917 
918 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
919 				{
920 					if(xLoc1%2==0 && checkYLoc%2==0)
921 					{
922 						locPtr = world.get_loc(xLoc1, checkYLoc);
923 						if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
924 							return 1;
925 					}
926 
927 					if(xLoc2%2==0 && checkYLoc%2==0)
928 					{
929 						locPtr = world.get_loc(xLoc2, checkYLoc);
930 						if(locPtr->region_id==regionId && locPtr->can_move(mobile_type))
931 							return 1;
932 					}
933 				}
934 				break;
935 
936 		case UNIT_AIR:
937 				for(checkXLoc=xLoc1; checkXLoc<=xLoc2; checkXLoc++)
938 				{
939 					if(checkXLoc%2==0 && yLoc1%2==0)
940 					{
941 						locPtr = world.get_loc(checkXLoc, yLoc1);
942 						if(locPtr->can_move(mobile_type))
943 							return 1;
944 					}
945 
946 					if(checkXLoc%2==0 && yLoc2%2==0)
947 					{
948 						locPtr = world.get_loc(checkXLoc, yLoc2);
949 						if(locPtr->can_move(mobile_type))
950 							return 1;
951 					}
952 				}
953 
954 				for(checkYLoc=yLoc1+1; checkYLoc<yLoc2; checkYLoc++)
955 				{
956 					if(xLoc1%2==0 && checkYLoc%2==0)
957 					{
958 						locPtr = world.get_loc(xLoc1, checkYLoc);
959 						if(locPtr->can_move(mobile_type))
960 							return 1;
961 					}
962 
963 					if(xLoc2%2==0 && checkYLoc%2==0)
964 					{
965 						locPtr = world.get_loc(xLoc2, checkYLoc);
966 						if(locPtr->can_move(mobile_type))
967 							return 1;
968 					}
969 				}
970 				break;
971 
972 		default:	err_here();
973 					break;
974 	}
975 
976 	return 0;
977 }
978 //----------- End of function Unit::free_space_for_range_attack -----------//