scummvm/engines/saga2/weapons.cpp
2022-10-29 14:33:52 +02:00

290 lines
8.3 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* aint32 with this program; if not, write to the Free Software
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
#include "saga2/saga2.h"
#include "saga2/weapons.h"
#include "saga2/spelshow.h"
#include "saga2/spellbuk.h"
#include "saga2/actor.h"
#include "saga2/spellio.h"
#include "saga2/hresmgr.h"
#include "saga2/combat.h"
namespace Saga2 {
//-------------------------------------------------------------------
// The initialization for spells is done in this module:
// - Display Effects are defined (ball spell, bolt spell, etc)
// - The spell definitions are loaded from the resource file
// - The spell internal effect definitions are loaded from disk
// and attached to the appropriate spell
// - Spell Color maps are loaded from the resource file
// - This file is also home to most of the global variables
// which relate to spell casting
//
static void loadWeaponData();
ProtoEffect *createNewProtoEffect(Common::SeekableReadStream *stream) {
ProtoEffect *pe = nullptr;
/* int16 item = */stream->readSint16LE(); // spell ID
int16 effectGroup = stream->readSint16LE(); // effect group
int16 effectType = stream->readSint16LE(); // effect ID
int16 targeting = stream->readSint16LE(); // targeting
int16 baseDice = stream->readSint16LE(); // base dice
int16 skillDice = stream->readSint16LE(); // skill dice
int16 baseDamage = stream->readSint16LE(); // attrib change
int16 skillDamage = stream->readSint16LE(); // attrib change
int16 reserved0 = stream->readSint16LE(); // min enchant
int16 reserved1 = stream->readSint16LE(); // min enchant
int16 diceSides = stream->readSint16LE();
if (diceSides == 0)
diceSides = 6;
switch (effectGroup) {
case kEffectNone:
return nullptr;
case kEffectAttrib:
pe = new ProtoEnchantment(makeEnchantmentID(effectGroup, effectType, baseDamage), reserved0, reserved1);
break;
case kEffectResist:
case kEffectImmune:
case kEffectOthers:
case kEffectNonActor:
pe = new ProtoEnchantment(makeEnchantmentID(effectGroup, effectType, skillDamage), reserved0, reserved1);
break;
case kEffectDamage:
pe = new ProtoDamage(baseDice, diceSides, skillDice, baseDamage,
(effectDamageTypes)effectType, 0, targeting & kSpellTargCaster, skillDamage);
break;
case kEffectDrains:
pe = new ProtoDrainage(baseDice, diceSides, skillDice, baseDamage,
(effectDrainsTypes)effectType, 0, targeting & kSpellTargCaster);
break;
case kEffectPoison:
pe = new ProtoEnchantment(makeEnchantmentID(baseDamage), // poison
reserved0, reserved1);
break;
case kEffectTAG:
pe = new ProtoTAGEffect((effectTAGTypes)effectType, skillDamage, baseDamage);
break;
case kEffectLocation:
pe = new ProtoLocationEffect((kEffectLocationTypes)effectType, baseDamage);
break;
case kEffectSpecial:
pe = new ProtoSpecialEffect(SagaSpellCall, baseDamage);
break;
}
if (pe == nullptr)
error("failed to alloc protoEffect");
return pe;
}
void initWeapons() {
loadWeaponData();
}
void cleanupWeapons() {
for (int i = 0; i < kMaxWeapons; i++)
g_vm->_weaponRack[i].killEffects();
}
WeaponStuff &getWeapon(weaponID i) {
if (i < g_vm->_loadedWeapons)
return g_vm->_weaponRack[i];
return g_vm->_weaponRack[kNullWeapon];
}
GameObject *getShieldItem(GameObject *defender) {
assert(isActor(defender));
Actor *a = (Actor *) defender;
GameObject *obj;
a->defensiveObject(&obj);
return obj;
}
/* ===================================================================== *
WeaponProtoEffect member functions
* ===================================================================== */
WeaponProtoEffect::~WeaponProtoEffect() {
if (_effect != nullptr)
delete _effect;
}
void WeaponProtoEffect::implement(Actor *enactor, GameObject *target, GameObject *, uint8) {
SpellTarget targ(target);
if (_effect != nullptr)
_effect->implement(enactor, &targ);
}
/* ===================================================================== *
WeaponStrikeEffect member functions
* ===================================================================== */
void WeaponStrikeEffect::implement(Actor *enactor, GameObject *target, GameObject *strikingObj, uint8 strength) {
assert(isActor(enactor));
assert(isObject(target) || isActor(target));
assert(isObject(strikingObj) || isActor(strikingObj));
int8 totalDice, totalBase;
totalDice = _dice + strength * _skillDice;
totalBase = _base + strength * _skillBase;
target->acceptDamage(enactor->thisID(), totalBase, _type, totalDice, _sides);
}
WeaponStuff::WeaponStuff() {
_effects = nullptr;
_master = kNullWeapon;
}
WeaponStuff::~WeaponStuff() {
while (_effects != nullptr) {
WeaponEffect *curEffect = _effects;
_effects = _effects->_next;
delete curEffect;
}
_master = kNullWeapon;
}
void WeaponStuff::killEffects() {
while (_effects != nullptr) {
WeaponEffect *curEffect = _effects;
_effects = _effects->_next;
delete curEffect;
}
}
void WeaponStuff::addEffect(WeaponEffect *we) {
WeaponEffect *e = _effects;
if (_effects) {
while (e->_next)
e = e->_next;
e->_next = we;
} else {
_effects = we;
}
}
void WeaponStuff::addEffect(Common::SeekableReadStream *stream) {
WeaponEffect *we;
/*int16 item = */stream->readSint16LE(); // spell ID
int16 effectGroup = stream->readSint16LE(); // effect group
if (effectGroup == kEffectStrike) {
effectDamageTypes effectType = (effectDamageTypes)stream->readSint16LE(); // effect ID
/*int16 targeting = */stream->readSint16LE(); // targeting
int16 baseDice = stream->readSint16LE(); // base dice
int16 skillDice = stream->readSint16LE(); // skill dice
int16 baseDamage = stream->readSint16LE(); // attrib change
int16 skillDamage = stream->readSint16LE(); // attrib change
/*int16 reserved0 = */stream->readSint16LE(); // min enchant
/* int16 reserved1 = */stream->readSint16LE(); // min enchant
int16 diceSides = stream->readSint16LE();
if (diceSides == 0)
diceSides = 6;
we = new WeaponStrikeEffect(effectType, baseDice, diceSides, skillDice, baseDamage, skillDamage);
} else {
stream->seek(0);
we = new WeaponProtoEffect(stream);
}
if (we == nullptr)
error("failed to alloc weapon effect");
if (_effects == nullptr)
_effects = we;
else {
WeaponEffect *tail;
for (tail = _effects; tail->_next; tail = tail->_next)
;
tail->_next = we;
}
}
void WeaponStuff::implement(Actor *enactor, GameObject *target, GameObject *strikingObj, uint8 strength) {
for (WeaponEffect *we = _effects; we != nullptr; we = we->_next)
we->implement(enactor, target, strikingObj, strength);
}
//-----------------------------------------------------------------------
static void loadWeaponData() {
hResContext *spellRes = auxResFile->newContext(MKTAG('I', 'T', 'E', 'M'), "weapon resources");
if (spellRes == nullptr || !spellRes->_valid)
error("Error accessing weapon resource group.");
debugC(1, kDebugLoading, "Loading Weapon Data");
// get spell effects
int16 i = 0;
while (spellRes->size(MKTAG('E', 'F', 'F', i)) > 0) {
Common::SeekableReadStream *stream = loadResourceToStream(spellRes, MKTAG('E', 'F', 'F', i), "weapon effect");
if (stream == nullptr)
error("Unable to load weapon effect %d", i);
int16 id = stream->readSint16LE();
stream->seek(0);
if (id) {
g_vm->_weaponRack[id].setID(id);
g_vm->_weaponRack[id].addEffect(stream);
}
delete stream;
i++;
}
g_vm->_loadedWeapons = i;
assert(i > 1);
debugC(1, kDebugLoading, "Loading Weapon Data, loaded %d entries", i);
auxResFile->disposeContext(spellRes);
}
} // end of namespace Saga2