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