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 "los.h"
23 #include "xarchive.h"
24 #include "other_misc.h"
25 #include "xapi.h"
26 #include "modifer.h"
27 #include "xshedule.h"
28 #include "game.h"
29 
30 // a creature which is currently being displayed
31 XCreature * XCreature::main_creature = NULL;
32 
Store(XFile * f)33 void ACTION_DATA::Store(XFile * f)
34 {
35 	f->Write(&action, sizeof(ACTION));
36 	item.Store(f);
37 }
38 
Restore(XFile * f)39 void ACTION_DATA::Restore(XFile * f)
40 {
41 	f->Read(&action, sizeof(ACTION));
42 	item.Restore(f);
43 }
44 
XCreature()45 XCreature::XCreature()
46 {
47 	total_cr++;
48 
49 	added_DV       = 0;
50 	added_PV       = 0;
51 	added_HIT      = 0;
52 	added_DMG      = 0;
53 	added_RNG      = 0;
54 	added_HP       = 0;
55 	added_PP       = 0;
56 	carried_weight = 0;
57 
58 	base_nutrio = 500;
59 	nutrio = 5000;
60 	nutrio_speed = 10;
61 
62 	_EXP = 0;
63 	level = 1;
64 	RNG = 3;
65 
66 	creature_size = CS_NORMAL;
67 	creature_person_type = CPT_HE;
68 
69 //   reg_count = 10;
70 //   reg_max = 10;
71 
72 	im = IM_CREATURE;
73 	xai = new XStandardAI(this);
74 //   ait = AI_SIMPLE;
75 	md = new XModifer();
76 	m = new XMagic();
77 	sk = new XSkills();
78 	wsk = new XWarSkills();
79 
80 	weight = 1000;
81 
82 	creature_class = CR_NONE;
83 
84 	tactics = TS_NORMAL;
85 	group_id = GID_NONE;
86 	food_feeling = FF_NORMAL;
87 }
88 
Invalidate()89 void XCreature::Invalidate()
90 {
91 //	INVALIDATE_ENTER();
92 
93 	contain.KillAll();
94 	components.KillAll();
95 
96 //	xai->Invalidate(); //deleting AI.
97 
98 	delete sk;
99 	sk = NULL;
100 
101 	if (md)
102 	{
103 		delete md;
104 		md = NULL;
105 	}
106 	delete m;
107 	m = NULL;
108 
109 	delete wsk;
110 	wsk = NULL;
111 
112 //   assert(aligment);
113 //   if (aligment)
114 //    delete aligment;
115 
116 	total_cr--;
117 
118 	XBaseObject::Invalidate();
119 //	INVALIDATE_LEAVE();
120 }
121 
Regenerate()122 void XCreature::Regenerate()
123 {
124 	if (_HP < GetMaxHP())
125 	{
126 		XSkill * xsk = sk->GetSkill(SKT_HEALING);
127 		int val = 1;
128 		if (xsk)
129 			val += xsk->GetLevel();
130 		if (vRand(20) < val)
131 		{
132 			int rest = MAX_HP / 100 + 1;
133 			if (xsk)
134 			{
135 				xsk->UseSkill();
136 				rest *= vRand((int)xsk->GetMastery() + 1);
137 			}
138 			onHeal(rest);
139 		}
140 	}
141 
142 	if (_PP < GetMaxPP())
143 	{
144 		XSkill * xsk = sk->GetSkill(SKT_CONCENTRATION);
145 		int val = 1;
146 		if (xsk)
147 			val += xsk->GetLevel();
148 		if (vRand() % 20 < val)
149 		{
150 			int rest = MAX_PP / 100 + 1;
151 			if (xsk)
152 			{
153 				xsk->UseSkill();
154 				rest *= (int)xsk->GetMastery();
155 			}
156 			onRestorePP(rest);
157 		}
158 	}
159 }
160 
onHeal(int _hp)161 int XCreature::onHeal(int _hp)
162 {
163 	int last_HP = _HP;
164 	int max_HP = GetMaxHP();
165 	_HP += _hp;
166 	if (_HP > max_HP)
167 		_HP = max_HP;
168 	return last_HP >= max_HP ? 0 : 1;
169 }
170 
onRestorePP(int _pp)171 int XCreature::onRestorePP(int _pp)
172 {
173 	int last_PP = _PP;
174 	int max_PP = GetMaxPP();
175 	_PP += _pp;
176 	if (_PP > max_PP)
177 		_PP = max_PP;
178 	return last_PP >= max_PP ? 0 : 1;
179 }
180 
stopAction()181 int XCreature::stopAction()
182 {
183 	if (action_data.action == A_USETOOL)
184 	{
185 		action_data.item->onUse(UIS_STOP, this);
186 	} else
187 	{
188 		if (action_data.item)
189 		{
190 			contain.Add(action_data.item.get());
191 		}
192 	}
193 	action_data.action = A_MOVE;
194 	action_data.item = NULL;
195 	isDisturb = 0; //prevents hero to continue automove when attaked by ghosts...
196 	return 1;
197 }
198 
continueEat()199 int XCreature::continueEat()
200 {
201 	XAnyFood * food = (XAnyFood *)action_data.item.get();
202 	assert(food->im & IM_FOOD);
203 	int res = food->onEat(this);
204 	if (res != 2)
205 	{
206 		action_data.action = A_MOVE;
207 		action_data.item = NULL;
208 	}
209 	return 1;
210 }
211 
212 
Eat(XAnyFood * food)213 int XCreature::Eat(XAnyFood * food)
214 {
215 	int res = food->onEat(this);
216 	if (res)
217 	{
218 		if (res == 2)
219 		{
220 			action_data.action = A_EAT;
221 			action_data.item = food;
222 			return 1;
223 		} else
224 			return 1;
225 	} else
226 		return 0;
227 }
228 
DecNutrio()229 int XCreature::DecNutrio()
230 {
231 	nutrio -= nutrio_speed;
232 	if (nutrio < 0)
233 	{
234 		Die(NULL);
235 		return 0;
236 	}
237 	return 1;
238 }
239 
Run()240 int XCreature::Run()
241 {
242 // HideOldView();
243 	AddRef();
244 
245 	if (action_data.action == A_EAT)
246 	{
247 		continueEat();
248 	} else
249 	if (action_data.action == A_READ)
250 	{
251 		continueRead();
252 	} else
253 	if (action_data.action == A_USETOOL)
254 	{
255 		continueUseItem();
256 	} else
257 	{
258 		NewMove();
259 	}
260 
261 	if (md && md->Run(this))
262 	{
263 		int atletics = sk->GetLevel(SKT_ATHLETICS);
264 		if (GetCarryState() >= CSTATE_STRAINED)
265 		{
266 			if (vRand(3000 / (5 + atletics)) == 0)
267 			{
268 				GainAttr(S_STR, 1);
269 				sk->UseSkill(SKT_ATHLETICS, 10);
270 			}
271 		} else
272 			if (vRand(6000 / (5 + atletics)) == 0)
273 			{
274 				GainAttr(S_DEX, 1);
275 				sk->UseSkill(SKT_ATHLETICS, 10);
276 			}
277 		DoMove();
278 	}
279 
280 	if (ttm <= 0 && isValid())
281 		ttm += GetSpeed();
282 
283 	Release();
284 // ShowNewView();
285 	return 1;
286 }
287 
DoMove()288 void XCreature::DoMove()
289 {
290 	if (l->map->XGetMovability(nx, ny) == 2 && (nx != x || ny != y))
291 		Attack();
292 	else if (TestMove() || (x == nx && y == ny))
293 	{
294 		Move();
295 	}
296 }
297 
Move()298 void XCreature::Move()
299 {
300 	XAnyPlace * new_place = l->map->GetPlace(nx, ny);
301 	XAnyPlace * old_place = l->map->GetPlace(x, y);
302 
303 	if (old_place && new_place)
304 	{
305 		new_place->onCreatureMove(this);
306 	}
307 	else if (new_place && !old_place)
308 	{
309 		new_place->onCreatureEnter(this);
310 	}
311 	else if (old_place && !new_place)
312 	{
313 		old_place->onCreatureLeave(this);
314 	}
315 
316 	//check if die when moved.
317 	if (!isValid())
318 		return;
319 
320 	Regenerate();
321 	l->map->ResMonster(x, y);
322 	l->map->SetMonster(nx, ny, this);
323 
324 	int flag = 1;
325 	if (x == nx && y == ny)
326 		flag = 0;
327 	x = nx;
328 	y = ny;
329 
330 	if (flag)
331 	{
332 		XMapObject * obj = l->map->GetSpecial(x, y);
333 		if (obj)
334 		{
335 			if (obj->im == IM_TRAP)
336 				((XTrap *)obj)->MoveIn(this);
337 
338 			if (obj->im == IM_TELEPORT)
339 				((XTeleport *)obj)->MoveIn(this);
340 
341 		}
342 	}
343 
344 }
345 
346 //////////////////////////////////////////////////////////////////////////////
347 
348 struct opaque_info
349 {
350 	XCreature * mover;
351 	XMap * map;
352 };
353 
is_grid_viewable(void * opaque,int x,int y)354 static int is_grid_viewable(void * opaque, int x, int y)
355 {
356 	opaque_info * info = (opaque_info *)opaque;
357 	XCreature * mover = info->mover;
358 	XCreature * tcr = mover->l->map->GetMonster(x, y);
359 	if ((tcr && tcr != mover && mover->isCreatureVisible(tcr) && tcr->xai->isEnemy(mover)))
360 	{
361 		mover->isDisturb = 0;
362 	}
363 
364 	return (mover->l->map->GetVisibility(x, y) != 0);
365 }
366 
set_grid_visible(void * opaque,int x,int y,int radius,int see_center)367 static int set_grid_visible(void * opaque, int x, int y, int radius, int see_center)
368 {
369 	opaque_info * info = (opaque_info *)opaque;
370 	XMap * map = info->map;
371 	if(!see_center && (map->GetVisibility(x, y) != 0)) return is_grid_viewable(opaque, x, y);
372 	map->SetVisible(x, y);
373 	return is_grid_viewable(opaque, x, y);
374 }
375 
set_grid_invisible(void * opaque,int x,int y,int radius,int see_center)376 static int set_grid_invisible(void * opaque, int x, int y, int radius, int see_center)
377 {
378 	opaque_info * info = (opaque_info *)opaque;
379 	info->map->ResVisible(x, y);
380 	return is_grid_viewable(opaque, x, y);
381 }
382 
HideOldView()383 void XCreature::HideOldView()
384 {
385 	opaque_info info = { this, l->map };
386 //   if (this == main_creature)
387 //   {
388 		LineOfSight(
389 			x,
390 			y,
391 			GetVisibleRadius(),
392 			&info,
393 			set_grid_invisible);
394 //   }
395 }
396 
ShowNewView()397 void XCreature::ShowNewView()
398 {
399 	opaque_info info = { this, l->map };
400 //   if (this == main_creature)
401 //   {
402 		LineOfSight(
403 			nx,
404 			ny,
405 			GetVisibleRadius(),
406 			&info,
407 			set_grid_visible);
408 //   }
409 }
410 
PutStatus()411 void XCreature::PutStatus()
412 {
413 	char buf[250];
414 
415 	vGotoXY(0, size_y - 3);
416 	vSetAttr(xLIGHTGRAY);
417 	vPutS(name);
418 
419 	if (XGame::isAvgPV)
420 	{
421 		sprintf(buf, "DV/PV:%d/%d  ", GetDV(), GetPV());
422 	} else
423 		sprintf(buf, "DV/PV:%d/%d  ", GetDV(), GetPV());
424 	vGotoXY(0, size_y - 2);
425 	vPutS(buf);
426 
427 	sprintf(buf, "St:%-2d Dx:%-2d To:%-2d Le:%-2d Wi:%-2d Ma:%-2d Pe:%-2d Ch:%-2d Sp:%-3d L:%s", GetStats(S_STR), GetStats(S_DEX), GetStats(S_TOU),
428 				GetStats(S_LEN),GetStats(S_WIL), GetStats(S_MAN), GetStats(S_PER), GetStats(S_CHR), 100000 / GetSpeed(), l->GetBriefName());
429 	vGotoXY(15, size_y - 3);
430 	vPutS(buf);
431 	vClrEol();
432 
433 	sprintf(buf, "HP:%d(%d)  PP:%d(%d)  ", _HP, GetMaxHP(), _PP, GetMaxPP());
434 	vGotoXY(14, size_y - 2);
435 	vPutS(buf);
436 
437 	sprintf(buf, "Exp(%d)%lu", level, _EXP);
438 	vGotoXY(38, size_y - 2);
439 	vPutS(buf);
440 
441 #ifdef _DEBUG
442 	sprintf(buf, "x:y[%d:%d]", x, y);
443 	vGotoXY(60, size_y - 2);
444 	vPutS(buf);
445 #endif
446 
447 
448 	if (nutrio > base_nutrio * 18)
449 	{
450 		sprintf(buf, MSG_RED "overfed! ");
451 	} else
452 		if (nutrio > base_nutrio * 14)
453 		{
454 			sprintf(buf, "bloated ");
455 		} else
456 			if (nutrio > base_nutrio * 10 && nutrio <= base_nutrio * 14)
457 			{
458 				sprintf(buf, "satiated ");
459 			} else
460 				if (nutrio > base_nutrio * 8 && nutrio <= base_nutrio * 10)
461 				{
462 					sprintf(buf, "");
463 				} else
464 					if (nutrio > base_nutrio * 6 && nutrio <= base_nutrio * 8)
465 					{
466 						sprintf(buf, "hungry ");
467 					} else
468 						if (nutrio > base_nutrio * 4 && nutrio <= base_nutrio * 6)
469 						{
470 							sprintf(buf, MSG_YELLOW "very hungry ");
471 							if (action_data.action != A_EAT)
472 								stopAction();
473 						} else
474 							if (nutrio > base_nutrio && nutrio <= base_nutrio * 4)
475 							{
476 								sprintf(buf, MSG_RED "weak ");
477 								if (action_data.action != A_EAT)
478 									stopAction();
479 							} else
480 								if (nutrio <= base_nutrio)
481 								{
482 									sprintf(buf, MSG_RED "dying! ");
483 									if (action_data.action != A_EAT)
484 										stopAction();
485 								}
486 
487 
488 	strcat(buf, MSG_LIGHTGRAY);
489 	vGotoXY(0, size_y - 1);
490 	vPutS(buf);
491 
492 	switch (action_data.action)
493 	{
494 		case A_READ		: vPutS("[reading] "); break;
495 		case A_EAT		: vPutS("[eating] "); break;
496 		case A_USETOOL	: vPutS("[using tool] "); break;
497 	}
498 
499 	CARRY_STATE cstate = GetCarryState();
500 	switch (cstate)
501 	{
502 		case CSTATE_NORMAL: break;
503 		case CSTATE_BURDENED:   vPutS("burdened "); break;
504 		case CSTATE_STRAINED:   vPutS("strained "); break;
505 		case CSTATE_OVERBURDEN: vPutS("overburdened "); break;
506 		default : break;
507 	};
508 
509 	md->toString(buf);
510 	vPutS(buf);
511 	vClrEol();
512 	vSetAttr(xLIGHTGRAY);
513 }
514 
NewMove()515 void XCreature::NewMove()
516 {
517 	xai->Move();
518 }
519 
GetSpeed()520 int XCreature::GetSpeed()
521 {
522 	int speed = ttmb;
523 	if (nutrio < base_nutrio * 8 && nutrio > base_nutrio * 4)
524 		speed = (int)(speed * 0.92);
525 	else
526 		if (nutrio < base_nutrio * 2)
527 			speed = (int)(speed * 1.2);
528 		else
529 			if (nutrio > base_nutrio * 12)
530 				speed = (int)(speed * 1.1);
531 
532 	int str = s->Get(S_STR);
533 	if (carried_weight >= str * 120 && carried_weight < str * 200)
534 	{
535 		speed = (int)(speed * 1.1);
536 	} else
537 		if (carried_weight >= str * 200 && carried_weight < str * 280)
538 		{
539 			speed = (int)(speed * 1.3);
540 		} else
541 			if (carried_weight >= str * 280)
542 			{
543 				speed = (int)(speed * 2);
544 			}
545 
546 	return speed;
547 }
548 
TestMove()549 int XCreature::TestMove()
550 {
551 	if (l->map->XGetMovability(nx, ny) == 0)
552 		return 1;
553 	else
554 		return 0;
555 }
556 
GetHIT()557 int XCreature::GetHIT()
558 {
559 	int tht = _HIT + added_HIT;
560 	return tht + GetTacticsHITBonus();
561 }
562 
GetDV(XCreature * attacker)563 int XCreature::GetDV(XCreature * attacker)
564 {
565 	int tdv = added_DV + _DV + GetTacticsDVBonus() + GetShieldDVBonus();
566 	return tdv < 1 ? 1 : tdv;
567 }
568 
GetShieldDVBonus()569 int XCreature::GetShieldDVBonus()
570 {
571 	XList<XBodyPart *>::iterator xbp = components.begin();
572 	while (xbp != components.end())
573 	{
574 		XItem * i = xbp->Item();
575 		if (i && i->im == IM_SHIELD)
576 		{
577 			int shld_skl = wsk->GetDV(WSK_SHIELD);
578 			int shield_dv = i->_DV;
579 			if (i->_DV < shld_skl)
580 				return i->_DV + shield_dv;
581 			else
582 				return shld_skl + shield_dv;
583 		}
584 		xbp++;
585 	}
586 	return 0;
587 }
588 
GetDMG()589 int XCreature::GetDMG()
590 {
591 //this function don't include 'hand' damage
592 //i.e. calculate additional damage. i.e. +dmg
593 	return added_DMG + GetTacticsDMGBonus();
594 }
595 
GetTacticsDVBonus()596 int XCreature::GetTacticsDVBonus()
597 {
598 	switch (tactics)
599 	{
600 		case TS_COWARD		: return (3 * (GetStats(S_DEX) + sk->GetLevel(SKT_TACTICS))) / 2; break;
601 		case TS_DEFENSIVE	: return GetStats(S_DEX) + sk->GetLevel(SKT_TACTICS); break;
602 		case TS_NORMAL		: return (2 * GetStats(S_DEX)) / 3 + sk->GetLevel(SKT_TACTICS); break;
603 		case TS_AGRESSIVE	: return GetStats(S_DEX) / 3 + sk->GetLevel(SKT_TACTICS); break;
604 		case TS_BERSERKER	: return GetStats(S_DEX) / 10 + sk->GetLevel(SKT_TACTICS); break; //compensate DV given by Dx
605 		default				: assert(0);
606 	}
607 	return 0;
608 }
609 
GetTacticsHITBonus()610 int XCreature::GetTacticsHITBonus()
611 {
612 	switch (tactics)
613 	{
614 		case TS_COWARD		: return GetStats(S_DEX) / 10 + sk->GetLevel(SKT_TACTICS) - 5; break;
615 		case TS_DEFENSIVE	: return GetStats(S_DEX) / 7 + sk->GetLevel(SKT_TACTICS) - 3; break;
616 		case TS_NORMAL		: return GetStats(S_DEX) / 4 + sk->GetLevel(SKT_TACTICS); break;
617 		case TS_AGRESSIVE	: return GetStats(S_DEX) / 3 + sk->GetLevel(SKT_TACTICS); break;
618 		case TS_BERSERKER	: return GetStats(S_DEX) / 2 + sk->GetLevel(SKT_TACTICS); break;
619 		default				: assert(0);
620 	}
621 	return 0;
622 }
623 
GetTacticsDMGBonus()624 int XCreature::GetTacticsDMGBonus()
625 {
626 	switch (tactics)
627 	{
628 		case TS_COWARD		: return GetStats(S_STR) / 20 + sk->GetLevel(SKT_TACTICS) - 3; break;
629 		case TS_DEFENSIVE	: return GetStats(S_STR) / 10 + sk->GetLevel(SKT_TACTICS)- 1; break;
630 		case TS_NORMAL		: return GetStats(S_STR) / 7 + sk->GetLevel(SKT_TACTICS); break;
631 		case TS_AGRESSIVE	: return GetStats(S_STR) / 5 + sk->GetLevel(SKT_TACTICS); break;
632 		case TS_BERSERKER	: return GetStats(S_STR) / 2 + sk->GetLevel(SKT_TACTICS); break;
633 		default				: assert(0);
634 	}
635 	return 0;
636 }
637 
GetPV()638 int XCreature::GetPV()
639 {
640 	return _PV + added_PV + GetStats(S_TOU) / 10;
641 }
642 
643 
GainAttr(STATS st,int val)644 int XCreature::GainAttr(STATS st, int val)
645 {
646 	int cur = s->Get(st);
647 	int max = max_stats.Get(st);
648 
649 	if (val > 0)
650 	{
651 		if (cur < max)
652 		{
653 			if (cur + val > max) val = max - cur;
654 			s->Modify(st, val);
655 			if (isHero())
656 			{
657 				switch (st)
658 				{
659 					case S_STR: msgwin.Add("You feel stronger!"); break;
660 					case S_DEX: msgwin.Add("You are becoming more graceful!"); break;
661 					case S_TOU: msgwin.Add("Your health increases!"); break;
662 					case S_MAN: msgwin.Add("You feel power surging through your body!"); break;
663 					case S_WIL: msgwin.Add("You feel more powerfull!"); break;
664 					case S_LEN: msgwin.Add("You feel smarter!"); break;
665 					case S_PER: msgwin.Add("You feel more perceptive!"); break;
666 					case S_CHR: msgwin.Add("Your beauty improves!"); break;
667 				}
668 			}
669 			return 1;
670 		}
671 	} else
672 	{
673 		if (cur - val > 1)
674 		{
675 			s->Modify(st, val);
676 			if (isHero())
677 			{
678 				switch (st)
679 				{
680 					case S_STR: msgwin.Add("Your muscles weaken!"); break;
681 					case S_DEX: msgwin.Add("You feel clumsy!"); break;
682 					case S_TOU: msgwin.Add("You feel like you might be getting sick!"); break;
683 					case S_MAN: msgwin.Add("You feel power draining from your body!"); break;
684 					case S_WIL: msgwin.Add("You feel diminished!"); break;
685 					case S_LEN: msgwin.Add("Thinking becomes more difficult!"); break;
686 					case S_PER: msgwin.Add("Your senses dull!"); break;
687 					case S_CHR: msgwin.Add("Your features harden!"); break;
688 				}
689 			}
690 			return 1;
691 		}
692 	}
693 	return 0;
694 }
695 
GainResist(RESISTANCE rs,int val)696 int XCreature::GainResist(RESISTANCE rs, int val)
697 {
698 	r->ChangeResistance(rs, val);
699 	if (val > 0)
700 	{
701 		switch (rs)
702 		{
703 			case R_FIRE: msgwin.Add("Your blood cools down!"); break;
704 			case R_COLD: msgwin.Add("Your skin grows warm!"); break;
705 			case R_ACID: msgwin.Add("Your stomach settles!"); break;
706 			case R_POISON: msgwin.Add("Your flesh tingles!"); break;
707 			case R_PARALYSE: msgwin.Add("Your movements grow stronger!"); break;
708 		}
709 	} else
710 	{
711 		switch (rs)
712 		{
713 			case R_FIRE: msgwin.Add("Your blood warms up!"); break;
714 			case R_COLD: msgwin.Add("Your skin grows cold!"); break;
715 			case R_ACID: msgwin.Add("You feel a pain in your stomach!"); break;
716 			case R_POISON: msgwin.Add("You feel vulnerable!"); break;
717 			case R_PARALYSE: msgwin.Add("Your movements are unsure!"); break;
718 		}
719 	}
720 	return 1;
721 }
722 
GetStats(STATS st)723 int XCreature::GetStats(STATS st)
724 {
725 	assert(st > S_UNKNOWN && st < S_EOF);
726 
727 	int res = s->Get(st) + added_stats.Get(st);
728 	return res > 0 ? res : 1;
729 }
730 
GetResistance(RESISTANCE tr)731 int XCreature::GetResistance(RESISTANCE tr)
732 {
733 	assert(r);
734 	return r->GetResistance(tr) + added_resists.GetResistance(tr);
735 
736 }
737 
Die(XCreature * killer)738 void XCreature::Die(XCreature * killer)
739 {
740 	assert(isValid());
741 
742 //  Drop all inventory to the ground
743 	XList<XBodyPart *>::iterator it;
744 	for(it = components.begin(); it != components.end(); it++)
745 	{
746 		if (it->Item())
747 		{
748 			it->UnWear()->Drop(l, x, y);
749 		}
750 	}
751 
752 	XItem * item;
753 	while((item = contain.RemoveFirst()) != NULL)
754 	{
755 		item->Drop(l, x, y);
756 	}
757 
758 	LastStep();
759 
760 	if (killer && killer != this)
761 	{
762 		xai->onDie(killer);
763 		killer->religion.KillCreature(killer, this);
764 		killer->AddExp(GetExp());
765 	}
766 
767 	xai->Invalidate();
768 	Invalidate();
769 }
770 
DropItem(XItem * i)771 int XCreature::DropItem(XItem * i)
772 {
773 	XAnyPlace * place = l->map->GetPlace(x, y);
774 	int flag = 1;
775 	if (place)
776 		flag = place->onCreatureDropItem(this, i);
777 
778 	if (flag)
779 	{
780 		UnCarryItem(i);
781 		i->Drop(l, x, y);
782 	}
783 	return flag;
784 }
785 
PickUpItem(XItem * i)786 int XCreature::PickUpItem(XItem * i)
787 {
788 	XAnyPlace * place = l->map->GetPlace(x, y);
789 	int flag = 1;
790 	if (place)
791 		flag = place->onCreaturePickItem(this, i);
792 
793 	if (flag)
794 	{
795 		if (CarryItem(i))
796 		{
797 			i->x = -1;
798 			i->y = -1;
799 
800 
801 		//  If picked item is a missile the creature is shooting with, add
802 		//  it to quiver instead of backpack
803 			XBodyPart * xbp = GetBodyPart(BP_MISSILE);
804 			if (xbp && xbp->Item() && xbp->Item()->Compare(i) == 0 && i->GetRef() == 0)
805 				xbp->Item()->Concat(i);
806 			else
807 			{
808 //				if (i->it != IT_CORPSE && i->reference > 0)
809 //					assert(0);
810 				contain.Add(i);
811 			}
812 			return 1;
813 		} else //if we can't pick item, then drop it
814 		{
815 			if (im & IM_HERO)
816 			{
817 				char bufx[256];
818 				char buf[256];
819 				i->toString(buf);
820 				sprintf(bufx, "%s is to heavy for you!", buf);
821 				msgwin.ClrMsg();
822 				msgwin.Add(bufx);
823 			}
824 			int tx = i->x;
825 			int ty = i->y;
826 			i->x = -1;
827 			i->y = -1;
828 			if (place)
829 				place->onCreatureDropItem(this, i);
830 			i->x = tx;
831 			i->y = ty;
832 
833 
834 //			PutItem(i);
835 			return 0;
836 		}
837 	} else
838 		return 0;
839 }
840 
GetGender()841 CR_GENDER XCreature::GetGender()
842 {
843 	switch(creature_person_type)
844 	{
845 		case CPT_HE:
846 		case CPT_NAMED_HE:
847 		case CPT_MALE_YOU:
848 			return GEN_MALE;
849 			break;
850 		case CPT_SHE:
851 		case CPT_NAMED_SHE:
852 		case CPT_FEMALE_YOU:
853 			return GEN_FEMALE;
854 			break;
855 		default:
856 			break;
857 	}
858 	return GEN_NEUTER;
859 }
860 
GetMaxHP()861 int XCreature::GetMaxHP()
862 {
863 	return MAX_HP + (MAX_HP * GetStats(S_TOU)) / 20;
864 }
865 
GetMaxPP()866 int XCreature::GetMaxPP()
867 {
868 	return MAX_PP + (MAX_PP * GetStats(S_MAN)) / 10;
869 }
870 
GetExp()871 int XCreature::GetExp()
872 {
873 	return base_exp + _EXP / 10;
874 }
875 
AddExp(unsigned long exp)876 void XCreature::AddExp(unsigned long exp)
877 {
878 	_EXP += exp;
879 	while (ExpOfLevel(level) <= _EXP)
880 	{
881 		IncLevel();
882 	}
883 }
884 
IncLevel()885 void XCreature::IncLevel()
886 {
887 	MAX_HP += vRand((GetStats(S_TOU) / 5) + 1) + 1;
888 	MAX_PP += vRand((GetStats(S_MAN) / 2) + 1) + 1;
889 	level++;
890 	if (level > XGame::best_cr_level)
891 	{
892 		XGame::best_cr_level = level;
893 		XGame::best_creature = this;
894 	}
895 }
896 
ExpOfLevel(int lev)897 unsigned long XCreature::ExpOfLevel(int lev)
898 {
899 	//return 4 * (unsigned long)(base_exp * pow(M_E, (lev / 6.0)) - base_exp / 2);
900 	return (unsigned long)(2 * base_exp * pow(lev, 2.5));
901 }
902 
GetHITFHBonus(XItem * weapon)903 int XCreature::GetHITFHBonus(XItem * weapon)
904 {
905 	XItem * h1 = GetItem(BP_HAND, 0);
906 	XItem * h2 = GetItem(BP_HAND, 1);
907 	int mult = (h1 && h2) ? 2 : 1;
908 	float f = (float)(5.0 * log((300.0 * GetStats(S_STR)) / (10.0 * (weapon->weight) * mult)));
909 	return vMin((int)f, 0/*GetStats(S_DEX) / (2 * mult)*/);
910 }
911 
GetDMGFHBonus(XItem * weapon)912 int XCreature::GetDMGFHBonus(XItem * weapon)
913 {
914 	return 0;
915 }
916 
GetRNDBodyPart()917 XBodyPart * XCreature::GetRNDBodyPart()
918 {
919 	XList<XBodyPart *>::iterator tmpbp = components.begin();
920 	int value = 0;
921 	for (; tmpbp != components.end(); tmpbp++)
922 		value += tmpbp->GetPartSize();
923 
924 	int v = value > 0 ? vRand() % value : 0;
925 
926 	tmpbp = components.begin();
927 	while (v > 0)
928 	{
929 		v -= tmpbp->GetPartSize();
930 		if (v <= 0)
931 			return tmpbp;
932 		tmpbp++;
933 	}
934 
935 	return NULL;
936 }
937 
GetRNDBodyPart(ITEM_MASK xim,RBP_FLAG rbpf)938 XBodyPart * XCreature::GetRNDBodyPart(ITEM_MASK xim, RBP_FLAG rbpf)
939 {
940 	if (rbpf == RBP_BLOCK && xim & IM_SHIELD)
941 	{
942 		XList<XBodyPart *>::iterator tmpxbp = components.begin();
943 		XItem * xsh = NULL;
944 		while(tmpxbp != components.end())
945 		{
946 			if (tmpxbp->Item() && tmpxbp->Item()->im & IM_SHIELD)
947 			{
948 				xsh = tmpxbp->Item();
949 				break;
950 			}
951 			tmpxbp++;
952 		}
953 		if (xsh && (vRand() % 100 < 5 * wsk->GetLevel(WSK_SHIELD) + 5))
954 			return tmpxbp;
955 	}
956 
957 
958 	XList<XBodyPart *>::iterator tmpxbp = components.begin();
959 	int count = 0;
960 	while(tmpxbp != components.end())
961 	{
962 		if (tmpxbp->GetProperIM() & xim) count++;
963 		tmpxbp++;
964 	}
965 
966 	if (count == 0) return NULL;
967 	int n = vRand() % count;
968 
969 	count = 0;
970 	tmpxbp = components.begin();
971 	while(tmpxbp != components.end())
972 	{
973 		if (tmpxbp->GetProperIM() & xim)
974 		{
975 			if (n == count)
976 				return tmpxbp;
977 			else
978 				count++;
979 		}
980 		tmpxbp++;
981 	}
982 	assert(0);
983 	return NULL;
984 }
985 
986 
987 
GetWoundMsg(int flag)988 char * XCreature::GetWoundMsg(int flag)
989 {
990 	float rel = (float)(GetMaxHP()) / ((float)_HP);
991 	if (rel <= 1.0)
992 	{
993 		if (flag)
994 			return "";
995 		else
996 			return "not wounded";
997 	} else if (rel < 1.3)
998 	{
999 		if (flag)
1000 			return "slightly wounds";
1001 		else
1002 			return "slightly wounded";
1003 	} else if (rel < 2.0)
1004 	{
1005 		if (flag)
1006 			return "wounds";
1007 		else
1008 			return "wounded";
1009 	} else if (rel < 3.0)
1010 	{
1011 		if (flag)
1012 			return "seriously wounds";
1013 		else
1014 			return "seriously wounded";
1015 	} else
1016 	{
1017 		if (flag)
1018 			return "critically wounds";
1019 		else
1020 			return "critically wounded";
1021 	}
1022 }
1023 
1024 
MoveStairWay()1025 void XCreature::MoveStairWay()
1026 {
1027 	XCreature * tc = this;
1028 	XLocation * xl = l;
1029 
1030 	XMapObject * spec = xl->map->GetSpecial(tc->x, tc->y);
1031 	if (spec && spec->im & IM_WAY)
1032 	{
1033 		XLocation * tgtloc = Game.locations[((XStairWay *)spec)->ln].get();
1034 		int tgt_x = spec->nx;
1035 		int tgt_y = spec->ny;
1036 		int n_x = tgt_x;
1037 		int n_y = tgt_y;
1038 
1039 		if (tgtloc->map->XGetMovability(tgt_x, tgt_y) != 0)
1040 		{
1041 			for (int i = -1; i < 2; i++)
1042 				for (int j = -1; j < 2; j++)
1043 					if (tgtloc->map->XGetMovability(tgt_x + i, tgt_y + j) == 0)
1044 					{
1045 						n_x = i + tgt_x;
1046 						n_y = j + tgt_y;
1047 					}
1048 		}
1049 
1050 		if (!tgtloc->map->GetMonster(n_x, n_y))
1051 		{
1052 			tc->LastStep();
1053 			tc->FirstStep(n_x, n_y, tgtloc);
1054 			tc->l = tgtloc;
1055 //			tc->Move(); //calling commented by vadim. seems that it is not necessary...
1056 			tc->action_data.action = A_MOVE;
1057 			if (tc->im & IM_HERO)
1058 			{
1059 				tgtloc->visited_by_hero = 1;
1060 				tgtloc->map->Put(tc);
1061 				vRefresh();
1062 			}
1063 			return;
1064 		} else if (tc->im & IM_HERO)
1065 		{
1066 			msgwin.Add("The way is blocked.");
1067 		}
1068 	}
1069 	return;
1070 }
1071 
GetRangeAttackInfo(int * range,int * hit,XDice * dmg)1072 void XCreature::GetRangeAttackInfo(int * range, int * hit, XDice * dmg)
1073 {
1074 	XItem * missile = GetItem(BP_MISSILE);
1075 	if (!missile)
1076 	{
1077 		*range = 0;
1078 		*hit = 0;
1079 		dmg->Setup(0, 0, 0);
1080 		return;
1081 	}
1082 
1083 	XItem * bow =  GetItem(BP_MISSILEWEAPON);
1084 	XSkill * skill = sk->GetSkill(SKT_ARCHERY);
1085 
1086 	int str = s->Get(S_STR);
1087 	int dex = s->Get(S_DEX);
1088 
1089 	*range = missile->RNG;
1090 	*hit = dex / 3;
1091 	dmg->Setup(&(missile->dice));
1092 
1093 	if (bow)
1094 	{
1095 		*range += bow->RNG;
1096 		dmg->Add(&(bow->dice));
1097 		*range += wsk->GetDV(bow->wt);
1098 		dmg->Z += wsk->GetDMG(bow->wt);
1099 		*hit += wsk->GetHIT(bow->wt);
1100 	} else
1101 	{
1102 		*range += RNG + str / 25;
1103 		dmg->Z += str / 10;
1104 		*range += wsk->GetDV(WSK_THROW);
1105 		dmg->Z += wsk->GetDMG(WSK_THROW);
1106 		*hit += wsk->GetHIT(WSK_THROW);
1107 	}
1108 
1109 	if (skill)
1110 	{
1111 		int lvl = skill->GetLevel();
1112 		dmg->Z += lvl / 2;
1113 		*range += lvl / 5;
1114 		*hit += lvl * 3;
1115 	}
1116 
1117 }
1118 
Shoot(int tx,int ty)1119 int XCreature::Shoot(int tx, int ty)
1120 {
1121 	if (tx == x && ty ==  y) return 0; //can't do suicide!
1122 
1123 	XItem * bow =  GetItem(BP_MISSILEWEAPON);
1124 	XItem * missile = GetItem(BP_MISSILE);
1125 	XSkill * skill = sk->GetSkill(SKT_ARCHERY);
1126 
1127 	if (!missile) return 0; //there are no missile to shoot!
1128 
1129 	int hit = 0;
1130 	int range = 0;
1131 	XDice dmg;
1132 	GetRangeAttackInfo(&range, &hit, &dmg);
1133 	int vis1 = isVisibleArea(x, y);
1134 	int vis2 = isVisibleArea(tx, ty);
1135 	if (vis1 || vis2)
1136 	{
1137 		msgwin.Add(GetNameEx(CRN_T1));
1138 		if (bow)
1139 		{
1140 			msgwin.Add(GetVerb("shoot"));
1141 			msgwin.Add("from");
1142 			msgwin.AddLast(bow->name);
1143 		} else
1144 		{
1145 			msgwin.Add(GetVerb("throw"));
1146 			msgwin.AddLast(missile->name);
1147 		}
1148 	}
1149 
1150 	MF_DATA mfd;
1151 	mfd.arrow_type = MFT_ARROW;
1152 	mfd.arrow_color = xBROWN;
1153 	mfd.l = l;
1154 	mfd.sx = x;
1155 	mfd.sy = y;
1156 	mfd.ex = tx;
1157 	mfd.ey = ty;
1158 	mfd.to_hit = hit;
1159 	mfd.max_range = range;
1160 	MF_RESULT res = MissileFlight(&mfd);
1161 	if (res == MF_BLOCK && (vis1 || vis2))
1162 	{
1163 		XCreature * tgt = l->map->GetMonster(mfd.pt.x, mfd.pt.y);
1164 		msgwin.Add(tgt->GetNameEx(CRN_T1));
1165 		msgwin.Add(tgt->GetVerb("deflect"));
1166 		msgwin.Add(missile->name);
1167 		msgwin.Add("with");
1168 		msgwin.Add(tgt->GetNameEx(CRN_T4));
1169 		msgwin.Add("shield.");
1170 	} else if (res == MF_HIT)
1171 	{
1172 		XCreature * tgt = l->map->GetMonster(mfd.pt.x, mfd.pt.y);
1173 
1174 		int tdam = dmg.Throw();
1175 
1176 		bool exact = false;
1177 		if (vRand(100) <= 2 + sk->GetLevel(SKT_FINDWEAKNESS))
1178 		{
1179 			exact = true;
1180 			tdam *= 3;
1181 			sk->UseSkill(SKT_FINDWEAKNESS);
1182 		}
1183 
1184 		int dam = tdam;
1185 
1186 		//get initial pv of armour
1187 		XBodyPart * txbp = tgt->GetRNDBodyPart();
1188 		int xpv = 0;
1189 		if (txbp && txbp->Item())
1190 			xpv = txbp->Item()->_PV;
1191 
1192 		if (txbp && txbp->bp_uin == BP_CLOAK)
1193 		{
1194 			XItem * xtmp = GetItem(BP_BODY);
1195 			if (xtmp)
1196 				xpv += xtmp->_PV;
1197 		}
1198 
1199 		bool ignore_pv = false;
1200 		if (vRand(100) <= 2 + sk->GetLevel(SKT_FINDWEAKNESS))
1201 		{
1202 			ignore_pv = true;
1203 			sk->UseSkill(SKT_FINDWEAKNESS);
1204 		} else
1205 			dam = dam - xpv;
1206 
1207 		if (vis1 || vis2)
1208 		{
1209 			msgwin.Add(missile->name);
1210 			if (exact)
1211 				msgwin.Add(GetVerb("exactly"));
1212 			msgwin.Add(tgt->GetVerb("hit"));
1213 			if (ignore_pv)
1214 			{
1215 				msgwin.Add(tgt->GetNameEx(CRN_T1));
1216 				if (dam > 0 && tgt->im & IM_HERO)
1217 					msgwin.AddLast(GetVerb("through a piece of armour"));
1218 				else
1219 					msgwin.Add(GetVerb("through a piece of armour"));
1220 			} else
1221 			{
1222 				if (tgt->im & IM_HERO)
1223 					msgwin.AddLast(tgt->GetNameEx(CRN_T1));
1224 				else
1225 					msgwin.Add(tgt->GetNameEx(CRN_T1));
1226 			}
1227 		}
1228 
1229 		if (bow)
1230 			wsk->UseSkill(bow->wt);
1231 		else
1232 			wsk->UseSkill(WSK_THROW);
1233 
1234 		if (dam > 0)
1235 		{
1236 			AttackCreature(tgt, dam);
1237 			if (skill)
1238 				skill->UseSkill();
1239 		} else
1240 		{
1241 			if (vis1 || vis2)
1242 			{
1243 				msgwin.Add("but does not manage to harm");
1244 				msgwin.AddLast(tgt->GetNameEx(CRN_T2));
1245 			}
1246 		}
1247 
1248 	} else
1249 	{
1250 		XCreature * tgt = l->map->GetMonster(mfd.pt.x, mfd.pt.y);
1251 		if (tgt && tgt->isVisible())
1252 		{
1253 			msgwin.Add(tgt->GetNameEx(CRN_T1));
1254 			msgwin.Add(tgt->GetVerb("avoid"));
1255 			msgwin.Add("missile.");
1256 		}
1257 	}
1258 
1259 
1260 	XItem * msl = (XItem *)missile->MakeCopy();
1261 
1262 	msl->quantity = 1;
1263 	// split missile
1264 	if (--missile->quantity <= 0)
1265 	{
1266 		XBodyPart * xbp = GetBodyPart(BP_MISSILE);
1267 		xbp->UnWear()->Invalidate();
1268 	}
1269 
1270 	if (vRand(3))
1271 		msl->Drop(l.get(), mfd.pt.x, mfd.pt.y);
1272 	else
1273 		msl->Invalidate();
1274 	return 1;
1275 }
1276 
GetBodyPart(BODYPART bp,int count)1277 XBodyPart * XCreature::GetBodyPart(BODYPART bp, int count)
1278 {
1279 	XBodyPart * xbp;
1280 	XList<XBodyPart *>::iterator it;
1281 	for (it = components.begin(); it != components.end(); it++)
1282 	{
1283 		xbp = static_cast<XBodyPart *>(static_cast<XObject *>(it));
1284 		if (xbp->bp_uin == bp && count-- == 0) return xbp;
1285 	}
1286 	return NULL;
1287 }
1288 
CanWear(XItem * item)1289 int XCreature::CanWear(XItem * item)
1290 {
1291 	for (XList<XBodyPart *>::iterator it = components.begin(); it != components.end(); it++)
1292 	{
1293 		if ((*it)->Fit(item->bp) && (*it)->Item() == NULL)
1294 			return 1;
1295 	}
1296 	return 0;
1297 }
1298 
Wear(XItem * item)1299 int XCreature::Wear(XItem * item)
1300 {
1301 	for (XList<XBodyPart *>::iterator it = components.begin(); it != components.end(); it++)
1302 	{
1303 		if ((*it)->Fit(item->bp) && (*it)->Item() == NULL)
1304 		{
1305 			(*it)->Wear(item);
1306 			return 1;
1307 		}
1308 	}
1309 	return 0;
1310 }
1311 
GetItem(BODYPART bp,int count)1312 XItem * XCreature::GetItem(BODYPART bp, int count)
1313 {
1314 	XBodyPart * xbp = GetBodyPart(bp, count);
1315 	if (xbp)
1316 		return xbp->Item();
1317 	else
1318 		return NULL;
1319 }
1320 
FirstStep(int _x,int _y,XLocation * _l)1321 void XCreature::FirstStep(int _x, int _y, XLocation * _l)
1322 {
1323 	x = _x;
1324 	y = _y;
1325 	nx = _x;
1326 	ny = _y;
1327 	SetLocation(_l);
1328 
1329 	assert(!l->map->GetMonster(_x, _y));
1330 	l->map->SetMonster(_x, _y, this);
1331 //   ShowNewView();
1332 }
1333 
LastStep()1334 void XCreature::LastStep()
1335 {
1336 //   HideOldView();
1337 	l->map->ResMonster(x, y);
1338 }
1339 
1340 
continueRead()1341 int XCreature::continueRead()
1342 {
1343 	XBook * book = (XBook *)action_data.item.get();
1344 	book->onRead(this);
1345 	if (book->left_to_read <= 0)
1346 	{
1347 		book->UnCarry();
1348 		book->Invalidate();
1349 		action_data.action = A_MOVE;
1350 		action_data.item = NULL;
1351 		if (vRand(5) == 0)
1352 			GainAttr(S_LEN, 1);
1353 	}
1354 	return 1;
1355 }
1356 
Read(XItem * item)1357 int XCreature::Read(XItem * item)
1358 {
1359 	XSkill * skill =  sk->GetSkill(SKT_LITERACY);
1360 	if (!skill)
1361 	{
1362 		if (im & IM_HERO)
1363 		{
1364 			msgwin.Add("You are illiterate!");
1365 		}
1366 		return 0;
1367 	}
1368 
1369 	if (item->im & IM_SCROLL)
1370 	{
1371 		skill->UseSkill();
1372 		((XScroll *)item)->onRead(this);
1373 		item->UnCarry();
1374 		item->Invalidate();
1375 		if (vRand(10) == 0)
1376 			GainAttr(S_LEN, 1);
1377 		return 1;
1378 	} else if (item->im & IM_BOOK)
1379 	{
1380 		((XBook *)item)->onRead(this);
1381 		if (((XBook *)item)->left_to_read <= 0)
1382 		{
1383 			item->UnCarry();
1384 			item->Invalidate();
1385 		} else
1386 		{
1387 			action_data.action = A_READ;
1388 			action_data.item = item;
1389 		}
1390 		return 1;
1391 	}
1392 	return 0;
1393 }
1394 
Store(XFile * f)1395 void XCreature::Store(XFile * f)
1396 {
1397 	XBaseObject::Store(f);
1398 
1399 	f->Write(&_EXP, sizeof(unsigned long));
1400 	f->Write(&added_DMG);
1401 	f->Write(&added_DV);
1402 	f->Write(&added_HIT);
1403 	f->Write(&added_HP);
1404 	f->Write(&added_PP);
1405 	f->Write(&added_PV);
1406 
1407 	f->Write(&attack_energy);
1408 	f->Write(&move_energy);
1409 	f->Write(&base_speed);
1410 	f->Write(&added_speed);
1411 
1412 	added_resists.Store(f);
1413 	f->Write(&added_RNG);
1414 	added_stats.Store(f);
1415 	f->Write(&base_exp);
1416 	f->Write(&base_nutrio);
1417 	f->Write(&carried_weight);
1418 	components.StoreList(f);
1419 
1420 	f->Write(&creature_class, sizeof(CREATURE_CLASS));
1421 	f->Write(&creature_size, sizeof(CREATURE_SIZE));
1422 	f->Write(&food_feeling, sizeof(FOOD_FEELING));
1423 	f->Write(&group_id, sizeof(GROUP_ID));
1424 
1425 	f->Write(&level);
1426 
1427 	m->Store(f);
1428 	md->Store(f);
1429 	f->Write(&nutrio);
1430 	f->Write(&nutrio_speed);
1431 	sk->Store(f);
1432 	f->Write(&tactics, sizeof(TACTICS_STATE));
1433 	wsk->Store(f);
1434 	XObject::StorePointer(f, xai);
1435 
1436 	action_data.Store(f);
1437 
1438 	contain.StoreList(f);
1439 	religion.Store(f);
1440 	max_stats.Store(f);
1441 	f->Write(&creature_person_type, sizeof(creature_person_type));
1442 	f->Write(&creature_name, sizeof(creature_name));
1443 }
1444 
1445 
Restore(XFile * f)1446 void XCreature::Restore(XFile * f)
1447 {
1448 	XBaseObject::Restore(f);
1449 
1450 	f->Read(&_EXP, sizeof(unsigned long));
1451 	f->Read(&added_DMG);
1452 	f->Read(&added_DV);
1453 	f->Read(&added_HIT);
1454 	f->Read(&added_HP);
1455 	f->Read(&added_PP);
1456 	f->Read(&added_PV);
1457 
1458 	f->Read(&attack_energy);
1459 	f->Read(&move_energy);
1460 	f->Read(&base_speed);
1461 	f->Read(&added_speed);
1462 
1463 
1464 	added_resists.Restore(f);
1465 	f->Read(&added_RNG);
1466 	added_stats.Restore(f);
1467 	f->Read(&base_exp);
1468 	f->Read(&base_nutrio);
1469 	f->Read(&carried_weight);
1470 
1471 	assert(components.empty());
1472 	components.RestoreList(f);
1473 
1474 	f->Read(&creature_class, sizeof(CREATURE_CLASS));
1475 	f->Read(&creature_size, sizeof(CREATURE_SIZE));
1476 	f->Read(&food_feeling, sizeof(FOOD_FEELING));
1477 	f->Read(&group_id, sizeof(GROUP_ID));
1478 
1479 	f->Read(&level);
1480 
1481 	m = new XMagic();
1482 	m->Restore(f);
1483 	md = new XModifer();
1484 	md->Restore(f, this);
1485 	f->Read(&nutrio);
1486 	f->Read(&nutrio_speed);
1487 	sk = new XSkills();
1488 	sk->Restore(f);
1489 	f->Read(&tactics, sizeof(TACTICS_STATE));
1490 	wsk = new XWarSkills();
1491 	wsk->Restore(f);
1492 
1493 	xai = (XStandardAI *)XObject::RestorePointer(f, this);
1494 
1495 	action_data.Restore(f);
1496 
1497 	assert(contain.empty());
1498 	contain.RestoreList(f);
1499 	religion.Restore(f);
1500 	max_stats.Restore(f);
1501 	f->Read(&creature_person_type, sizeof(creature_person_type));
1502 	f->Read(&creature_name, sizeof(creature_name));
1503 
1504 	if (!isHero()) //skip restoing of descriptions and other for hero
1505 		XCreatureStorage::RestoreCreatureInfo(this);
1506 }
1507 
1508 
GetCreatureStrength()1509 int XCreature::GetCreatureStrength()
1510 {
1511 	int tdv = GetDV();
1512 	int tpv = GetPV();
1513 	if (tdv <= 0)
1514 		tdv = 1;
1515 	if (tpv <= 0)
1516 		tpv = 1;
1517 	int dv_pv_bonus = ((tdv * tpv * tpv) / 10 + (_DV * _PV * _PV));
1518 	int thit = GetHIT() / 10;
1519 	int tdmg = (dice.X * dice.Y + dice.Z + GetDMG());
1520 	if (thit <= 0)
1521 		thit = 1;
1522 	if (tdmg <= 0)
1523 		tdmg = 1;
1524 
1525 	int hit_dmg_bonus = thit * tdmg * GetMaxHP();
1526 
1527 	return 20 + hit_dmg_bonus + dv_pv_bonus;
1528 
1529 }
1530 
GetTarget(TARGET_REASON tr,XPoint * pt,int max_range,XObject ** back)1531 int XCreature::GetTarget(TARGET_REASON tr, XPoint * pt, int max_range, XObject ** back)
1532 {
1533 	switch (tr)
1534 	{
1535 		case TR_ATTACK_TARGET:
1536 
1537 			return xai->GetTargetPos(pt);
1538 			break;
1539 
1540 	}
1541 	return 0;
1542 }
1543 
Chat(XCreature * chatter,char * msg)1544 int XCreature::Chat(XCreature * chatter, char * msg)
1545 {
1546 	return 0;
1547 }
1548 
ContainItem(XItem * item)1549 bool XCreature::ContainItem(XItem * item)
1550 {
1551 	if (CarryItem(item))
1552 	{
1553 		contain.Add(item);
1554 		return true;
1555 	} else
1556 		return false;
1557 }
1558 
CarryItem(XItem * item)1559 bool XCreature::CarryItem(XItem * item)
1560 {
1561 	if(item->GetOwner() && item->GetOwner().get() == this) return 1;
1562 
1563 	carried_weight += item->weight * item->quantity;
1564 	if (GetCarryState() == CSTATE_DIE)
1565 	{
1566 		carried_weight -= item->weight * item->quantity;
1567 		return false;
1568 	} else
1569 	{
1570 		item->SetOwner(this);
1571 		return true;
1572 	}
1573 }
1574 
UnCarryItem(XItem * item)1575 void XCreature::UnCarryItem(XItem * item)
1576 {
1577 	if(item->GetOwner())
1578 	{
1579 		assert(item->GetOwner().get() == this);
1580 		carried_weight -= item->weight * item->quantity;
1581 	}
1582 	item->SetOwner(NULL);
1583 }
1584 
1585 
CarryValue(CARRY_STATE cs)1586 int XCreature::CarryValue(CARRY_STATE cs)
1587 {
1588 	int str = s->Get(S_STR) + added_stats.Get(S_STR);
1589 	switch (cs)
1590 	{
1591 		case CSTATE_NORMAL      : return str * 120; break;
1592 		case CSTATE_BURDENED : return str * 200; break;
1593 		case CSTATE_STRAINED : return str * 280; break;
1594 		case CSTATE_OVERBURDEN  : return str * 360; break;
1595 		case CSTATE_DIE         : return str * 600; break;
1596 		default              : assert(0); break;
1597 	};
1598 	return 0;
1599 }
1600 
GetCarryState()1601 CARRY_STATE XCreature::GetCarryState()
1602 {
1603 	if (carried_weight < CarryValue(CSTATE_NORMAL))
1604 		return CSTATE_NORMAL;
1605 	else
1606 		if (carried_weight < CarryValue(CSTATE_BURDENED))
1607 			return CSTATE_BURDENED;
1608 		else
1609 			if (carried_weight < CarryValue(CSTATE_STRAINED))
1610 				return CSTATE_STRAINED;
1611 			else
1612 				if (carried_weight < CarryValue(CSTATE_OVERBURDEN))
1613 					return CSTATE_OVERBURDEN;
1614 				else
1615 					return CSTATE_DIE;
1616 
1617 
1618 }
1619 
GetVisibleRadius()1620 int XCreature::GetVisibleRadius()
1621 {
1622 	int perception = s->Get(S_PER);
1623 	if (perception < 5)
1624 		return 3;
1625 	else if (perception < 10)
1626 		return 4;
1627 	else if (perception < 20)
1628 		return 5;
1629 	else if (perception < 50)
1630 		return 6;
1631 	else if (perception < 90)
1632 		return 7;
1633 	else
1634 		return 8;
1635 }
1636 
onGiveItem(XCreature * giver,XItem * item)1637 int XCreature::onGiveItem(XCreature * giver, XItem * item)
1638 {
1639 	return 0;
1640 }
1641 
MoneyOp(int money_count)1642 int XCreature::MoneyOp(int money_count)
1643 {
1644 	it_iterator it = contain.begin();
1645 	while (it != contain.end())
1646 	{
1647 		if (it->im & IM_MONEY)
1648 			break;
1649 		it++;
1650 	}
1651 	if (it != contain.end())
1652 	{
1653 		XItem * money = it;
1654 		if (money_count >= 0)
1655 		{
1656 			carried_weight -= money->quantity / 10;
1657 			money->quantity += money_count;
1658 			carried_weight += money->quantity / 10;
1659 			return money->quantity;
1660 		} else
1661 		{
1662 			if (money->quantity + money_count > 0)
1663 			{
1664 				carried_weight -= money->quantity / 10;
1665 				money->quantity += money_count;
1666 				carried_weight += money->quantity / 10;
1667 				return money->quantity;
1668 			} else
1669 				if (money->quantity + money_count == 0)
1670 				{
1671 					carried_weight -= money->quantity / 10;
1672 					contain.Remove(it)->Invalidate();
1673 					return 0;
1674 				} else
1675 				{
1676 					return money->quantity + money_count;
1677 				}
1678 		}
1679 	} else
1680 	{
1681 		if (money_count > 0)
1682 		{
1683 			carried_weight += money_count / 10;
1684 			contain.Add(new XMoney(money_count));
1685 			return money_count;
1686 		} else
1687 		{
1688 			return money_count;
1689 		}
1690 	}
1691 	return 0;
1692 }
1693 
GetGenderStr()1694 char * XCreature::GetGenderStr()
1695 {
1696 	CR_GENDER g = GetGender();
1697 	if(g == GEN_MALE)
1698 		return "male";
1699 	else if(g == GEN_FEMALE)
1700 		return "female";
1701 	else
1702 		return "neuter";
1703 }
1704 
GetNameEx(CR_NAME_TYPE crn)1705 char * XCreature::GetNameEx(CR_NAME_TYPE crn)
1706 {
1707 	static char cool_buf[1024]; //hope the name will never larger than
1708 	if (isVisible())
1709 	{
1710 		switch (creature_person_type)
1711 		{
1712 			case CPT_YOU:
1713 			case CPT_MALE_YOU:
1714 			case CPT_FEMALE_YOU:
1715 				switch (crn)
1716 				{
1717 					case CRN_T1: return "you";
1718 					case CRN_T2: return "you";
1719 					case CRN_T3: return "you";
1720 					case CRN_T4: return "your";
1721 				}
1722 
1723 			case CPT_NAMED_HE:
1724 				switch (crn)
1725 				{
1726 					case CRN_T1: return name;
1727 					case CRN_T2: return "he";
1728 					case CRN_T3: return "him";
1729 					case CRN_T4: return "his";
1730 				}
1731 
1732 			case CPT_NAMED_SHE:
1733 				switch (crn)
1734 				{
1735 					case CRN_T1: return name;
1736 					case CRN_T2: return "she";
1737 					case CRN_T3: return "her";
1738 					case CRN_T4: return "hers";
1739 				}
1740 
1741 			case CPT_NAMED_IT:
1742 				switch (crn)
1743 				{
1744 					case CRN_T1: return name;
1745 					case CRN_T2: return "it";
1746 					case CRN_T3: return "it";
1747 					case CRN_T4: return "its";
1748 				}
1749 
1750 
1751 			case CPT_HE:
1752 				switch (crn)
1753 				{
1754 					case CRN_T1: sprintf(cool_buf, "the %s", name); return cool_buf;
1755 					case CRN_T2: return "he";
1756 					case CRN_T3: return "him";
1757 					case CRN_T4: return "his";
1758 				}
1759 
1760 			case CPT_SHE:
1761 				switch (crn)
1762 				{
1763 					case CRN_T1: sprintf(cool_buf, "the female %s", name); return cool_buf;
1764 					case CRN_T2: return "she";
1765 					case CRN_T3: return "her";
1766 					case CRN_T4: return "hers";
1767 				}
1768 
1769 			case CPT_IT:
1770 				switch (crn)
1771 				{
1772 					case CRN_T1: sprintf(cool_buf, "the %s", name); return cool_buf;
1773 					case CRN_T2: return "it";
1774 					case CRN_T3: return "it";
1775 					case CRN_T4: return "its";
1776 				}
1777 		}
1778 	} else
1779 	{
1780 		switch (crn)
1781 		{
1782 			case CRN_T1: return "someone";
1783 			case CRN_T2: return "it";
1784 			case CRN_T3: return "it";
1785 			case CRN_T4: return "its";
1786 		}
1787 	}
1788 	assert(0);
1789 	return "";
1790 }
1791 
GetVerb(char * verb)1792 char * XCreature::GetVerb(char * verb)
1793 {
1794 	static char cool_buf[1024]; //hope the name will never larger than
1795 	if (creature_person_type & CPT_YOU)
1796 		sprintf(cool_buf, "%s", verb);
1797 	else
1798 	{
1799 		char lch = verb[strlen(verb) - 1];
1800 		if (lch == 's' || lch == 'h')
1801 			sprintf(cool_buf, "%ses", verb);
1802 		else
1803 			sprintf(cool_buf, "%ss", verb);
1804 	}
1805 	return cool_buf;
1806 }
1807 
1808