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 "creature.h"
22 #include "modifer.h"
23 #include "xapi.h"
24 
Attack()25 void XCreature::Attack()
26 {
27 	assert(isValid());
28 	XCreature * target = l->map->GetMonster(nx, ny);
29 	assert(target);
30 
31 	XItem * it1 = GetItem(BP_HAND, 0);
32 	XItem * it2 = GetItem(BP_HAND, 1);
33 	int flag = 0;
34 	int ttm_res = 0;
35 
36 	if (it1 && it1->im & IM_WEAPON)
37 	{
38 		ttm_res += MeleeAttack(target, it1);
39 		flag++;
40 	}
41 
42 	if (it2 && it2->im & IM_WEAPON && target->isValid())
43 	{
44 		ttm_res += MeleeAttack(target, it2);
45 		flag++;
46 	}
47 
48 	if (flag == 0)
49 	{
50 		ttm_res += MeleeAttack(target, NULL);
51 	}
52 }
53 
54 /*
55 int XCreature::AttackCreature(ATTACK_DATA * pData)
56 {
57 	XCreature * target = pData->target;
58 	XCreature * attacker = pData->attacker;
59 
60 	//check for visibility both of target and attacker
61 	int vis1 = 0;
62 	if (attaker)
63 		vis1 = isVisible();
64 	int vis2 = target->isVisible();
65 
66 	//check if was an enemy for backstab
67 	int wasEnemy = 0;
68 	if (attaker)
69 		target->xai->isEnemy(this);
70 
71 	//check for basic of DV, HIT
72 	int todv = target->GetDV();
73 	int tohit = pData->toHIT;
74 
75 	if (tohit < 0)
76 	{
77 		todv -= tohit; // a - (-b) == a + b;
78 		tohit = 0;
79 	}
80 
81 	int p = (tohit * 150 + 1) / (tohit + todv + 1) - todv;
82 	int v = vRand(100);
83 
84 	if (v < p || pData->isAbsHit) //if was successful hit (or always hit)
85 	{
86 		int sb = target->GetShieldDVBonus();
87 		if (sb > 0 && vRand(todv) < sb && !pData->isAbsHit) //test for block
88 		{
89 			if (vis1 || vis2) //The attack was blocked
90 			{
91 				if (pData->attacker_name)
92 				{
93 					msgwin.Add(target->GetNameEx(CRN_T1));
94 					msgwin.Add(target->GetVerb("deflect"));
95 					msgwin.Add(attacker_name);
96 					msgwin.Add("with");
97 					msgwin.Add(target->GetNameEx(CRN_T4));
98 					msgwin.Add("shield.");
99 				} else
100 				{
101 					msgwin.Add(GetNameEx(CRN_T1));
102 					msgwin.Add(GetVerb("attack"));
103 					msgwin.AddLast(target->GetNameEx(CRN_T1));
104 					msgwin.Add(target->GetNameEx(CRN_T1));
105 					msgwin.Add(target->GetVerb("block"));
106 					msgwin.Add(GetNameEx(CRN_T1));
107 					msgwin.Add("with");
108 					msgwin.Add(target->GetNameEx(CRN_T4));
109 					msgwin.Add("shield.");
110 				}
111 			}
112 			target->wsk->UseSkill(WSK_SHIELD);
113 		} else
114 		{
115 			//get random part of body
116 			XBodyPart * txbp = target->GetRNDBodyPart();
117 
118 			//get initial damage
119 			int tdam = pData->baseDAM;
120 
121 			//get initial pv of armour
122 			int xpv = 0;
123 			if (txbp && txbp->Item())
124 				xpv = txbp->Item()->_PV;
125 
126 			if (txbp && txbp->bp_uin == BP_CLOAK)
127 			{
128 				XItem * xtmp = GetItem(BP_BODY);
129 				if (xtmp)
130 					xpv += xtmp->_PV;
131 			}
132 
133 			//critical hit
134 			int crtical_flag = 0;
135 			if (attacker)
136 			{
137 				if (vRand(100) < (2 + sk->GetLevel(SKT_FINDWEAKNESS)))
138 				{
139 					sk->UseSkill(SKT_FINDWEAKNESS);
140 					crtical_flag = 1;
141 					tdam *= 3;
142 				}
143 			} else
144 			{
145 				if (vRand(100) < 2)
146 				{
147 					crtical_flag = 1;
148 					tdam *= 3;
149 				}
150 			}
151 
152 			//backstab
153 			int backstab = 0;
154 			if (pData->weapon && (!target->isCreatureVisible(this) || !wasEnemy))
155 			{
156 				if (attacker)
157 				{
158 					if (vRand(100) < sk->GetLevel(SKT_BACKSTABBING) * 5 + 5)
159 					{
160 						backstab = 1;
161 						tdam *= 3;
162 						sk->UseSkill(SKT_BACKSTABBING, 3);
163 					}
164 				} else
165 				{
166 					if (vRand(100) < 5)
167 					{
168 						backstab = 1;
169 						tdam *= 3;
170 					}
171 				}
172 			}
173 
174 			int dam = tdam;
175 			int p_flag = 0;
176 
177 			//ignore armour
178 			if (attacker)
179 			{
180 				if (vRand(100) > (2 + sk->GetLevel(SKT_FINDWEAKNESS)) || xpv == 0)
181 					dam = tdam - xpv;
182 				else
183 				{
184 					sk->UseSkill(SKT_FINDWEAKNESS);
185 					p_flag = 1;
186 				}
187 			} else
188 			{
189 				if (vRand(100) > 2 || xpv == 0)
190 					dam = tdam - xpv;
191 				else
192 					p_flag = 1;
193 			}
194 
195 			if (vis1 || vis2)
196 			{
197 				if (attacker)
198 					msgwin.Add(attacker->GetNameEx(CRN_T1));
199 				else
200 					msgwin.Add(pData->attacker_name);
201 
202 				if (weapon)
203 				{
204 					if (crtical_flag)
205 						msgwin.Add("exactly");
206 
207 					if(backstab)
208 						msgwin.Add(GetVerb("stab"));
209 					else
210 						msgwin.Add(GetVerb("hit"));
211 				} else
212 				{
213 					if (crtical_flag)
214 						msgwin.Add("exactly");
215 					msgwin.Add(GetVerb(GetMeleeAttackMsg()));
216 				}
217 
218 				if (p_flag)
219 				{
220 					msgwin.Add(target->GetNameEx(CRN_T1));
221 					if (target->isHero()) //last message for hero.
222 						msgwin.Add("penetrating a piece of armour.");
223 					else
224 						msgwin.Add("penetrating a piece of armour");
225 				} else
226 					if (target->isHero()) //last message for hero
227 						msgwin.AddLast(target->GetNameEx(CRN_T1));
228 					else
229 						msgwin.Add(target->GetNameEx(CRN_T1));
230 			}
231 
232 
233 			if (!weapon)
234 			{
235 				int dmg = dam;
236 				for (XQList<MELEE_ATTACK>::iterator tit = melee_attack->begin(); tit != melee_attack->end(); tit++)
237 				{
238 					if (vRand(100) < (*tit).prob)
239 					{
240 						dam += target->CauseEffect(this, dmg, (*tit).r_attack);
241 					}
242 				}
243 				AttackCreature(target, dam);
244 			} else
245 			{
246 				dam = target->CauseEffect(dam, weapon->brt);
247 				AttackCreature(target, dam);
248 			}
249 		}
250 	} else
251 	{
252 		if (vis1 || vis2)
253 		{
254 			msgwin.Add(GetNameEx(CRN_T1));
255 			msgwin.Add(GetVerb("attack"));
256 			char xxbuf[256];
257 			sprintf(xxbuf, "%s, but %s.", target->GetNameEx(CRN_T1), GetVerb("miss"));
258 			msgwin.Add(xxbuf);
259 		}
260 	}
261 }
262 */
263 
MeleeAttack(XCreature * target,XItem * weapon)264 int XCreature::MeleeAttack(XCreature * target, XItem * weapon)
265 {
266 	assert(isValid());
267 
268 	//need for backstub.
269 	int wasEnemy = target->xai->isEnemy(this);
270 
271 	//~~~check this in future
272 	target->xai->onWasAttacked(this);
273 	target->stopAction();
274 	//~~~~~
275 
276 	bool vis1 = isVisible();
277 	bool vis2 = target->isVisible();
278 
279 	int res = 0;
280 	int tohit;
281 	if (weapon)
282 	{
283 		res += (wsk->GetUseTime(weapon->wt) * GetSpeed()) / 1000;
284 		tohit = GetHIT() + wsk->GetHIT(weapon->wt) + GetHITFHBonus(weapon);
285 
286 	} else
287 	{
288 		res += (wsk->GetUseTime(WSK_UNARMED) * GetSpeed()) / 1000;
289 		tohit = GetHIT() + wsk->GetHIT(WSK_UNARMED);
290 	}
291 
292 	int todv = target->GetDV();
293 
294 	if (tohit < 0)
295 	{
296 		todv -= tohit; // a - (-b) == a + b;
297 		tohit = 0;
298 	}
299 
300 	int p =  (int)(1000 * ((float)(tohit + 1) / (tohit + todv + 1)));
301 	int v = vRand() % 1000;
302 
303 	if (v < p)
304 	{
305 		//test for block
306 		int sb = target->GetShieldDVBonus();
307 		if (sb > 0 && vRand(todv) < sb)
308 		{
309 			if (vis1 || vis2)
310 			{
311 				msgwin.Add(GetNameEx(CRN_T1));
312 				msgwin.Add(GetVerb("attack"));
313 				msgwin.AddLast(target->GetNameEx(CRN_T1));
314 				msgwin.Add(target->GetNameEx(CRN_T1));
315 				msgwin.Add(target->GetVerb("block"));
316 				msgwin.Add(GetNameEx(CRN_T1));
317 				msgwin.Add("with");
318 				msgwin.Add(target->GetNameEx(CRN_T4));
319 				msgwin.Add("shield.");
320 			}
321 			target->wsk->UseSkill(WSK_SHIELD);
322 		} else
323 		{
324 			//get random part of body
325 			XBodyPart * txbp = target->GetRNDBodyPart();
326 
327 			//get initial damage
328 			int tdam;
329 			if (weapon)
330 			{
331 				tdam = weapon->dice.Throw() + wsk->GetDMG(weapon->wt) + GetDMGFHBonus(weapon) + GetDMG();
332 				wsk->UseSkill(weapon->wt);
333 			}
334 			else
335 			{
336 				tdam = dice.Throw() + GetDMG() + wsk->GetDMG(WSK_UNARMED);
337 				wsk->UseSkill(WSK_UNARMED);
338 			}
339 
340 			//get initial pv of armour
341 			int xpv = 0;
342 			if (txbp && txbp->Item())
343 				xpv = txbp->Item()->_PV;
344 
345 			if (txbp && txbp->bp_uin == BP_CLOAK)
346 			{
347 				XItem * xtmp = GetItem(BP_BODY);
348 				if (xtmp)
349 					xpv += xtmp->_PV;
350 			}
351 
352 			//write hiter name
353 			if (vis1 || vis2)
354 				msgwin.Add(GetNameEx(CRN_T1));
355 
356 			//critical hit
357 			int crtical_flag = 0;
358 			if ((vRand() % 100) < (2 + sk->GetLevel(SKT_FINDWEAKNESS)))
359 			{
360 				sk->UseSkill(SKT_FINDWEAKNESS);
361 				crtical_flag = 1;
362 				tdam *= 3;
363 			}
364 
365 			//backstab
366 			int backstab = 0;
367 			if (weapon && (!target->isCreatureVisible(this) || !wasEnemy))
368 			{
369 				if (vRand(100) < sk->GetLevel(SKT_BACKSTABBING) * 5 + 5)
370 				{
371 					backstab = 1;
372 					tdam *= 3;
373 					sk->UseSkill(SKT_BACKSTABBING, 3);
374 				}
375 			}
376 
377 			int dam = tdam;
378 			int p_flag = 0;
379 			if ((vRand() % 100) > (2 + sk->GetLevel(SKT_FINDWEAKNESS)) || xpv == 0)
380 				dam = tdam - xpv;
381 			else
382 			{
383 				sk->UseSkill(SKT_FINDWEAKNESS);
384 				p_flag = 1;
385 			}
386 
387 			if (vis1 || vis2)
388 			{
389 				if (weapon)
390 				{
391 					if (crtical_flag)
392 						msgwin.Add("exactly");
393 
394 					if(backstab)
395 						msgwin.Add(GetVerb("stab"));
396 					else
397 						msgwin.Add(GetVerb("hit"));
398 				} else
399 				{
400 					if (crtical_flag)
401 						msgwin.Add("exactly");
402 					msgwin.Add(GetVerb(GetMeleeAttackMsg()));
403 				}
404 
405 				if (p_flag)
406 				{
407 					msgwin.Add(target->GetNameEx(CRN_T1));
408 					if (target->isHero())
409 						msgwin.Add("penetrating a piece of armour.");
410 					else
411 						msgwin.Add("penetrating a piece of armour");
412 				} else
413 					if (target->isHero())
414 						msgwin.AddLast(target->GetNameEx(CRN_T1));
415 					else
416 						msgwin.Add(target->GetNameEx(CRN_T1));
417 			}
418 
419 
420 			if (!weapon)
421 			{
422 				int dmg = dam;
423 				for (XQList<MELEE_ATTACK>::iterator tit = melee_attack->begin(); tit != melee_attack->end(); tit++)
424 				{
425 					if (vRand(100) < (*tit).prob)
426 					{
427 						if ((*tit).r_attack > 0)
428 							dam += target->CauseEffect(this, dmg, (*tit).r_attack);
429 						else
430 							;
431 					}
432 				}
433 				AttackCreature(target, dam);
434 			} else
435 			{
436 				dam = target->CauseEffect(dam, weapon->brt);
437 				AttackCreature(target, dam);
438 			}
439 		}
440 	} else
441 	{
442 		if (vis1 || vis2)
443 		{
444 			msgwin.Add(GetNameEx(CRN_T1));
445 			msgwin.Add(GetVerb("attack"));
446 			char xxbuf[256];
447 			sprintf(xxbuf, "%s, but %s.", target->GetNameEx(CRN_T1), GetVerb("miss"));
448 			msgwin.Add(xxbuf);
449 		}
450 	}
451 
452 	return res;
453 }
454 
455 
InflictDamage(DAMAGE_DATA * pData)456 int XCreature::InflictDamage(DAMAGE_DATA * pData)
457 {
458 	assert(pData->target->isValid());
459 	if (pData->attacker)
460 		pData->target->xai->onWasAttacked(pData->attacker);
461 	pData->target->stopAction();
462 
463 	int damage = pData->damage;
464 	damage -= pData->target->_PV; // always - monster PV
465 
466 	if (damage <= 0)
467 	{
468 		if (pData->target->isHero())
469 		{
470 			if (pData->attacker_name)
471 				msgwin.Add(pData->attacker_name);
472 			else
473 				msgwin.Add(pData->attacker->GetNameEx(CRN_T1));
474 			msgwin.Add("does not manage to harm you.");
475 		} else if (pData->target->isVisible())
476 		{
477 			msgwin.Add("but does not manage to harm");
478 			msgwin.AddLast(pData->target->GetNameEx(CRN_T3));
479 		}
480 	} else
481 	{
482 		pData->target->_HP -= damage;
483 
484 		if (pData->target->_HP > 0)
485 		{
486 			if (pData->target->isVisible() && !pData->target->isHero())
487 			{
488 				char buf[256];
489 				sprintf(buf, "and %s %s.",  pData->target->GetWoundMsg(1), pData->target->GetNameEx(CRN_T3));
490 				msgwin.Add(buf);
491 			}
492 		} else
493 		{ // and kill IT!!!
494 			if (pData->target->isVisible() && !pData->target->isHero())
495 			{
496 				msgwin.Add("and");
497 				if(pData->target->creature_class & CR_UNDEAD)
498 				{
499 					if (pData->attacker_name)
500 						msgwin.Add("destroys");
501 					else
502 						msgwin.Add(pData->attacker->GetVerb("destroys"));
503 				}
504 				else
505 				{
506 					if (pData->attacker_name)
507 						msgwin.Add("kills");
508 					else
509 						msgwin.Add(pData->attacker->GetVerb("kills"));
510 				}
511 				msgwin.AddLast(pData->target->GetNameEx(CRN_T3));
512 			}
513 			pData->target->Die(pData->attacker);
514 			cr_kiled++; //temporary counter special for statistic
515 			return 1;
516 		}
517 	}
518 	return 0;
519 }
520 
AttackCreature(XCreature * target,int dmg)521 int XCreature::AttackCreature(XCreature * target, int dmg)
522 {
523 	assert(isValid());
524 	target->xai->onWasAttacked(this);
525 	target->stopAction();
526 	if (target->onAttacked(this, dmg))
527 	{
528 		target->Die(this);
529 		cr_kiled++; //temporare counter special for statistic
530 		return 1;
531 	}
532 	return 0;
533 }
534 
AttackCreature(XCreature * target,int dmg,RESISTANCE tr,const char * magic_name)535 int XCreature::AttackCreature(XCreature * target, int dmg, RESISTANCE tr, const char * magic_name)
536 {
537 	assert(isValid());
538 	target->xai->onWasAttacked(this);
539 	target->stopAction();
540 	if (target->onAttackedByMagic(this, dmg, tr, magic_name))
541 	{
542 		target->Die(this);
543 		cr_kiled++; //temporare counter special for statistic
544 		return 1;
545 	}
546 	return 0;
547 }
548 
549 
onMagicDamage(int dmg,RESISTANCE tr)550 int XCreature::onMagicDamage(int dmg, RESISTANCE tr)
551 {
552 	assert(isValid());
553 	int damage = dmg - (dmg * GetResistance(tr)) / 100;
554 	return damage < 0 ? 0 : damage;
555 }
556 
CauseEffect(int dmg,BRAND_TYPE brt)557 int XCreature::CauseEffect(int dmg, BRAND_TYPE brt)
558 {
559 	int damage = dmg;
560 
561 	if (brt > BR_NONE)
562 	{
563 		if (brt & BR_FIRE)
564 		{
565 			damage += onMagicDamage(dmg, R_FIRE);
566 		}
567 		if (brt & BR_COLD)
568 		{
569 			damage += onMagicDamage(dmg, R_COLD);
570 		}
571 		if (brt & BR_DEMONSLAYER && creature_class & CR_DEMON)
572 		{
573 			damage += dmg * 3;
574 		}
575 		if (brt & BR_ORCSLAYER && creature_class & CR_ORC)
576 		{
577 			damage += dmg * 3;
578 		}
579 	}
580 	return damage;
581 }
582 
CauseEffect(XCreature * attacker,int dmg,RESISTANCE tr)583 int XCreature::CauseEffect(XCreature * attacker, int dmg, RESISTANCE tr)
584 {
585 	int damage = dmg - (dmg * GetResistance(tr)) / 100;
586 	damage = damage < 0 ? 0 : damage;
587 
588 	if (damage > 0)
589 	{
590 		switch (tr)
591 		{
592 			case R_WHITE:
593 			case R_BLACK:
594 			case R_FIRE:
595 			case R_WATER:
596 			case R_AIR:
597 			case R_EARTH:
598 			case R_ACID:
599 			case R_COLD:
600 				break;
601 			case R_POISON:
602 				md->Add(MOD_POISON, damage, this, attacker);
603 				break;
604 			case R_DISEASE:
605 				md->Add(MOD_DISEASE, damage, this, attacker);
606 				break;
607 			case R_PARALYSE:
608 				md->Add(MOD_PARALYSE, damage, this, attacker);
609 				break;
610 			case R_STUN:
611 				md->Add(MOD_STUN, damage, this, attacker);
612 				break;
613 			case R_CONFUSE:
614 				md->Add(MOD_CONFUSE, damage, this, attacker);
615 				break;
616 			case R_BLIND:
617 //				md->Add(MOD_BLIND, damage, this, attacker);
618 				break;
619 			default:
620 				return 0;
621 		}
622 	}
623 	return damage;
624 }
625 
onAttacked(XCreature * attacker,int dmg)626 int XCreature::onAttacked(XCreature * attacker, int dmg)
627 {
628 	assert(isValid());
629 	int damage = dmg;
630 	damage -= _PV; // always - monster PV
631 
632 	if (damage > 0)
633 		_HP -= damage;
634 
635 	if (_HP > 0)
636 	{
637 		if (l->map->GetVisible(x, y))
638 		{
639 			char buf[256];
640 			if (damage <= 0)
641 			{
642 				if (isHero())
643 				{
644 					msgwin.Add(attacker->GetNameEx(CRN_T1));
645 					msgwin.Add("does not manage to harm");
646 					msgwin.AddLast(GetNameEx(CRN_T3));
647 				} else
648 				{
649 					msgwin.Add("but does not manage to harm");
650 					msgwin.AddLast(GetNameEx(CRN_T3));
651 				}
652 			} else if (!isHero())
653 			{
654 				sprintf(buf, "and %s %s.",  GetWoundMsg(1), GetNameEx(CRN_T3));
655 				msgwin.Add(buf);
656 			}
657 		}
658 
659 		if (vRand() % 1000 < 300)
660 		{
661 			md->Add(MOD_WOUND, damage / 4, this, attacker);
662 		}
663 
664 		if (vRand() % 1000 < 250)
665 		{
666 			md->Add(MOD_STUN, damage / 4, this, attacker);
667 		}
668 
669 		if (vRand() % 1000 < 150)
670 		{
671 			md->Add(MOD_CONFUSE, damage / 4, this, attacker);
672 		}
673 
674 
675 		return 0;
676 	} else
677 	{ // and kill IT!!!
678 		if (l->map->GetVisible(x, y) && !isHero())
679 		{
680 			msgwin.Add("and");
681 			if(creature_class & CR_UNDEAD)
682 				msgwin.Add(attacker->GetVerb("destroy"));
683 			else
684 				msgwin.Add(attacker->GetVerb("kill"));
685 			msgwin.AddLast(GetNameEx(CRN_T3));
686 		}
687 		return 1;
688 	}
689 }
690 
691 
onAttackedByMagic(XCreature * attacker,int dmg,RESISTANCE tr,const char * magic_name)692 int XCreature::onAttackedByMagic(XCreature * attacker, int dmg, RESISTANCE tr, const char * magic_name)
693 {
694 	char buf[256];
695 	int  damage = onMagicDamage(dmg, tr) - _PV;
696 
697 	if (damage > 0)
698 		_HP -= damage;
699 
700 	if (_HP > 0)
701 	{
702 		if (l->map->GetVisible(x, y))
703 		{
704 			if (damage <= 0)
705 			{
706 				if (isHero())
707 				{
708 					msgwin.Add(magic_name);
709 					msgwin.Add("does not manage to harm");
710 					msgwin.AddLast(GetNameEx(CRN_T3));
711 				} else
712 				{
713 					msgwin.Add("but does not manage to harm");
714 					msgwin.AddLast(GetNameEx(CRN_T3));
715 				}
716 			} else if (!isHero())
717 			{
718 				sprintf(buf, "and %s %s.",  attacker->GetWoundMsg(1), GetNameEx(CRN_T3));
719 				msgwin.Add(buf);
720 			}
721 		}
722 		return 0;
723 	} else
724 	{ // and kill IT!!!
725 		if (l->map->GetVisible(x, y))
726 		{
727 			msgwin.Add("and");
728 			if(creature_class & CR_UNDEAD)
729 				msgwin.Add(attacker->GetVerb("destroy"));
730 			else
731 				msgwin.Add(attacker->GetVerb("kill"));
732 			msgwin.AddLast(GetNameEx(CRN_T3));
733 		}
734 		return 1;
735 	}
736 }
737 
738 
MissileFlight(MF_DATA * mfd)739 MF_RESULT XCreature::MissileFlight(MF_DATA * mfd)
740 {
741 	XMap * tmap = mfd->l->map;
742 	int range = mfd->max_range;
743 	float fdx = float(mfd->ex - mfd->sx);
744 	float fdy = float(mfd->ey - mfd->sy);
745 	float xrng = (float)sqrt(fdx * fdx + fdy * fdy);
746 	float cos_alpha = 0, sin_alpha = 0;
747 	if (xrng > 0)
748 	{
749 		cos_alpha = fdx / xrng;
750 		sin_alpha = fdy / xrng;
751 	}
752 
753 	float mx = (float)mfd->sx;
754 	float my = (float)mfd->sy;
755 	int epx;
756 	int epy;
757 	int fl_range = 0;
758 
759 	bool self_flag = false;
760 	if (fdx == fdy && fdy == 0.0)
761 		self_flag = true;
762 
763 	while ((range > 0 && (fabs(mfd->ex - mx) >= 0.5f || fabs(mfd->ey - my) >= 0.5f)) || self_flag)
764 	{
765 		self_flag = false;
766 		range--;
767 		fl_range++;
768 		float nmx = mx + cos_alpha;
769 		float nmy = my + sin_alpha;
770 		epx = vRound(nmx);
771 		epy = vRound(nmy);
772 
773 		if (tmap->GetMovability(epx, epy) >= MO_WALL)
774 		{
775 			epx = vRound(mx);
776 			epy = vRound(my);
777 			break;
778 		}
779 
780 		if (tmap->GetVisible(epx, epy) && __animation_flag)
781 		{
782 			tmap->Put(main_creature);
783 			if (mfd->arrow_type == MFT_BALL)
784 				tmap->PutChar(epx, epy, '*', mfd->arrow_color);
785 			else
786 			{
787 				int ttx = vRound(nmx - mx);
788 				int tty = vRound(nmy - my);
789 				if (ttx * tty == 1)
790 					tmap->PutChar(epx, epy, '\\', mfd->arrow_color);
791 				else if (ttx * tty == -1)
792 					tmap->PutChar(epx, epy, '/', mfd->arrow_color);
793 				else if (ttx == 0)
794 					tmap->PutChar(epx, epy, '|', mfd->arrow_color);
795 				else if (tty == 0)
796 					tmap->PutChar(epx, epy, '-', mfd->arrow_color);
797 			}
798 
799 			vRefresh();
800 			vDelay(__animation_flag);
801 		}
802 
803 		mx = nmx;
804 		my = nmy;
805 
806 		XCreature * tgt = tmap->GetMonster(epx, epy);
807 		if (tgt)
808 		{
809 			int tdv = tgt->GetDV() + fl_range * tgt->GetDV() / 4;
810 			int tht = mfd->to_hit > 0 ? mfd->to_hit : 1;
811 			if (tdv < 0)
812 			{
813 				tht -= tdv;
814 				tdv = 0;
815 			}
816 			int p =  (100 * tht + 1) / (tht + tdv + 1) - tdv;
817 			if (vRand(100) <= p)
818 			{
819 				mfd->pt.x = epx;
820 				mfd->pt.y = epy;
821 				//check for shield
822 				int sb = tgt->GetShieldDVBonus();
823 				sb += fl_range * sb / 4;
824 				if (sb > 0 && vRand(tdv) < sb)
825 				{
826 					tgt->wsk->UseSkill(WSK_SHIELD);
827 					return MF_BLOCK;
828 				}
829 				return MF_HIT;
830 			}
831 		}
832 	}
833 
834 	mfd->pt.x = epx;
835 	mfd->pt.y = epy;
836 	return MF_AVOID;
837 }
838 
839 
840 
continueUseItem()841 int XCreature::continueUseItem()
842 {
843 	assert(isValid());
844 	XItem * tool = (XItem *)action_data.item.get();
845 	assert(tool->im & IM_TOOL);
846 	RESULT res = tool->onUse(UIS_CONTINUE, this);
847 	if (res != CONTINUE)
848 	{
849 		action_data.action = A_MOVE;
850 		action_data.item = NULL;
851 	}
852 	return 1;
853 }
854 
UseItem(XItem * item)855 int XCreature::UseItem(XItem * item)
856 {
857 	assert(isValid());
858 	RESULT res = item->onUse(UIS_BEGIN, this);
859 	if (res)
860 	{
861 		if (res == CONTINUE)
862 		{
863 			action_data.action = A_USETOOL;
864 			action_data.item = item;
865 			return 1;
866 		} else
867 			return 1;
868 	} else
869 		return 0;
870 
871 	return 1;
872 }
873 
TargetOp(TARGET_REASON tr,XTREQUEST * rq,XTRESPONSE * rp)874 int XCreature::TargetOp(TARGET_REASON tr, XTREQUEST * rq, XTRESPONSE * rp)
875 {
876 	return 0;
877 }
878 
Sacrifice(XItem * item)879 void XCreature::Sacrifice(XItem * item)
880 {
881 	assert(isValid());
882 	religion.SacrificeItem(this, item, D_UNKNOWN);
883 }
884 
885 
isCreatureVisible(XCreature * cr)886 int XCreature::isCreatureVisible(XCreature * cr)
887 {
888 	assert(isValid());
889 	if ((cr->GetResistance(R_INVISIBLE) <= GetResistance(R_SEEINVISIBLE)) || cr == this)
890 		return 1;
891 	else
892 		return 0;
893 
894 }
895 
isVisible()896 bool XCreature::isVisible()
897 {
898 	assert(isValid());
899 	if (l->map->GetVisible(x, y))
900 	{
901 		if (XCreature::main_creature->isCreatureVisible(this))
902 			return true;
903 		else
904 			return false;
905 	} else
906 		return false;
907 }
908 
909