1 /*
2 This file is part of "Avanor, the Land of Mystery" roguelike game
3 Home page: http://www.avanor.com/
4 Copyright (C) 2000-2003 Vadim Gaidukevich
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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "std_ai.h"
22 #include "xapi.h"
23 #include "other_misc.h"
24 #include "game.h"
25 
26 REGISTER_CLASS(XStandardAI);
27 
XStandardAI(XCreature * _cr)28 XStandardAI::XStandardAI(XCreature * _cr) : guard_area(1, 1, 2, 3)
29 {
30 	ai_owner = _cr;
31 	ai_flag = AIF_NONE; //(AI_FLAG)(AIF_RANDOM_MOVE | AIF_ALLOW_PICK_UP);
32 
33 	enemy_class = CR_ALL;
34 	last_moved_way = NULL;
35 
36 	companion_command = CC_NONE;
37 	invisible_hunting_mode = 0;
38 	invisible_x = -1;
39 	invisible_y = -1;
40 //	last_enemy_x = -1;
41 //	last_enemy_y = -1;
42 }
43 
Invalidate()44 void XStandardAI::Invalidate()
45 {
46 	ai_owner = NULL;
47 	for (int i = 0; i < ENEMY_LIST_SIZE; i++) personal_enemy[i] = NULL;
48 
49 	XObject::Invalidate();
50 }
51 
AnalyzeGrid(int j,int i,int w)52 void XStandardAI::AnalyzeGrid(int j, int i, int w)
53 {
54 	//test for monsters
55 	XCreature * tgt = ai_owner->l->map->GetMonster(j, i);
56 	if (tgt && !ai_owner->isCreatureVisible(tgt))
57 		tgt = NULL;
58 
59 	//if (tgt && (w < enemy_dist && w > 0 && isEnemy(tgt)) || (ai_flag & AIF_PROTECT_AREA && tgt->group_id != ai_owner->group_id))
60 	if (tgt && w < enemy_dist && w > 0 && isEnemy(tgt))
61 	{
62 		enemy = tgt;
63 		enemy_dist = w;
64 	}
65 
66 	//test for friends if
67 	if (ai_flag & AIF_ALLOW_PACK && tgt && !isEnemy(tgt) && w > 0)
68 	{
69 		friends_count++;
70 		//make summ of all friend coord, then div it on friend count
71 		//so we got center of the pack
72 		friend_avg_x += tgt->x;
73 		friend_avg_y += tgt->y;
74 	}
75 
76 
77 	//test for items
78 	if (ai_flag & AIF_ALLOW_PICK_UP &&
79 		(ai_owner->l->map->GetItemCount(j, i) > 0) && (w < item_dist))
80 	{
81 		XAnyPlace * pl = ai_owner->l->map->GetPlace(j, i);
82 		if (!pl)
83 		{
84 			item_dist = w;
85 			item_x = j;
86 			item_y = i;
87 		}
88 	}
89 
90 
91 	//test for ways
92 	XMapObject * spec = ai_owner->l->map->GetSpecial(j, i);
93 	if (spec && spec->im & IM_WAY && (w < way_dist) && spec != last_moved_way &&
94 		(((spec->view == '>') && (ai_flag & AIF_ALLOW_MOVE_WAY_DOWN)) ||
95 			((spec->view == '<') && (ai_flag & AIF_ALLOW_MOVE_WAY_UP)))
96 		)
97 	{
98 		if (((XStairWay*)spec)->ln != L_MAIN || ai_flag & AIF_ALLOW_MOVE_OUT)
99 		{
100 			way_dist = w;
101 			way_x = j;
102 			way_y = i;
103 		}
104 	}
105 }
106 
107 
Move()108 void XStandardAI::Move()
109 {
110 	if (last_enemy.get())
111 	{
112 		if (!last_enemy->isValid() || !ai_owner->isCreatureVisible(last_enemy))
113 			last_enemy = NULL;
114 	}
115 
116 // initializing variables
117 	enemy         = NULL;
118 	enemy_dist    = 10000;
119 	item_dist     = 10000;
120 	item_x        = 0;
121 	item_y        = 0;
122 	way_dist      = 10000;
123 	way_x         = 0;
124 	way_y         = 0;
125 
126 	//if no last enemy to attack only than process grids...
127 //	if (!last_enemy.get())
128 	{
129 		friends_count = 1;
130 		friend_avg_x  = ai_owner->x;
131 		friend_avg_y  = ai_owner->y;
132 
133 		// processing all visible grids
134 		AnalyzeView(ai_owner->GetVisibleRadius());
135 
136 		//calculate avg coordinats for AIF_ALLOW_PACK
137 		friend_avg_x = vRound((float)friend_avg_x / (float)friends_count);
138 		friend_avg_y = vRound((float)friend_avg_y / (float)friends_count);
139 	}
140 
141 // trying to wear some item
142 	if (ai_flag & AIF_ALLOW_WEAR_ITEM && enemy_dist > 1 && Wear())
143 	{
144 		ai_owner->nx = ai_owner->x;
145 		ai_owner->ny = ai_owner->y;
146 		return;
147 	}
148 
149 
150 	int was_attack = 0;
151 	int was_item_pick = 0;
152 	int was_stair_way = 0;
153 
154 	// first of all, execute order of companion to attack
155 	bool order_executing = false;
156 	if (companion && companion_command == CC_ATTACK)
157 	{
158 		if (ordered_enemy && ordered_enemy->isValid())
159 			enemy = ordered_enemy;
160 		else
161 		{
162 			ordered_enemy = NULL;
163 			companion_command = CC_NONE;
164 		}
165 	}
166 
167 	if (enemy)//second try to attack enemy
168 	{
169 		was_attack = AttackEnemy(enemy->x, enemy->y);
170 		if (was_attack)
171 			last_enemy = enemy;
172 	} else if (invisible_hunting_mode > 0)
173 	{
174 		was_attack = AttackEnemy(invisible_x, invisible_y);
175 		if (!was_attack || (ai_owner->x == ai_owner->nx && ai_owner->y == ai_owner->ny))
176 		{
177 			invisible_x = -1;
178 			invisible_y = -1;
179 			invisible_hunting_mode = 0;
180 		}
181 	}else if (companion && (companion_command == CC_FOLLOW || companion_command == CC_NONE)
182 		&& MoveTo(companion->x, companion->y, companion->l))
183 	{
184 		//do nothing....
185 	} else if (last_enemy)
186 	{
187 		if (!MoveTo(last_enemy->x, last_enemy->y, last_enemy->l))
188 			last_enemy = NULL;
189 	} else if (ai_flag & AIF_EXECUTE_SCRIPT) //execute script when nothing to do
190 	{
191 		RunScript();
192 	} else if (ai_flag & AIF_ALLOW_PICK_UP && !(ai_owner->l->map->GetItemList(ai_owner->x, ai_owner->y))->empty() && !ai_owner->l->map->GetPlace(ai_owner->x, ai_owner->y))
193 	{
194 		if (PickUpItems())
195 			return;
196 	} else if (ai_flag & AIF_ALLOW_PICK_UP && item_dist < 10000)
197 	{
198 		MoveTo(item_x, item_y);
199 		was_item_pick = 1;
200 	} else if (ai_flag & (AIF_ALLOW_MOVE_WAY_DOWN | AIF_ALLOW_MOVE_WAY_UP) && way_dist < 10000 && !(ai_flag && AIF_GUARD_AREA))
201 	{
202 
203 		XMapObject * spec = ai_owner->l->map->GetSpecial(ai_owner->x, ai_owner->y);
204 		if (spec && spec->im & IM_WAY  &&
205 		(((spec->view == '>') && (ai_flag & AIF_ALLOW_MOVE_WAY_DOWN)) ||
206 			((spec->view == '<') && (ai_flag & AIF_ALLOW_MOVE_WAY_UP))))
207 		{
208 			ai_owner->MoveStairWay();
209 			last_moved_way = ai_owner->l->map->GetSpecial(ai_owner->x, ai_owner->y);
210 		} else
211 		{
212 			MoveTo(way_x, way_y);
213 		}
214 		was_stair_way = 1;
215 	} else if (ai_flag & AIF_ALLOW_PACK) //Allow to create packs....
216 	{
217 		XPoint direction_point;
218 		XPoint target_point(friend_avg_x, friend_avg_y);
219 		GetRandDirection(&target_point, &direction_point);
220 		ai_owner->nx = ai_owner->x + direction_point.x;
221 		ai_owner->ny = ai_owner->y + direction_point.y;
222 		if (ai_owner->l->map->XGetMovability(ai_owner->nx, ai_owner->ny) != 0)
223 		{
224 			ai_owner->nx = ai_owner->x + vRand(3) - 1;
225 			ai_owner->ny = ai_owner->y + vRand(3) - 1;
226 		}
227 	} else if (ai_flag & AIF_RANDOM_MOVE)
228 	{
229 		ai_owner->nx = ai_owner->x + vRand(3) - 1;
230 		ai_owner->ny = ai_owner->y + vRand(3) - 1;
231 	}
232 
233 	//we can leave area only to pursuit enemy, other wise - comback
234 	if (!was_attack && !was_item_pick && (ai_flag & AIF_GUARD_AREA))
235 	{
236 		if (guard_area_location != ai_owner->l->ln || !guard_area.PointIn(ai_owner->nx, ai_owner->ny))
237 		{
238 			MoveTo((guard_area.left + guard_area.right) / 2, (guard_area.top + guard_area.bottom) / 2, Game.locations[guard_area_location]);
239 		}
240 	}
241 
242 	//Prevents from attacking friends...
243 	XCreature * tgt = ai_owner->l->map->GetMonster(ai_owner->nx, ai_owner->ny);
244 	if (tgt)
245 	{
246 		if (!isEnemy(tgt))
247 		{
248 			ai_owner->nx = ai_owner->x;
249 			ai_owner->ny = ai_owner->y;
250 		} else if (!ai_owner->isCreatureVisible(tgt))
251 		{
252 			invisible_x = tgt->x;
253 			invisible_y = tgt->y;
254 			invisible_hunting_mode = 1;
255 		}
256 	}
257 }
258 
259 const int find_path_deep = 200;
260 
FindPath(XPoint * target,XPoint * direction)261 int XStandardAI::FindPath(XPoint * target, XPoint * direction)
262 {
263 	int dist_x = abs(target->x - ai_owner->x);
264 	int dist_y = abs(target->y - ai_owner->y);
265 	if (dist_x > find_path_deep || dist_y > find_path_deep)
266 	{
267 		direction->x = 0;
268 		direction->y = 0;
269 		return 0;
270 	}
271 	int center_x = (target->x + ai_owner->x) / 2;
272 	int center_y = (target->y + ai_owner->y) / 2;
273 
274 	int path_flags[find_path_deep + 4][find_path_deep + 4];
275 	memset(path_flags, 0, (find_path_deep + 4) * (find_path_deep + 4) * sizeof(int));
276 
277 	int map_x = center_x - find_path_deep / 2 + 2;
278 	int map_y = center_y - find_path_deep / 2 + 2;
279 	XRect map_rect(map_x, map_y, map_x + find_path_deep, map_y + find_path_deep);
280 
281 	XPOINT pa[8 * find_path_deep];
282 	XPOINT pb[8 * find_path_deep];
283 
284 	path_flags[target->x - map_x][target->y - map_y] = 1;
285 	pa[0].x = target->x;
286 	pa[0].y = target->y;
287 	int stop_flag = 1;
288 	XPOINT * pc = pa;
289 	int list_len_pc = 1;
290 	XPOINT * pd = pb;
291 	for (int i = 2; i < find_path_deep + 2 && stop_flag; i++)
292 	{
293 		int list_len_pd = 0;
294 		//assert(list_len_pc < 100);
295 		for (int j = 0; j < list_len_pc; j++)
296 		{
297 			XPOINT * cpt = &pc[j];
298 			if (map_rect.PointIn(cpt->x - 1, cpt->y - 1) &&
299 				path_flags[cpt->x - map_x - 1][cpt->y - map_y - 1] == 0)
300 			{
301 				if (cpt->x - 1 == ai_owner->x &&
302 					cpt->y - 1 == ai_owner->y)
303 				{
304 					stop_flag = 0;
305 					direction->x = 1;
306 					direction->y = 1;
307 					break;
308 				}
309 				if (ai_owner->l->map->XGetMovability(cpt->x - 1, cpt->y - 1) == 0)
310 				{
311 					path_flags[cpt->x - map_x - 1][cpt->y - map_y - 1] = i;
312 					pd[list_len_pd].x = cpt->x - 1;
313 					pd[list_len_pd].y = cpt->y - 1;
314 					list_len_pd++;
315 				}
316 			}
317 
318 			if (map_rect.PointIn(cpt->x - 0, cpt->y - 1) &&
319 				path_flags[cpt->x - map_x - 0][cpt->y - map_y - 1] == 0)
320 			{
321 				if (cpt->x - 0 == ai_owner->x &&
322 					cpt->y - 1 == ai_owner->y)
323 				{
324 					stop_flag = 0;
325 					direction->x = 0;
326 					direction->y = 1;
327 					break;
328 				}
329 				if (ai_owner->l->map->XGetMovability(cpt->x - 0, cpt->y - 1) == 0)
330 				{
331 					path_flags[cpt->x - map_x - 0][cpt->y - map_y - 1] = i;
332 					pd[list_len_pd].x = cpt->x - 0;
333 					pd[list_len_pd].y = cpt->y - 1;
334 					list_len_pd++;
335 				}
336 			}
337 
338 
339 			if (map_rect.PointIn(cpt->x + 1, cpt->y - 1) &&
340 				path_flags[cpt->x - map_x + 1][cpt->y - map_y - 1] == 0)
341 			{
342 				if (cpt->x + 1 == ai_owner->x &&
343 					cpt->y - 1 == ai_owner->y)
344 				{
345 					stop_flag = 0;
346 					direction->x = -1;
347 					direction->y = 1;
348 					break;
349 				}
350 				if (ai_owner->l->map->XGetMovability(cpt->x + 1, cpt->y - 1) == 0)
351 				{
352 					path_flags[cpt->x - map_x + 1][cpt->y - map_y - 1] = i;
353 					pd[list_len_pd].x = cpt->x + 1;
354 					pd[list_len_pd].y = cpt->y - 1;
355 					list_len_pd++;
356 				}
357 			}
358 
359 			if (map_rect.PointIn(cpt->x + 1, cpt->y + 0) &&
360 				path_flags[cpt->x - map_x + 1][cpt->y - map_y + 0] == 0)
361 			{
362 				if (cpt->x + 1 == ai_owner->x &&
363 					cpt->y + 0 == ai_owner->y)
364 				{
365 					stop_flag = 0;
366 					direction->x = -1;
367 					direction->y = 0;
368 					break;
369 				}
370 				if (ai_owner->l->map->XGetMovability(cpt->x + 1, cpt->y + 0) == 0)
371 				{
372 					path_flags[cpt->x - map_x + 1][cpt->y - map_y + 0] = i;
373 					pd[list_len_pd].x = cpt->x + 1;
374 					pd[list_len_pd].y = cpt->y + 0;
375 					list_len_pd++;
376 				}
377 			}
378 
379 			if (map_rect.PointIn(cpt->x - 1, cpt->y + 0) &&
380 				path_flags[cpt->x - map_x - 1][cpt->y - map_y + 0] == 0)
381 			{
382 				if (cpt->x - 1 == ai_owner->x &&
383 					cpt->y + 0 == ai_owner->y)
384 				{
385 					stop_flag = 0;
386 					direction->x = +1;
387 					direction->y = 0;
388 					break;
389 				}
390 				if (ai_owner->l->map->XGetMovability(cpt->x - 1, cpt->y + 0) == 0)
391 				{
392 					path_flags[cpt->x - map_x - 1][cpt->y - map_y + 0] = i;
393 					pd[list_len_pd].x = cpt->x - 1;
394 					pd[list_len_pd].y = cpt->y + 0;
395 					list_len_pd++;
396 				}
397 
398 			}
399 
400 			if (map_rect.PointIn(cpt->x - 1, cpt->y + 1) &&
401 				path_flags[cpt->x - map_x - 1][cpt->y - map_y + 1] == 0)
402 			{
403 				if (cpt->x - 1 == ai_owner->x &&
404 					cpt->y + 1 == ai_owner->y)
405 				{
406 					stop_flag = 0;
407 					direction->x = +1;
408 					direction->y = -1;
409 					break;
410 				}
411 				if (ai_owner->l->map->XGetMovability(cpt->x - 1, cpt->y + 1) == 0)
412 				{
413 					path_flags[cpt->x - map_x - 1][cpt->y - map_y + 1] = i;
414 					pd[list_len_pd].x = cpt->x - 1;
415 					pd[list_len_pd].y = cpt->y + 1;
416 					list_len_pd++;
417 				}
418 			}
419 
420 			if (map_rect.PointIn(cpt->x + 0, cpt->y + 1) &&
421 				path_flags[cpt->x - map_x + 0][cpt->y - map_y + 1] == 0)
422 			{
423 				if (cpt->x + 0 == ai_owner->x &&
424 					cpt->y + 1 == ai_owner->y)
425 				{
426 					stop_flag = 0;
427 					direction->x = 0;
428 					direction->y = -1;
429 					break;
430 				}
431 
432 				if (ai_owner->l->map->XGetMovability(cpt->x + 0, cpt->y + 1) == 0)
433 				{
434 					path_flags[cpt->x - map_x + 0][cpt->y - map_y + 1] = i;
435 					pd[list_len_pd].x = cpt->x + 0;
436 					pd[list_len_pd].y = cpt->y + 1;
437 					list_len_pd++;
438 				}
439 			}
440 
441 			if (map_rect.PointIn(cpt->x + 1, cpt->y + 1) &&
442 				path_flags[cpt->x - map_x + 1][cpt->y - map_y + 1] == 0)
443 			{
444 				if (cpt->x + 1 == ai_owner->x &&
445 					cpt->y + 1 == ai_owner->y)
446 				{
447 					stop_flag = 0;
448 					direction->x = -1;
449 					direction->y = -1;
450 					break;
451 				}
452 				if (ai_owner->l->map->XGetMovability(cpt->x + 1, cpt->y + 1) == 0)
453 				{
454 					path_flags[cpt->x - map_x + 1][cpt->y - map_y + 1] = i;
455 					pd[list_len_pd].x = cpt->x + 1;
456 					pd[list_len_pd].y = cpt->y + 1;
457 					list_len_pd++;
458 				}
459 			}
460 		}
461 		XPOINT * pt = pd;
462 		pd = pc;
463 		pc = pt;
464 		list_len_pc = list_len_pd;
465 	}
466 	return 1;
467 };
468 
GetDirection(XPoint * target,XPoint * direction)469 void XStandardAI::GetDirection(XPoint * target, XPoint * direction)
470 {
471 	int dx = sgn(target->x - ai_owner->x);
472 	int dy = sgn(target->y - ai_owner->y);
473 
474 	if (ai_owner->x + dx == target->x && ai_owner->y + dy == target->y)
475 	{
476 		direction->x = dx;
477 		direction->y = dy;
478 		return;
479 	}
480 
481 	FindPath(target, direction);
482 /*	if (ai_owner->l->map->XGetMovability(ai_owner->x + dx, ai_owner->y + dy) != 0)
483 	{
484 		FindPath(target, direction);
485 	} else
486 	{
487 		direction->x = dx;
488 		direction->y = dy;
489 	}*/
490 }
491 
492 
GetRandDirection(XPoint * target,XPoint * direction)493 void XStandardAI::GetRandDirection(XPoint * target, XPoint * direction)
494 {
495 	int dx = sgn(target->x - ai_owner->x);
496 	int dy = sgn(target->y - ai_owner->y);
497 
498 	if (ai_owner->x + dx == target->x && ai_owner->y + dy == target->y)
499 	{
500 		direction->x = dx;
501 		direction->y = dy;
502 		return;
503 	}
504 
505 	if (ai_owner->l->map->XGetMovability(ai_owner->x + dx, ai_owner->y + dy) != 0)
506 	{
507 		int tx = vRand(3) - 1;
508 		int ty = vRand(3) - 1;
509 		dx += tx;
510 		dy += ty;
511 		direction->x = sgn(dx);
512 		direction->y = sgn(dy);
513 	} else
514 	{
515 		direction->x = dx;
516 		direction->y = dy;
517 	}
518 }
519 
520 
GetExactDirection(XPoint * target,XPoint * direction)521 void XStandardAI::GetExactDirection(XPoint * target, XPoint * direction)
522 {
523 	direction->x = sgn(target->x - ai_owner->x);
524 	direction->y = sgn(target->y - ai_owner->y);
525 }
526 
527 
isEnemy(XCreature * cr)528 int XStandardAI::isEnemy(XCreature * cr)
529 {
530 	if (cr == companion || (ai_flag & AIF_GUARD_AREA && cr->group_id == ai_owner->group_id))
531 		return 0;
532 	if (enemy_class & cr->creature_class && ai_owner->view != cr->view)
533 		return 1;
534 	if (ai_flag & AIF_PROTECT_AREA && cr->group_id != ai_owner->group_id && cr->x >= guard_area.left && cr->x < guard_area.right && cr->y >= guard_area.top && cr->y < guard_area.bottom)
535 		return 1;
536 	return isPersonalEnemy(cr);
537 }
538 
isPersonalEnemy(XCreature * cr)539 int XStandardAI::isPersonalEnemy(XCreature * cr)
540 {
541 	for (int i = 0; i < ENEMY_LIST_SIZE; i++)
542 		if (personal_enemy[i] == cr) return 1;
543 	return 0;
544 /*
545 	for (XQueue::iterator it = personal_enemy.begin(); it != personal_enemy.end(); it++)
546 		if (it == cr) return 1;
547 
548 	return 0;
549 */
550 }
551 
SetAIFlag(AI_FLAG aif)552 void XStandardAI::SetAIFlag(AI_FLAG aif)
553 {
554 	ai_flag = (AI_FLAG)(ai_flag | aif);
555 }
556 
ResAIFlag(AI_FLAG aif)557 void XStandardAI::ResAIFlag(AI_FLAG aif)
558 {
559 	ai_flag = (AI_FLAG)((ai_flag | aif) ^ aif);
560 }
561 
SetEnemyClass(CREATURE_CLASS cr_class)562 void XStandardAI::SetEnemyClass(CREATURE_CLASS cr_class)
563 {
564 	enemy_class = cr_class;
565 }
566 
Wear()567 int XStandardAI::Wear()
568 {
569 	it_iterator it;
570 	for (it = ai_owner->contain.begin(); it != ai_owner->contain.end(); it++)
571 	{
572 		XItem * item = static_cast<XItem *>(static_cast<XObject *>(it));
573 
574 		XBodyPart * xbp = ai_owner->GetBodyPart(item->bp);
575 		if (!xbp) continue;
576 
577 		XItem * old_item = ai_owner->GetItem(item->bp);
578 		if (old_item)
579 		{
580 			XBodyPart * xbp1 = ai_owner->GetBodyPart(item->bp, 1);
581 			if (xbp1)
582 			{
583 				XItem * old_item1 = ai_owner->GetItem(item->bp, 1);
584 				if (!old_item1 || (old_item1->GetValue() < old_item->GetValue()))
585 				{
586 					xbp = xbp1;
587 					old_item = old_item1;
588 				}
589 			}
590 		}
591 
592 		int new_item_val = item->GetValue(), old_item_val = 0;
593 		if (old_item) old_item_val = old_item->GetValue();
594 
595 		if ((old_item_val >= new_item_val) || !(xbp->GetProperIM() & item->im)) continue;
596 
597 		if (old_item) ai_owner->contain.Add(xbp->UnWear());
598 
599 		xbp->Wear(item);
600 		ai_owner->contain.Remove(it);
601 
602 		if (ai_owner->isVisible())
603 		{
604 			char xbuf[256];
605 			char tbuf[256];
606 			item->toString(tbuf);
607 			switch(item->im)
608 			{
609 				case IM_WEAPON :
610 				case IM_MISSILEW :
611 					sprintf(xbuf, "%s has wielded %s.", ai_owner->name, tbuf); break;
612 				case IM_MISSILE :
613 					sprintf(xbuf, "%s has armed %s.", ai_owner->name, tbuf); break;
614 				default :
615 					sprintf(xbuf, "%s puts on %s.", ai_owner->name, tbuf); break;
616 			}
617 			msgwin.Add(xbuf);
618 		}
619 
620 		return 1;
621 	}
622 
623 //  Sacrifice useless items
624 	int s_flag = 0;
625 
626 	it = ai_owner->contain.begin();
627 	while(it != ai_owner->contain.end())
628 	{
629 		XItem * item = it;
630 		it++;
631 		assert(item->isValid());
632 		if(item->GetValue() > 800)
633 			continue;
634 		if (item->im & IM_FOOD && item->it != IT_CORPSE)
635 			continue;
636 
637 		if (item->im & (IM_SCROLL | IM_BOOK | IM_POTION | IM_MISSILE | IM_MONEY))
638 			continue;
639 
640 
641 
642 		it--;
643 		it = ai_owner->contain.erase(it);
644 		ai_owner->Sacrifice(item);
645 		s_flag = 1;
646 		break;
647 	}
648 
649 	return 0;
650 }
651 
652 
RecursiveWayFound(XLocation * tl,XLocation * tgt_l)653 XStairWay * RecursiveWayFound(XLocation * tl, XLocation * tgt_l)
654 {
655 	tl->way_found_flag = false;
656 	for (XQList<XObject*>::iterator it = tl->ways_list.begin(); it != tl->ways_list.end(); it++)
657 	{
658 		XStairWay * way = (XStairWay *)(*it);
659 		if (way->ln == tgt_l->ln)
660 			return way;
661 		if (Game.locations[way->ln] && Game.locations[way->ln]->way_found_flag)
662 		{
663 			XStairWay * tway = RecursiveWayFound(Game.locations[way->ln], tgt_l);
664 			if (tway)
665 				return way; //we need to find only top(closest) WAY at this time
666 		}
667 	}
668 	return NULL;
669 }
670 
RWayFound(XLocation * tl,XLocation * tgt_l)671 XStairWay * RWayFound(XLocation * tl, XLocation * tgt_l)
672 {
673 	for (int i = 0; i < L_EOF; i++)
674 	{
675 		if (Game.locations[i])
676 			Game.locations[i]->way_found_flag = true;
677 	}
678 	return RecursiveWayFound(tl, tgt_l);
679 }
680 
681 
MoveTo(int x,int y,XLocation * l)682 int XStandardAI::MoveTo(int x, int y, XLocation * l)
683 {
684 	//if it is not this location, than try to way to nearest location
685 	if (l && l->ln != ai_owner->l->ln)
686 	{
687 		if (!(ai_flag & AIF_FIND_WAY))
688 			return 0;
689 		//try to find StairWay to creature...
690 		XStairWay * way = RWayFound(ai_owner->l, l);
691 		if (!way)
692 			return 0;
693 		if (ai_owner->x == way->x && ai_owner->y == way->y)
694 		{
695 			ai_owner->MoveStairWay();
696 			return 1;
697 		} else
698 		{
699 			XPoint direction_point;
700 			XPoint target_point(way->x, way->y);
701 			GetDirection(&target_point, &direction_point);
702 			ai_owner->nx = ai_owner->x + direction_point.x;
703 			ai_owner->ny = ai_owner->y + direction_point.y;
704 			return 1;
705 		}
706 	} else //if it is this location than move to...
707 	{
708 		XPoint direction_point;
709 		XPoint target_point(x, y);
710 		GetDirection(&target_point, &direction_point);
711 		ai_owner->nx = ai_owner->x + direction_point.x;
712 		ai_owner->ny = ai_owner->y + direction_point.y;
713 		return 1;
714 	}
715 }
716 
TryToRunAway()717 int XStandardAI::TryToRunAway() //from enemy
718 {
719 	assert(enemy);
720 	int dx = sgn(ai_owner->x - enemy->x);
721 	int dy = sgn(ai_owner->y - enemy->y);
722 
723 	ai_owner->nx = ai_owner->x + dx;
724 	ai_owner->ny = ai_owner->y + dy;
725 	if (ai_owner->l->map->XGetMovability(ai_owner->nx, ai_owner->ny) == 0)
726 		return 1;
727 
728 	dx += vRand(3) - 1;
729 	dy += vRand(3) - 1;
730 	dx = sgn(dx);
731 	dy = sgn(dy);
732 	ai_owner->nx = ai_owner->x + dx;
733 	ai_owner->ny = ai_owner->y + dy;
734 	if (ai_owner->l->map->XGetMovability(ai_owner->nx, ai_owner->ny) == 0)
735 		return 1;
736 
737 	dx += vRand(3) - 1;
738 	dy += vRand(3) - 1;
739 	dx = sgn(dx);
740 	dy = sgn(dy);
741 	ai_owner->nx = ai_owner->x + dx;
742 	ai_owner->ny = ai_owner->y + dy;
743 	if (ai_owner->l->map->XGetMovability(ai_owner->nx, ai_owner->ny) == 0)
744 		return 1;
745 
746 	ai_owner->nx = ai_owner->x;
747 	ai_owner->ny = ai_owner->y;
748 	if (abs(ai_owner->x - enemy->x) > 1 || abs(ai_owner->y - enemy->y) > 1)
749 		return 1;
750 
751 	return 0;
752 }
753 
AttackEnemy(int ex,int ey)754 int XStandardAI::AttackEnemy(int ex, int ey)
755 {
756 	assert(isValid());
757 
758 	 //try to run away if we must or can
759 	if (ai_flag & AIF_COWARD && enemy &&
760 		(enemy->GetExp() / 10 > ai_owner->GetExp() * friends_count //creature is more powerfull
761 		|| ai_owner->GetMaxHP() / ai_owner->_HP > 4) // less than 25% of _HP
762 		&& TryToRunAway())
763 		return 0;
764 
765 	XPoint direction_point;
766 	XPoint target_point(ex, ey);
767 	GetDirection(&target_point, &direction_point);
768 
769 	if (CastSpell() || Shoot() || ReadScroll() || DrinkPotion())
770 	{
771 		ai_owner->nx = ai_owner->x;
772 		ai_owner->ny = ai_owner->y;
773 	} else
774 	{
775 		ai_owner->nx = ai_owner->x + direction_point.x;
776 		ai_owner->ny = ai_owner->y + direction_point.y;
777 	}
778 	assert(isValid());
779 	return 1;
780 
781 }
782 
CastSpell()783 int XStandardAI::CastSpell()
784 {
785 	if (ai_owner->m->spells.empty()) return 0;
786 
787 	int flag = 0;
788 //	try to heal self
789 	if (ai_owner->_HP < ai_owner->GetMaxHP() / 3)
790 	{
791 		XList<XSpell *>::iterator spell = ai_owner->m->spells.begin();
792 		for (; spell != ai_owner->m->spells.end(); spell++)
793 		{
794 			if ((spell->spell_name == SPELL_CURE_LIGHT_WOUNDS ||
795 				spell->spell_name == SPELL_CURE_SERIOUS_WOUNDS ||
796 				spell->spell_name == SPELL_CURE_CRITICAL_WOUNDS ||
797 				spell->spell_name == SPELL_CURE_MORTAL_WOUNDS ||
798 				spell->spell_name == SPELL_HEAL) &&
799 				spell->GetManaCost() <= ai_owner->_PP)
800 			{
801 				ai_owner->m->Cast(spell, ai_owner);
802 				flag = 1;
803 				break;
804 			}
805 		}
806 	}
807 	//try to attack
808 	if (flag == 0 && enemy)
809 	{
810 		int r_enemy = (int)sqrt((enemy->x - ai_owner->x) * (enemy->x - ai_owner->x) +
811 			(enemy->y - ai_owner->y) * (enemy->y - ai_owner->y));
812 
813 
814 		XList<XSpell *>::iterator spell = ai_owner->m->spells.begin();
815 		for (; spell != ai_owner->m->spells.end(); spell++)
816 		{
817 			if ((spell->spell_name == SPELL_MAGIC_ARROW ||
818 				spell->spell_name == SPELL_FIRE_BOLT ||
819 				spell->spell_name == SPELL_ICE_BOLT ||
820 				spell->spell_name == SPELL_LIGHTNING_BOLT ||
821 				spell->spell_name == SPELL_ACID_BOLT)
822 				&& spell->GetManaCost() <= ai_owner->_PP)
823 			{
824 				ai_owner->m->Cast(spell, ai_owner);
825 				flag = 1;
826 				break;
827 			}
828 		}
829 	}
830 	return flag;
831 }
832 
ReadScroll()833 int XStandardAI::ReadScroll()
834 {
835 	for (it_iterator it = ai_owner->contain.begin(); it != ai_owner->contain.end(); it++)
836 	{
837 		if (!(it->im & IM_SCROLL)) continue;
838 		XScroll * scroll = static_cast<XScroll *>(static_cast<XObject *>(it));
839 		if (scroll->sc_name == SCROLL_MAGIC_ARROW ||
840 			scroll->sc_name == SCROLL_FIRE_BOLT ||
841 			scroll->sc_name == SCROLL_ICE_BOLT ||
842 			scroll->sc_name == SCROLL_LIGHTNING_BOLT ||
843 			scroll->sc_name == SCROLL_ACID_BOLT)
844 		{
845 			scroll->onRead(ai_owner);
846 			if (--scroll->quantity <= 0)
847 			{
848 				scroll->Invalidate();
849 			}
850 			return 1;
851 		}
852 	}
853 	return 0;
854 }
855 
DrinkPotion()856 int XStandardAI::DrinkPotion()
857 {
858 	if (ai_owner->_HP < ai_owner->GetMaxHP() / 3)
859 	{
860 		for (it_iterator i = ai_owner->contain.begin(); i != ai_owner->contain.end(); i++)
861 		{
862 			XItem * it = i;
863 			if (it->im & IM_POTION)
864 			{
865 				XPotion * pot = (XPotion *)it;
866 				if (pot->pn == PN_HEALING ||
867 					pot->pn == PN_CURE_LIGHT_WOUNDS ||
868 					pot->pn == PN_CURE_SERIOUS_WOUNDS ||
869 					pot->pn == PN_CURE_CRITICAL_WOUNDS ||
870 					pot->pn == PN_CURE_MORTAL_WOUNDS)
871 				{
872 					XPotion * np = (XPotion *)pot->MakeCopy();
873 					np->onDrink(ai_owner);
874 					if (pot->quantity > 1)
875 					{
876 						pot->quantity--;
877 					}
878 					else
879 					{
880 						XObject * obj = ai_owner->contain.Remove(i);
881 						assert(obj->xguid == pot->xguid);
882 						pot->Invalidate();
883 					}
884 					return 1;
885 				}
886 			}
887 		}
888 
889 	}
890 	return 0;
891 }
892 
Shoot()893 int XStandardAI::Shoot()
894 {
895 	int hit;
896 	int range;
897 	XDice dmg;
898 	ai_owner->GetRangeAttackInfo(&range, &hit, &dmg);
899 
900 	if (enemy)
901 	{
902 		int r = (int)sqrt((enemy->x - ai_owner->x) * (enemy->x - ai_owner->x) +
903 			(enemy->y - ai_owner->y) * (enemy->y - ai_owner->y));
904 		if (r <= range)
905 		{
906 			ai_owner->Shoot(enemy->x, enemy->y);
907 			return 1;
908 		}
909 	}
910 
911 	return 0;
912 }
913 
914 
PickUpItems()915 int XStandardAI::PickUpItems()
916 {
917 	XItemList * item_list = ai_owner->l->map->GetItemList(ai_owner->x, ai_owner->y);
918 	bool item_picked = false;
919 
920 	it_iterator it = item_list->begin();
921 	while (it != item_list->end())
922 	{
923 		if (it->im & IM_CHEST)
924 			break;
925 		XItem * tit = it;
926 		it = item_list->erase(it);
927 		if (ai_owner->PickUpItem(tit))
928 		{
929 			item_picked = true;
930 			continue;
931 		} else
932 		{
933 			item_list->Add(tit);
934 			break;
935 		}
936 	}
937 
938 	if (!item_picked) return 0;
939 
940 	ai_owner->nx = ai_owner->x;
941     ai_owner->ny = ai_owner->y;
942 
943 	if (ai_owner->isVisible())
944 	{
945 		msgwin.Add(ai_owner->name);
946 		msgwin.Add("picks something up from the ground.");
947 	}
948 	return 1;
949 }
950 
SetArea(XRect * area,LOCATION ln)951 void XStandardAI::SetArea(XRect * area, LOCATION ln)
952 {
953 	guard_area.Setup(area);
954 	guard_area_location = ln;
955 }
956 
onWasAttacked(XCreature * attacker)957 void XStandardAI::onWasAttacked(XCreature * attacker)
958 {
959 	AddPersonalEnemy(attacker);
960 	if (ai_owner->group_id != GID_NONE)
961 		SetGroupEnemy(attacker);
962 	if (ai_owner->isCreatureVisible(attacker))
963 		last_enemy = attacker;
964 	invisible_x = attacker->x;
965 	invisible_y = attacker->y;
966 	invisible_hunting_mode = 1;
967 }
968 
onDie(XCreature * killer)969 void XStandardAI::onDie(XCreature * killer)
970 {
971 	if (killer)
972 		SetGroupEnemy(killer);
973 }
974 
SetGroupEnemy(XCreature * enemy)975 void XStandardAI::SetGroupEnemy(XCreature * enemy)
976 {
977 	if (ai_owner->group_id != GID_NONE && enemy)
978 	{
979 		//hack!!!
980 		XObject * o = enemy->root;
981 
982 		while (o)
983 		{
984 			if (o->im & IM_CREATURE && ((XCreature *)o)->group_id == ai_owner->group_id)
985 			{
986 				((XCreature *)o)->xai->AddPersonalEnemy(enemy);
987 				//((XCreature *)o)->xai->SetLastEnemy(enemy->x, enemy->y);
988 				((XCreature *)o)->xai->ResAIFlag(AIF_GUARD_AREA);
989 				((XCreature *)o)->xai->enemy = (XCreature *)o;
990 			}
991 			o = o->next;
992 		}
993 	}
994 }
995 
onSteal(XCreature * rogue)996 void XStandardAI::onSteal(XCreature * rogue)
997 {
998 	AddPersonalEnemy(rogue);
999 }
1000 
AddPersonalEnemy(XCreature * cr)1001 void XStandardAI::AddPersonalEnemy(XCreature * cr)
1002 {
1003 	int i;
1004 
1005 	for (i = 0; i < ENEMY_LIST_SIZE; i++)
1006 	{
1007 		if (!personal_enemy[i])
1008 		{
1009 			personal_enemy[i] = cr;
1010 			return;
1011 		}
1012 	}
1013 
1014 	for (i = 1; i < ENEMY_LIST_SIZE; i++)
1015 		personal_enemy[i - 1] = personal_enemy[i].get();
1016 
1017 	personal_enemy[ENEMY_LIST_SIZE - 1] = cr;
1018 
1019 /*
1020 	int enemy_count = 0;
1021 	for (XQueue::iterator it = personal_enemy.begin(); it != personal_enemy.end(); it++)
1022 	{
1023 		if (it == cr) return;	// already in list
1024 		enemy_count++;
1025 	}
1026 
1027 	if (enemy_count >= ENEMY_LIST_SIZE) personal_enemy.RemoveFirst();
1028 	personal_enemy.AddUnsorted(cr);
1029 */
1030 }
1031 
RemovePersonalEnemy(XCreature * cr)1032 void XStandardAI::RemovePersonalEnemy(XCreature * cr)
1033 {
1034 	for (int i = 0; i < ENEMY_LIST_SIZE; i++)
1035 	{
1036 		if (personal_enemy[i].get() == cr)
1037 		{
1038 			personal_enemy[i] = NULL;
1039 			return;
1040 		}
1041 	}
1042 /*
1043 	for (XQueue::iterator it = personal_enemy.begin(); it != personal_enemy.end(); it++)
1044 	{
1045 		if (it == cr)
1046 		{
1047 			personal_enemy.Remove(it);
1048 			return;
1049 		}
1050 	}
1051 */
1052 }
1053 
Chat(XCreature * chatter,char * msg)1054 int XStandardAI::Chat(XCreature * chatter, char * msg)
1055 {
1056 	if (!ai_owner->Chat(chatter, msg))
1057 		msgwin.Add(ai_owner->StdAnswer());
1058 	return 1;
1059 }
1060 
onGiveItem(XCreature * giver,XItem * item)1061 int XStandardAI::onGiveItem(XCreature * giver, XItem * item)
1062 {
1063 	return ai_owner->onGiveItem(giver, item);
1064 }
1065 
1066 
GetTargetPos(XPoint * pt)1067 int XStandardAI::GetTargetPos(XPoint * pt)
1068 {
1069 	if (enemy)
1070 	{
1071 		pt->x = enemy->x;
1072 		pt->y = enemy->y;
1073 		return 1;
1074 
1075 	} else
1076 		return 0;
1077 }
1078 
1079 
CanMoveHere(int px,int py)1080 bool XStandardAI::CanMoveHere(int px, int py)
1081 {
1082 	if (ai_owner->l->map->XGetMovability(px, py) != 0)
1083 	{
1084 		XCreature * tgt = ai_owner->l->map->GetMonster(px, py);
1085 		if (tgt && isEnemy(tgt))
1086 			return true;
1087 		else
1088 			return false;
1089 	} else
1090 		return true;
1091 }
1092 
1093 
Store(XFile * f)1094 void XStandardAI::Store(XFile * f)
1095 {
1096 	XObject::Store(f);
1097 	f->Write(&ai_flag, sizeof(AI_FLAG));
1098 	f->Write(&enemy_class, sizeof(CREATURE_CLASS));
1099 	f->Write(&invisible_x, sizeof(int));
1100 	f->Write(&invisible_y, sizeof(int));
1101 	f->Write(&invisible_hunting_mode, sizeof(int));
1102 	XObject::StorePointer(f, last_moved_way);
1103 
1104 	last_enemy.Store(f);
1105 	companion.Store(f);
1106 	ordered_enemy.Store(f);
1107 	f->Write(&companion_command, sizeof(COMPANION_COMMAND));
1108 	for (int i = 0; i < ENEMY_LIST_SIZE; i++) personal_enemy[i].Store(f);
1109 	ai_owner.Store(f);
1110 	guard_area.Store(f);
1111 	f->Write(&guard_area_location, sizeof(LOCATION));
1112 	known_traps.StoreList(f);
1113 }
1114 
Restore(XFile * f)1115 void XStandardAI::Restore(XFile * f)
1116 {
1117 	XObject::Restore(f);
1118 	f->Read(&ai_flag, sizeof(AI_FLAG));
1119 	f->Read(&enemy_class, sizeof(CREATURE_CLASS));
1120 	f->Read(&invisible_x, sizeof(int));
1121 	f->Read(&invisible_y, sizeof(int));
1122 	f->Read(&invisible_hunting_mode, sizeof(int));
1123 	last_moved_way = (XMapObject *)RestorePointer(f, this);
1124 	last_enemy.Restore(f);
1125 	companion.Restore(f);
1126 	ordered_enemy.Restore(f);
1127 	f->Read(&companion_command, sizeof(COMPANION_COMMAND));
1128 	for (int i = 0; i < ENEMY_LIST_SIZE; i++) personal_enemy[i].Restore(f);
1129 	ai_owner.Restore(f);
1130 	guard_area.Restore(f);
1131 	f->Read(&guard_area_location, sizeof(LOCATION));
1132 	known_traps.RestoreList(f);
1133 }
1134 
1135 
1136 
1137 /////////////// scripting support
1138 
ExecuteScript(XQList<SCRIPT_CMD> * scr)1139 void XStandardAI::ExecuteScript(XQList<SCRIPT_CMD> * scr)
1140 {
1141 	script.clear();
1142 	for (XQList<SCRIPT_CMD>::iterator it = scr->begin(); it != scr->end(); it++)
1143 		script.push_back(*it);
1144 
1145 	SetAIFlag(AIF_EXECUTE_SCRIPT);
1146 	ResAIFlag(AIF_GUARD_AREA);
1147 }
1148 
RunScript()1149 void XStandardAI::RunScript()
1150 {
1151 	SCRIPT_CMD cmd = *script.begin();
1152 
1153 	bool flag = false;
1154 
1155 	switch (cmd.cmd)
1156 	{
1157 		case SCC_NONE: break;
1158 
1159 		case SCC_MOVE_POINT:
1160 			MoveTo(cmd.pt_x, cmd.pt_y, Game.locations[cmd.ln]);
1161 			if (cmd.pt_x == ai_owner->nx && cmd.pt_y == ai_owner->ny && cmd.ln == ai_owner->l->ln)
1162 				flag = true;
1163 			break;
1164 
1165 		case SCC_COLLECT_MUSHROOM:
1166 			{
1167 				XMapObject * obj = ai_owner->l->map->GetSpecial(ai_owner->x, ai_owner->y);
1168 				if (obj && obj->isValid() && obj->im == IM_OTHER)
1169 				{
1170 					XItem * tit = (XItem *)(obj->Pick(ai_owner));
1171 					char buf[256];
1172 					char buf2[256];
1173 					tit->toString(buf);
1174 					if (ai_owner->PickUpItem(tit))
1175 					{
1176 						if (ai_owner->isVisible())
1177 						{
1178 							sprintf(buf2, "%s collects %s.", ai_owner->GetNameEx(CRN_T1), buf);
1179 							msgwin.Add(buf2);
1180 						}
1181 						if (vRand(2) == 0)
1182 							flag = true;
1183 					} else
1184 					{
1185 						tit->Invalidate();
1186 					}
1187 				} else
1188 				{
1189 					ai_owner->nx = ai_owner->x + vRand(3) - 1;
1190 					ai_owner->ny = ai_owner->y + vRand(3) - 1;
1191 				}
1192 			}
1193 			break;
1194 
1195 		case SCC_DROP_ITEM:
1196 			{
1197 				XItemList::iterator it = ai_owner->contain.begin();
1198 				while (it != ai_owner->contain.end())
1199 				{
1200 					if (it->im & cmd.im)
1201 					{
1202 						XItem * item = it;
1203 						it = ai_owner->contain.erase(it);
1204 						ai_owner->DropItem(item);
1205 					} else
1206 						it++;
1207 				}
1208 				flag = true;
1209 			}
1210 			break;
1211 	}
1212 
1213 	if (flag)
1214 	{
1215 		script.push_back(cmd);
1216 		script.pop_front();
1217 	}
1218 }
1219 
1220 
LearnTraps()1221 void XStandardAI::LearnTraps()
1222 {
1223 	for (int i = guard_area.left; i < guard_area.right; i++)
1224 		for (int j = guard_area.top; j < guard_area.bottom; j++)
1225 		{
1226 			XMapObject * pO = ai_owner->l->map->GetSpecial(i, j);
1227 			if (pO && pO->im & IM_TRAP)
1228 				known_traps.push_back(pO);
1229 		}
1230 }
1231 
isKnowThisTrap(XMapObject * trap)1232 bool XStandardAI::isKnowThisTrap(XMapObject * trap)
1233 {
1234 	for (XList<XMapObject *>::iterator it = known_traps.begin(); it != known_traps.end(); it++)
1235 	{
1236 		if (it == trap)
1237 			return true;
1238 	}
1239 	return false;
1240 }
1241 
1242