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