Initial commit
This commit is contained in:
551
engines/saga2/spellio.cpp
Normal file
551
engines/saga2/spellio.cpp
Normal file
@@ -0,0 +1,551 @@
|
||||
/* 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/idtypes.h"
|
||||
#include "saga2/magic.h"
|
||||
#include "saga2/effects.h"
|
||||
#include "saga2/spellbuk.h"
|
||||
#include "saga2/tcoords.h"
|
||||
#include "saga2/rect.h"
|
||||
#include "saga2/spellio.h"
|
||||
#include "saga2/spelshow.h"
|
||||
#include "saga2/saveload.h"
|
||||
|
||||
namespace Saga2 {
|
||||
|
||||
/* ===================================================================== *
|
||||
partial SpellDisplayPrototype implementation
|
||||
* ===================================================================== */
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ctor
|
||||
|
||||
SpellDisplayPrototype::SpellDisplayPrototype(ResourceSpellItem *rsi) {
|
||||
_effect = rsi->effect; // Effect ID
|
||||
_effParm1 = 0; // effect setting 1
|
||||
_effParm2 = 0; // effect setting 1
|
||||
_effParm3 = 0; // effect setting 1
|
||||
_effParm4 = 0; // effect setting 1
|
||||
_scatter = kDiFlagZero; // direction init mode
|
||||
_elasticity = (effectCollisionCont) rsi->effectronElasticity; // collision flags
|
||||
_maxAge = rsi->maxAge; // auto self-destruct age
|
||||
_implementAge = rsi->implAge; // auto self-destruct age
|
||||
_primarySpriteID = rsi->baseSprite; // RES_ID(x, y, z, 0) to get sprites
|
||||
_primarySpriteNo = rsi->spriteCount; // sprites available
|
||||
_secondarySpriteID = rsi->baseSprite2; // RES_ID(x, y, z, 0) to get sprites
|
||||
_secondarySpriteNo = rsi->spriteCount2; // sprites available
|
||||
//_effCount=0; // effectrons to allocate
|
||||
_colorMap[0] = rsi->cm0;
|
||||
_colorMap[1] = rsi->cm1;
|
||||
_colorMap[2] = 0;
|
||||
_colorMap[3] = 0;
|
||||
_ID = kSpellNone;
|
||||
}
|
||||
|
||||
/* ===================================================================== *
|
||||
partial SpellStuff implementation
|
||||
* ===================================================================== */
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// init from res file
|
||||
|
||||
void SpellStuff::setupFromResource(ResourceSpellItem *rsi) {
|
||||
_master = (SpellID) rsi->spell;
|
||||
_display = (SpellID) rsi->spell;
|
||||
_targetableTypes = (SpellTargetingTypes) rsi->targs;
|
||||
_targetTypes = (SpellApplicationTypes) rsi->applys;
|
||||
_manaType = (SpellManaID) rsi->manaType;
|
||||
_manaUse = rsi->manaAmount;
|
||||
_shape = (effectAreas) rsi->effect;
|
||||
_size = 0;
|
||||
_sound = rsi->soundID;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// add spell internal effect
|
||||
|
||||
void SpellStuff::addEffect(ResourceSpellEffect *rse) {
|
||||
ProtoEffect *pe = nullptr;
|
||||
assert(rse && rse->spell == _master);
|
||||
switch (rse->effectGroup) {
|
||||
case kEffectNone :
|
||||
return;
|
||||
case kEffectAttrib :
|
||||
pe = new ProtoEnchantment(
|
||||
makeEnchantmentID(
|
||||
rse->effectGroup,
|
||||
rse->effectType,
|
||||
rse->attribModifier),
|
||||
rse->enchTimeLo,
|
||||
rse->enchTimeHi);
|
||||
break;
|
||||
case kEffectResist :
|
||||
case kEffectImmune :
|
||||
case kEffectOthers :
|
||||
case kEffectNonActor :
|
||||
pe = new ProtoEnchantment(
|
||||
makeEnchantmentID(
|
||||
rse->effectGroup,
|
||||
rse->effectType,
|
||||
rse->flagSet),
|
||||
rse->enchTimeLo,
|
||||
rse->enchTimeHi);
|
||||
break;
|
||||
case kEffectDamage :
|
||||
pe = new ProtoDamage(
|
||||
rse->baseDice,
|
||||
rse->diceSides ? rse->diceSides : 6,
|
||||
rse->skillDice,
|
||||
0,
|
||||
(effectDamageTypes) rse->effectType,
|
||||
0,
|
||||
rse->targeting & kSpellTargCaster);
|
||||
break;
|
||||
case kEffectDrains :
|
||||
pe = new ProtoDrainage(
|
||||
rse->baseDice,
|
||||
rse->diceSides ? rse->diceSides : 6,
|
||||
rse->skillDice,
|
||||
0,
|
||||
(effectDrainsTypes) rse->effectType,
|
||||
0,
|
||||
rse->targeting & kSpellTargCaster);
|
||||
break;
|
||||
case kEffectTAG :
|
||||
pe = new ProtoTAGEffect(
|
||||
(effectTAGTypes) rse->effectType,
|
||||
rse->flagSet,
|
||||
rse->attribModifier);
|
||||
break;
|
||||
case kEffectLocation :
|
||||
pe = new ProtoLocationEffect(
|
||||
(kEffectLocationTypes) rse->effectType,
|
||||
rse->attribModifier);
|
||||
break;
|
||||
case kEffectSpecial : {
|
||||
switch (rse->effectType) {
|
||||
case kSpecialDispellHelpfulEnch : // clears helpful enchantments
|
||||
pe = new ProtoSpecialEffect(DispellProtections, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialDispellHarmfulEnch : // clears harmful enchantments
|
||||
pe = new ProtoSpecialEffect(DispellCurses, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialKill : // death spell
|
||||
pe = new ProtoSpecialEffect(DeathSpell, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialRessurect : // raise dead spell
|
||||
pe = new ProtoSpecialEffect(Resurrect, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialTeleport : // Teleportation
|
||||
pe = new ProtoSpecialEffect(TeleportToLocation, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialCreateActor : // Create an actor or wall
|
||||
pe = new ProtoSpecialEffect(CreateWraith, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialSagaFunc : // calls a saga function
|
||||
pe = new ProtoSpecialEffect(SagaSpellCall, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialRejoin : // Create an actor or wall
|
||||
pe = new ProtoSpecialEffect(Rejoin, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialCreateWWisp : // calls a saga function
|
||||
pe = new ProtoSpecialEffect(CreateWWisp, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialCreateFWisp : // calls a saga function
|
||||
pe = new ProtoSpecialEffect(CreateFWisp, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialCreateWraith : // calls a saga function
|
||||
pe = new ProtoSpecialEffect(CreateWraith, rse->attribModifier);
|
||||
break;
|
||||
case kSpecialCreateFood : // calls a saga function
|
||||
pe = new ProtoSpecialEffect(CreateFood, rse->attribModifier);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pe == nullptr)
|
||||
error("failed to alloc protoEffect");
|
||||
|
||||
if (_effects == nullptr)
|
||||
_effects = pe;
|
||||
else {
|
||||
ProtoEffect *tail;
|
||||
for (tail = _effects; tail->_next; tail = tail->_next) ;
|
||||
tail->_next = pe;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// init spells
|
||||
|
||||
void initSpellState() {
|
||||
}
|
||||
|
||||
void saveSpellState(Common::OutSaveFile *outS) {
|
||||
debugC(2, kDebugSaveload, "Saving SpellState");
|
||||
g_vm->_activeSpells->write(outS);
|
||||
}
|
||||
|
||||
void loadSpellState(Common::InSaveFile *in) {
|
||||
debugC(2, kDebugSaveload, "Loading SpellState");
|
||||
|
||||
g_vm->_activeSpells->read(in);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// cleanup active spells
|
||||
|
||||
void cleanupSpellState() {
|
||||
g_vm->_activeSpells->wipe();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// cleanup active _spells
|
||||
|
||||
StorageSpellTarget::StorageSpellTarget(SpellTarget &st) {
|
||||
GameObject *go = nullptr;
|
||||
ActiveItem *ai;
|
||||
type = st.getType();
|
||||
loc = st.getPoint();
|
||||
if (type == SpellTarget::kSpellTargetObject) {
|
||||
if (type == SpellTarget::kSpellTargetObject)
|
||||
go = st.getObject();
|
||||
else
|
||||
go = nullptr;
|
||||
}
|
||||
|
||||
if (go)
|
||||
obj = go->thisID();
|
||||
else
|
||||
obj = Nothing;
|
||||
|
||||
if (type == SpellTarget::kSpellTargetTAG)
|
||||
ai = st.getTAG();
|
||||
else
|
||||
ai = nullptr;
|
||||
|
||||
if (ai)
|
||||
tag = ai->thisID();
|
||||
else
|
||||
tag = NoActiveItem;
|
||||
}
|
||||
|
||||
StorageSpellInstance::StorageSpellInstance(SpellInstance &si) {
|
||||
implementAge = si._implementAge; // age at which to implement the spell effects
|
||||
effect = si._effect->thisID(); // effect prototype of the current effect
|
||||
dProto = si._dProto->thisID(); // effect prototype of the current effect
|
||||
caster = si._caster->thisID();
|
||||
target = StorageSpellTarget(*si._target);
|
||||
world = si._world->thisID();
|
||||
age = si._age;
|
||||
spell = si._spell;
|
||||
maxAge = si._maxAge;
|
||||
effSeq = si._effSeq; // which effect in a sequence is being played
|
||||
eListSize = si._eList._count;
|
||||
}
|
||||
|
||||
StorageSpellTarget::StorageSpellTarget() {
|
||||
type = 0;
|
||||
loc = Nowhere;
|
||||
obj = Nothing;
|
||||
tag = NoActiveItem;
|
||||
}
|
||||
|
||||
void StorageSpellTarget::read(Common::InSaveFile *in) {
|
||||
ActiveItemID tagID;
|
||||
|
||||
type = in->readSint16LE();
|
||||
loc.load(in);
|
||||
obj = in->readUint16LE();
|
||||
tagID = in->readSint16LE();
|
||||
tag = ActiveItemID(tagID);
|
||||
}
|
||||
|
||||
void StorageSpellTarget::write(Common::MemoryWriteStreamDynamic *out) {
|
||||
out->writeSint16LE(type);
|
||||
loc.write(out);
|
||||
out->writeUint16LE(obj);
|
||||
out->writeSint16LE(tag.val);
|
||||
}
|
||||
|
||||
StorageSpellInstance::StorageSpellInstance() : implementAge(0), effect(0), dProto(kSpellNone), caster(0),
|
||||
world(0), age(0), spell(kSpellNone), maxAge(0), effSeq(0), eListSize(0) {
|
||||
}
|
||||
|
||||
void StorageSpellInstance::read(Common::InSaveFile *in) {
|
||||
implementAge = in->readSint32LE();
|
||||
effect = in->readUint16LE();
|
||||
dProto = (SpellID)in->readByte();
|
||||
caster = in->readUint16LE();
|
||||
target.read(in);
|
||||
world = in->readUint16LE();
|
||||
age = in->readSint32LE();
|
||||
spell = (SpellID)in->readByte();
|
||||
maxAge = in->readSint32LE();
|
||||
effSeq = in->readSint16LE();
|
||||
eListSize = in->readSint16LE();
|
||||
}
|
||||
|
||||
void StorageSpellInstance::write(Common::MemoryWriteStreamDynamic *out) {
|
||||
out->writeSint32LE(implementAge);
|
||||
out->writeUint16LE(effect);
|
||||
out->writeByte(dProto);
|
||||
out->writeUint16LE(caster);
|
||||
target.write(out);
|
||||
out->writeUint16LE(world);
|
||||
out->writeSint32LE(age);
|
||||
out->writeByte(spell);
|
||||
out->writeSint32LE(maxAge);
|
||||
out->writeSint16LE(effSeq);
|
||||
out->writeSint16LE(eListSize);
|
||||
}
|
||||
|
||||
SpellTarget::SpellTarget(StorageSpellTarget &sst) {
|
||||
_type = (SpellTarget::spellTargetType) sst.type;
|
||||
_loc = sst.loc;
|
||||
_next = nullptr;
|
||||
if (sst.obj != Nothing)
|
||||
_obj = GameObject::objectAddress(sst.obj);
|
||||
else
|
||||
_obj = nullptr;
|
||||
if (sst.tag != NoActiveItem)
|
||||
_tag = ActiveItem::activeItemAddress(sst.tag);
|
||||
else
|
||||
_tag = nullptr;
|
||||
}
|
||||
|
||||
SpellInstance::SpellInstance(StorageSpellInstance &ssi) {
|
||||
_implementAge = ssi.implementAge; // age at which to implement the spell effects
|
||||
_dProto = (*g_vm->_sdpList)[ssi.dProto];
|
||||
_caster = GameObject::objectAddress(ssi.caster);
|
||||
_target = new SpellTarget(ssi.target);
|
||||
GameObject *go = GameObject::objectAddress(ssi.world);
|
||||
assert(isWorld(go));
|
||||
_world = (GameWorld *) go;
|
||||
_age = ssi.age;
|
||||
_spell = ssi.spell;
|
||||
_maxAge = ssi.maxAge;
|
||||
_effSeq = 0;
|
||||
_effect = (*g_vm->_edpList)[ssi.effect];
|
||||
while (_effSeq < ssi.effSeq) // which effect in a sequence is being played
|
||||
_effect = _effect->_next;
|
||||
}
|
||||
|
||||
size_t SpellDisplayList::saveSize() {
|
||||
size_t total = 0;
|
||||
|
||||
total += sizeof(_count);
|
||||
if (_count) {
|
||||
for (int i = 0; i < _count; i++)
|
||||
total += _spells[i]->saveSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void SpellDisplayList::write(Common::OutSaveFile *outS) {
|
||||
outS->write("SPEL", 4);
|
||||
CHUNK_BEGIN;
|
||||
out->writeUint16LE(_count);
|
||||
|
||||
debugC(3, kDebugSaveload, "... count = %d", _count);
|
||||
|
||||
if (_count) {
|
||||
for (int i = 0; i < _count; i++) {
|
||||
debugC(3, kDebugSaveload, "Saving Spell Instance %d", i);
|
||||
StorageSpellInstance ssi = StorageSpellInstance(*_spells[i]);
|
||||
ssi.write(out);
|
||||
_spells[i]->writeEffect(out);
|
||||
}
|
||||
}
|
||||
CHUNK_END;
|
||||
}
|
||||
|
||||
void SpellDisplayList::read(Common::InSaveFile *in) {
|
||||
uint16 tCount;
|
||||
|
||||
tCount = in->readUint16LE();
|
||||
|
||||
debugC(3, kDebugSaveload, "... count = %d", tCount);
|
||||
|
||||
assert(tCount < _maxCount);
|
||||
if (tCount) {
|
||||
for (int i = 0; i < tCount; i++) {
|
||||
debugC(3, kDebugSaveload, "Loading Spell Instance %d", i);
|
||||
SpellInstance *si;
|
||||
StorageSpellInstance ssi;
|
||||
ssi.read(in);
|
||||
si = new SpellInstance(ssi);
|
||||
add(si);
|
||||
si->readEffect(in, ssi.eListSize);
|
||||
}
|
||||
}
|
||||
assert(tCount == _count);
|
||||
}
|
||||
|
||||
void SpellDisplayList::wipe() {
|
||||
for (int i = 0; i < _maxCount; i++)
|
||||
if (_spells[i]) {
|
||||
delete _spells[i];
|
||||
_spells[i] = nullptr;
|
||||
_count--;
|
||||
}
|
||||
|
||||
assert(_count == 0);
|
||||
}
|
||||
|
||||
size_t SpellInstance::saveSize() {
|
||||
size_t total = 0;
|
||||
total += sizeof(StorageSpellInstance);
|
||||
if (_eList._count)
|
||||
for (int32 i = 0; i < _eList._count; i++) {
|
||||
total += sizeof(StorageEffectron);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void SpellInstance::writeEffect(Common::MemoryWriteStreamDynamic *out) {
|
||||
if (_eList._count > 0 && !(_maxAge > 0 && (_age + 1) > _maxAge))
|
||||
for (int32 i = 0; i < _eList._count; i++) {
|
||||
StorageEffectron se = StorageEffectron(*_eList._displayList[i]._efx);
|
||||
se.write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void SpellInstance::readEffect(Common::InSaveFile *in, uint16 eListSize) {
|
||||
assert(eListSize == _effect->_nodeCount);
|
||||
_eList._count = _effect->_nodeCount; //sdp->effCount;
|
||||
if (_eList._count)
|
||||
for (int32 i = 0; i < _eList._count; i++) {
|
||||
StorageEffectron se;
|
||||
se.read(in);
|
||||
Effectron *e = new Effectron(se, this);
|
||||
_eList._displayList[i]._efx = e;
|
||||
}
|
||||
}
|
||||
|
||||
StorageEffectron::StorageEffectron() {
|
||||
flags = 0;
|
||||
size = Extent16(0, 0);
|
||||
hitBox = Rect16(0, 0, 0, 0);
|
||||
screenCoords = Point16(0, 0);
|
||||
partno = 0;
|
||||
start = Nowhere;
|
||||
finish = Nowhere;
|
||||
current = Nowhere;
|
||||
velocity = TilePoint(0, 0, 0);
|
||||
acceleration = TilePoint(0, 0, 0);
|
||||
totalSteps = 0;
|
||||
stepNo = 0;
|
||||
hgt = 0;
|
||||
brd = 0;
|
||||
pos = 0;
|
||||
spr = 0;
|
||||
age = 0;
|
||||
}
|
||||
|
||||
StorageEffectron::StorageEffectron(Effectron &e) {
|
||||
flags = e._flags;
|
||||
size = e._size;
|
||||
hitBox = e._hitBox;
|
||||
screenCoords = e._screenCoords;
|
||||
partno = e._partno;
|
||||
start = e._start;
|
||||
finish = e._finish;
|
||||
current = e._current;
|
||||
velocity = e._velocity;
|
||||
acceleration = e._acceleration;
|
||||
totalSteps = e._totalSteps;
|
||||
stepNo = e._stepNo;
|
||||
hgt = e._hgt;
|
||||
brd = e._brd;
|
||||
pos = e._pos;
|
||||
spr = e._spr;
|
||||
age = e._age;
|
||||
}
|
||||
|
||||
void StorageEffectron::read(Common::InSaveFile *in) {
|
||||
flags = in->readUint32LE();
|
||||
size.load(in);
|
||||
hitBox.read(in);
|
||||
partno = in->readSint16LE();
|
||||
screenCoords.load(in);
|
||||
start.load(in);
|
||||
finish.load(in);
|
||||
current.load(in);
|
||||
velocity.load(in);
|
||||
acceleration.load(in);
|
||||
totalSteps = in->readUint16LE();
|
||||
stepNo = in->readUint16LE();
|
||||
hgt = in->readSint16LE();
|
||||
brd = in->readSint16LE();
|
||||
pos = in->readSint32LE();
|
||||
spr = in->readSint32LE();
|
||||
age = in->readSint32LE();
|
||||
}
|
||||
|
||||
void StorageEffectron::write(Common::MemoryWriteStreamDynamic *out) {
|
||||
out->writeUint32LE(flags);
|
||||
size.write(out);
|
||||
hitBox.write(out);
|
||||
out->writeSint16LE(partno);
|
||||
screenCoords.write(out);
|
||||
start.write(out);
|
||||
finish.write(out);
|
||||
current.write(out);
|
||||
velocity.write(out);
|
||||
acceleration.write(out);
|
||||
out->writeUint16LE(totalSteps);
|
||||
out->writeUint16LE(stepNo);
|
||||
out->writeSint16LE(hgt);
|
||||
out->writeSint16LE(brd);
|
||||
out->writeSint32LE(pos);
|
||||
out->writeSint32LE(spr);
|
||||
out->writeSint32LE(age);
|
||||
}
|
||||
|
||||
Effectron::Effectron(StorageEffectron &se, SpellInstance *si) {
|
||||
_flags = se.flags;
|
||||
_size = se.size;
|
||||
_hitBox = se.hitBox;
|
||||
_screenCoords = se.screenCoords;
|
||||
_partno = se.partno;
|
||||
_start = se.start;
|
||||
_finish = se.finish;
|
||||
_current = se.current;
|
||||
_velocity = se.velocity;
|
||||
_acceleration = se.acceleration;
|
||||
_totalSteps = se.totalSteps;
|
||||
_stepNo = se.stepNo;
|
||||
_hgt = se.hgt;
|
||||
_brd = se.brd;
|
||||
_pos = se.pos;
|
||||
_spr = se.spr;
|
||||
_age = se.age;
|
||||
_parent = si;
|
||||
}
|
||||
|
||||
} // end of namespace Saga2
|
||||
Reference in New Issue
Block a user