1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/ultima8/misc/pent_include.h"
24 #include "ultima/ultima8/kernel/object_manager.h"
25 #include "ultima/ultima8/kernel/kernel.h"
26 #include "ultima/ultima8/kernel/delay_process.h"
27 #include "ultima/ultima8/usecode/uc_machine.h"
28 #include "ultima/ultima8/usecode/uc_list.h"
29 #include "ultima/ultima8/misc/direction_util.h"
30 #include "ultima/ultima8/games/game_data.h"
31 #include "ultima/ultima8/world/fire_type.h"
32 #include "ultima/ultima8/graphics/anim_dat.h"
33 #include "ultima/ultima8/graphics/main_shape_archive.h"
34 #include "ultima/ultima8/graphics/shape.h"
35 #include "ultima/ultima8/world/actors/actor_anim_process.h"
36 #include "ultima/ultima8/world/actors/animation_tracker.h"
37 #include "ultima/ultima8/world/actors/anim_action.h"
38 #include "ultima/ultima8/world/actors/npc_dat.h"
39 #include "ultima/ultima8/world/actors/resurrection_process.h"
40 #include "ultima/ultima8/world/actors/clear_feign_death_process.h"
41 #include "ultima/ultima8/world/actors/pathfinder_process.h"
42 #include "ultima/ultima8/world/actors/loiter_process.h"
43 #include "ultima/ultima8/world/actors/guard_process.h"
44 #include "ultima/ultima8/world/actors/combat_process.h"
45 #include "ultima/ultima8/world/actors/attack_process.h"
46 #include "ultima/ultima8/world/actors/pace_process.h"
47 #include "ultima/ultima8/world/actors/surrender_process.h"
48 #include "ultima/ultima8/world/actors/rolling_thunder_process.h"
49 #include "ultima/ultima8/world/bobo_boomer_process.h"
50 #include "ultima/ultima8/world/world.h"
51 #include "ultima/ultima8/world/current_map.h"
52 #include "ultima/ultima8/world/sprite_process.h"
53 #include "ultima/ultima8/world/target_reticle_process.h"
54 #include "ultima/ultima8/world/item_selection_process.h"
55 #include "ultima/ultima8/world/actors/main_actor.h"
56 #include "ultima/ultima8/world/get_object.h"
57 #include "ultima/ultima8/world/item_factory.h"
58 #include "ultima/ultima8/world/loop_script.h"
59 #include "ultima/ultima8/audio/audio_process.h"
60 #include "ultima/ultima8/audio/music_process.h"
61 #include "ultima/ultima8/world/gravity_process.h"
62
63 namespace Ultima {
64 namespace Ultima8 {
65
66 static const unsigned int BACKPACK_SHAPE = 529;
67
DEFINE_RUNTIME_CLASSTYPE_CODE(Actor)68 DEFINE_RUNTIME_CLASSTYPE_CODE(Actor)
69
70 Actor::Actor() : _strength(0), _dexterity(0), _intelligence(0),
71 _hitPoints(0), _mana(0), _alignment(0), _enemyAlignment(0),
72 _lastAnim(Animation::stand), _animFrame(0), _direction(dir_north),
73 _fallStart(0), _unkByte(0), _actorFlags(0), _combatTactic(0),
74 _homeX(0), _homeY(0), _homeZ(0), _currentActivityNo(0),
75 _lastActivityNo(0), _activeWeapon(0), _lastTickWasHit(0),
76 _attackMoveStartFrame(0), _attackMoveTimeout(0),
77 _attackMoveDodgeFactor(1), _attackAimFlag(false) {
78 _defaultActivity[0] = 0;
79 _defaultActivity[1] = 0;
80 _defaultActivity[2] = 0;
81 }
82
~Actor()83 Actor::~Actor() {
84 }
85
assignObjId()86 uint16 Actor::assignObjId() {
87 if (_objId == 0xFFFF)
88 _objId = ObjectManager::get_instance()->assignActorObjId(this);
89
90 Std::list<Item *>::iterator iter;
91 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
92 (*iter)->assignObjId();
93 (*iter)->setParent(_objId);
94 }
95
96 return _objId;
97 }
98
getMaxMana() const99 int16 Actor::getMaxMana() const {
100 return static_cast<int16>(2 * getInt());
101 }
102
getMaxHP() const103 uint16 Actor::getMaxHP() const {
104 return static_cast<uint16>(2 * getStr());
105 }
106
loadMonsterStats()107 bool Actor::loadMonsterStats() {
108 if (GAME_IS_CRUSADER)
109 return loadMonsterStatsCru();
110 else
111 return loadMonsterStatsU8();
112 }
113
loadMonsterStatsCru()114 bool Actor::loadMonsterStatsCru() {
115 const NPCDat *npcData = GameData::get_instance()->getNPCDataForShape(getShape());
116
117 if (!npcData)
118 return false;
119
120 setStr(npcData->getMaxHp() / 2);
121 setHP(npcData->getMaxHp());
122 _defaultActivity[0] = npcData->getDefaultActivity(0);
123 _defaultActivity[1] = npcData->getDefaultActivity(1);
124 _defaultActivity[2] = npcData->getDefaultActivity(2);
125
126 // TODO: Give them the default weapon for their type here.
127
128 return true;
129 }
130
loadMonsterStatsU8()131 bool Actor::loadMonsterStatsU8() {
132
133 const ShapeInfo *shapeinfo = getShapeInfo();
134 const MonsterInfo *mi = nullptr;
135 if (shapeinfo) mi = shapeinfo->_monsterInfo;
136 if (!mi)
137 return false;
138
139 uint16 hp;
140 if (mi->_maxHp <= mi->_minHp)
141 hp = mi->_minHp;
142 else
143 hp = mi->_minHp + getRandom() % (mi->_maxHp - mi->_minHp);
144 setHP(hp);
145
146 uint16 dex;
147 if (mi->_maxDex <= mi->_minDex)
148 dex = mi->_minDex;
149 else
150 dex = mi->_minDex + getRandom() % (mi->_maxDex - mi->_minDex);
151 setDex(dex);
152
153 uint8 new_alignment = mi->_alignment;
154 setAlignment(new_alignment & 0x0F);
155 setEnemyAlignment((new_alignment & 0xF0) >> 4); // !! CHECKME
156
157 return true;
158 }
159
giveTreasure()160 bool Actor::giveTreasure() {
161 MainShapeArchive *mainshapes = GameData::get_instance()->getMainShapes();
162 const ShapeInfo *shapeinfo = getShapeInfo();
163 const MonsterInfo *mi = nullptr;
164 if (shapeinfo) mi = shapeinfo->_monsterInfo;
165 if (!mi)
166 return false;
167
168 const Std::vector<TreasureInfo> &treasure = mi->_treasure;
169
170 for (unsigned int i = 0; i < treasure.size(); ++i) {
171 const TreasureInfo &ti = treasure[i];
172 Item *item;
173
174 // check map
175 int currentmap = World::get_instance()->getCurrentMap()->getNum();
176 if (ti._map != 0 && ((ti._map > 0 && ti._map != currentmap) ||
177 (ti._map < 0 && -ti._map == currentmap))) {
178 continue;
179 }
180
181 // check chance
182 if (ti._chance < 0.999 &&
183 (static_cast<double>(getRandom()) / U8_RAND_MAX) > ti._chance) {
184 continue;
185 }
186
187 // determine count/quantity
188 int count;
189 if (ti._minCount >= ti._maxCount)
190 count = ti._minCount;
191 else
192 count = ti._minCount + (getRandom() % (ti._maxCount - ti._minCount));
193
194 if (!ti._special.empty()) {
195 if (ti._special == "weapon") {
196
197 // NB: this is rather biased towards weapons with low _shapes...
198 for (unsigned int s = 0; s < mainshapes->getCount(); ++s) {
199 const ShapeInfo *si = mainshapes->getShapeInfo(s);
200 if (!si->_weaponInfo) continue;
201
202 int chance = si->_weaponInfo->_treasureChance;
203 if (!chance) continue;
204
205 int r = getRandom() % 100;
206 #if 0
207 pout << "weapon (" << s << ") chance: " << r << "/"
208 << chance << Std::endl;
209 #endif
210 if (r >= chance) continue;
211
212 // create the weapon
213 item = ItemFactory::createItem(s,
214 0, // frame
215 count, // quality
216 Item::FLG_DISPOSABLE,//flags
217 0, // npcnum,
218 0, // mapnum
219 0, true); // ext.flags,_objId
220 item->moveToContainer(this);
221 item->randomGumpLocation();
222 break;
223 }
224 } else if (ti._special == "sorcfocus") {
225 // CONSTANTS! (and lots of them...)
226 int shapeNum = 397;
227 int frameNum;
228 uint16 qualityNum;
229
230 if (getRandom() % 10 < 8) {
231 // wand
232 if (getRandom() % 10 < 4) {
233 // charged
234 frameNum = 0;
235 qualityNum = 3 + (getRandom() % 4) + // charges
236 ((1 + (getRandom() % 4)) << 8); // spell
237 } else {
238 frameNum = 15;
239 qualityNum = 0;
240 }
241
242 item = ItemFactory::createItem(shapeNum, frameNum, qualityNum,
243 Item::FLG_DISPOSABLE,
244 0, 0, 0, true);
245 item->moveToContainer(this);
246 item->randomGumpLocation();
247 }
248
249 if (getRandom() % 10 < 6) {
250 // rod
251 if (getRandom() % 10 < 2) {
252 // charged
253 frameNum = 3;
254 qualityNum = 3 + (getRandom() % 4) + // charges
255 ((1 + (getRandom() % 7)) << 8); // spell
256 } else {
257 frameNum = 16;
258 qualityNum = 0;
259 }
260
261 item = ItemFactory::createItem(shapeNum, frameNum, qualityNum,
262 Item::FLG_DISPOSABLE,
263 0, 0, 0, true);
264 item->moveToContainer(this);
265 item->randomGumpLocation();
266 }
267
268 if (getRandom() % 10 < 5) {
269 // symbol
270 if (getRandom() % 10 < 5) {
271 // charged
272 frameNum = 12;
273 uint8 spell = 1 + (getRandom() % 11);
274 qualityNum = spell << 8;
275 if (spell < 4) {
276 qualityNum += 3 + (getRandom() % 4);
277 } else {
278 // symbol can only have one charge of anything
279 // other than ignite/extinguish
280 qualityNum += 1;
281 }
282 } else {
283 frameNum = 19;
284 qualityNum = 0;
285 }
286
287 item = ItemFactory::createItem(shapeNum, frameNum, qualityNum,
288 Item::FLG_DISPOSABLE,
289 0, 0, 0, true);
290 item->moveToContainer(this);
291 item->randomGumpLocation();
292 }
293
294 if (getRandom() % 10 < 2) {
295 // demon talisman
296 if (getRandom() % 10 < 2) {
297 // charged
298 frameNum = 9;
299 qualityNum = 1 + (getRandom() % 2) + // charges
300 ((10 + (getRandom() % 2)) << 8); // spell
301 } else {
302 frameNum = 18;
303 qualityNum = 0;
304 }
305
306 item = ItemFactory::createItem(shapeNum, frameNum, qualityNum,
307 Item::FLG_DISPOSABLE,
308 0, 0, 0, true);
309 item->moveToContainer(this);
310 item->randomGumpLocation();
311 }
312
313 } else {
314 pout << "Unhandled special treasure: " << ti._special
315 << Std::endl;
316 }
317 continue;
318 }
319
320 // if _shapes.size() == 1 and the given shape is SF_QUANTITY,
321 // then produce a stack of that shape (ignoring frame)
322
323 if (ti._shapes.size() == 1) {
324 uint32 shapeNum = ti._shapes[0];
325 const ShapeInfo *si = mainshapes->getShapeInfo(shapeNum);
326 if (!si) {
327 perr << "Trying to create treasure with an invalid shapeNum ("
328 << shapeNum << ")" << Std::endl;
329 continue;
330 }
331 if (si->hasQuantity()) {
332 // CHECKME: which flags?
333 item = ItemFactory::createItem(shapeNum,
334 0, // frame
335 count, // quality
336 Item::FLG_DISPOSABLE, // flags
337 0, // npcnum,
338 0, // mapnum
339 0, true); // ext. flags, _objId
340 item->moveToContainer(this);
341 item->randomGumpLocation();
342 item->callUsecodeEvent_combine(); // this sets the right frame
343 continue;
344 }
345 }
346
347 if (ti._shapes.empty() || ti._frames.empty()) {
348 perr << "No shape/frame set in treasure" << Std::endl;
349 continue;
350 }
351
352 // we need to produce a number of items
353 for (int j = 0; (int)j < count; ++j) {
354 // pick shape
355 int n = getRandom() % ti._shapes.size();
356 uint32 shapeNum = ti._shapes[n];
357
358 // pick frame
359 n = getRandom() % ti._frames.size();
360 uint32 frameNum = ti._frames[n];
361
362 const ShapeInfo *si = GameData::get_instance()->getMainShapes()->
363 getShapeInfo(shapeNum);
364 if (!si) {
365 perr << "Trying to create treasure with an invalid shapeNum ("
366 << shapeNum << ")" << Std::endl;
367 continue;
368 }
369 uint16 qual = 0;
370 if (si->hasQuantity())
371 qual = 1;
372
373 // CHECKME: flags?
374 item = ItemFactory::createItem(shapeNum,
375 frameNum, // frameNum
376 qual, // quality
377 Item::FLG_DISPOSABLE, // flags
378 0, // npcnum,
379 0, // mapnum
380 0, true); // ext. flags, _objId
381 item->moveToContainer(this);
382 item->randomGumpLocation();
383 }
384 }
385
386 return true;
387 }
388
removeItem(Item * item)389 bool Actor::removeItem(Item *item) {
390 if (!Container::removeItem(item)) return false;
391
392 item->clearFlag(FLG_EQUIPPED); // unequip if necessary
393
394 return true;
395 }
396
setEquip(Item * item,bool checkwghtvol)397 bool Actor::setEquip(Item *item, bool checkwghtvol) {
398 uint32 equiptype = item->getShapeInfo()->_equipType;
399 bool backpack = (item->getShape() == BACKPACK_SHAPE);
400
401 // valid item type?
402 if (equiptype == ShapeInfo::SE_NONE && !backpack) return false;
403
404 // now check 'equipment slots'
405 // we can have one item of each equipment type, plus one backpack
406 for (Std::list<Item *>::const_iterator iter = _contents.begin(); iter != _contents.end(); ++iter) {
407 if ((*iter)->getObjId() == item->getObjId()) continue;
408
409 uint32 cet = (*iter)->getShapeInfo()->_equipType;
410 bool cbackpack = ((*iter)->getShape() == BACKPACK_SHAPE);
411
412 // already have an item with the same equiptype
413 if (cet == equiptype || (cbackpack && backpack)) return false;
414 }
415
416 if (!item->moveToContainer(this, checkwghtvol)) return false;
417 item->clearFlag(FLG_CONTAINED);
418 item->setFlag(FLG_EQUIPPED);
419 item->setZ(equiptype);
420
421 return true;
422 }
423
getEquip(uint32 type) const424 uint16 Actor::getEquip(uint32 type) const {
425 Std::list<Item *>::const_iterator iter;
426 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
427 uint32 cet = (*iter)->getShapeInfo()->_equipType;
428 bool cbackpack = ((*iter)->getShape() == BACKPACK_SHAPE);
429
430 if ((*iter)->hasFlags(FLG_EQUIPPED) &&
431 (cet == type || (cbackpack && type == 7))) { // !! constant
432 return (*iter)->getObjId();
433 }
434 }
435
436 return 0;
437 }
438
teleport(int newmap,int32 newx,int32 newy,int32 newz)439 void Actor::teleport(int newmap, int32 newx, int32 newy, int32 newz) {
440 uint16 newmapnum = static_cast<uint16>(newmap);
441
442 // Set the mapnum
443 setMapNum(newmapnum);
444
445 // Put it in the void
446 moveToEtherealVoid();
447
448 // Move it to this map
449 if (newmapnum == World::get_instance()->getCurrentMap()->getNum()) {
450 #ifdef DEBUG
451 perr << "Actor::teleport: " << getObjId() << " to " << newmap << ","
452 << newx << "," << newy << "," << newz << Std::endl;
453 #endif
454 move(newx, newy, newz);
455 }
456 // Move it to another map
457 else {
458 World::get_instance()->etherealRemove(_objId);
459 _x = newx;
460 _y = newy;
461 _z = newz;
462 }
463 }
464
doAnim(Animation::Sequence anim,Direction dir,unsigned int steps)465 uint16 Actor::doAnim(Animation::Sequence anim, Direction dir, unsigned int steps) {
466 if (dir < 0 || dir > 16) {
467 perr << "Actor::doAnim: Invalid _direction (" << dir << ")" << Std::endl;
468 return 0;
469 }
470
471 if (dir == dir_current)
472 dir = getDir();
473
474 #if 0
475 if (tryAnim(anim, dir)) {
476 perr << "Actor::doAnim: tryAnim = Ok!" << Std::endl;
477 } else {
478 perr << "Actor::doAnim: tryAnim = bad!" << Std::endl;
479 }
480 #endif
481
482 if (GAME_IS_CRUSADER) {
483 // Crusader sets some flags on animation start
484 // Small hack: When switching from 16-dir to 8-dir, fix the direction
485 if (animDirMode(anim) == dirmode_8dirs)
486 dir = static_cast<Direction>(dir - (static_cast<uint32>(dir) % 2));
487
488 if (anim == Animation::readyWeapon || anim == Animation::stopRunningAndDrawSmallWeapon ||
489 anim == Animation::combatStand || anim == Animation::attack || anim == Animation::kneel ||
490 anim == Animation::kneelAndFire || anim == Animation::reloadSmallWeapon)
491 setActorFlag(ACT_WEAPONREADY);
492 else
493 clearActorFlag(ACT_WEAPONREADY);
494
495 if (anim == Animation::kneelStartCru || anim == Animation::kneelAndFire ||
496 anim == Animation::kneelAndFireSmallWeapon ||
497 anim == Animation::kneelAndFireLargeWeapon ||
498 anim == Animation::kneelCombatRollLeft ||
499 anim == Animation::kneelCombatRollRight ||
500 anim == Animation::combatRollLeft ||
501 anim == Animation::combatRollRight ||
502 anim == Animation::kneelingAdvance ||
503 anim == Animation::kneelingRetreat) {
504 setActorFlag(ACT_KNEELING);
505 } else if (anim != Animation::kneel) {
506 clearActorFlag(ACT_KNEELING);
507 }
508
509 const uint32 frameno = Kernel::get_instance()->getFrameNum();
510 switch(anim) {
511 case Animation::walk:
512 case Animation::retreat: // SmallWeapon
513 _attackMoveStartFrame = frameno;
514 _attackMoveTimeout = 120;
515 _attackMoveDodgeFactor = 3;
516 break;
517 case Animation::run:
518 case Animation::kneelCombatRollLeft:
519 case Animation::kneelCombatRollRight:
520 case Animation::stopRunningAndDrawLargeWeapon:
521 case Animation::stopRunningAndDrawSmallWeapon:
522 case Animation::jumpForward:
523 case Animation::jump:
524 case Animation::combatRollLeft:
525 case Animation::combatRollRight:
526 //case Animation::startRunSmallWeapon:
527 //case Animation::startRunLargeWeapon:
528 case Animation::startRun:
529 _attackMoveStartFrame = frameno;
530 _attackMoveTimeout = 120;
531 _attackMoveDodgeFactor = 2;
532 break;
533 case Animation::slideLeft:
534 case Animation::slideRight:
535 _attackMoveStartFrame = frameno;
536 _attackMoveTimeout = 60;
537 _attackMoveDodgeFactor = 3;
538 break;
539 case Animation::startKneeling:
540 case Animation::stopKneeling:
541 _attackMoveStartFrame = frameno;
542 _attackMoveTimeout = 75;
543 _attackMoveDodgeFactor = 3;
544 break;
545 default:
546 break;
547 }
548
549 }
550
551 #if 0
552 if (_objId == 1) {
553 int32 x, y, z;
554 getLocation(x, y, z);
555 int32 actionno = AnimDat::getActionNumberForSequence(anim, this);
556 const AnimAction *action = GameData::get_instance()->getMainShapes()->getAnim(getShape(), actionno);
557 debug(6, "Actor::doAnim(%d, %d, %d) from (%d, %d, %d) frame repeat %d", anim, dir, steps, x, y, z, action ? action->getFrameRepeat() : -1);
558 }
559 #endif
560
561 Process *p = new ActorAnimProcess(this, anim, dir, steps);
562
563 return Kernel::get_instance()->addProcess(p);
564 }
565
doAnimAfter(Animation::Sequence anim,Direction dir,ProcId waitfor)566 uint16 Actor::doAnimAfter(Animation::Sequence anim, Direction dir, ProcId waitfor) {
567 ProcId newanim = doAnim(anim, dir);
568 if (newanim && waitfor) {
569 Process *newproc = Kernel::get_instance()->getProcess(newanim);
570 newproc->waitFor(waitfor);
571 }
572 return newanim;
573 }
574
isBusy() const575 bool Actor::isBusy() const {
576 uint32 count = Kernel::get_instance()->getNumProcesses(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE);
577 return count != 0;
578 }
579
hasAnim(Animation::Sequence anim)580 bool Actor::hasAnim(Animation::Sequence anim) {
581 AnimationTracker tracker;
582
583 return tracker.init(this, anim, dir_north);
584 }
585
setToStartOfAnim(Animation::Sequence anim)586 void Actor::setToStartOfAnim(Animation::Sequence anim) {
587 AnimationTracker tracker;
588 if (tracker.init(this, anim, getDir())) {
589 const AnimFrame *f = tracker.getAnimFrame();
590 setFrame(f->_frame);
591 if ((GAME_IS_U8 && f->is_flipped())
592 || (GAME_IS_CRUSADER && f->is_cruflipped())) {
593 setFlag(Item::FLG_FLIPPED);
594 } else {
595 clearFlag(Item::FLG_FLIPPED);
596 }
597 setLastAnim(anim);
598 }
599 }
600
tryAnim(Animation::Sequence anim,Direction dir,unsigned int steps,PathfindingState * state)601 Animation::Result Actor::tryAnim(Animation::Sequence anim, Direction dir,
602 unsigned int steps, PathfindingState *state) {
603 if (dir < 0 || dir > 16) return Animation::FAILURE;
604
605 if (dir == dir_current)
606 dir = getDir();
607
608 AnimationTracker tracker;
609 if (!tracker.init(this, anim, dir, state))
610 return Animation::FAILURE;
611
612 const AnimAction *animaction = tracker.getAnimAction();
613
614 if (!animaction) return Animation::FAILURE;
615
616 unsigned int curstep = 0;
617
618 while (tracker.step() && (!steps || curstep >= steps)) {
619 curstep++;
620 }
621
622 if (tracker.isBlocked() &&
623 !animaction->hasFlags(AnimAction::AAF_UNSTOPPABLE)) {
624 return Animation::FAILURE;
625 }
626
627 if (state) {
628 tracker.updateState(*state);
629 state->_lastAnim = anim;
630 state->_direction = dir;
631 }
632
633
634 if (tracker.isUnsupported()) {
635 return Animation::END_OFF_LAND;
636 }
637
638 // isUnsupported only checks for AFF_ONGROUND, we need either
639 int32 end[3], dims[3];
640 getFootpadWorld(dims[0], dims[1], dims[2]);
641 tracker.getPosition(end[0], end[1], end[2]);
642
643 CurrentMap *cm = World::get_instance()->getCurrentMap();
644
645 UCList uclist(2);
646 LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
647 cm->surfaceSearch(&uclist, script, sizeof(script),
648 getObjId(), end, dims,
649 false, true, false);
650 for (uint32 i = 0; i < uclist.getSize(); i++) {
651 Item *item = getItem(uclist.getuint16(i));
652 if (item->getShapeInfo()->is_land())
653 return Animation::SUCCESS;
654 }
655
656 return Animation::END_OFF_LAND;
657 }
658
animDirMode(Animation::Sequence anim) const659 DirectionMode Actor::animDirMode(Animation::Sequence anim) const {
660 int32 actionno = AnimDat::getActionNumberForSequence(anim, this);
661 const AnimAction *action = GameData::get_instance()->getMainShapes()->getAnim(getShape(), actionno);
662
663 if (!action)
664 return dirmode_8dirs;
665 return action->getDirCount() == 8 ? dirmode_8dirs : dirmode_16dirs;
666 }
667
turnTowardDir(Direction targetdir,ProcId prevpid)668 uint16 Actor::turnTowardDir(Direction targetdir, ProcId prevpid /* = 0 */) {
669 bool combatRun = hasActorFlags(Actor::ACT_COMBATRUN);
670 Direction curdir = getDir();
671 bool combat = isInCombat() && !combatRun;
672 Animation::Sequence standanim = Animation::stand;
673 bool surrendered = hasActorFlags(Actor::ACT_SURRENDERED);
674
675 int stepDelta = Direction_GetShorterTurnDelta(curdir, targetdir);
676 Animation::Sequence turnanim = Animation::stand;
677 if (GAME_IS_U8) {
678 if (stepDelta == -1) {
679 turnanim = Animation::lookLeft;
680 } else {
681 turnanim = Animation::lookRight;
682 }
683 }
684
685 if (targetdir == curdir || targetdir == dir_current)
686 return 0; // nothing to do.
687
688 if (combat) {
689 turnanim = Animation::combatStand;
690 standanim = Animation::combatStand;
691 } else if (surrendered) {
692 turnanim = Animation::surrenderStand;
693 standanim = Animation::surrenderStand;
694 }
695
696 ProcId animpid = 0;
697
698 // Create a sequence of turn animations from
699 // our current direction to the new one
700 DirectionMode mode = animDirMode(turnanim);
701
702 // slight hack - avoid making 8-step turns if we need to swap
703 // to/from a 16-step direction - we'll never get to the right
704 // direction that way.
705 if (static_cast<uint32>(targetdir) % 2 !=
706 static_cast<uint32>(curdir) % 2) {
707 mode = dirmode_16dirs;
708 }
709
710 bool done = false;
711 Direction firstanimdir = curdir;
712
713 // Skip animating "stand" for the current direction in Crusader.
714 if (GAME_IS_CRUSADER)
715 firstanimdir = Direction_TurnByDelta(curdir, stepDelta, mode);
716
717 for (Direction dir = firstanimdir; !done; dir = Direction_TurnByDelta(dir, stepDelta, mode)) {
718 Animation::Sequence nextanim = turnanim;
719 if (dir == targetdir) {
720 nextanim = standanim;
721 done = true;
722 }
723
724 animpid = doAnim(nextanim, dir);
725 if (prevpid) {
726 Process *proc = Kernel::get_instance()->getProcess(animpid);
727 assert(proc);
728 proc->waitFor(prevpid);
729 }
730
731 prevpid = animpid;
732 }
733
734 return animpid;
735 }
736
setActivity(int activity)737 uint16 Actor::setActivity(int activity) {
738 if (GAME_IS_CRUSADER)
739 return setActivityCru(activity);
740 else
741 return setActivityU8(activity);
742 }
743
setActivityU8(int activity)744 uint16 Actor::setActivityU8(int activity) {
745 switch (activity) {
746 case 0: // loiter
747 Kernel::get_instance()->addProcess(new LoiterProcess(this));
748 return Kernel::get_instance()->addProcess(new DelayProcess(1));
749 break;
750 case 1: // combat
751 setInCombatU8();
752 return 0;
753 case 2: // stand
754 // NOTE: temporary fall-throughs!
755 return doAnim(Animation::stand, dir_current);
756
757 default:
758 perr << "Actor::setActivityU8: invalid activity (" << activity << ")"
759 << Std::endl;
760 }
761
762 return 0;
763 }
764
setActivityCru(int activity)765 uint16 Actor::setActivityCru(int activity) {
766 if (isDead() || World::get_instance()->getControlledNPCNum() == _objId
767 || hasActorFlags(ACT_WEAPONREADY) || activity == 0)
768 return 0;
769
770 if ((World::get_instance()->getGameDifficulty() == 4) && (getRandom() % 2 == 0)) {
771 if (activity == 5)
772 activity = 0xa;
773 if (activity == 9)
774 activity = 0xb;
775 }
776
777 if (_currentActivityNo == activity || (isInCombat() && activity != 0xc))
778 return 0;
779
780 _lastActivityNo = _currentActivityNo;
781 _currentActivityNo = activity;
782
783 if (isInCombat())
784 clearInCombat();
785
786 if (!hasFlags(FLG_FASTAREA))
787 return 0;
788
789 Kernel *kernel = Kernel::get_instance();
790
791 static const uint16 PROCSTYPES_TO_KILL[] = {
792 ActorAnimProcess::ACTOR_ANIM_PROC_TYPE,
793 PathfinderProcess::PATHFINDER_PROC_TYPE,
794 0x255, // PaceProcess
795 0x257, // LoiterProcess
796 // 0x258, // Stand Process (we don't have a process for this)
797 AttackProcess::ATTACK_PROCESS_TYPE,
798 0x25e, // GuardProcess
799 0x25f // SurrenderProcess
800 };
801 for (int i = 0; i < ARRAYSIZE(PROCSTYPES_TO_KILL); i++) {
802 kernel->killProcesses(_objId, PROCSTYPES_TO_KILL[i], true);
803 }
804
805 switch (activity) {
806 case 1: // stand
807 return doAnim(Animation::stand, dir_current);
808 case 2: // loiter
809 return kernel->addProcess(new LoiterProcess(this));
810 case 3: // pace
811 return kernel->addProcess(new PaceProcess(this));
812 case 4:
813 case 6:
814 // Does nothing in game..
815 break;
816 case 7:
817 if (_lastActivityNo != 7)
818 return kernel->addProcess(new SurrenderProcess(this));
819 break;
820 case 8:
821 return kernel->addProcess(new GuardProcess(this));
822 case 5:
823 case 9:
824 case 0xa:
825 case 0xb:
826 case 0xc:
827 // attack
828 setInCombatCru(activity);
829 return 0;
830 case 0xd:
831 // Only in No Regret
832 setActorFlag(ACT_INCOMBAT);
833 return kernel->addProcess(new RollingThunderProcess(this));
834 case 0x70:
835 return setActivity(getDefaultActivity(0));
836 case 0x71:
837 return setActivity(getDefaultActivity(1));
838 case 0x72:
839 return setActivity(getDefaultActivity(2));
840 default:
841 perr << "Actor::setActivityCru: invalid activity (" << activity << ")"
842 << Std::endl;
843 return doAnim(Animation::stand, dir_current);
844 }
845
846 return 0;
847 }
848
849
getArmourClass() const850 uint32 Actor::getArmourClass() const {
851 const ShapeInfo *si = getShapeInfo();
852 if (si->_monsterInfo)
853 return si->_monsterInfo->_armourClass;
854 else
855 return 0;
856 }
857
getDefenseType() const858 uint16 Actor::getDefenseType() const {
859 const ShapeInfo *si = getShapeInfo();
860 if (si->_monsterInfo)
861 return si->_monsterInfo->_defenseType;
862 else
863 return 0;
864 }
865
getDefendingDex() const866 int16 Actor::getDefendingDex() const {
867 return getDex();
868 }
869
getAttackingDex() const870 int16 Actor::getAttackingDex() const {
871 return getDex();
872 }
873
getDamageType() const874 uint16 Actor::getDamageType() const {
875 const ShapeInfo *si = getShapeInfo();
876 if (si->_monsterInfo)
877 return si->_monsterInfo->_damageType;
878 else
879 return WeaponInfo::DMG_NORMAL;
880 }
881
882
getDamageAmount() const883 int Actor::getDamageAmount() const {
884 const ShapeInfo *si = getShapeInfo();
885 if (si->_monsterInfo) {
886
887 int min = static_cast<int>(si->_monsterInfo->_minDmg);
888 int max = static_cast<int>(si->_monsterInfo->_maxDmg);
889
890 int damage = (getRandom() % (max - min + 1)) + min;
891
892 return damage;
893 } else {
894 return 1;
895 }
896 }
897
setDefaultActivity(int no,uint16 activity)898 void Actor::setDefaultActivity(int no, uint16 activity) {
899 assert(no >= 0 && no < 3);
900 _defaultActivity[no] = activity;
901 }
902
getDefaultActivity(int no) const903 uint16 Actor::getDefaultActivity(int no) const {
904 assert(no >= 0 && no < 3);
905 return _defaultActivity[no];
906 }
907
setHomePosition(int32 x,int32 y,int32 z)908 void Actor::setHomePosition(int32 x, int32 y, int32 z) {
909 _homeX = x;
910 _homeY = y;
911 _homeZ = z;
912 }
913
getHomePosition(int32 & x,int32 & y,int32 & z) const914 void Actor::getHomePosition(int32 &x, int32 &y, int32 &z) const {
915 x = _homeX;
916 y = _homeY;
917 z = _homeZ;
918 }
919
920
receiveHit(uint16 other,Direction dir,int damage,uint16 damage_type)921 void Actor::receiveHit(uint16 other, Direction dir, int damage, uint16 damage_type) {
922 if (GAME_IS_U8) {
923 receiveHitU8(other, dir, damage, damage_type);
924 } else {
925 receiveHitCru(other, dir, damage, damage_type);
926 }
927 }
928
receiveHitCru(uint16 other,Direction dir,int damage,uint16 damage_type)929 void Actor::receiveHitCru(uint16 other, Direction dir, int damage, uint16 damage_type) {
930 //
931 // This is a big stack of constants and hard-coded things.
932 // It's like that in the original game.
933 //
934 Actor *attacker = getActor(other);
935 AudioProcess *audio = AudioProcess::get_instance();
936 Kernel *kernel = Kernel::get_instance();
937 uint32 shape = getShape();
938 World *world = World::get_instance();
939
940 // Special case for Vargas, who has a shield.
941 if (GAME_IS_REMORSE && shape == 0x3ac && world->getVargasShield() > 0) {
942 uint16 currentanim = 0;
943 if (isBusy()) {
944 ActorAnimProcess *proc = dynamic_cast<ActorAnimProcess *>(Kernel::get_instance()->findProcess(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE));
945 Animation::Sequence action = proc->getAction();
946 if (action == Animation::teleportIn || action == Animation::teleportOut || action == Animation::teleportInReplacement || action == Animation::teleportOutReplacement)
947 return;
948 currentanim = proc->getPid();
949 }
950
951 ProcId teleout = doAnimAfter(Animation::teleportOutReplacement, dir_current, currentanim);
952 doAnimAfter(Animation::teleportInReplacement, dir_current, teleout);
953 int newval = MAX(0, static_cast<int>(world->getVargasShield()) - damage);
954 world->setVargasShield(static_cast<uint32>(newval));
955 return;
956 } else if (GAME_IS_REGRET && shape == 0x5b1) {
957 warning("TODO: Finish Shape 0x5b1 special case for No Regret.");
958 /*
959 _bossHealth = _bossHealth - damage;
960 if (_bossHealth < 1) {
961 _bossHealth = 0;
962 Sprite_Int_Create(0x4f1,10,0x13,3,local_8,local_6,local_4);
963 Item_PlaySFXCru((uint *)pitemno,0x1eb);
964 } else {
965 Sprite_Int_Create(0x4f1,0,9,3,local_8,local_6,local_4);
966 }
967 */
968 }
969
970 if (isDead() && (getShape() != 0x5d6 || GAME_IS_REMORSE))
971 return;
972
973 _lastTickWasHit = Kernel::get_instance()->getTickNum();
974
975 if (shape != 1 && this != getControlledActor()) {
976 Actor *controlled = getControlledActor();
977 if (!isInCombat()) {
978 setActivity(getDefaultActivity(2)); // get activity from field 0xA
979 if (!isInCombat()) {
980 setInCombatCru(5);
981 CombatProcess *combat = getCombatProcess();
982 if (combat && controlled) {
983 combat->setTarget(controlled->getObjId());
984 }
985 }
986 } else {
987 if (getCurrentActivityNo() == 8) {
988 setActivity(5);
989 }
990 setInCombatCru(5);
991 CombatProcess *combat = getCombatProcess();
992 if (combat && controlled) {
993 combat->setTarget(controlled->getObjId());
994 }
995 }
996
997 // If the attacker is the controlled npc and this actor is not pathfinding
998 if (attacker && attacker == getControlledActor() &&
999 kernel->findProcess(_objId, PathfinderProcess::PATHFINDER_PROC_TYPE) != nullptr) {
1000 int32 x, y, z;
1001 int32 ox, oy, oz;
1002 getLocation(x, y, z);
1003 attacker->getLocation(ox, oy, oz);
1004 int32 maxdiff = MAX(MAX(abs(x - ox), abs(y - oy)), abs(z - oz));
1005 if (maxdiff < 641 && isOnScreen()) {
1006 // TODO: implement the equivalent of this function. For now, we always
1007 // cancel pathfinding for the NPC.
1008 // uint32 direction = static_cast<uint32>(Get_WorldDirection(y - oy, x - ox));
1009 // int result = FUN_1128_1755(this, attacker, direction, 0, 0, 0);
1010 // if (result) {
1011 kernel->killProcesses(_objId, PathfinderProcess::PATHFINDER_PROC_TYPE, true);
1012 // }
1013 }
1014 }
1015 } else {
1016 damage = receiveShieldHit(damage, damage_type);
1017 }
1018
1019 if (hasActorFlags(ACT_IMMORTAL | ACT_INVINCIBLE))
1020 damage = 0;
1021
1022 if (damage > _hitPoints)
1023 damage = _hitPoints;
1024
1025 setHP(static_cast<uint16>(_hitPoints - damage));
1026
1027 if (_hitPoints == 0) {
1028 // Die!
1029 die(damage_type, damage, dir);
1030 } else if (damage) {
1031 // Not dead yet.
1032 if (!isRobotCru()) {
1033 uint16 sfxno;
1034 if (hasExtFlags(EXT_FEMALE)) {
1035 sfxno = 0xd8; // female scream
1036 } else {
1037 sfxno = 0x8f; // male scream
1038 }
1039 if (audio && !audio->isSFXPlayingForObject(sfxno, other)) {
1040 audio->playSFX(sfxno, 0x10, other, 1, false);
1041 }
1042 }
1043 if (damage_type == 0xf || damage_type == 7) {
1044 if (shape == 1) {
1045 kernel->killProcesses(_objId, PathfinderProcess::PATHFINDER_PROC_TYPE, true);
1046 doAnim(static_cast<Animation::Sequence>(0x37), dir_current);
1047 } else if (shape == 0x4e6 || shape == 0x338 || shape == 0x385 || shape == 899) {
1048 if (!(getRandom() % 3)) {
1049 // Randomly stun the NPC for these damage types.
1050 // CHECK ME: is this time accurate?
1051 Process *attack = kernel->findProcess(_objId, AttackProcess::ATTACK_PROCESS_TYPE);
1052 uint stun = ((getRandom() % 10) + 8) * 60;
1053 if (attack && stun) {
1054 Process *delay = new DelayProcess(stun);
1055 kernel->addProcess(delay);
1056 attack->waitFor(delay);
1057 }
1058 }
1059 }
1060 }
1061 }
1062 }
1063
1064 #define RAND_ELEM(array) (array[getRandom() % ARRAYSIZE(array)])
1065
tookHitCru()1066 void Actor::tookHitCru() {
1067 AudioProcess *audio = AudioProcess::get_instance();
1068 Animation::Sequence lastanim = getLastAnim();
1069 bool isfemale = hasExtFlags(EXT_FEMALE);
1070 if (!audio)
1071 return;
1072
1073 if (lastanim == Animation::unknownAnim30 || lastanim == Animation::startRunLargeWeapon) {
1074 if (canSeeControlledActor(true)) {
1075 if (getRandom() % 4)
1076 setActivity(5);
1077 else
1078 setActivity(10);
1079 }
1080 } else if (GAME_IS_REMORSE) {
1081 uint32 shape = getShape();
1082 if (shape == 0x385 || shape == 0x4e6) {
1083 explode(2, 0);
1084 clearFlag(FLG_IN_NPC_LIST | FLG_GUMP_OPEN);
1085 } else if (shape == 0x576 || shape == 0x596) {
1086 bool violence = true; // Game::I_isViolenceEnabled
1087 if (!violence)
1088 return;
1089
1090 static const uint16 FEMALE_SFX[] = {0xb, 0xa};
1091 static const uint16 MALE_SFX[] = {0x65, 0x66, 0x67};
1092 int nsounds = isfemale ? ARRAYSIZE(FEMALE_SFX) : ARRAYSIZE(MALE_SFX);
1093 const uint16 *sounds = isfemale ? FEMALE_SFX : MALE_SFX;
1094
1095 for (int i = 0; i < nsounds; i++) {
1096 if (audio->isSFXPlayingForObject(sounds[i], _objId))
1097 return;
1098 }
1099
1100 audio->playSFX(sounds[getRandom() % nsounds], 0x80, _objId, 1);
1101 }
1102 } else if (GAME_IS_REGRET) {
1103 switch (getShape()) {
1104 case 0x596: {
1105 static const uint16 FEMALE_SFX[] = {0x212, 0x211};
1106 static const uint16 MALE_SFX[] = {0x213, 0x214};
1107 int sfxno = isfemale ? RAND_ELEM(FEMALE_SFX) : RAND_ELEM(MALE_SFX);
1108 audio->playSFX(sfxno, 0x80, _objId, 1);
1109 break;
1110 }
1111 case 0x576: {
1112 static const uint16 FEMALE_SFX[] = {0x3D, 0x77, 0x210};
1113 static const uint16 MALE_SFX[] = {0x24F, 0x250, 0x201, 0x200};
1114 bool violence = true; // Game::I_isViolenceEnabled
1115 if (!violence)
1116 return;
1117
1118 int nsounds = isfemale ? ARRAYSIZE(FEMALE_SFX) : ARRAYSIZE(MALE_SFX);
1119 const uint16 *sounds = isfemale ? FEMALE_SFX : MALE_SFX;
1120
1121 for (int i = 0; i < nsounds; i++) {
1122 if (audio->isSFXPlayingForObject(sounds[i], _objId))
1123 return;
1124 }
1125
1126 audio->playSFX(sounds[getRandom() % nsounds], 0x80, _objId, 1);
1127 return;
1128 }
1129 case 0x385:
1130 case 0x4e6: {
1131 explode(2, false);
1132 clearFlag(FLG_GUMP_OPEN | FLG_IN_NPC_LIST);
1133 break;
1134 }
1135 case 0x5d6: {
1136 static const uint16 MALE_SFX[] = {0x21B, 0x21A, 0x21C};
1137 static const uint16 FEMALE_SFX[] = {0x21D, 0x215};
1138 int sfxno = isfemale ? RAND_ELEM(FEMALE_SFX) : RAND_ELEM(MALE_SFX);
1139 audio->playSFX(sfxno, 0x80, _objId, 1);
1140 break;
1141 }
1142 case 0x62d: {
1143 static const uint16 MALE_SFX[] = {0x217, 0x218};
1144 static const uint16 FEMALE_SFX[] = {0x219, 0x216};
1145 int sfxno = isfemale ? RAND_ELEM(FEMALE_SFX) : RAND_ELEM(MALE_SFX);
1146 audio->playSFX(sfxno, 0x80, _objId, 1);
1147 break;
1148 }
1149 case 0x656:
1150 case 0x278: {
1151 static const uint16 MALE_SFX[] = {0x20B, 0x20C, 0x20D};
1152 static const uint16 FEMALE_SFX[] = {0x20E, 0x20F};
1153 int sfxno = isfemale ? RAND_ELEM(FEMALE_SFX) : RAND_ELEM(MALE_SFX);
1154 audio->playSFX(sfxno, 0x80, _objId, 1);
1155 break;
1156 }
1157 case 0x5b1: {
1158 Process *proc = new BoboBoomerProcess(this);
1159 Kernel::get_instance()->addProcess(proc);
1160 break;
1161 }
1162 case 0x58f:
1163 case 0x59c: {
1164 static const uint16 SOUNDS[] = {0xD9, 0xDA};
1165 audio->playSFX(RAND_ELEM(SOUNDS), 0x80, _objId, 1);
1166 break;
1167 }
1168 default:
1169 break;
1170 }
1171 }
1172 }
1173
receiveHitU8(uint16 other,Direction dir,int damage,uint16 damage_type)1174 void Actor::receiveHitU8(uint16 other, Direction dir, int damage, uint16 damage_type) {
1175 if (isDead())
1176 return; // already dead, so don't bother
1177
1178 Item *hitter = getItem(other);
1179 Actor *attacker = getActor(other);
1180
1181 if (damage == 0 && attacker) {
1182 damage = attacker->getDamageAmount();
1183 }
1184
1185 if (damage_type == 0 && hitter) {
1186 damage_type = hitter->getDamageType();
1187 }
1188
1189 if (other == 1 && attacker && attacker->getLastAnim() != Animation::kick) {
1190 // _strength for kicks is accumulated in AvatarMoverProcess
1191 MainActor *av = getMainActor();
1192 av->accumulateStr(damage / 4);
1193 }
1194
1195 pout << "Actor " << getObjId() << " received hit from " << other
1196 << " (dmg=" << damage << ",type=" << ConsoleStream::hex << damage_type
1197 << ConsoleStream::dec << "). ";
1198
1199 damage = calculateAttackDamage(other, damage, damage_type);
1200
1201 if (!damage) {
1202 pout << "No damage." << Std::endl;
1203 } else {
1204 pout << "Damage: " << damage << Std::endl;
1205 }
1206
1207 if (damage >= 4 && _objId == 1 && attacker) {
1208 // play blood sprite
1209 int start = 0, end = 12;
1210 if (dir > dir_east) {
1211 start = 13;
1212 end = 25;
1213 }
1214
1215 int32 xv, yv, zv;
1216 getLocation(xv, yv, zv);
1217 zv += (getRandom() % 24);
1218 Process *sp = new SpriteProcess(620, start, end, 1, 1, xv, yv, zv);
1219 Kernel::get_instance()->addProcess(sp);
1220 }
1221
1222 if (damage > 0 && !hasActorFlags(ACT_IMMORTAL | ACT_INVINCIBLE)) {
1223 if (damage >= _hitPoints) {
1224 // we're dead
1225
1226 if (hasActorFlags(ACT_WITHSTANDDEATH)) {
1227 // or maybe not...
1228
1229 setHP(getMaxHP());
1230 AudioProcess *audioproc = AudioProcess::get_instance();
1231 if (audioproc) audioproc->playSFX(59, 0x60, _objId, 0);
1232 clearActorFlag(ACT_WITHSTANDDEATH);
1233 } else {
1234 die(damage_type, damage, dir);
1235 }
1236 return;
1237 }
1238
1239 // not dead yet
1240 setHP(static_cast<uint16>(_hitPoints - damage));
1241 }
1242
1243 ProcId fallingprocid = 0;
1244 if (_objId == 1 && damage > 0) {
1245 if ((damage_type & WeaponInfo::DMG_FALLING) && damage >= 6) {
1246 // high falling damage knocks you down
1247 doAnim(Animation::fallBackwards, dir_current);
1248
1249 // TODO: shake head after getting back up when not in combat
1250 return;
1251 }
1252
1253 // got hit, so abort current animation
1254 fallingprocid = killAllButFallAnims(false);
1255 }
1256
1257 // if avatar was blocking; do a quick stopBlock/startBlock and play SFX
1258 if (_objId == 1 && getLastAnim() == Animation::startBlock) {
1259 ProcId anim1pid = doAnim(Animation::stopBlock, dir_current);
1260 ProcId anim2pid = doAnim(Animation::startBlock, dir_current);
1261
1262 Process *anim1proc = Kernel::get_instance()->getProcess(anim1pid);
1263 Process *anim2proc = Kernel::get_instance()->getProcess(anim2pid);
1264 assert(anim1proc);
1265 assert(anim2proc);
1266 anim2proc->waitFor(anim1proc);
1267
1268 int sfx;
1269 if (damage)
1270 sfx = 50 + (getRandom() % 2); // constants!
1271 else
1272 sfx = 20 + (getRandom() % 3); // constants!
1273 AudioProcess *audioproc = AudioProcess::get_instance();
1274 if (audioproc) audioproc->playSFX(sfx, 0x60, _objId, 0);
1275 return;
1276 }
1277
1278 // TODO: target needs to stumble/fall/call for help/...(?)
1279
1280 if (_objId != 1) {
1281 ObjId target = 1;
1282 if (attacker)
1283 target = attacker->getObjId();
1284 if (!isInCombat())
1285 setInCombatU8();
1286
1287 CombatProcess *cp = getCombatProcess();
1288 assert(cp);
1289 cp->setTarget(target);
1290
1291 if (target == 1) {
1292 // call for help
1293 }
1294 }
1295
1296 if (damage && !fallingprocid) {
1297 ProcId anim1pid = doAnim(Animation::stumbleBackwards, dir);
1298 ProcId anim2pid;
1299 if (isInCombat())
1300 // not doing this would cause you to re-draw your weapon when hit
1301 anim2pid = doAnim(Animation::combatStand, dir);
1302 else
1303 anim2pid = doAnim(Animation::stand, dir);
1304 Process *anim1proc = Kernel::get_instance()->getProcess(anim1pid);
1305 Process *anim2proc = Kernel::get_instance()->getProcess(anim2pid);
1306 assert(anim1proc);
1307 assert(anim2proc);
1308 anim2proc->waitFor(anim1proc);
1309 }
1310 }
1311
die(uint16 damageType,uint16 damagePts,Direction srcDir)1312 ProcId Actor::die(uint16 damageType, uint16 damagePts, Direction srcDir) {
1313 setHP(0);
1314 setActorFlag(ACT_DEAD);
1315 setFlag(FLG_BROKEN);
1316 clearActorFlag(ACT_INCOMBAT);
1317
1318 if (GAME_IS_U8)
1319 return dieU8(damageType);
1320 else
1321 return dieCru(damageType, damagePts, srcDir);
1322 }
1323
dieU8(uint16 damageType)1324 ProcId Actor::dieU8(uint16 damageType) {
1325 ProcId animprocid = 0;
1326 #if 1
1327 animprocid = killAllButFallAnims(true);
1328 #else
1329 Kernel::get_instance()->killProcesses(getObjId(), Kernel::PROC_TYPE_ALL, true);
1330 #endif
1331
1332 if (!animprocid)
1333 animprocid = doAnim(Animation::die, dir_current);
1334
1335 MainActor *avatar = getMainActor();
1336 // if hostile to avatar
1337 if (getEnemyAlignment() & avatar->getAlignment()) {
1338 if (avatar->isInCombat()) {
1339 // play victory fanfare
1340 MusicProcess::get_instance()->playCombatMusic(109);
1341 // and resume combat music afterwards
1342 MusicProcess::get_instance()->queueMusic(98);
1343 }
1344 }
1345
1346 destroyContents();
1347 giveTreasure();
1348
1349 const ShapeInfo *shapeinfo = getShapeInfo();
1350 const MonsterInfo *mi = nullptr;
1351 if (shapeinfo) mi = shapeinfo->_monsterInfo;
1352
1353 if (mi && mi->_resurrection && !(damageType & WeaponInfo::DMG_FIRE)) {
1354 // this monster will be resurrected after a while
1355
1356 pout << "Actor::die: scheduling resurrection" << Std::endl;
1357
1358 int timeout = ((getRandom() % 25) + 5) * 30; // 5-30 seconds
1359
1360 Process *resproc = new ResurrectionProcess(this);
1361 Kernel::get_instance()->addProcess(resproc);
1362
1363 Process *delayproc = new DelayProcess(timeout);
1364 Kernel::get_instance()->addProcess(delayproc);
1365
1366 ProcId animpid = doAnim(Animation::standUp, dir_current);
1367 Process *animproc = Kernel::get_instance()->getProcess(animpid);
1368 assert(animproc);
1369
1370 resproc->waitFor(delayproc);
1371 animproc->waitFor(resproc);
1372 }
1373
1374 if (mi && mi->_explode) {
1375 // this monster explodes when it dies
1376
1377 pout << "Actor::die: exploding" << Std::endl;
1378
1379 int count = 5;
1380 Shape *explosionshape = GameData::get_instance()->getMainShapes()
1381 ->getShape(mi->_explode);
1382 assert(explosionshape);
1383 unsigned int framecount = explosionshape->frameCount();
1384
1385 for (int i = 0; i < count; ++i) {
1386 Item *piece = ItemFactory::createItem(mi->_explode,
1387 getRandom() % framecount,
1388 0, // qual
1389 Item::FLG_FAST_ONLY, //flags,
1390 0, // npcnum
1391 0, // mapnum
1392 0, true // ext. flags, _objId
1393 );
1394 piece->move(_x - 128 + 32 * (getRandom() % 6),
1395 _y - 128 + 32 * (getRandom() % 6),
1396 _z + getRandom() % 8); // move to near actor's position
1397 piece->hurl(-25 + (getRandom() % 50),
1398 -25 + (getRandom() % 50),
1399 10 + (getRandom() % 10),
1400 4); // (wrong?) CONSTANTS!
1401 }
1402 }
1403
1404 return animprocid;
1405 }
1406
dieCru(uint16 damageType,uint16 damagePts,Direction srcDir)1407 ProcId Actor::dieCru(uint16 damageType, uint16 damagePts, Direction srcDir) {
1408 bool is_robot = isRobotCru();
1409 bool created_koresh = false;
1410 const uint32 startshape = getShape();
1411
1412 World *world = World::get_instance();
1413
1414 world->getCurrentMap()->removeTargetItem(this);
1415
1416 if (world->getControlledNPCNum() == _objId) {
1417 TargetReticleProcess::get_instance()->avatarMoved();
1418 if (_objId != 1) {
1419 world->setControlledNPCNum(0);
1420 }
1421 }
1422
1423 ProcId lastanim = 0;
1424 Kernel::get_instance()->killProcesses(_objId, Kernel::PROC_TYPE_ALL, true);
1425
1426 destroyContents();
1427 giveTreasure();
1428
1429 if (getShape() == 0x5d6 && GAME_IS_REGRET) {
1430 // you only die twice.. (frozen person breaking into pieces)
1431 if (!isBusy()) {
1432 setShape(0x5ef);
1433 setToStartOfAnim(Animation::fallBackwardsCru);
1434 }
1435 } else if (damageType == 3 || damageType == 4 || damageType == 10 || damageType == 12) {
1436 if (!is_robot /* && violence enabled */) {
1437 const FireType *ft = GameData::get_instance()->getFireType(damageType);
1438 assert(ft);
1439 uint16 dmg2 = ft->getRandomDamage();
1440 if (damageType == 3) {
1441 dmg2 *= 3;
1442 } else {
1443 dmg2 *= 2;
1444 }
1445 dmg2 /= 5;
1446 if (dmg2 <= damagePts) {
1447 moveToEtherealVoid();
1448 CurrentMap *cm = world->getCurrentMap();
1449 /* 0x576 - flaming guy running around */
1450 bool can_create_koresh = cm->isValidPosition(_x, _y, _z, 0x576, _objId);
1451 returnFromEtherealVoid();
1452
1453 if (can_create_koresh) {
1454 created_koresh = true;
1455 Direction rundir = _direction;
1456 setShape(0x576);
1457 setToStartOfAnim(Animation::walk);
1458
1459 int num_random_steps = getRandom() % 9;
1460 // switch to an 8-dir value
1461 if (rundir % 2)
1462 rundir = static_cast<Direction>((rundir + 1) % 16);
1463
1464 for (int i = 0; i < num_random_steps; i++) {
1465 rundir = Direction_TurnByDelta(rundir, (int)(getRandom() % 3) - 1, dirmode_8dirs);
1466 lastanim = doAnimAfter(Animation::walk, rundir, lastanim);
1467 }
1468
1469 lastanim = doAnimAfter(Animation::fallBackwardsCru, dir_current, lastanim);
1470
1471 int num_random_falls = (getRandom() % 3) + 1;
1472 for (int i = 0; i < num_random_falls; i++) {
1473 lastanim = doAnimAfter(Animation::fallForwardsCru, dir_current, lastanim);
1474 }
1475
1476 lastanim = doAnimAfter(Animation::kneelCombatRollLeft, dir_current, lastanim);
1477 tookHitCru();
1478 }
1479 }
1480 }
1481 } else if (damageType == 6) {
1482 if (!is_robot) {
1483 /* 1423 = plasma death */
1484 setShape(0x58f);
1485 setToStartOfAnim(Animation::stand);
1486 }
1487 } else if (damageType == 14) {
1488 if (!is_robot) {
1489 if (true /*isViolenceEnabled()*/) {
1490 /* 1430 = fire death skeleton */
1491 setShape(0x596);
1492 setToStartOfAnim(Animation::fallBackwardsCru);
1493 }
1494 }
1495 } else if (damageType == 15) {
1496 if (!is_robot) {
1497 setShape(0x59c);
1498 setToStartOfAnim(Animation::fallBackwardsCru);
1499 }
1500 } else if ((damageType == 0x10) || (damageType == 0x12)) {
1501 if (!is_robot) {
1502 setShape(0x5d6);
1503 setToStartOfAnim(Animation::fallBackwardsCru);
1504 }
1505 } else if (damageType == 0x11) {
1506 if (!is_robot) {
1507 setShape(0x62d);
1508 setToStartOfAnim(Animation::fallBackwardsCru);
1509 }
1510 } else if (damageType == 0x14) {
1511 if (!is_robot) {
1512 if (true /*isViolenceEnabled()*/) {
1513 setShape(0x278);
1514 setToStartOfAnim(Animation::fallBackwardsCru);
1515 }
1516 }
1517 } else if (damageType == 7 && _objId == 1) {
1518 lastanim = doAnimAfter(Animation::electrocuted, dir_current, lastanim);
1519 }
1520
1521 if (!created_koresh) {
1522 bool fall_backwards = true;
1523 bool fall_random_dir = false;
1524
1525 if (GAME_IS_REGRET) {
1526 uint32 shape = getShape();
1527 if (startshape == shape) {
1528 if (shape == 0x5ff || shape == 0x5d7) {
1529 setShape(0x606);
1530 setToStartOfAnim(Animation::fallBackwardsCru);
1531 } else if (shape == 0x625 || shape == 0x626) {
1532 setShape(0x62e);
1533 setToStartOfAnim(Animation::fallBackwardsCru);
1534 } else if (shape == 0x5f0 || shape == 0x2c3) {
1535 setShape(0x5d5);
1536 setToStartOfAnim(Animation::fallBackwardsCru);
1537 } else if (shape == 0x62f || shape == 0x630) {
1538 setShape(0x631);
1539 setToStartOfAnim(Animation::fallBackwardsCru);
1540 }
1541 }
1542 }
1543
1544
1545 Direction dirtest[9];
1546 /* 0x383 == 899 (robot), 1423 = plasma death, 1430 = fire death skeleton */
1547 if (getShape() != 899 && getShape() != 0x58f && getShape() != 0x596) {
1548 dirtest[0] = _direction;
1549 for (int i = 1; i < 9; i = i + 1) {
1550 char testdir = (i % 2) ? 1 : -1;
1551 dirtest[i] = Direction_TurnByDelta(_direction, ((i + 1) / 2) * testdir, dirmode_8dirs);
1552 }
1553 for (int i = 0; i < 9; i++) {
1554 if (dirtest[i] == srcDir) {
1555 if (i == 8 || i == 9) {
1556 fall_random_dir = true;
1557 } else {
1558 fall_backwards = false;
1559 }
1560 break;
1561 }
1562 }
1563 } else {
1564 fall_random_dir = true;
1565 }
1566
1567 if (!hasAnim(Animation::fallForwardsCru)) {
1568 lastanim = doAnimAfter(Animation::fallBackwardsCru, dir_current, lastanim);
1569 } else {
1570 if (fall_random_dir) {
1571 fall_backwards = (getRandom() % 2) == 0;
1572 }
1573 if (fall_backwards) {
1574 lastanim = doAnimAfter(Animation::fallBackwardsCru, dir_current, lastanim);
1575 } else {
1576 lastanim = doAnimAfter(Animation::fallForwardsCru, dir_current, lastanim);
1577 }
1578 }
1579
1580 if (!is_robot) {
1581 uint16 sfxno;
1582 static const uint16 FADING_SCREAM_SFX[] = { 0xD9, 0xDA };
1583 static const uint16 MALE_DEATH_SFX[] = { 0x88, 0x8C, 0x8F };
1584 static const uint16 FEMALE_DEATH_SFX[] = { 0xD8, 0x10 };
1585 if (damageType == 0xf) {
1586 sfxno = FADING_SCREAM_SFX[getRandom() % 2];
1587 } else {
1588 if (hasExtFlags(EXT_FEMALE)) {
1589 sfxno = FEMALE_DEATH_SFX[getRandom() % 2];
1590 } else {
1591 sfxno = MALE_DEATH_SFX[getRandom() % 3];
1592 }
1593 }
1594 AudioProcess::get_instance()->playSFX(sfxno, 0x10, _objId, 0, true);
1595 }
1596 }
1597
1598 return lastanim;
1599 }
1600
killAllButCombatProcesses()1601 void Actor::killAllButCombatProcesses() {
1602 // loop over all processes, keeping only the relevant ones
1603 ProcessIter iter = Kernel::get_instance()->getProcessBeginIterator();
1604 ProcessIter endproc = Kernel::get_instance()->getProcessEndIterator();
1605 for (; iter != endproc; ++iter) {
1606 Process *p = *iter;
1607 if (!p) continue;
1608 if (p->getItemNum() != _objId) continue;
1609 if (p->is_terminated()) continue;
1610
1611 uint16 type = p->getType();
1612
1613 if (type != 0xF0 && type != 0xF2 && type != 0x208 && type != 0x21D &&
1614 type != 0x220 && type != 0x238 && type != 0x243) {
1615 p->fail();
1616 }
1617 }
1618 }
1619
killAllButFallAnims(bool death)1620 ProcId Actor::killAllButFallAnims(bool death) {
1621 ProcId fallproc = 0;
1622
1623 Kernel *kernel = Kernel::get_instance();
1624
1625 if (death) {
1626 // if dead, we want to kill everything but animations
1627 kernel->killProcessesNotOfType(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE, true);
1628 } else {
1629 // otherwise, need to focus on combat, so kill everything else
1630 killAllButCombatProcesses();
1631 }
1632
1633 // loop over all animation processes, keeping only the relevant ones
1634 ProcessIter iter = Kernel::get_instance()->getProcessBeginIterator();
1635 ProcessIter endproc = Kernel::get_instance()->getProcessEndIterator();
1636 for (; iter != endproc; ++iter) {
1637 ActorAnimProcess *p = dynamic_cast<ActorAnimProcess *>(*iter);
1638 if (!p) continue;
1639 if (p->getItemNum() != _objId) continue;
1640 if (p->is_terminated()) continue;
1641
1642 Animation::Sequence action = p->getAction();
1643
1644 if (action == Animation::die) {
1645 fallproc = p->getPid();
1646 continue;
1647 }
1648
1649 if (!death && action == Animation::standUp) {
1650 fallproc = p->getPid();
1651 } else {
1652 p->fail();
1653 }
1654 }
1655
1656 return fallproc;
1657 }
1658
calculateAttackDamage(uint16 other,int damage,uint16 damage_type)1659 int Actor::calculateAttackDamage(uint16 other, int damage, uint16 damage_type) {
1660 Actor *attacker = getActor(other);
1661
1662 uint16 defense_type = getDefenseType();
1663
1664 // most damage types are blocked straight away by defense types
1665 damage_type &= ~(defense_type & ~(WeaponInfo::DMG_MAGIC |
1666 WeaponInfo::DMG_UNDEAD |
1667 WeaponInfo::DMG_PIERCE));
1668
1669 // immunity to non-magical weapons
1670 if ((defense_type & WeaponInfo::DMG_MAGIC) &&
1671 !(damage_type & WeaponInfo::DMG_MAGIC)) {
1672 damage = 0;
1673 }
1674
1675 bool slayer = false;
1676
1677 // _special attacks
1678 if (damage && damage_type) {
1679 if (damage_type & WeaponInfo::DMG_SLAYER) {
1680 if (getRandom() % 10 == 0) {
1681 slayer = true;
1682 damage = 255; // instant kill
1683 }
1684 }
1685
1686 if ((damage_type & WeaponInfo::DMG_UNDEAD) &&
1687 (defense_type & WeaponInfo::DMG_UNDEAD)) {
1688 damage *= 2; // double damage against undead
1689 }
1690
1691 if ((defense_type & WeaponInfo::DMG_PIERCE) &&
1692 !(damage_type & (WeaponInfo::DMG_BLADE |
1693 WeaponInfo::DMG_FIRE |
1694 WeaponInfo::DMG_PIERCE))) {
1695 damage /= 2; // resistance to blunt damage
1696 }
1697 } else {
1698 damage = 0;
1699 }
1700
1701 // armour
1702 if (damage && !(damage_type & WeaponInfo::DMG_PIERCE) && !slayer) {
1703 // blocking?
1704 if ((getLastAnim() == Animation::startBlock ||
1705 getLastAnim() == Animation::stopBlock) &&
1706 !hasActorFlags(ACT_STUNNED)) {
1707 damage -= getStr() / 5;
1708 }
1709
1710 int ACmod = 3 * getArmourClass();
1711 if (damage_type & WeaponInfo::DMG_FIRE)
1712 ACmod /= 2; // armour doesn't protect from fire as well
1713
1714 if (hasActorFlags(ACT_STUNNED))
1715 ACmod /= 2; // stunned?
1716
1717 if (ACmod > 100) ACmod = 100;
1718
1719 // TODO: replace rounding bias by something random
1720 damage = ((100 - ACmod) * damage) / 100;
1721
1722 if (damage < 0) damage = 0;
1723 }
1724
1725 // to-hit
1726 if (damage && !(damage_type & WeaponInfo::DMG_PIERCE) && attacker) {
1727 bool hit = false;
1728 int16 attackdex = attacker->getAttackingDex();
1729 int16 defenddex = getDefendingDex();
1730 if (attackdex < 0) attackdex = 0;
1731 if (defenddex <= 0) defenddex = 1;
1732
1733 if (hasActorFlags(ACT_STUNNED) ||
1734 (getRandom() % (attackdex + 3) > getRandom() % defenddex)) {
1735 hit = true;
1736 }
1737
1738 // TODO: give avatar an extra chance to hit monsters
1739 // with defense_type DMG_PIERCE
1740
1741 if (hit && other == 1) {
1742 MainActor *av = getMainActor();
1743 if (attackdex > defenddex)
1744 av->accumulateDex(2 * (attackdex - defenddex));
1745 else
1746 av->accumulateDex(2);
1747 }
1748
1749 if (!hit) {
1750 damage = 0;
1751 }
1752 }
1753
1754 return damage;
1755 }
1756
isFalling() const1757 bool Actor::isFalling() const {
1758 ProcId gravitypid = getGravityPID();
1759 if (!gravitypid)
1760 return false;
1761
1762 // TODO: this is not exactly the same as the Crusader implementation,
1763 // but pretty close. Is that ok?
1764 GravityProcess *proc = dynamic_cast<GravityProcess *>(Kernel::get_instance()->getProcess(gravitypid));
1765 return (proc && proc->is_active());
1766 }
1767
getCombatProcess()1768 CombatProcess *Actor::getCombatProcess() {
1769 Process *p = Kernel::get_instance()->findProcess(_objId, 0xF2); // CONSTANT!
1770 if (!p)
1771 return nullptr;
1772 CombatProcess *cp = dynamic_cast<CombatProcess *>(p);
1773 assert(cp);
1774
1775 return cp;
1776 }
1777
getAttackProcess()1778 AttackProcess *Actor::getAttackProcess() {
1779 Process *p = Kernel::get_instance()->findProcess(_objId, AttackProcess::ATTACK_PROCESS_TYPE);
1780 if (!p)
1781 return nullptr;
1782 AttackProcess *ap = dynamic_cast<AttackProcess *>(p);
1783 assert(ap);
1784
1785 return ap;
1786 }
1787
setInCombat(int activity)1788 void Actor::setInCombat(int activity) {
1789 if (GAME_IS_U8)
1790 setInCombatU8();
1791 else
1792 setInCombatCru(activity);
1793 }
1794
setInCombatU8()1795 void Actor::setInCombatU8() {
1796 if ((_actorFlags & ACT_INCOMBAT) != 0) return;
1797
1798 assert(getCombatProcess() == nullptr);
1799
1800 // kill any processes belonging to this actor
1801 Kernel::get_instance()->killProcesses(getObjId(), Kernel::PROC_TYPE_ALL, true);
1802
1803 // perform _special actions
1804 ProcId castproc = callUsecodeEvent_cast(0);
1805
1806 CombatProcess *cp = new CombatProcess(this);
1807 Kernel::get_instance()->addProcess(cp);
1808
1809 // wait for any _special actions to finish before starting to fight
1810 if (castproc)
1811 cp->waitFor(castproc);
1812
1813 setActorFlag(ACT_INCOMBAT);
1814 }
1815
setInCombatCru(int activity)1816 void Actor::setInCombatCru(int activity) {
1817 if ((_actorFlags & ACT_INCOMBAT) != 0) return;
1818
1819 assert(getAttackProcess() == nullptr);
1820
1821 setActorFlag(ACT_INCOMBAT);
1822
1823 if (getObjId() == World::get_instance()->getControlledNPCNum())
1824 return;
1825
1826 AttackProcess *ap = new AttackProcess(this);
1827 Kernel::get_instance()->addProcess(ap);
1828
1829 if (getLastActivityNo() == 8) {
1830 // Coming from guard process.. set some flag in ap
1831 ap->setField97();
1832 }
1833 if (activity == 0xc) {
1834 // This sets timer 3 of the attack process
1835 // to some random timer value in the future
1836 ap->setTimer3();
1837 }
1838
1839 uint16 animproc = 0;
1840 if (activity == 9 || activity == 0xb) {
1841 ap->setIsActivity9OrB();
1842 animproc = doAnim(Animation::readyWeapon, dir_current);
1843 } else {
1844 animproc = doAnim(Animation::stand, dir_current);
1845 }
1846 if (animproc) {
1847 // Do the animation first
1848 ap->waitFor(animproc);
1849 }
1850
1851 if (activity == 0xa || activity == 0xb) {
1852 ap->setIsActivityAOrB();
1853 }
1854 }
1855
clearInCombat()1856 void Actor::clearInCombat() {
1857 if ((_actorFlags & ACT_INCOMBAT) == 0) return;
1858
1859 Process *p;
1860 if (GAME_IS_U8) {
1861 p = getCombatProcess();
1862 } else {
1863 p = getAttackProcess();
1864 }
1865 if (p)
1866 p->terminate();
1867
1868 clearActorFlag(ACT_INCOMBAT);
1869 }
1870
canSeeControlledActor(bool forcombat)1871 bool Actor::canSeeControlledActor(bool forcombat) {
1872 const Actor *controlled = getControlledActor();
1873 if (!controlled)
1874 return false;
1875
1876 if (!isOnScreen())
1877 return false;
1878
1879 Direction dirtocontrolled = getDirToItemCentre(*controlled);
1880 Direction curdir = getDir();
1881
1882 /* TODO: There are extra checks in here in the original
1883 if (forcombat) {
1884 Animation::Sequence lastanim = getLastAnim();
1885 ((lastanim == Animation::unknownAnim30 || lastanim == Animation::startRunLargeWeapon) && currentAnimFrame > 1)) {
1886 bool left;
1887 if (lastanim == Animation::unknownAnim30) {
1888 left = false;
1889 if (((currentdir != 8) && (currentdir != 10)) && (currentdir != 0xc))
1890 left = true;
1891 } else {
1892 left = true;
1893 if (((currentdir != 8) && (currentdir != 10)) && (currentdir != 0xc)) {
1894 left = false;
1895 }
1896 if (leftflag) {
1897 currentdir = Direction_TurnByDelta(curdir, -4, dirmode_16dirs);
1898 } else {
1899 currentdir = Direction_TurnByDelta(curdir, 4, dirmode_16dirs);
1900 }
1901 }
1902 */
1903
1904 if (dirtocontrolled == curdir ||
1905 dirtocontrolled == Direction_OneLeft(curdir, dirmode_16dirs) ||
1906 dirtocontrolled == Direction_OneRight(curdir, dirmode_16dirs) ||
1907 dirtocontrolled == Direction_TurnByDelta(curdir, 2, dirmode_16dirs) ||
1908 dirtocontrolled == Direction_TurnByDelta(curdir, -2, dirmode_16dirs))
1909 {
1910 return getRangeIfVisible(*controlled) > 0;
1911 }
1912
1913 return false;
1914
1915 }
1916
collideMove(int32 x,int32 y,int32 z,bool teleports,bool force,ObjId * hititem,uint8 * dirs)1917 int32 Actor::collideMove(int32 x, int32 y, int32 z, bool teleports, bool force,
1918 ObjId *hititem, uint8 *dirs) {
1919 int32 result = Item::collideMove(x, y, z, teleports, force, hititem, dirs);
1920 if (this == getControlledActor() && GAME_IS_CRUSADER) {
1921 TargetReticleProcess::get_instance()->avatarMoved();
1922 ItemSelectionProcess::get_instance()->avatarMoved();
1923 }
1924 return result;
1925 }
1926
activeWeaponIsSmall() const1927 bool Actor::activeWeaponIsSmall() const {
1928 const Item *wpn = getItem(_activeWeapon);
1929 if (wpn) {
1930 const WeaponInfo *wi = wpn->getShapeInfo()->_weaponInfo;
1931 return wi && (wi->_small != 0);
1932 }
1933 return false;
1934 }
1935
1936
areEnemiesNear()1937 bool Actor::areEnemiesNear() {
1938 UCList uclist(2);
1939 LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
1940 CurrentMap *currentmap = World::get_instance()->getCurrentMap();
1941 currentmap->areaSearch(&uclist, script, sizeof(script), this, 0x800, false);
1942
1943 for (unsigned int i = 0; i < uclist.getSize(); ++i) {
1944 Actor *npc = getActor(uclist.getuint16(i));
1945 if (!npc) continue;
1946 if (npc == this) continue;
1947
1948 if (npc->hasActorFlags(ACT_DEAD | ACT_FEIGNDEATH)) continue;
1949 if (!npc->hasActorFlags(ACT_INCOMBAT)) continue;
1950
1951 // TODO: check if hostile.
1952 // Might not be strictly necessary, though. This function is only
1953 // used on the avatar, and any NPCs in combat mode around the avatar
1954 // are most likely hostile... (and if they're not hostile, they're
1955 // probably in combat mode because something hostile _is_ nearby)
1956
1957 return true;
1958 }
1959
1960 return false;
1961 }
1962
schedule(uint32 time)1963 uint16 Actor::schedule(uint32 time) {
1964 if (isDead())
1965 return 0;
1966
1967 uint32 ret = callUsecodeEvent_schedule(time);
1968
1969 return static_cast<uint16>(ret);
1970 }
1971
1972 //static
createActor(uint32 shape,uint32 frame)1973 Actor *Actor::createActor(uint32 shape, uint32 frame) {
1974 Actor *newactor = ItemFactory::createActor(shape, frame, 0,
1975 Item::FLG_IN_NPC_LIST,
1976 0, 0, 0, true);
1977 if (!newactor)
1978 return nullptr;
1979 uint16 objID = newactor->getObjId();
1980
1981 // set stats
1982 if (!newactor->loadMonsterStats()) {
1983 perr << "I_createActor failed to set stats for actor (" << shape
1984 << ")." << Std::endl;
1985 }
1986
1987 Actor *av = getMainActor();
1988 newactor->setMapNum(av->getMapNum());
1989 newactor->setNpcNum(objID);
1990 newactor->setFlag(FLG_ETHEREAL);
1991 World::get_instance()->etherealPush(objID);
1992
1993 return newactor;
1994 }
1995
dumpInfo() const1996 void Actor::dumpInfo() const {
1997 Container::dumpInfo();
1998
1999 pout << "hp: " << _hitPoints << ", mp: " << _mana << ", str: " << _strength
2000 << ", dex: " << _dexterity << ", int: " << _intelligence
2001 << ", ac: " << getArmourClass() << ", defense: " << ConsoleStream::hex
2002 << getDefenseType() << " align: " << getAlignment() << " enemy: "
2003 << getEnemyAlignment() << ", flags: " << _actorFlags
2004 << ConsoleStream::dec << Std::endl;
2005 }
2006
addFireAnimOffsets(int32 & x,int32 & y,int32 & z)2007 void Actor::addFireAnimOffsets(int32 &x, int32 &y, int32 &z) {
2008 assert(GAME_IS_CRUSADER);
2009 Animation::Sequence fireanim = (isKneeling() ? Animation::kneelAndFire : Animation::attack);
2010 uint32 actionno = AnimDat::getActionNumberForSequence(fireanim, this);
2011 Direction dir = getDir();
2012
2013 const AnimAction *animaction = GameData::get_instance()->getMainShapes()->getAnim(getShape(), actionno);
2014 if (!animaction)
2015 return;
2016
2017 for (unsigned int i = 0; i < animaction->getSize(); i++) {
2018 const AnimFrame &frame = animaction->getFrame(dir, i);
2019 if (frame.is_cruattack()) {
2020 x += frame.cru_attackx();
2021 y += frame.cru_attacky();
2022 z += frame.cru_attackz();
2023 return;
2024 }
2025 }
2026 }
2027
saveData(Common::WriteStream * ws)2028 void Actor::saveData(Common::WriteStream *ws) {
2029 Container::saveData(ws);
2030 ws->writeUint16LE(_strength);
2031 ws->writeUint16LE(_dexterity);
2032 ws->writeUint16LE(_intelligence);
2033 ws->writeUint16LE(_hitPoints);
2034 ws->writeUint16LE(_mana);
2035 ws->writeUint16LE(_alignment);
2036 ws->writeUint16LE(_enemyAlignment);
2037 ws->writeUint16LE(_lastAnim);
2038 ws->writeUint16LE(_animFrame);
2039 ws->writeUint16LE(Direction_ToUsecodeDir(_direction));
2040 ws->writeUint32LE(_fallStart);
2041 ws->writeUint32LE(_actorFlags);
2042 ws->writeByte(_unkByte);
2043
2044 if (GAME_IS_CRUSADER) {
2045 ws->writeUint16LE(_defaultActivity[0]);
2046 ws->writeUint16LE(_defaultActivity[1]);
2047 ws->writeUint16LE(_defaultActivity[2]);
2048 ws->writeUint16LE(_combatTactic);
2049 ws->writeUint32LE(_homeX);
2050 ws->writeUint32LE(_homeY);
2051 ws->writeUint32LE(_homeZ);
2052 ws->writeUint16LE(_currentActivityNo);
2053 ws->writeUint16LE(_lastActivityNo);
2054 ws->writeUint16LE(_activeWeapon);
2055 ws->writeSint32LE(_lastTickWasHit);
2056 ws->writeByte(0); // unused, keep for backward compatibility
2057 ws->writeUint32LE(_attackMoveStartFrame);
2058 ws->writeUint32LE(_attackMoveTimeout);
2059 ws->writeUint16LE(_attackMoveDodgeFactor);
2060 ws->writeByte(_attackAimFlag ? 1 : 0);
2061 }
2062 }
2063
loadData(Common::ReadStream * rs,uint32 version)2064 bool Actor::loadData(Common::ReadStream *rs, uint32 version) {
2065 if (!Container::loadData(rs, version)) return false;
2066
2067 _strength = static_cast<int16>(rs->readUint16LE());
2068 _dexterity = static_cast<int16>(rs->readUint16LE());
2069 _intelligence = static_cast<int16>(rs->readUint16LE());
2070 _hitPoints = rs->readUint16LE();
2071 _mana = static_cast<int16>(rs->readUint16LE());
2072 _alignment = rs->readUint16LE();
2073 _enemyAlignment = rs->readUint16LE();
2074 _lastAnim = static_cast<Animation::Sequence>(rs->readUint16LE());
2075 _animFrame = rs->readUint16LE();
2076 _direction = Direction_FromUsecodeDir(rs->readUint16LE());
2077 _fallStart = rs->readUint32LE();
2078 _actorFlags = rs->readUint32LE();
2079 _unkByte = rs->readByte();
2080
2081 if (GAME_IS_CRUSADER) {
2082 _defaultActivity[0] = rs->readUint16LE();
2083 _defaultActivity[1] = rs->readUint16LE();
2084 _defaultActivity[2] = rs->readUint16LE();
2085 _combatTactic = rs->readUint16LE();
2086 _homeX = rs->readUint32LE();
2087 _homeY = rs->readUint32LE();
2088 _homeZ = rs->readUint32LE();
2089 _currentActivityNo = rs->readUint16LE();
2090 _lastActivityNo = rs->readUint16LE();
2091 _activeWeapon = rs->readUint16LE();
2092 _lastTickWasHit = rs->readSint32LE();
2093 rs->readByte(); // unused, keep for backward compatibility
2094 _attackMoveStartFrame = rs->readUint32LE();
2095 _attackMoveTimeout = rs->readUint32LE();
2096 _attackMoveDodgeFactor = rs->readUint16LE();
2097 _attackAimFlag = rs->readByte() != 0;
2098 }
2099
2100 return true;
2101 }
2102
2103
I_isNPC(const uint8 * args,unsigned int)2104 uint32 Actor::I_isNPC(const uint8 *args, unsigned int /*argsize*/) {
2105 ARG_ACTOR_FROM_PTR(actor);
2106 if (!actor) return 0;
2107 return 1;
2108 }
2109
I_getMap(const uint8 * args,unsigned int)2110 uint32 Actor::I_getMap(const uint8 *args, unsigned int /*argsize*/) {
2111 ARG_ACTOR_FROM_PTR(actor);
2112 if (!actor) return 0;
2113
2114 return actor->getMapNum();
2115 }
2116
I_teleport(const uint8 * args,unsigned int)2117 uint32 Actor::I_teleport(const uint8 *args, unsigned int /*argsize*/) {
2118 ARG_ACTOR_FROM_PTR(actor);
2119 ARG_UINT16(newx);
2120 ARG_UINT16(newy);
2121 ARG_UINT16(newz);
2122 ARG_UINT16(newmap);
2123 if (!actor) return 0;
2124
2125 if (GAME_IS_CRUSADER) {
2126 newx *= 2;
2127 newy *= 2;
2128 }
2129
2130 actor->teleport(newmap, newx, newy, newz);
2131 return 0;
2132 }
2133
I_doAnim(const uint8 * args,unsigned int)2134 uint32 Actor::I_doAnim(const uint8 *args, unsigned int /*argsize*/) {
2135 ARG_ACTOR_FROM_PTR(actor);
2136 ARG_UINT16(anim);
2137 ARG_UINT16(dir); // seems to be 0-8
2138 ARG_UINT16(unk1); // this is almost always 10000 in U8.Maybe speed-related?
2139 ARG_UINT16(unk2); // appears to be 0 or 1. Some flag?
2140
2141 if (!actor) return 0;
2142
2143 //
2144 // HACK: In Crusader, we do translation on the animations so we want to remap
2145 // most of them, but for direct commands from the usecode we add a bitflag for
2146 // no remapping
2147 //
2148 if (GAME_IS_CRUSADER) {
2149 anim |= Animation::crusaderAbsoluteAnimFlag;
2150 }
2151
2152 return actor->doAnim(static_cast<Animation::Sequence>(anim), Direction_FromUsecodeDir(dir));
2153 }
2154
I_getDir(const uint8 * args,unsigned int)2155 uint32 Actor::I_getDir(const uint8 *args, unsigned int /*argsize*/) {
2156 ARG_ACTOR_FROM_PTR(actor);
2157 if (!actor) return 0;
2158
2159 return Direction_ToUsecodeDir(actor->getDir());
2160 }
2161
I_getLastAnimSet(const uint8 * args,unsigned int)2162 uint32 Actor::I_getLastAnimSet(const uint8 *args, unsigned int /*argsize*/) {
2163 ARG_ACTOR_FROM_PTR(actor);
2164 if (!actor) return 0;
2165
2166 return AnimDat::getActionNumberForSequence(actor->getLastAnim(), actor);
2167 }
2168
I_getStr(const uint8 * args,unsigned int)2169 uint32 Actor::I_getStr(const uint8 *args, unsigned int /*argsize*/) {
2170 ARG_ACTOR_FROM_PTR(actor);
2171 if (!actor) return 0;
2172
2173 return actor->getStr();
2174 }
2175
I_getDex(const uint8 * args,unsigned int)2176 uint32 Actor::I_getDex(const uint8 *args, unsigned int /*argsize*/) {
2177 ARG_ACTOR_FROM_PTR(actor);
2178 if (!actor) return 0;
2179
2180 return actor->getDex();
2181 }
2182
I_getInt(const uint8 * args,unsigned int)2183 uint32 Actor::I_getInt(const uint8 *args, unsigned int /*argsize*/) {
2184 ARG_ACTOR_FROM_PTR(actor);
2185 if (!actor) return 0;
2186
2187 return actor->getInt();
2188 }
2189
I_getHp(const uint8 * args,unsigned int)2190 uint32 Actor::I_getHp(const uint8 *args, unsigned int /*argsize*/) {
2191 ARG_ACTOR_FROM_PTR(actor);
2192 if (!actor) return 0;
2193
2194 return actor->getHP();
2195 }
2196
I_getMaxHp(const uint8 * args,unsigned int)2197 uint32 Actor::I_getMaxHp(const uint8 *args, unsigned int /*argsize*/) {
2198 ARG_ACTOR_FROM_PTR(actor);
2199 if (!actor) return 0;
2200
2201 return actor->getMaxHP();
2202 }
2203
I_getMana(const uint8 * args,unsigned int)2204 uint32 Actor::I_getMana(const uint8 *args, unsigned int /*argsize*/) {
2205 ARG_ACTOR_FROM_PTR(actor);
2206 if (!actor) return 0;
2207
2208 return actor->getMana();
2209 }
2210
I_getAlignment(const uint8 * args,unsigned int)2211 uint32 Actor::I_getAlignment(const uint8 *args, unsigned int /*argsize*/) {
2212 ARG_ACTOR_FROM_PTR(actor);
2213 if (!actor) return 0;
2214
2215 return actor->getAlignment();
2216 }
2217
I_getEnemyAlignment(const uint8 * args,unsigned int)2218 uint32 Actor::I_getEnemyAlignment(const uint8 *args, unsigned int /*argsize*/) {
2219 ARG_ACTOR_FROM_PTR(actor);
2220 if (!actor) return 0;
2221
2222 return actor->getEnemyAlignment();
2223 }
2224
I_setStr(const uint8 * args,unsigned int)2225 uint32 Actor::I_setStr(const uint8 *args, unsigned int /*argsize*/) {
2226 ARG_ACTOR_FROM_PTR(actor);
2227 ARG_SINT16(str);
2228 if (!actor) return 0;
2229
2230 actor->setStr(str);
2231 return 0;
2232 }
2233
I_setDex(const uint8 * args,unsigned int)2234 uint32 Actor::I_setDex(const uint8 *args, unsigned int /*argsize*/) {
2235 ARG_ACTOR_FROM_PTR(actor);
2236 ARG_SINT16(dex);
2237 if (!actor) return 0;
2238
2239 actor->setDex(dex);
2240 return 0;
2241 }
2242
I_setInt(const uint8 * args,unsigned int)2243 uint32 Actor::I_setInt(const uint8 *args, unsigned int /*argsize*/) {
2244 ARG_ACTOR_FROM_PTR(actor);
2245 ARG_SINT16(int_);
2246 if (!actor) return 0;
2247
2248 actor->setStr(int_);
2249 return 0;
2250 }
2251
I_setHp(const uint8 * args,unsigned int)2252 uint32 Actor::I_setHp(const uint8 *args, unsigned int /*argsize*/) {
2253 ARG_ACTOR_FROM_PTR(actor);
2254 ARG_UINT16(hp);
2255 if (!actor) return 0;
2256
2257 actor->setHP(hp);
2258 return 0;
2259 }
2260
I_addHp(const uint8 * args,unsigned int)2261 uint32 Actor::I_addHp(const uint8 *args, unsigned int /*argsize*/) {
2262 ARG_ACTOR_FROM_PTR(actor);
2263 ARG_UINT16(hp);
2264
2265 if (actor) {
2266 int max = actor->getMaxHP();
2267 int cur = actor->getHP();
2268 if (cur < max) {
2269 actor->setHP(MIN(max, cur + hp));
2270 return 1;
2271 }
2272 }
2273 return 0;
2274 }
2275
I_setMana(const uint8 * args,unsigned int)2276 uint32 Actor::I_setMana(const uint8 *args, unsigned int /*argsize*/) {
2277 ARG_ACTOR_FROM_PTR(actor);
2278 ARG_SINT16(mp);
2279 if (!actor) return 0;
2280
2281 actor->setMana(mp);
2282 return 0;
2283 }
2284
I_setAlignment(const uint8 * args,unsigned int)2285 uint32 Actor::I_setAlignment(const uint8 *args, unsigned int /*argsize*/) {
2286 ARG_ACTOR_FROM_PTR(actor);
2287 ARG_UINT16(a);
2288 if (!actor) return 0;
2289
2290 actor->setAlignment(a);
2291 return 0;
2292 }
2293
I_setEnemyAlignment(const uint8 * args,unsigned int)2294 uint32 Actor::I_setEnemyAlignment(const uint8 *args, unsigned int /*argsize*/) {
2295 ARG_ACTOR_FROM_PTR(actor);
2296 ARG_UINT16(a);
2297 if (!actor) return 0;
2298
2299 actor->setEnemyAlignment(a);
2300 return 0;
2301 }
2302
I_isInCombat(const uint8 * args,unsigned int)2303 uint32 Actor::I_isInCombat(const uint8 *args, unsigned int /*argsize*/) {
2304 ARG_ACTOR_FROM_PTR(actor);
2305 if (!actor) return 0;
2306
2307 if (actor->isInCombat())
2308 return 1;
2309 else
2310 return 0;
2311 }
2312
I_setInCombat(const uint8 * args,unsigned int)2313 uint32 Actor::I_setInCombat(const uint8 *args, unsigned int /*argsize*/) {
2314 ARG_ACTOR_FROM_PTR(actor);
2315 if (!actor) return 0;
2316
2317 assert(GAME_IS_U8);
2318 actor->setInCombatU8();
2319
2320 return 0;
2321 }
2322
I_clrInCombat(const uint8 * args,unsigned int)2323 uint32 Actor::I_clrInCombat(const uint8 *args, unsigned int /*argsize*/) {
2324 ARG_ACTOR_FROM_PTR(actor);
2325 if (!actor) return 0;
2326
2327 actor->clearInCombat();
2328
2329 return 0;
2330 }
2331
I_setTarget(const uint8 * args,unsigned int)2332 uint32 Actor::I_setTarget(const uint8 *args, unsigned int /*argsize*/) {
2333 ARG_ACTOR_FROM_PTR(actor);
2334 ARG_UINT16(target);
2335 if (!actor) return 0;
2336
2337 if (GAME_IS_U8) {
2338 CombatProcess *cp = actor->getCombatProcess();
2339 if (!cp) {
2340 actor->setInCombatU8();
2341 cp = actor->getCombatProcess();
2342 }
2343 if (!cp) {
2344 warning("Actor::I_setTarget: failed to enter combat mode");
2345 return 0;
2346 }
2347
2348 cp->setTarget(target);
2349 } else {
2350 if (actor->isDead() || actor->getObjId() == 1)
2351 return 0;
2352
2353 actor->setActivityCru(5);
2354 AttackProcess *ap = actor->getAttackProcess();
2355 if (!ap) {
2356 warning("Actor::I_setTarget: failed to enter attack mode");
2357 return 0;
2358 }
2359 ap->setTarget(target);
2360 }
2361
2362 return 0;
2363 }
2364
I_getTarget(const uint8 * args,unsigned int)2365 uint32 Actor::I_getTarget(const uint8 *args, unsigned int /*argsize*/) {
2366 ARG_ACTOR_FROM_PTR(actor);
2367 if (!actor) return 0;
2368
2369 CombatProcess *cp = actor->getCombatProcess();
2370
2371 if (!cp) return 0;
2372
2373 return static_cast<uint32>(cp->getTarget());
2374 }
2375
2376
I_isEnemy(const uint8 * args,unsigned int)2377 uint32 Actor::I_isEnemy(const uint8 *args, unsigned int /*argsize*/) {
2378 ARG_ACTOR_FROM_PTR(actor);
2379 ARG_ACTOR_FROM_ID(other);
2380 if (!actor) return 0;
2381 if (!other) return 0;
2382
2383 if (actor->getEnemyAlignment() & other->getAlignment())
2384 return 1;
2385 else
2386 return 0;
2387 }
2388
I_isDead(const uint8 * args,unsigned int)2389 uint32 Actor::I_isDead(const uint8 *args, unsigned int /*argsize*/) {
2390 ARG_ACTOR_FROM_PTR(actor);
2391 if (!actor) return 1;
2392
2393 if (actor->isDead())
2394 return 1;
2395 else
2396 return 0;
2397 }
2398
I_setDead(const uint8 * args,unsigned int)2399 uint32 Actor::I_setDead(const uint8 *args, unsigned int /*argsize*/) {
2400 ARG_ACTOR_FROM_PTR(actor);
2401 if (!actor) return 0;
2402
2403 actor->setActorFlag(ACT_DEAD);
2404 if (GAME_IS_CRUSADER) {
2405 actor->setFlag(FLG_BROKEN);
2406 World::get_instance()->getCurrentMap()->removeTargetItem(actor);
2407 }
2408
2409 return 0;
2410 }
2411
I_clrDead(const uint8 * args,unsigned int)2412 uint32 Actor::I_clrDead(const uint8 *args, unsigned int /*argsize*/) {
2413 ARG_ACTOR_FROM_PTR(actor);
2414 if (!actor) return 0;
2415
2416 actor->clearActorFlag(ACT_DEAD);
2417 if (GAME_IS_CRUSADER) {
2418 actor->clearFlag(FLG_BROKEN);
2419 World::get_instance()->getCurrentMap()->addTargetItem(actor);
2420 }
2421
2422 return 0;
2423 }
2424
I_isImmortal(const uint8 * args,unsigned int)2425 uint32 Actor::I_isImmortal(const uint8 *args, unsigned int /*argsize*/) {
2426 ARG_ACTOR_FROM_PTR(actor);
2427 if (!actor) return 0;
2428
2429 if (actor->hasActorFlags(ACT_IMMORTAL))
2430 return 1;
2431 else
2432 return 0;
2433 }
2434
I_setImmortal(const uint8 * args,unsigned int)2435 uint32 Actor::I_setImmortal(const uint8 *args, unsigned int /*argsize*/) {
2436 ARG_ACTOR_FROM_PTR(actor);
2437 if (!actor) return 0;
2438
2439 actor->setActorFlag(ACT_IMMORTAL);
2440 actor->clearActorFlag(ACT_INVINCIBLE);
2441
2442 return 0;
2443 }
2444
I_clrImmortal(const uint8 * args,unsigned int)2445 uint32 Actor::I_clrImmortal(const uint8 *args, unsigned int /*argsize*/) {
2446 ARG_ACTOR_FROM_PTR(actor);
2447 if (!actor) return 0;
2448
2449 actor->clearActorFlag(ACT_IMMORTAL);
2450
2451 return 0;
2452 }
2453
I_isWithstandDeath(const uint8 * args,unsigned int)2454 uint32 Actor::I_isWithstandDeath(const uint8 *args, unsigned int /*argsize*/) {
2455 ARG_ACTOR_FROM_PTR(actor);
2456 if (!actor) return 0;
2457
2458 if (actor->hasActorFlags(ACT_WITHSTANDDEATH))
2459 return 1;
2460 else
2461 return 0;
2462 }
2463
I_setWithstandDeath(const uint8 * args,unsigned int)2464 uint32 Actor::I_setWithstandDeath(const uint8 *args, unsigned int /*argsize*/) {
2465 ARG_ACTOR_FROM_PTR(actor);
2466 if (!actor) return 0;
2467
2468 actor->setActorFlag(ACT_WITHSTANDDEATH);
2469
2470 return 0;
2471 }
2472
I_clrWithstandDeath(const uint8 * args,unsigned int)2473 uint32 Actor::I_clrWithstandDeath(const uint8 *args, unsigned int /*argsize*/) {
2474 ARG_ACTOR_FROM_PTR(actor);
2475 if (!actor) return 0;
2476
2477 actor->clearActorFlag(ACT_WITHSTANDDEATH);
2478
2479 return 0;
2480 }
2481
I_isFeignDeath(const uint8 * args,unsigned int)2482 uint32 Actor::I_isFeignDeath(const uint8 *args, unsigned int /*argsize*/) {
2483 ARG_ACTOR_FROM_PTR(actor);
2484 if (!actor) return 0;
2485
2486 if (actor->hasActorFlags(ACT_FEIGNDEATH))
2487 return 1;
2488 else
2489 return 0;
2490 }
2491
I_setFeignDeath(const uint8 * args,unsigned int)2492 uint32 Actor::I_setFeignDeath(const uint8 *args, unsigned int /*argsize*/) {
2493 ARG_ACTOR_FROM_PTR(actor);
2494 if (!actor) return 0;
2495
2496 if (actor->hasActorFlags(ACT_FEIGNDEATH))
2497 return 0;
2498
2499 actor->setActorFlag(ACT_FEIGNDEATH);
2500
2501 ProcId animfallpid = actor->doAnim(Animation::die, dir_current);
2502 Process *animfallproc = Kernel::get_instance()->getProcess(animfallpid);
2503 assert(animfallproc);
2504
2505 ProcId animstandpid = actor->doAnim(Animation::standUp, dir_current);
2506 Process *animstandproc = Kernel::get_instance()->getProcess(animstandpid);
2507 assert(animstandproc);
2508
2509 Process *delayproc = new DelayProcess(900); // 30 seconds
2510 Kernel::get_instance()->addProcess(delayproc);
2511
2512 Process *clearproc = new ClearFeignDeathProcess(actor);
2513 Kernel::get_instance()->addProcess(clearproc);
2514
2515 // do them in order (fall, stand, wait, clear)
2516
2517 clearproc->waitFor(delayproc);
2518 delayproc->waitFor(animstandproc);
2519 animstandproc->waitFor(animfallproc);
2520
2521 return 0;
2522 }
2523
I_clrFeignDeath(const uint8 * args,unsigned int)2524 uint32 Actor::I_clrFeignDeath(const uint8 *args, unsigned int /*argsize*/) {
2525 ARG_ACTOR_FROM_PTR(actor);
2526 if (!actor) return 0;
2527
2528 actor->clearActorFlag(ACT_FEIGNDEATH);
2529
2530 return 0;
2531 }
2532
I_pathfindToItem(const uint8 * args,unsigned int)2533 uint32 Actor::I_pathfindToItem(const uint8 *args, unsigned int /*argsize*/) {
2534 ARG_ACTOR_FROM_PTR(actor);
2535 ARG_OBJID(id2);
2536 Item *item = getItem(id2);
2537 if (!actor) return 0;
2538 if (!item) return 0;
2539
2540 return Kernel::get_instance()->addProcess(
2541 new PathfinderProcess(actor, id2));
2542 }
2543
I_pathfindToPoint(const uint8 * args,unsigned int)2544 uint32 Actor::I_pathfindToPoint(const uint8 *args, unsigned int /*argsize*/) {
2545 ARG_ACTOR_FROM_PTR(actor);
2546 ARG_UINT16(x);
2547 ARG_UINT16(y);
2548 ARG_UINT8(z);
2549 ARG_NULL16(); // unknown. Only one instance of this in U8, values are 5,1.
2550 if (!actor) return 0;
2551
2552 if (GAME_IS_CRUSADER) {
2553 x *= 2;
2554 y *= 2;
2555 }
2556
2557 return Kernel::get_instance()->addProcess(
2558 new PathfinderProcess(actor, x, y, z));
2559 }
2560
I_areEnemiesNear(const uint8 * args,unsigned int)2561 uint32 Actor::I_areEnemiesNear(const uint8 *args, unsigned int /*argsize*/) {
2562 ARG_ACTOR_FROM_PTR(actor);
2563 if (!actor) return 0;
2564
2565 if (actor->areEnemiesNear())
2566 return 1;
2567 else
2568 return 0;
2569 }
2570
I_isBusy(const uint8 * args,unsigned int)2571 uint32 Actor::I_isBusy(const uint8 *args, unsigned int /*argsize*/) {
2572 ARG_ACTOR_FROM_PTR(actor);
2573
2574 if (actor->isBusy())
2575 return 1;
2576 else
2577 return 0;
2578 }
2579
I_createActor(const uint8 * args,unsigned int)2580 uint32 Actor::I_createActor(const uint8 *args, unsigned int /*argsize*/) {
2581 ARG_UC_PTR(ptr);
2582 ARG_UINT16(shape);
2583 ARG_UINT16(frame);
2584
2585 //!! do we need to flag actor as temporary?
2586
2587 Actor *newactor = createActor(shape, frame);
2588 if (!newactor) {
2589 perr << "I_createActor failed to create actor (" << shape
2590 << ")." << Std::endl;
2591 return 0;
2592 }
2593 uint16 objID = newactor->getObjId();
2594
2595 uint8 buf[2];
2596 buf[0] = static_cast<uint8>(objID);
2597 buf[1] = static_cast<uint8>(objID >> 8);
2598 UCMachine::get_instance()->assignPointer(ptr, buf, 2);
2599
2600 #if 0
2601 perr << "I_createActor: created actor #" << objID << " with shape " << shape << Std::endl;
2602 #endif
2603
2604 return objID;
2605 }
2606
I_createActorCru(const uint8 * args,unsigned int)2607 uint32 Actor::I_createActorCru(const uint8 *args, unsigned int /*argsize*/) {
2608 ARG_ITEM_FROM_PTR(item);
2609 ARG_ITEM_FROM_ID(other);
2610
2611 if (!item || !other)
2612 return 0;
2613
2614 const int gameDifficulty = World::get_instance()->getGameDifficulty();
2615 int npcDifficulty = (item->getMapNum() & 3) + 1;
2616
2617 if (gameDifficulty < npcDifficulty)
2618 return 0;
2619
2620 uint16 dtableidx = other->getNpcNum();
2621
2622 const NPCDat *npcData = GameData::get_instance()->getNPCData(dtableidx);
2623 if (!npcData)
2624 return 0;
2625
2626 int dir = item->getNpcNum() & 0xf;
2627 int frame = (dir * 2 + 4) & 0xf;
2628 uint16 shape = npcData->getShapeNo();
2629
2630 enum extflags ext = static_cast<extflags>(0);
2631 if (shape == 0x597 || shape == 0x3ac)
2632 ext = EXT_FEMALE;
2633
2634 Actor *newactor = ItemFactory::createActor(shape, frame, 0,
2635 Item::FLG_IN_NPC_LIST | Item::FLG_DISPOSABLE,
2636 0, 0, ext, true);
2637 if (!newactor) {
2638 perr << "I_createActorCru failed to create actor ("
2639 << npcData->getShapeNo() << ")." << Std::endl;
2640 return 0;
2641 }
2642
2643 // Most of these will be overwritten below, but this is cleaner..
2644 bool loaded = newactor->loadMonsterStats();
2645 if (!loaded) {
2646 perr << "I_createActorCru failed to load monster stats ("
2647 << npcData->getShapeNo() << ")." << Std::endl;
2648 return 0;
2649 }
2650
2651 newactor->setDir(Direction_FromUsecodeDir(dir));
2652
2653 int32 x, y, z;
2654 item->getLocation(x, y, z);
2655 newactor->move(x, y, z);
2656
2657 newactor->setDefaultActivity(0, other->getQuality() >> 8);
2658 newactor->setDefaultActivity(1, item->getQuality() >> 8);
2659 newactor->setDefaultActivity(2, other->getMapNum());
2660
2661 newactor->setUnkByte(item->getQuality() & 0xff);
2662
2663 bool wpnflag = (item->getMapNum() & 4);
2664 uint16 wpntype = npcData->getWpnType();
2665 uint16 wpntype2 = npcData->getWpnType2();
2666
2667 if (World::get_instance()->getGameDifficulty() == 4) {
2668 wpntype = NPCDat::randomlyGetStrongerWeaponTypes(shape);
2669 wpntype2 = wpntype;
2670 }
2671
2672 if ((!wpntype || !wpnflag) && wpntype2) {
2673 wpntype = wpntype2;
2674 }
2675
2676 // TODO: Nasty hard coded list.. use the ini file for this.
2677 static const int WPNSHAPES[] = {0, 0x032E, 0x032F, 0x0330, 0x038C, 0x0332, 0x0333,
2678 0x0334, 0x038E, 0x0388, 0x038A, 0x038D, 0x038B, 0x0386,
2679 // Regret-specific weapon types
2680 0x05F6, 0x05F5, 0x0198};
2681 if (wpntype && wpntype < ARRAYSIZE(WPNSHAPES)) {
2682 // wpntype is an offset into wpn table
2683 Item *weapon = ItemFactory::createItem(WPNSHAPES[wpntype], 0, 0, 0, 0, newactor->getMapNum(), 0, true);
2684 if (weapon) {
2685 weapon->moveToContainer(newactor, false);
2686 newactor->_activeWeapon = weapon->getObjId();
2687 }
2688 }
2689
2690 newactor->setCombatTactic(0);
2691 newactor->setHomePosition(x, y, z);
2692
2693 /*
2694 TODO: once I know what this field is.. seems to never be used in game?
2695 newactor->setField0x12(item->getNpcNum() >> 4);
2696 */
2697
2698 return newactor->getObjId();
2699 }
2700
I_setUnkByte(const uint8 * args,unsigned int)2701 uint32 Actor::I_setUnkByte(const uint8 *args, unsigned int /*argsize*/) {
2702 ARG_ACTOR_FROM_PTR(actor);
2703 ARG_UINT16(value);
2704 if (actor)
2705 actor->setUnkByte(static_cast<uint8>(value & 0xff));
2706 return 0;
2707 }
2708
I_getUnkByte(const uint8 * args,unsigned int)2709 uint32 Actor::I_getUnkByte(const uint8 *args, unsigned int /*argsize*/) {
2710 ARG_ACTOR_FROM_PTR(actor);
2711 if (!actor) return 0;
2712
2713 return actor->getUnkByte();
2714 }
2715
I_setActivity(const uint8 * args,unsigned int)2716 uint32 Actor::I_setActivity(const uint8 *args, unsigned int /*argsize*/) {
2717 ARG_ACTOR_FROM_PTR(actor);
2718 ARG_UINT16(activity);
2719 if (!actor) return 0;
2720
2721 return actor->setActivity(activity);
2722 }
2723
I_setAirWalkEnabled(const uint8 * args,unsigned int)2724 uint32 Actor::I_setAirWalkEnabled(const uint8 *args, unsigned int /*argsize*/) {
2725 ARG_ACTOR_FROM_PTR(actor);
2726 ARG_UINT16(enabled);
2727 if (!actor) return 0;
2728
2729 if (enabled)
2730 actor->setActorFlag(ACT_AIRWALK);
2731 else
2732 actor->clearActorFlag(ACT_AIRWALK);
2733
2734 return 0;
2735 }
2736
2737
I_getAirWalkEnabled(const uint8 * args,unsigned int)2738 uint32 Actor::I_getAirWalkEnabled(const uint8 *args, unsigned int /*argsize*/) {
2739 ARG_ACTOR_FROM_PTR(actor);
2740 if (!actor) return 0;
2741
2742 if (actor->hasActorFlags(ACT_AIRWALK))
2743 return 1;
2744 else
2745 return 0;
2746 }
2747
I_schedule(const uint8 * args,unsigned int)2748 uint32 Actor::I_schedule(const uint8 *args, unsigned int /*argsize*/) {
2749 ARG_ACTOR_FROM_PTR(actor);
2750 ARG_UINT32(time);
2751 if (!actor) return 0;
2752
2753 return actor->schedule(time);
2754 }
2755
2756
I_getEquip(const uint8 * args,unsigned int)2757 uint32 Actor::I_getEquip(const uint8 *args, unsigned int /*argsize*/) {
2758 ARG_ACTOR_FROM_PTR(actor);
2759 ARG_UINT16(type);
2760 if (!actor) return 0;
2761
2762 return actor->getEquip(type + 1);
2763 }
2764
I_setEquip(const uint8 * args,unsigned int)2765 uint32 Actor::I_setEquip(const uint8 *args, unsigned int /*argsize*/) {
2766 ARG_ACTOR_FROM_PTR(actor);
2767 ARG_UINT16(type);
2768 ARG_ITEM_FROM_ID(item);
2769 if (!actor) return 0;
2770 if (!item) return 0;
2771
2772 if (!actor->setEquip(item, false))
2773 return 0;
2774
2775 // check it was added to the right slot
2776 assert(item->getZ() == type + 1 || (item->getShape() == BACKPACK_SHAPE && type == 6));
2777
2778 return 1;
2779 }
2780
I_setDefaultActivity0(const uint8 * args,unsigned int)2781 uint32 Actor::I_setDefaultActivity0(const uint8 *args, unsigned int /*argsize*/) {
2782 ARG_ACTOR_FROM_PTR(actor);
2783 ARG_UINT16(activity);
2784 if (!actor) return 0;
2785
2786 actor->setDefaultActivity(0, activity);
2787 return 0;
2788 }
2789
I_setDefaultActivity1(const uint8 * args,unsigned int)2790 uint32 Actor::I_setDefaultActivity1(const uint8 *args, unsigned int /*argsize*/) {
2791 ARG_ACTOR_FROM_PTR(actor);
2792 ARG_UINT16(activity);
2793 if (!actor) return 0;
2794
2795 actor->setDefaultActivity(1, activity);
2796 return 0;
2797 }
2798
I_setDefaultActivity2(const uint8 * args,unsigned int)2799 uint32 Actor::I_setDefaultActivity2(const uint8 *args, unsigned int /*argsize*/) {
2800 ARG_ACTOR_FROM_PTR(actor);
2801 ARG_UINT16(activity);
2802 if (!actor) return 0;
2803
2804 actor->setDefaultActivity(2, activity);
2805 return 0;
2806 }
2807
I_getDefaultActivity0(const uint8 * args,unsigned int)2808 uint32 Actor::I_getDefaultActivity0(const uint8 *args, unsigned int /*argsize*/) {
2809 ARG_ACTOR_FROM_PTR(actor);
2810 if (!actor) return 0;
2811
2812 return actor->getDefaultActivity(0);
2813 }
2814
I_getDefaultActivity1(const uint8 * args,unsigned int)2815 uint32 Actor::I_getDefaultActivity1(const uint8 *args, unsigned int /*argsize*/) {
2816 ARG_ACTOR_FROM_PTR(actor);
2817 if (!actor) return 0;
2818
2819 return actor->getDefaultActivity(1);
2820 }
2821
I_getDefaultActivity2(const uint8 * args,unsigned int)2822 uint32 Actor::I_getDefaultActivity2(const uint8 *args, unsigned int /*argsize*/) {
2823 ARG_ACTOR_FROM_PTR(actor);
2824 if (!actor) return 0;
2825
2826 return actor->getDefaultActivity(2);
2827 }
2828
I_setCombatTactic(const uint8 * args,unsigned int)2829 uint32 Actor::I_setCombatTactic(const uint8 *args, unsigned int /*argsize*/) {
2830 ARG_ACTOR_FROM_PTR(actor);
2831 if (!actor) return 0;
2832 ARG_UINT16(tactic);
2833
2834 actor->setCombatTactic(tactic);
2835 return 0;
2836 }
2837
I_getCurrentActivityNo(const uint8 * args,unsigned int)2838 uint32 Actor::I_getCurrentActivityNo(const uint8 *args, unsigned int /*argsize*/) {
2839 ARG_ACTOR_FROM_PTR(actor);
2840 if (!actor) return 0;
2841
2842 return actor->getCurrentActivityNo();
2843 }
2844
I_getLastActivityNo(const uint8 * args,unsigned int)2845 uint32 Actor::I_getLastActivityNo(const uint8 *args, unsigned int /*argsize*/) {
2846 ARG_ACTOR_FROM_PTR(actor);
2847 if (!actor) return 0;
2848
2849 return actor->getLastActivityNo();
2850 }
2851
I_turnToward(const uint8 * args,unsigned int)2852 uint32 Actor::I_turnToward(const uint8 *args, unsigned int /*argsize*/) {
2853 ARG_ACTOR_FROM_PTR(actor);
2854 if (!actor) return 0;
2855
2856 ARG_UINT16(dir);
2857 ARG_UINT16(dir16);
2858
2859 Direction newdir = Direction_FromUsecodeDir(dir);
2860 Direction curdir = actor->getDir();
2861 Direction oneleft = Direction_OneLeft(curdir, dirmode_16dirs);
2862 Direction oneright = Direction_OneRight(curdir, dirmode_16dirs);
2863
2864 if (curdir == newdir ||
2865 (!dir16 && (newdir == oneleft || newdir == oneright)))
2866 return 0;
2867
2868 return actor->turnTowardDir(newdir);
2869 }
2870
I_isKneeling(const uint8 * args,unsigned int)2871 uint32 Actor::I_isKneeling(const uint8 *args, unsigned int /*argsize*/) {
2872 ARG_ACTOR_FROM_PTR(actor);
2873 if (!actor) return 0;
2874
2875 return actor->isKneeling() ? 1 : 0;
2876 }
2877
I_isFalling(const uint8 * args,unsigned int)2878 uint32 Actor::I_isFalling(const uint8 *args, unsigned int /*argsize*/) {
2879 ARG_ACTOR_FROM_PTR(actor);
2880 if (!actor) return 0;
2881
2882 return actor->isFalling() ? 1 : 0;
2883 }
2884
2885 } // End of namespace Ultima8
2886 } // End of namespace Ultima
2887