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/world/actors/main_actor.h"
24 #include "ultima/ultima8/world/teleport_egg.h"
25 #include "ultima/ultima8/world/current_map.h"
26 #include "ultima/ultima8/kernel/kernel.h"
27 #include "ultima/ultima8/world/actors/teleport_to_egg_process.h"
28 #include "ultima/ultima8/world/target_reticle_process.h"
29 #include "ultima/ultima8/world/camera_process.h"
30 #include "ultima/ultima8/graphics/shape_info.h"
31 #include "ultima/ultima8/ultima8.h"
32 #include "ultima/ultima8/world/actors/avatar_death_process.h"
33 #include "ultima/ultima8/kernel/delay_process.h"
34 #include "ultima/ultima8/games/game_data.h"
35 #include "ultima/ultima8/graphics/anim_dat.h"
36 #include "ultima/ultima8/graphics/wpn_ovlay_dat.h"
37 #include "ultima/ultima8/graphics/main_shape_archive.h"
38 #include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
39 #include "ultima/ultima8/audio/audio_process.h"
40 #include "ultima/ultima8/world/world.h"
41 #include "ultima/ultima8/world/get_object.h"
42 #include "ultima/ultima8/usecode/uc_list.h"
43 #include "ultima/ultima8/usecode/uc_machine.h"
44 #include "ultima/ultima8/world/loop_script.h"
45 #include "ultima/ultima8/world/fire_type.h"
46 #include "ultima/ultima8/world/sprite_process.h"
47 #include "ultima/ultima8/world/actors/avatar_gravity_process.h"
48 #include "ultima/ultima8/world/actors/actor_anim_process.h"
49 #include "ultima/ultima8/audio/music_process.h"
50 #include "ultima/ultima8/world/actors/anim_action.h"
51
52 namespace Ultima {
53 namespace Ultima8 {
54
55 DEFINE_RUNTIME_CLASSTYPE_CODE(MainActor)
56
57 ShapeInfo *MainActor::_kneelingShapeInfo = nullptr;
58
MainActor()59 MainActor::MainActor() : _justTeleported(false), _accumStr(0), _accumDex(0),
60 _accumInt(0), _cruBatteryType(ChemicalBattery), _keycards(0),
61 _activeInvItem(0), _shieldType(0), _shieldSpriteProc(0) {
62 }
63
~MainActor()64 MainActor::~MainActor() {
65 if (_kneelingShapeInfo) {
66 delete _kneelingShapeInfo;
67 _kneelingShapeInfo = nullptr;
68 }
69 }
70
ensureGravityProcess()71 GravityProcess *MainActor::ensureGravityProcess() {
72 AvatarGravityProcess *p;
73 if (_gravityPid) {
74 p = dynamic_cast<AvatarGravityProcess *>(
75 Kernel::get_instance()->getProcess(_gravityPid));
76 } else {
77 p = new AvatarGravityProcess(this, 0);
78 Kernel::get_instance()->addProcess(p);
79 p->init();
80 }
81 assert(p);
82 return p;
83 }
84
CanAddItem(Item * item,bool checkwghtvol)85 bool MainActor::CanAddItem(Item *item, bool checkwghtvol) {
86
87 if (!Actor::CanAddItem(item, checkwghtvol)) return false;
88 if (item->getParent() == _objId) return true; // already in here
89
90 // now check 'equipment slots'
91 // we can have one item of each equipment type, plus one backpack
92
93 if (GAME_IS_U8) {
94 const unsigned int backpack_shape = 529; //!! *cough* constant
95 uint32 equiptype = item->getShapeInfo()->_equipType;
96 bool backpack = (item->getShape() == backpack_shape);
97
98 // valid item type?
99 if (equiptype == ShapeInfo::SE_NONE && !backpack) return false;
100
101 Std::list<Item *>::iterator iter;
102 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
103 uint32 cet = (*iter)->getShapeInfo()->_equipType;
104 bool cbackpack = ((*iter)->getShape() == backpack_shape);
105
106 // already have an item with the same equiptype
107 if (cet == equiptype || (cbackpack && backpack)) return false;
108 }
109 } else if (GAME_IS_CRUSADER) {
110 // TODO: Enforce the number of slots by family here (weapon / ammo / other)
111 // For now just enforce no limit.
112 return true;
113 }
114
115 return true;
116 }
117
addItem(Item * item,bool checkwghtvol)118 bool MainActor::addItem(Item *item, bool checkwghtvol) {
119 if (!Actor::addItem(item, checkwghtvol)) return false;
120
121 item->setFlag(FLG_EQUIPPED);
122
123 uint32 equiptype = item->getShapeInfo()->_equipType;
124 item->setZ(equiptype);
125
126 return true;
127 }
128
addItemCru(Item * item,bool showtoast)129 int16 MainActor::addItemCru(Item *item, bool showtoast) {
130 // This code is a little ugly, it's a somewhat close
131 // re-implementation of the original and could do
132 // with some cleanup.
133
134 if (!item || !item->getShape())
135 return 0;
136
137 int shapeno = item->getShape();
138 int32 x, y, z;
139 getLocation(x, y, z);
140
141 CruPickupAreaGump *pickupArea = CruPickupAreaGump::get_instance();
142 assert(pickupArea);
143
144 if (shapeno == 0x4ed) {
145 Item *credits = getFirstItemWithShape(shapeno, true);
146 if (credits) {
147 uint16 q = item->getQuality();
148 uint32 newq = credits->getQuality() + q;
149 if (newq > 64000)
150 newq = 64000;
151 credits->setQuality(newq);
152 credits->callUsecodeEvent_combine();
153 if (showtoast)
154 pickupArea->addPickup(item, true);
155 item->destroy();
156 } else {
157 item->setFrame(0);
158 item->moveToContainer(this);
159 if (!_activeInvItem)
160 _activeInvItem = item->getObjId();
161 if (showtoast)
162 pickupArea->addPickup(item, true);
163 }
164 return 1;
165 }
166
167 switch (static_cast<ShapeInfo::SFamily>(item->getShapeInfo()->_family)) {
168 case ShapeInfo::SF_CRUWEAPON: { // 0xa
169 Item *weapon = getFirstItemWithShape(shapeno, true);
170 if (!weapon) {
171 // New weapon. Add it.
172 const WeaponInfo *winfo = item->getShapeInfo()->_weaponInfo;
173 assert(winfo);
174 if (winfo->_ammoType == 0) {
175 item->setQuality(0);
176 item->callUsecodeEvent_combine();
177 } else {
178 item->setQuality(winfo->_clipSize);
179 }
180 item->setLocation(x, y, z);
181 item->moveToContainer(this);
182 if (!_activeWeapon)
183 _activeWeapon = item->getObjId();
184 if (showtoast)
185 pickupArea->addPickup(item, false);
186 }
187 break;
188 }
189 case ShapeInfo::SF_CRUAMMO: { // 0xb
190 Item *ammo = getFirstItemWithShape(shapeno, true);
191 if (!ammo) {
192 // don't have this ammo yet, add it
193 item->setQuality(1);
194 item->callUsecodeEvent_combine();
195 item->moveToContainer(this);
196 if (showtoast)
197 pickupArea->addPickup(item, false);
198 return 1;
199 } else {
200 // already have this, add some ammo.
201 uint16 q = ammo->getQuality();
202 if (q < 0x14) {
203 ammo->setQuality(q + 1);
204 ammo->callUsecodeEvent_combine();
205 if (showtoast)
206 pickupArea->addPickup(item, false);
207 item->destroy();
208 return 1;
209 }
210 }
211 break;
212 }
213 case ShapeInfo::SF_CRUBOMB: // 0xc
214 case ShapeInfo::SF_CRUINVITEM: // 0xd
215 if (shapeno == 0x111) {
216 addKeycard(item->getQuality() & 0xff);
217 if (showtoast) {
218 pickupArea->addPickup(item, false);
219 }
220 item->destroy();
221 return 1;
222 } else if ((shapeno == 0x3a2) || (shapeno == 0x3a3) || (shapeno == 0x3a4)) {
223 // Batteries
224 if (showtoast)
225 pickupArea->addPickup(item, false);
226 item->destroy();
227 int plusenergy = 0;
228 CruBatteryType oldbattery = _cruBatteryType;
229 if (shapeno == 0x3a2) {
230 if (oldbattery == NoBattery) {
231 setBatteryType(ChemicalBattery);
232 } else {
233 plusenergy = 0x9c4;
234 }
235 } else if (shapeno == 0x3a4) {
236 if (oldbattery < FissionBattery) {
237 setBatteryType(FissionBattery);
238 } else {
239 plusenergy = 5000;
240 }
241 } else if (shapeno == 0x3a3) {
242 if (oldbattery < FusionBattery) {
243 setBatteryType(FusionBattery);
244 } else {
245 plusenergy = 10000;
246 }
247 }
248 if (plusenergy) {
249 int newenergy = getMana() + plusenergy;
250 if (newenergy > getMaxEnergy())
251 newenergy = getMaxEnergy();
252 setMana(newenergy);
253 }
254 return 1;
255 } else {
256 Item *existing = getFirstItemWithShape(shapeno, true);
257 if (!existing) {
258 // Shields. Note, these are the same in Remorse and Regret.
259 if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
260 uint16 shieldtype;
261 switch (shapeno) {
262 default:
263 case 0x52e:
264 shieldtype = 1;
265 break;
266 case 0x52f:
267 shieldtype = 2;
268 break;
269 case 0x530:
270 shieldtype = 3;
271 break;
272 }
273 if (_shieldType < shieldtype) {
274 _shieldType = shieldtype;
275 }
276 if (showtoast)
277 pickupArea->addPickup(item, false);
278 item->destroy();
279 return 1;
280 } else {
281 item->setFrame(0);
282 item->setQuality(1);
283 item->callUsecodeEvent_combine();
284 item->moveToContainer(this);
285 if (showtoast)
286 pickupArea->addPickup(item, true);
287 if (!_activeInvItem)
288 _activeInvItem = item->getObjId();
289 return 1;
290 }
291 } else {
292 // Already have this item..
293 if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
294 // shields, already have one, destroy the new one.
295 item->destroy();
296 return 1;
297 } else if (shapeno == 0x560) {
298 uint16 q = existing->getQuality();
299 if (q < 0x14) {
300 existing->setQuality(q + 1);
301 existing->callUsecodeEvent_combine();
302 item->setQuality(1); // Count of picked up item is always 1
303 if (showtoast)
304 pickupArea->addPickup(item, true);
305 item->destroy();
306 return 1;
307 }
308 } else {
309 uint16 q = existing->getQuality();
310 if (q < 10) {
311 existing->setQuality(q + 1);
312 existing->callUsecodeEvent_combine();
313 item->setQuality(1); // Count of picked up item is always 1
314 if (showtoast)
315 pickupArea->addPickup(item, true);
316 item->destroy();
317 return 1;
318 }
319 }
320 }
321 }
322 break;
323 default:
324 break;
325 }
326
327 return 0;
328 }
329
removeItemCru(Item * item)330 bool MainActor::removeItemCru(Item *item) {
331 warning("TODO: Implement MainActor::removeItemCru");
332 return false;
333 }
334
getShapeInfoFromGameInstance() const335 const ShapeInfo *MainActor::getShapeInfoFromGameInstance() const {
336 const ShapeInfo *info = Item::getShapeInfoFromGameInstance();
337
338 if (!(_actorFlags & ACT_KNEELING) || GAME_IS_U8)
339 return info;
340
341 // When kneeling in Crusader, return a modified shape with a lower height.
342 if (!_kneelingShapeInfo) {
343 _kneelingShapeInfo = new ShapeInfo();
344 // Not great coupling here, we know most fields don't need filling out..
345 _kneelingShapeInfo->_flags = info->_flags;
346 _kneelingShapeInfo->_x = info->_x;
347 _kneelingShapeInfo->_y = info->_y;
348 _kneelingShapeInfo->_weight = info->_weight;
349 _kneelingShapeInfo->_volume = info->_volume;
350 _kneelingShapeInfo->_family = info->_family;
351 _kneelingShapeInfo->_z = info->_z - 4;
352 }
353
354 return _kneelingShapeInfo;
355 }
356
move(int32 x,int32 y,int32 z)357 void MainActor::move(int32 x, int32 y, int32 z) {
358 Actor::move(x, y, z);
359 if (_shieldSpriteProc != 0) {
360 SpriteProcess *sprite = dynamic_cast<SpriteProcess *>(
361 Kernel::get_instance()->getProcess(_shieldSpriteProc));
362 if (sprite) {
363 sprite->move(x, y, z);
364 }
365 }
366 }
367
teleport(int mapNum,int32 x,int32 y,int32 z)368 void MainActor::teleport(int mapNum, int32 x, int32 y, int32 z) {
369 World *world = World::get_instance();
370
371 uint16 oldmap = getMapNum();
372
373 // (attempt to) load the new map
374 if (!world->switchMap(mapNum)) {
375 perr << "MainActor::teleport(): switchMap(" << mapNum << ") failed!" << Std::endl;
376 return;
377 }
378
379 Actor::teleport(mapNum, x, y, z);
380
381 if (GAME_IS_CRUSADER && (x || y) && oldmap == mapNum) {
382 // Keep the camera on the avatar (the snap process will update on next move)
383 CameraProcess::GetCameraProcess()->moveToLocation(x, y, z);
384 }
385
386 _justTeleported = true;
387 }
388
389 // teleport to TeleportEgg
390 // NB: be careful when calling this from a process, as it might kill
391 // all running processes
teleport(int mapNum,int teleport_id)392 void MainActor::teleport(int mapNum, int teleport_id) {
393 int oldmap = getMapNum();
394 int32 oldx, oldy, oldz;
395 getLocation(oldx, oldy, oldz);
396
397 World *world = World::get_instance();
398 CurrentMap *currentmap = world->getCurrentMap();
399
400 pout << "MainActor::teleport(): teleporting to map " << mapNum
401 << ", egg " << teleport_id << Std::endl;
402
403 setMapNum(mapNum);
404
405 // (attempt to) load the new map
406 if (!world->switchMap(mapNum)) {
407 perr << "MainActor::teleport(): switchMap() failed!" << Std::endl;
408 setMapNum(oldmap);
409 return;
410 }
411
412 // find destination
413 TeleportEgg *egg = currentmap->findDestination(teleport_id);
414 if (!egg) {
415 perr << "MainActor::teleport(): destination egg not found!"
416 << Std::endl;
417 teleport(oldmap, oldx, oldy, oldz);
418 return;
419 }
420 int32 xv, yv, zv;
421 egg->getLocation(xv, yv, zv);
422
423 pout << "Found destination: " << xv << "," << yv << "," << zv << Std::endl;
424 egg->dumpInfo();
425
426 if (GAME_IS_CRUSADER) {
427 // Keep the camera on the avatar (the snap process will update on next move)
428 // We don't add a new camera process here, as that would update the fast area
429 // before the cachein calls above have run.
430 CameraProcess::GetCameraProcess()->moveToLocation(xv, yv, zv);
431 }
432
433 Actor::teleport(mapNum, xv, yv, zv);
434
435 _justTeleported = true;
436 }
437
getDefenseType() const438 uint16 MainActor::getDefenseType() const {
439 uint16 type = 0;
440
441 Std::list<Item *>::const_iterator iter;
442 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
443 uint32 frameNum = (*iter)->getFrame();
444 const ShapeInfo *si = (*iter)->getShapeInfo();
445 if (si->_armourInfo) {
446 type |= si->_armourInfo[frameNum]._defenseType;
447 }
448 }
449
450 return type;
451 }
452
getArmourClass() const453 uint32 MainActor::getArmourClass() const {
454 uint32 armour = 0;
455
456 Std::list<Item *>::const_iterator iter;
457 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
458 uint32 frameNum = (*iter)->getFrame();
459 const ShapeInfo *si = (*iter)->getShapeInfo();
460 if (si->_armourInfo) {
461 armour += si->_armourInfo[frameNum]._armourClass;
462 }
463 if (si->_weaponInfo) {
464 armour += si->_weaponInfo->_armourBonus;
465 }
466 }
467
468 return armour;
469 }
470
getDefendingDex() const471 int16 MainActor::getDefendingDex() const {
472 int16 dex = getDex();
473
474 Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
475 if (weapon) {
476 const ShapeInfo *si = weapon->getShapeInfo();
477 assert(si->_weaponInfo);
478 dex += si->_weaponInfo->_dexDefendBonus;
479 }
480
481 if (dex <= 0) dex = 1;
482
483 return dex;
484 }
485
getAttackingDex() const486 int16 MainActor::getAttackingDex() const {
487 int16 dex = getDex();
488
489 Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
490 if (weapon) {
491 const ShapeInfo *si = weapon->getShapeInfo();
492 assert(si->_weaponInfo);
493 dex += si->_weaponInfo->_dexAttackBonus;
494 }
495
496 return dex;
497 }
498
getDamageType() const499 uint16 MainActor::getDamageType() const {
500 Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
501
502 if (weapon) {
503 // weapon equipped?
504
505 const ShapeInfo *si = weapon->getShapeInfo();
506 assert(si->_weaponInfo);
507
508 return si->_weaponInfo->_damageType;
509 }
510
511 return Actor::getDamageType();
512 }
513
getDamageAmount() const514 int MainActor::getDamageAmount() const {
515 int damage = 0;
516
517 if (getLastAnim() == Animation::kick) {
518 // kick
519
520 int kick_bonus = 0;
521 Item *legs = getItem(getEquip(ShapeInfo::SE_LEGS));
522 if (legs) {
523 const ShapeInfo *si = legs->getShapeInfo();
524 assert(si->_armourInfo);
525 kick_bonus = si->_armourInfo[legs->getFrame()]._kickAttackBonus;
526 }
527
528 damage = (getRandom() % (getStr() / 2 + 1)) + kick_bonus;
529
530 return damage;
531
532 }
533
534 Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
535
536
537 if (weapon) {
538 // weapon equipped?
539
540 const ShapeInfo *si = weapon->getShapeInfo();
541 assert(si->_weaponInfo);
542
543 int base = si->_weaponInfo->_baseDamage;
544 int mod = si->_weaponInfo->_damageModifier;
545
546 damage = (getRandom() % (mod + 1)) + base + getStr() / 5;
547
548 return damage;
549 }
550
551 // no weapon?
552
553 damage = (getRandom() % (getStr() / 2 + 1)) + 1;
554
555 return damage;
556 }
557
setInCombat(int activity)558 void MainActor::setInCombat(int activity) {
559 setActorFlag(ACT_INCOMBAT);
560 if (GAME_IS_U8)
561 MusicProcess::get_instance()->playCombatMusic(98); // CONSTANT!
562 }
563
clearInCombat()564 void MainActor::clearInCombat() {
565 clearActorFlag(ACT_INCOMBAT);
566 if (GAME_IS_U8)
567 MusicProcess::get_instance()->restoreMusic();
568 }
569
die(uint16 damageType,uint16 damagePts,Direction srcDir)570 ProcId MainActor::die(uint16 damageType, uint16 damagePts, Direction srcDir) {
571 ProcId animprocid = Actor::die(damageType, damagePts, srcDir);
572
573 Ultima8Engine *app = Ultima8Engine::get_instance();
574 assert(app);
575
576 app->setAvatarInStasis(true);
577
578 Process *deathproc = new AvatarDeathProcess();
579 Kernel::get_instance()->addProcess(deathproc);
580
581 Process *delayproc = new DelayProcess(30 * 5); // 5 seconds
582 Kernel::get_instance()->addProcess(delayproc);
583
584 Process *animproc = Kernel::get_instance()->getProcess(animprocid);
585
586 if (animproc)
587 delayproc->waitFor(animproc);
588
589 deathproc->waitFor(delayproc);
590
591 MusicProcess *music = MusicProcess::get_instance();
592 if (GAME_IS_U8 && music) {
593 music->unqueueMusic();
594 music->playCombatMusic(44); // CONSTANT!!
595 }
596
597 if (GAME_IS_CRUSADER) {
598 // Force a reticle update
599 TargetReticleProcess::get_instance()->avatarMoved();
600 }
601
602 return animprocid;
603 }
604
accumulateStr(int n)605 void MainActor::accumulateStr(int n) {
606 // already max?
607 if (_strength == 25) return; //!! constant
608
609 _accumStr += n;
610 if (_accumStr >= 650 || getRandom() % (650 - _accumStr) == 0) { //!! constant
611 _strength++;
612 _accumStr = 0;
613 AudioProcess *audioproc = AudioProcess::get_instance();
614 if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
615 pout << "Gained _strength!" << Std::endl;
616 }
617 }
618
accumulateDex(int n)619 void MainActor::accumulateDex(int n) {
620 // already max?
621 if (_dexterity == 25) return; //!! constant
622
623 _accumDex += n;
624 if (_accumDex >= 650 || getRandom() % (650 - _accumDex) == 0) { //!! constant
625 _dexterity++;
626 _accumDex = 0;
627 AudioProcess *audioproc = AudioProcess::get_instance();
628 if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
629 pout << "Gained _dexterity!" << Std::endl;
630 }
631 }
632
accumulateInt(int n)633 void MainActor::accumulateInt(int n) {
634 // already max?
635 if (_intelligence == 25) return; //!! constant
636
637 _accumInt += n;
638 if (_accumInt >= 650 || getRandom() % (650 - _accumInt) == 0) { //!! constant
639 _intelligence++;
640 _accumInt = 0;
641 AudioProcess *audioproc = AudioProcess::get_instance();
642 if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
643 pout << "Gained _intelligence!" << Std::endl;
644 }
645 }
646
getWeaponOverlay(const WeaponOverlayFrame * & frame,uint32 & shape)647 void MainActor::getWeaponOverlay(const WeaponOverlayFrame *&frame, uint32 &shape) {
648 shape = 0;
649 frame = 0;
650
651 if (!isInCombat() && _lastAnim != Animation::unreadyWeapon) return;
652
653 uint32 action = AnimDat::getActionNumberForSequence(_lastAnim, this);
654
655 ObjId weaponid;
656 if (GAME_IS_U8)
657 weaponid = getEquip(ShapeInfo::SE_WEAPON);
658 else
659 weaponid = getActiveWeapon();
660
661 Item *weapon = getItem(weaponid);
662 if (!weapon) return;
663
664 const ShapeInfo *shapeinfo = weapon->getShapeInfo();
665 if (!shapeinfo) return;
666
667 WeaponInfo *weaponinfo = shapeinfo->_weaponInfo;
668 if (!weaponinfo) return;
669
670 shape = weaponinfo->_overlayShape;
671
672 const WpnOvlayDat *wpnovlay = GameData::get_instance()->getWeaponOverlay();
673 frame = wpnovlay->getOverlayFrame(action, weaponinfo->_overlayType,
674 _direction, _animFrame);
675
676 if (frame == nullptr)
677 shape = 0;
678 }
679
getMaxEnergy()680 int16 MainActor::getMaxEnergy() {
681 switch (_cruBatteryType) {
682 case ChemicalBattery:
683 return 0x9c4; // docs say 2500, code says otherwise..
684 case FissionBattery:
685 return 5000;
686 case FusionBattery:
687 return 10000;
688 default:
689 return 0;
690 }
691 }
692
hasKeycard(int num) const693 bool MainActor::hasKeycard(int num) const {
694 if (num > 31)
695 return 0;
696
697 return _keycards & (1 << num);
698 }
699
addKeycard(int bitno)700 void MainActor::addKeycard(int bitno) {
701 if (bitno > 31 || bitno < 0)
702 return;
703 _keycards |= (1 << bitno);
704 }
705
getIdOfNextItemInList(const Std::vector<Item * > & items,uint16 current)706 static uint16 getIdOfNextItemInList(const Std::vector<Item *> &items, uint16 current) {
707 const int n = items.size();
708 if (n == 0)
709 return 0;
710 if (n == 1)
711 return items[0]->getObjId();
712
713 int i;
714 for (i = 0; i < n; i++) {
715 if (items[i]->getObjId() == current) {
716 i++;
717 break;
718 }
719 }
720 return items[i % n]->getObjId();
721 }
722
nextWeapon()723 void MainActor::nextWeapon() {
724 Std::vector<Item *> weapons;
725 getItemsWithShapeFamily(weapons, ShapeInfo::SF_CRUWEAPON, true);
726 _activeWeapon = getIdOfNextItemInList(weapons, _activeWeapon);
727
728 // Update combat stance in case we switched big/small weapon.
729 if (_lastAnim == Animation::combatStand) {
730 if (isBusy()) {
731 // Corner case - need to stop active "stand"
732 // animation to correct it.
733 Kernel::get_instance()->killProcesses(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE, true);
734 }
735 doAnim(Animation::combatStand, dir_current);
736 }
737 }
738
nextInvItem()739 void MainActor::nextInvItem() {
740 Std::vector<Item *> items;
741 getItemsWithShapeFamily(items, ShapeInfo::SF_CRUINVITEM, true);
742 getItemsWithShapeFamily(items, ShapeInfo::SF_CRUBOMB, true);
743 if (GAME_IS_REMORSE) {
744 Item *credits = getFirstItemWithShape(0x4ed, true);
745 if (credits)
746 items.push_back(credits);
747 }
748 _activeInvItem = getIdOfNextItemInList(items, _activeInvItem);
749 }
750
saveData(Common::WriteStream * ws)751 void MainActor::saveData(Common::WriteStream *ws) {
752 Actor::saveData(ws);
753 uint8 jt = _justTeleported ? 1 : 0;
754 ws->writeByte(jt);
755 ws->writeUint32LE(_accumStr);
756 ws->writeUint32LE(_accumDex);
757 ws->writeUint32LE(_accumInt);
758
759 if (GAME_IS_CRUSADER) {
760 ws->writeByte(static_cast<byte>(_cruBatteryType));
761 ws->writeUint32LE(_keycards);
762 ws->writeUint16LE(_activeInvItem);
763 ws->writeUint16LE(_shieldType);
764 ws->writeUint16LE(_shieldSpriteProc);
765 }
766
767 uint8 namelength = static_cast<uint8>(_name.size());
768 ws->writeByte(namelength);
769 for (unsigned int i = 0; i < namelength; ++i)
770 ws->writeByte(static_cast<uint8>(_name[i]));
771
772 }
773
loadData(Common::ReadStream * rs,uint32 version)774 bool MainActor::loadData(Common::ReadStream *rs, uint32 version) {
775 if (!Actor::loadData(rs, version)) return false;
776
777 _justTeleported = (rs->readByte() != 0);
778 _accumStr = static_cast<int32>(rs->readUint32LE());
779 _accumDex = static_cast<int32>(rs->readUint32LE());
780 _accumInt = static_cast<int32>(rs->readUint32LE());
781
782 if (GAME_IS_CRUSADER) {
783 _cruBatteryType = static_cast<CruBatteryType>(rs->readByte());
784 _keycards = rs->readUint32LE();
785 _activeInvItem = rs->readUint16LE();
786 _shieldType = rs->readUint16LE();
787 _shieldSpriteProc = rs->readUint16LE();
788 }
789
790 uint8 namelength = rs->readByte();
791 _name.resize(namelength);
792 for (unsigned int i = 0; i < namelength; ++i)
793 _name[i] = rs->readByte();
794
795 return true;
796 }
797
I_teleportToEgg(const uint8 * args,unsigned int argsize)798 uint32 MainActor::I_teleportToEgg(const uint8 *args, unsigned int argsize) {
799 uint16 mapnum;
800 if (argsize == 6) {
801 ARG_UINT16(map);
802 mapnum = map;
803 } else {
804 // Crusader teleport intrinsic 096 uses main actor map.
805 // Intrinsic 079 provides a map argument.
806 assert(argsize == 4);
807 MainActor *av = getMainActor();
808 mapnum = av->getMapNum();
809 }
810
811 ARG_UINT16(teleport_id);
812 ARG_UINT16(put_in_stasis); // 0/1
813
814 return Kernel::get_instance()->addProcess(
815 new TeleportToEggProcess(mapnum, teleport_id));
816 }
817
I_accumulateStrength(const uint8 * args,unsigned int)818 uint32 MainActor::I_accumulateStrength(const uint8 *args,
819 unsigned int /*argsize*/) {
820 ARG_SINT16(n);
821 MainActor *av = getMainActor();
822 av->accumulateStr(n);
823
824 return 0;
825 }
826
I_accumulateDexterity(const uint8 * args,unsigned int)827 uint32 MainActor::I_accumulateDexterity(const uint8 *args,
828 unsigned int /*argsize*/) {
829 ARG_SINT16(n);
830 MainActor *av = getMainActor();
831 av->accumulateDex(n);
832
833 return 0;
834 }
835
I_accumulateIntelligence(const uint8 * args,unsigned int)836 uint32 MainActor::I_accumulateIntelligence(const uint8 *args,
837 unsigned int /*argsize*/) {
838 ARG_SINT16(n);
839 MainActor *av = getMainActor();
840 av->accumulateInt(n);
841
842 return 0;
843 }
844
I_clrAvatarInCombat(const uint8 *,unsigned int)845 uint32 MainActor::I_clrAvatarInCombat(const uint8 * /*args*/,
846 unsigned int /*argsize*/) {
847 MainActor *av = getMainActor();
848 av->clearInCombat();
849
850 return 0;
851 }
852
I_setAvatarInCombat(const uint8 *,unsigned int)853 uint32 MainActor::I_setAvatarInCombat(const uint8 * /*args*/,
854 unsigned int /*argsize*/) {
855 MainActor *av = getMainActor();
856 // Note: only happens in U8, so activity num is not important.
857 av->setInCombat(0);
858
859 return 0;
860 }
861
I_isAvatarInCombat(const uint8 *,unsigned int)862 uint32 MainActor::I_isAvatarInCombat(const uint8 * /*args*/,
863 unsigned int /*argsize*/) {
864 MainActor *av = getMainActor();
865 if (av->isInCombat())
866 return 1;
867 else
868 return 0;
869 }
870
I_getMaxEnergy(const uint8 * args,unsigned int)871 uint32 MainActor::I_getMaxEnergy(const uint8 *args,
872 unsigned int /*argsize*/) {
873 ARG_ACTOR_FROM_PTR(actor);
874 MainActor *av = getMainActor();
875 if (!av || actor != av) {
876 return 0;
877 }
878 return av->getMaxEnergy();
879 }
880
I_hasKeycard(const uint8 * args,unsigned int)881 uint32 MainActor::I_hasKeycard(const uint8 *args,
882 unsigned int /*argsize*/) {
883 ARG_UINT16(num);
884 MainActor *av = getMainActor();
885 if (!av)
886 return 0;
887 return av->hasKeycard(num);
888 }
889
I_clrKeycards(const uint8 * args,unsigned int)890 uint32 MainActor::I_clrKeycards(const uint8 *args,
891 unsigned int /*argsize*/) {
892 MainActor *av = getMainActor();
893 if (!av)
894 return 0;
895 av->clrKeycards();
896 return 0;
897 }
898
I_addItemCru(const uint8 * args,unsigned int)899 uint32 MainActor::I_addItemCru(const uint8 *args,
900 unsigned int /*argsize*/) {
901 MainActor *av = getMainActor();
902 ARG_ITEM_FROM_ID(item);
903 ARG_UINT16(showtoast);
904
905 if (!av || !item)
906 return 0;
907
908 if (av->addItemCru(item, showtoast != 0))
909 return 1;
910
911 return 0;
912 }
913
I_getNumberOfCredits(const uint8 * args,unsigned int)914 uint32 MainActor::I_getNumberOfCredits(const uint8 *args,
915 unsigned int /*argsize*/) {
916 MainActor *av = getMainActor();
917 if (av) {
918 Item *item = av->getFirstItemWithShape(0x4ed, true);
919 if (item)
920 return item->getQuality();
921 }
922 return 0;
923 }
924
I_switchMap(const uint8 * args,unsigned int)925 uint32 MainActor::I_switchMap(const uint8 *args,
926 unsigned int /*argsize*/) {
927 ARG_UINT16(mapnum);
928 MainActor *av = getMainActor();
929 if (av) {
930 av->teleport(mapnum, 0x1e); // CONSTANT
931 }
932 return 0;
933 }
934
I_removeItemCru(const uint8 * args,unsigned int)935 uint32 MainActor::I_removeItemCru(const uint8 *args,
936 unsigned int /*argsize*/) {
937 MainActor *av = getMainActor();
938 ARG_ITEM_FROM_ID(item);
939
940 if (!av || !item)
941 return 0;
942
943 if (av->removeItemCru(item))
944 return 1;
945
946 return 0;
947 }
948
949
useInventoryItem(uint32 shapenum)950 void MainActor::useInventoryItem(uint32 shapenum) {
951 Item *item = getFirstItemWithShape(shapenum, true);
952 useInventoryItem(item);
953 }
954
useInventoryItem(Item * item)955 void MainActor::useInventoryItem(Item *item) {
956 if (!item)
957 return;
958 if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
959 pout << "Can't use item: avatarInStasis" << Std::endl;
960 return;
961 }
962 const int32 shapenum = item->getShape();
963 if (shapenum == 0x4ed && GAME_IS_CRUSADER) {
964 // Do nothing for Credits
965 return;
966 }
967 item->callUsecodeEvent_use();
968
969 // 0x4d4 = datalink, 0x52d = scanner, 0x52e = ionic,
970 // 0x52f = plasma, 0x530 = graviton
971 // Note: These are the same in Remorse and Regret.
972 if (GAME_IS_CRUSADER && (shapenum != 0x4d4 && shapenum != 0x52d &&
973 shapenum != 0x530 && shapenum != 0x52f &&
974 shapenum != 0x52e)) {
975 uint16 q = item->getQuality();
976 item->setQuality(q - 1);
977 item->callUsecodeEvent_combine();
978 q = item->getQuality();
979 if (q == 0) {
980 const ObjId id = item->getObjId();
981 item->destroy();
982 if (id == _activeInvItem)
983 nextInvItem();
984 }
985 }
986 }
987
receiveShieldHit(int damage,uint16 damage_type)988 int MainActor::receiveShieldHit(int damage, uint16 damage_type) {
989 uint16 shieldtype = getShieldType();
990 if (shieldtype == 3) {
991 shieldtype = 4;
992 }
993
994 const FireType *firetype = GameData::get_instance()->getFireType(damage_type);
995 int energy = getMana();
996 Kernel *kernel = Kernel::get_instance();
997
998 if (shieldtype && firetype && firetype->getShieldCost() && (firetype->getShieldMask() & shieldtype)
999 && firetype->getShieldCost() <= energy) {
1000 setMana(energy - firetype->getShieldCost());
1001 damage = 0;
1002 AudioProcess *audio = AudioProcess::get_instance();
1003 audio->playSFX(0x48, 0x10, _objId, 1, true);
1004
1005 // If there's no active shield sprite, create a new one.
1006 if (!_shieldSpriteProc || kernel->getProcess(_shieldSpriteProc) == nullptr) {
1007 // Create the shield damage sprite
1008 uint16 shieldsprite;
1009 uint16 shieldstartframe;
1010 uint16 shieldendframe;
1011 bool remembersprite;
1012 int32 x, y, z;
1013
1014 switch (shieldtype) {
1015 case 1:
1016 shieldsprite = 0x5a9;
1017 shieldstartframe = 7;
1018 shieldendframe = 0xd;
1019 remembersprite = false;
1020 // NOTE: In the game, this is put in the location of the
1021 // hit. For now just put in centre.
1022 getCentre(x, y, z);
1023 break;
1024 case 2:
1025 shieldsprite = 0x5a9;
1026 shieldstartframe = 0;
1027 shieldendframe = 6;
1028 remembersprite = false;
1029 getCentre(x, y, z);
1030 break;
1031 default:
1032 shieldsprite = 0x52b;
1033 shieldstartframe = 0;
1034 shieldendframe = 8;
1035 getLocation(x, y, z);
1036 x += 0x10;
1037 y += 0x18;
1038 remembersprite = true;
1039 break;
1040 }
1041 Process *p = new SpriteProcess(shieldsprite, shieldstartframe,
1042 shieldendframe, 1, 4, x, y, z);
1043 kernel->addProcess(p);
1044 if (remembersprite) {
1045 _shieldSpriteProc = p->getPid();
1046 } else {
1047 _shieldSpriteProc = 0;
1048 }
1049 }
1050 }
1051 return damage;
1052 }
1053
detonateBomb()1054 void MainActor::detonateBomb() {
1055 // search area for shape 0x55F (1375) - DETPAC
1056 UCList uclist(2);
1057 LOOPSCRIPT(script, LS_SHAPE_EQUAL(0x55F));
1058 CurrentMap *currentmap = World::get_instance()->getCurrentMap();
1059 currentmap->areaSearch(&uclist, script, sizeof(script), nullptr,
1060 0x800, true, _x, _y);
1061 for (unsigned int i = 0; i < uclist.getSize(); ++i) {
1062 Item *founditem = getItem(uclist.getuint16(i));
1063 if (founditem->hasFlags(FLG_CONTAINED))
1064 continue;
1065 founditem->callUsecodeEvent_use();
1066 }
1067 return;
1068 }
1069
1070
1071 } // End of namespace Ultima8
1072 } // End of namespace Ultima
1073