scummvm/engines/saga2/objproto.cpp
2024-09-16 01:53:47 +03:00

2963 lines
84 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* 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/gdraw.h"
#include "saga2/objects.h"
#include "saga2/grabinfo.h"
#include "saga2/contain.h"
#include "saga2/motion.h"
#include "saga2/player.h"
#include "saga2/script.h"
#include "saga2/document.h"
#include "saga2/magic.h"
#include "saga2/weapons.h"
#include "saga2/spellbuk.h"
#include "saga2/combat.h"
#include "saga2/tile.h"
#include "saga2/methods.r"
#include "saga2/pclass.r"
namespace Saga2 {
extern SpellStuff *spellBook;
extern uint8 identityColors[256];
#ifdef __WATCOMC__
#pragma off (unreferenced);
#endif
extern ObjectSoundFXs *objectSoundFXTable; // the global object sound effects table
#if DEBUG
extern bool massAndBulkCount;
#endif
/* ===================================================================== *
Functions
* ===================================================================== */
ObjectID ProtoObj::placeObject() {
return 2;
}
// Check if item can be contained by this object
bool ProtoObj::canContain(ObjectID dObj, ObjectID item) {
return false;
}
// Determine if the object can contain another object at the specified
// slot
bool ProtoObj::canContainAt(
ObjectID dObj,
ObjectID item,
const TilePoint &where) {
return false;
}
// Determine if this type of object is two handed
bool ProtoObj::isTwoHanded(ObjectID) {
return false;
}
// Determine if this type of object is a missile
bool ProtoObj::isMissile() {
return false;
}
//Create Container Window
// ContainerWindow *ProtoObj::makeWindow( GameObject *Obj ) { return NULL; }
// generic actions
// Simple use command
bool ProtoObj::use(ObjectID dObj, ObjectID enactor) {
assert(dObj != Nothing);
int16 scriptResult;
// Setup use cursor, if necessary
if (setUseCursor(dObj)) return true;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onUse,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return useAction(dObj, enactor);
}
// The default action is not to set up a use cursor
bool ProtoObj::setUseCursor(ObjectID dObj) {
return false;
}
// The virtual use action command
bool ProtoObj::useAction(ObjectID dObj, ObjectID enactor) {
return false;
}
// UseOn object command
bool ProtoObj::useOn(ObjectID dObj, ObjectID enactor, ObjectID item) {
assert(dObj != Nothing);
assert(item != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onUseOn,
dObj, enactor, item))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
// If script has not aborted action call virtual useOnAction
// function
return useOnAction(dObj, enactor, item);
}
// Perform the use on action
bool ProtoObj::useOnAction(ObjectID dObj, ObjectID enactor, ObjectID item) {
return false;
}
// UseOn active item command
bool ProtoObj::useOn(ObjectID dObj, ObjectID enactor, ActiveItem *item) {
assert(dObj != Nothing);
assert(item != nullptr);
int16 scrResult;
scriptCallFrame scf;
scriptResult sResult;
scf.invokedObject = dObj;
scf.enactor = enactor;
scf.directObject = dObj;
scf.indirectTAI = item->thisID();
scf.value = 0;
// Call the SAGA script, if there is one.
sResult = runObjectMethod(dObj, Method_GameObject_onUseOnTAI, scf);
// If the script actually ran, and it didn't return a code
// telling us to abort the action...
if (sResult == kScriptResultFinished)
scrResult = scf.returnVal;
else
scrResult = kActionResultNotDone;
if (scrResult != kActionResultNotDone)
return scrResult == kActionResultSuccess;
return useOnAction(dObj, enactor, item);
}
// Perform the use on action
bool ProtoObj::useOnAction(ObjectID dObj, ObjectID enactor, ActiveItem *item) {
return false;
}
// UseOn location command
bool ProtoObj::useOn(ObjectID dObj, ObjectID enactor, const Location &loc) {
assert(dObj != Nothing);
assert(loc != Nowhere && loc._context != Nothing);
/* int16 scrResult;
scriptCallFrame scf;
scriptResult sResult=(scriptResult) kActionResultNotDone;
scf.invokedObject = dObj;
scf.enactor = enactor;
scf.directObject = dObj;
//scf.indirectTAI = item->thisID();
// scf.indirectObject = indirectObj;
scf.value = 0;
// Call the SAGA script, if there is one.
// sResult = runObjectMethod( dObj, Method_GameObject_onUseOnTAI, scf );
// If the script actually ran, and it didn't return a code
// telling us to abort the action...
if ( sResult == kScriptResultFinished )
scrResult=scf.returnVal;
else
scrResult=kActionResultNotDone;
*//*
// Handle object script in a standard fashion
if ( ( scriptResult = stdActionScript(
Method_GameObject_useOnTAI,
dObj, enactor, item->thisID() ) )
!= kActionResultNotDone )
return scriptResult == kActionResultSuccess;
// If script has not aborted action call virtual useOnAction
// function
*//*
if ( scrResult != kActionResultNotDone )
return scrResult == kActionResultSuccess;
*/
return useOnAction(dObj, enactor, loc);
}
// Perform the use on action
bool ProtoObj::useOnAction(ObjectID dObj, ObjectID enactor, const Location &loc) {
return false;
}
//-----------------------------------------------------------------------
// Returns true if object in continuous use.
bool ProtoObj::isObjectBeingUsed(GameObject *) {
return false;
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool ProtoObj::useSlotAvailable(GameObject *, Actor *) {
return false;
}
// Open this object
bool ProtoObj::open(ObjectID dObj, ObjectID enactor) {
assert(dObj != Nothing);
int16 scriptResult;
if (!canOpen(dObj, enactor)) return false;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onOpen,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return openAction(dObj, enactor);
}
// Virtual function to determine if this object can be opened
bool ProtoObj::canOpen(ObjectID, ObjectID) {
return false;
}
// Virtual function to actually open the object
bool ProtoObj::openAction(ObjectID, ObjectID) {
return false;
}
// Close this object
bool ProtoObj::close(ObjectID dObj, ObjectID enactor) {
assert(dObj != Nothing);
int16 scriptResult;
GameObject *dObjPtr = GameObject::objectAddress(dObj);
if (!dObjPtr->isOpen()) return false;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onClose,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return closeAction(dObj, enactor);
}
// Virtual function to actually close the object
bool ProtoObj::closeAction(ObjectID, ObjectID) {
return false;
}
// Take this object
bool ProtoObj::take(ObjectID dObj, ObjectID enactor, int16 num) {
assert(dObj != Nothing);
assert(g_vm->_mouseInfo->getObjectId() == Nothing);
// >>> this needs to be dynamic!
if (mass > 200 || bulk > 200) return false;
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onTake,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return takeAction(dObj, enactor, num);
}
// Virtual function to take the object
bool ProtoObj::takeAction(ObjectID, ObjectID) {
return false;
}
// Virtual function to take the object
bool ProtoObj::takeAction(ObjectID, ObjectID, int16 num) {
return false;
}
// Drop this object at the specified location
bool ProtoObj::drop(ObjectID dObj, ObjectID enactor, const Location &loc, int16 num) {
assert(dObj != Nothing);
if (!canDropAt(dObj, enactor, loc)) return false;
// Handle object script in a non-standard fashion
scriptCallFrame scf;
scriptResult sResult;
scf.invokedObject = dObj;
scf.enactor = enactor;
scf.directObject = dObj;
scf.indirectObject = loc._context;
scf.coords = loc;
scf.value = 0;
// Call the SAGA script, if there is one.
sResult = runObjectMethod(dObj, Method_GameObject_onDrop, scf);
// If the script actually ran, and it didn't return a code
// telling us to abort the action...
if (sResult == kScriptResultFinished
&& scf.returnVal != kActionResultNotDone)
return scf.returnVal == kActionResultSuccess;
return dropAction(dObj, enactor, loc, num);
}
// Virtual function to determine if this object can be dropped at the
// specified location
bool ProtoObj::canDropAt(ObjectID, ObjectID, const Location &) {
return false;
}
// Virtual function to drop the object
bool ProtoObj::dropAction(ObjectID, ObjectID, const Location &, int16) {
return false;
}
// drop an object onto another object and handle the result.
bool ProtoObj::dropOn(ObjectID dObj, ObjectID enactor, ObjectID target, int16 count) {
assert(dObj != Nothing);
// this prevents objects from being dropped on themselves
if (target == dObj) return true;
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onDropOn,
dObj, enactor, target, count))
!= kActionResultNotDone) {
return scriptResult == kActionResultSuccess;
}
// At this point we should probably _split_ the object...
return dropOnAction(dObj, enactor, target, count);
}
// Do the actual drop
bool ProtoObj::dropOnAction(ObjectID dObj, ObjectID enactor, ObjectID target, int count) {
ProtoObj *targetProto = GameObject::protoAddress(target);
// For now, we just re-send the drop message to the object
// being dropped on...
return targetProto->acceptDrop(target, enactor, dObj, count);
}
// drop this object onto a TAG
bool ProtoObj::dropOn(
ObjectID dObj,
ObjectID enactor,
ActiveItem *target,
const Location &loc,
int16 num) {
assert(dObj != Nothing);
assert(target != nullptr);
assert(isWorld(loc._context));
return dropOnAction(dObj, enactor, target, loc, num);
}
// Virtual function to drop an object on a TAG
bool ProtoObj::dropOnAction(
ObjectID,
ObjectID,
ActiveItem *,
const Location &,
int16) {
return false;
}
// Strike another object with this object
bool ProtoObj::strike(ObjectID dObj, ObjectID enactor, ObjectID item) {
assert(isObject(dObj) || isActor(dObj));
assert(isObject(item) || isActor(item));
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onStrike,
dObj, enactor, item))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return strikeAction(dObj, enactor, item);
}
// Virtual function to strike another object with this object
bool ProtoObj::strikeAction(ObjectID, ObjectID, ObjectID) {
return false;
}
// Damage another object with this object
bool ProtoObj::damage(ObjectID dObj, ObjectID enactor, ObjectID target) {
assert(isObject(dObj) || isActor(dObj));
assert(isObject(target) || isActor(target));
int16 scriptResult;
if ((scriptResult = stdActionScript(
Method_GameObject_onDamage,
dObj, enactor, target))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return damageAction(dObj, enactor, target);
}
// Virtual function to damage another object with this object
bool ProtoObj::damageAction(ObjectID, ObjectID, ObjectID) {
return false;
}
// Eat this object
bool ProtoObj::eat(ObjectID dObj, ObjectID enactor) {
assert(dObj != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
warning("ProtoObj::eat: Method_GameObject_onEat undefined");
if ((scriptResult = stdActionScript(
Method_GameObject_onEat,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return eatAction(dObj, enactor);
}
// Virtual function to eat this object
bool ProtoObj::eatAction(ObjectID, ObjectID) {
return false;
}
// Insert this object into another object
bool ProtoObj::insert(ObjectID dObj, ObjectID enactor, ObjectID item) {
assert(dObj != Nothing);
assert(item != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
warning("ProtoObj::insert: Method_GameObject_onInsert undefined");
if ((scriptResult = stdActionScript(
Method_GameObject_onInsert,
dObj, enactor, item))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return insertAction(dObj, enactor, item);
}
// Virtual function to insert this object into another object
bool ProtoObj::insertAction(ObjectID, ObjectID, ObjectID) {
return false;
}
// Remove this object from the object it is in
bool ProtoObj::remove(ObjectID dObj, ObjectID enactor) {
assert(dObj != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
warning("ProtoObj::remove: Method_GameObject_onRemove undefined");
if ((scriptResult = stdActionScript(
Method_GameObject_onRemove,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return removeAction(dObj, enactor);
}
// Virtual function to remove this object from the object it is in
bool ProtoObj::removeAction(ObjectID, ObjectID) {
return false;
}
// Handle the effects of an object being dropped on this object
bool ProtoObj::acceptDrop(
ObjectID dObj,
ObjectID enactor,
ObjectID droppedObj,
int count) {
assert(dObj != Nothing);
assert(droppedObj != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptDrop,
dObj, enactor, droppedObj, count))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptDropAction(dObj, enactor, droppedObj, count);
}
// Virtual function to handle the effects of an object being dropped
// on this object
bool ProtoObj::acceptDropAction(ObjectID dObj, ObjectID enactor, ObjectID droppedObj, int count) {
return acceptInsertion(dObj, enactor, droppedObj, count);
}
bool ProtoObj::acceptDamage(
ObjectID dObj,
ObjectID enactor,
int8 absDamage,
effectDamageTypes dType,
int8 dice,
uint8 sides,
int8 perDieMod) {
int16 scriptResult;
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptDamage,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptDamageAction(
dObj,
enactor,
absDamage,
dType,
dice,
sides,
perDieMod);
}
// Virtual function to damage this object directly
bool ProtoObj::acceptDamageAction(
ObjectID,
ObjectID,
int8,
effectDamageTypes,
int8,
uint8,
int8) {
return true;
}
bool ProtoObj::acceptHealing(
ObjectID dObj,
ObjectID enactor,
int8 absDamage,
int8 dice,
uint8 sides,
int8 perDieMod) {
int8 pdm = perDieMod;
int16 damage = 0;
assert(dObj != Nothing);
damage = absDamage;
if (dice)
for (int d = 0; d < ABS(dice); d++)
damage += (g_vm->_rnd->getRandomNumber(sides - 1) + pdm + 1) * (dice > 0 ? 1 : -1);
return acceptHealingAction(dObj, enactor, damage);
}
// Virtual function to damage this object directly
bool ProtoObj::acceptHealingAction(ObjectID, ObjectID, int8) {
return false;
}
// Accept strike from another object (allows this object to cause
// damage to the striking object).
bool ProtoObj::acceptStrike(
ObjectID dObj,
ObjectID enactor,
ObjectID strikingObj,
uint8 skillIndex) {
assert(dObj != Nothing);
assert(strikingObj != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptStrike,
dObj, enactor, strikingObj))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptStrikeAction(
dObj,
enactor,
strikingObj,
skillIndex);
}
// Virtual function to handle the effects of a strike on this object
bool ProtoObj::acceptStrikeAction(
ObjectID,
ObjectID,
ObjectID,
uint8) {
return true;
}
// Unlock or lock this object with a key.
bool ProtoObj::acceptLockToggle(
ObjectID dObj,
ObjectID enactor,
uint8 keyCode) {
assert(dObj != Nothing);
int16 scriptResult;
if (!canToggleLock(dObj, enactor, keyCode)) return false;
// Handle object script in a standard fashion
warning("ProtoObj::acceptLockToggle: Method_GameObject_onAcceptLockToggle undefined");
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptLockToggle,
dObj, enactor, Nothing))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptLockToggleAction(dObj, enactor, keyCode);
}
// Virtual function to determine if the lock can be toggled
bool ProtoObj::canToggleLock(ObjectID, ObjectID, uint8) {
return false;
}
// Virtual function to actually toggle the lock
bool ProtoObj::acceptLockToggleAction(ObjectID, ObjectID, uint8) {
return false;
}
// Mix this object with another.
bool ProtoObj::acceptMix(ObjectID dObj, ObjectID enactor, ObjectID mixObj) {
assert(dObj != Nothing);
assert(mixObj != Nothing);
int16 scriptResult;
// Handle object script in a standard fashion
warning("ProtoObj::acceptMix: Method_GameObject_onAcceptMix undefined");
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptMix,
dObj, enactor, mixObj))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptMixAction(dObj, enactor, mixObj);
}
// Virtual function to mix this object with another
bool ProtoObj::acceptMixAction(ObjectID, ObjectID, ObjectID) {
return false;
}
// Insert another object into this object.
bool ProtoObj::acceptInsertion(
ObjectID dObj,
ObjectID enactor,
ObjectID item,
int16 count) {
assert(dObj != Nothing);
assert(item != Nothing);
if (!canContain(dObj, item)) return false;
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptInsertion,
dObj, enactor, item, count))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptInsertionAction(dObj, enactor, item, count);
}
// Virtual function to insert an object into this object
bool ProtoObj::acceptInsertionAction(ObjectID, ObjectID, ObjectID, int16) {
return false;
}
// Insert another object into this object at a specified slot
bool ProtoObj::acceptInsertionAt(
ObjectID dObj,
ObjectID enactor,
ObjectID item,
const TilePoint &where,
int16 num) {
assert(dObj != Nothing);
assert(item != Nothing);
if (!canContainAt(dObj, item, where)) return false;
int16 scriptResult;
// Handle object script in a standard fashion
if ((scriptResult = stdActionScript(
Method_GameObject_onAcceptInsertion,
dObj, enactor, item))
!= kActionResultNotDone)
return scriptResult == kActionResultSuccess;
return acceptInsertionAtAction(dObj, enactor, item, where, num);
}
// Virtual function to insert another object into this object at a
// specified slot
bool ProtoObj::acceptInsertionAtAction(
ObjectID, ObjectID, ObjectID, const TilePoint &, int16) {
return false;
}
// Creates a color translation table for this object
void ProtoObj::getColorTranslation(ColorTable map) {
buildColorTable(map, colorMap, ARRAYSIZE(colorMap));
}
uint16 ProtoObj::containmentSet() {
return 0; // the prototye object is not contained in anything
}
// return the sprite data
ObjectSpriteInfo ProtoObj::getSprite(GameObject *obj, enum spriteTypes spr, int16 count) {
ObjectSpriteInfo sprInfo = { nullptr, static_cast<bool>((flags & kObjPropFlipped) != 0) };
int16 openOffset = ((flags & kObjPropVisOpen) && obj->isOpen()) ? 1 : 0;
switch (spr) {
case kObjOnGround:
// If the object is a moving missile return the correct missile
// sprite
if (obj->isMoving()
&& obj->isMissile()
&& obj->_data.missileFacing < 16) {
int16 sprIndex;
if (obj->_data.missileFacing < 8)
sprIndex = obj->_data.missileFacing;
else {
sprIndex = 16 - obj->_data.missileFacing;
sprInfo.flipped = true;
}
sprInfo.sp = missileSprites->sprite(sprIndex);
} else {
sprInfo.sp = objectSprites->sprite(groundSprite + openOffset + obj->getSprOffset(count));
sprInfo.flipped =
(flags & ResourceObjectPrototype::kObjPropFlipped) != 0;
}
break;
case kObjInContainerView:
case kObjAsMousePtr:
sprInfo.sp = objectSprites->sprite(iconSprite + openOffset + obj->getSprOffset(count));
sprInfo.flipped =
(flags & ResourceObjectPrototype::kObjPropFlipped) != 0;
break;
}
return sprInfo;
}
// return the address of the sprite when held in hand
Sprite *ProtoObj::getOrientedSprite(GameObject *obj, int16 offset) {
return nullptr;
}
int16 ProtoObj::stdActionScript(
int method,
ObjectID dObj,
ObjectID enactor,
ObjectID indirectObj) {
scriptCallFrame scf;
scriptResult sResult;
scf.invokedObject = dObj;
scf.enactor = enactor;
scf.directObject = dObj;
scf.indirectObject = indirectObj;
scf.value = 0;
// Call the SAGA script, if there is one.
sResult = runObjectMethod(dObj, method, scf);
// If the script actually ran, and it didn't return a code
// telling us to abort the action...
if (sResult == kScriptResultFinished)
return scf.returnVal;
return kActionResultNotDone;
}
int16 ProtoObj::stdActionScript(
int method,
ObjectID dObj,
ObjectID enactor,
ObjectID indirectObj,
int16 value) {
scriptCallFrame scf;
scriptResult sResult;
scf.invokedObject = dObj;
scf.enactor = enactor;
scf.directObject = dObj;
scf.indirectObject = indirectObj;
scf.value = value;
// Call the SAGA script, if there is one.
sResult = runObjectMethod(dObj, method, scf);
// If the script actually ran, and it didn't return a code
// telling us to abort the action...
if (sResult == kScriptResultFinished)
return scf.returnVal;
return kActionResultNotDone;
}
// Initiate an attack using this type of object
void ProtoObj::initiateAttack(ObjectID, ObjectID) {}
// Initiate a defense using this type of object
void ProtoObj::initiateDefense(ObjectID, ObjectID, ObjectID) {}
// Get projectile for missile weapons
GameObject *ProtoObj::getProjectile(ObjectID, ObjectID) {
return nullptr;
}
// Get spell from a magic object
GameObject *ProtoObj::getSpell(ObjectID) {
return nullptr;
}
// Determine if this type of object can block an attack
bool ProtoObj::canBlock() {
return false;
}
// Return a mask of bits indicating the directions relative to the
// wielders facing in which this object can defend
uint8 ProtoObj::defenseDirMask() {
return 0;
}
// ------------------------------------------------------------------------
// Compute how much damage this defensive object will absorb
uint8 ProtoObj::adjustDamage(uint8 damage) {
return damage;
}
// ------------------------------------------------------------------------
// Return the fight stance approriate to this weapon
int16 ProtoObj::fightStanceAction(ObjectID actor) {
return kActionStand;
}
// ------------------------------------------------------------------------
// Get the value of the user's skill which applies to this object
uint8 ProtoObj::getSkillValue(ObjectID) {
return 0;
}
// ------------------------------------------------------------------------
// Cause the user's associated skill to grow
void ProtoObj::applySkillGrowth(ObjectID, uint8) {}
// ------------------------------------------------------------------------
// Given an object sound effect record, which sound should be made
// when this object is damaged
uint8 ProtoObj::getDamageSound(const ObjectSoundFXs &) {
// Default is no sound
return 0;
}
// ------------------------------------------------------------------------
// Background update function, called once every few seconds
void ProtoObj::doBackgroundUpdate(GameObject *obj) {
TilePoint location = obj->getLocation();
GameWorld *w = obj->world();
int u = location.u >> kSectorShift;
int v = location.v >> kSectorShift;
if (w == nullptr) {
obj->deactivate();
return;
}
Sector *sect = w->getSector(u, v);
if (sect == nullptr)
return;
if (!sect->isActivated()) {
obj->deactivate();
return;
}
}
// ------------------------------------------------------------------------
bool ProtoObj::canFitBulkwise(GameObject *, GameObject *) {
return false;
}
// ------------------------------------------------------------------------
bool ProtoObj::canFitMasswise(GameObject *, GameObject *) {
return false;
}
// ------------------------------------------------------------------------
// Return the maximum mass capacity for the specified container
uint16 ProtoObj::massCapacity(GameObject *) {
return 0;
}
// ------------------------------------------------------------------------
// Return the maximum bulk capacity for the specified container
uint16 ProtoObj::bulkCapacity(GameObject *) {
return 0;
}
/* ==================================================================== *
InventoryProto class
* ==================================================================== */
uint16 InventoryProto::containmentSet() {
return kIsTangible;
}
bool InventoryProto::takeAction(ObjectID dObj, ObjectID enactor, int16 num) {
g_vm->_mouseInfo->copyObject(dObj, GrabInfo::kIntDrop, num);
return true;
}
bool InventoryProto::canDropAt(
ObjectID,
ObjectID enactor,
const Location &loc) {
assert(enactor != Nothing);
// If we're not dropping it onto a world, we're okay
if (!isWorld(loc._context)) return true;
GameObject *enactorPtr = GameObject::objectAddress(enactor);
// If we're trying to drop it into a different world or if
// we're dropping it more than 4 metatile widths away from the
// enactor, fail
if (enactorPtr->IDParent() != loc._context
|| (loc - enactorPtr->getLocation()).quickHDistance()
> kTileUVSize * kPlatformWidth * 4)
return false;
return true;
}
bool InventoryProto::dropAction(
ObjectID dObj,
ObjectID enactor,
const Location &loc,
int16 num) {
assert(loc._context != Nothing);
assert(dObj != Nothing);
assert(enactor != Nothing);
GameObject *dObjPtr = GameObject::objectAddress(dObj);
Actor *enactorPtr = (Actor *)GameObject::objectAddress(enactor);
/* // Determine if we're dropping an object from the actor's hands.
if ( enactor != loc._context )
{
if ( dObj == enactorPtr->rightHandObject )
enactorPtr->rightHandObject = Nothing;
if ( dObj == enactorPtr->leftHandObject )
enactorPtr->leftHandObject = Nothing;
}
*/
// If this object is on a TAG release it
if (dObjPtr->_data.currentTAG != NoActiveItem) {
ActiveItem::activeItemAddress(dObjPtr->_data.currentTAG)->release(
enactor, dObj);
dObjPtr->_data.currentTAG = NoActiveItem;
}
if (isWorld(loc._context)) {
ProtoObj *enactorProto = enactorPtr->proto();
TilePoint enactorLoc(enactorPtr->getLocation());
TilePoint vector = loc - enactorLoc;
GameObject *extractedObj = nullptr;
// Split the merged object if needed.
if (dObjPtr->isMergeable() // If mergeable
&& num < dObjPtr->getExtra()) { // And not dropping whole pile
if (num == 0) return false; // If mergeing zero, then do nothing
extractedObj = dObjPtr->extractMerged(dObjPtr->getExtra() - num);
if (extractedObj == nullptr)
return false;
extractedObj->move(
Location(dObjPtr->getLocation(), dObjPtr->IDParent()));
}
if (enactorPtr->inReach(loc)) {
dObjPtr->move(loc);
// Make sure the game engine knows that it may scavenge this
// object if necessary
if (!dObjPtr->isImportant())
dObjPtr->setScavengable(true);
} else {
int16 offsetDist = enactorProto->crossSection + crossSection;
Direction vectorDir = vector.quickDir();
int16 mapNum = enactorPtr->getMapNum();
TilePoint startPt = Nowhere;
int i;
static const int8 dirOffsetTable[] = { 0, 1, -1, 2, -2, 3, -3 };
for (i = 0; i < ARRAYSIZE(dirOffsetTable); i++) {
TilePoint testPt;
Direction testDir;
testDir = (vectorDir + dirOffsetTable[i]) & 0x7;
testPt = enactorLoc + incDirTable[testDir] * offsetDist;
testPt.z += enactorProto->height >> 1;
if (checkBlocked(dObjPtr, mapNum, testPt) == kBlockageNone) {
startPt = testPt;
break;
}
}
if (startPt == Nowhere) {
if (extractedObj != nullptr)
GameObject::mergeWith(extractedObj, dObjPtr, extractedObj->getExtra());
return false;
}
dObjPtr->move(Location(startPt, loc._context));
// Make sure the game engine knows that it may scavenge this
// object if necessary
if (!dObjPtr->isImportant())
dObjPtr->setScavengable(true);
MotionTask::throwObjectTo(*dObjPtr, loc);
}
} else {
GameObject *targetObj = GameObject::objectAddress(loc._context);
return targetObj->acceptInsertionAt(enactor, dObj, loc, num);
}
return true;
}
bool InventoryProto::dropOnAction(
ObjectID dObj,
ObjectID enactor,
ActiveItem *target,
const Location &loc,
int16 num) {
assert(dObj != Nothing);
assert(target != nullptr);
assert(isWorld(loc._context));
if (drop(dObj, enactor, loc, num)) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
// If we weren't thrown, try triggering the TAG
if (!dObjPtr->isMoving() && target->trigger(enactor, dObj))
dObjPtr->_data.currentTAG = target->thisID();
return true;
}
return false;
}
// Virtual function to handle the effects of an object being dropped
// on this object
bool InventoryProto::acceptDropAction(
ObjectID dObj,
ObjectID enactor,
ObjectID droppedObj,
int count) {
GameObject *dropObject = GameObject::objectAddress(droppedObj);
GameObject *targetObject = GameObject::objectAddress(dObj);
int mergeState = GameObject::canStackOrMerge(dropObject, targetObject);
if (mergeState == kCanMerge)
return targetObject->merge(enactor, droppedObj, count);
else if (mergeState == kCanStack)
return targetObject->stack(enactor, droppedObj);
return ProtoObj::acceptDropAction(dObj, enactor, droppedObj, count);
}
bool InventoryProto::acceptStrikeAction(
ObjectID dObj,
ObjectID enactor,
ObjectID strikingObj,
uint8) {
assert(isObject(dObj) || isActor(dObj));
assert(isActor(enactor));
assert(isObject(strikingObj) || isActor(strikingObj));
GameObject *weapon = GameObject::objectAddress(strikingObj);
return weapon->damage(enactor, dObj);
}
/* ==================================================================== *
PhysicalContainerProto class
* ==================================================================== */
//void PhysicalContainerProto::setTotalRows(ContainerView *cv)
//{
// cv->totalRows =
//
// };
uint16 PhysicalContainerProto::containmentSet() {
return InventoryProto::containmentSet() | kIsContainer;
}
bool PhysicalContainerProto::canContain(ObjectID dObj, ObjectID item) {
GameObject *itemPtr = GameObject::objectAddress(item);
GameObject *pPtr;
// Add recursive check: Make sure that the container isn't already
// inside of "item". Do this by looking at all of the ancestors of
// dObj and make sure that none of them equal "item".
for (pPtr = GameObject::objectAddress(dObj); pPtr; pPtr = pPtr->parent()) {
if (pPtr == itemPtr) return false;
}
return dObj != item
&& (itemPtr->containmentSet() & ProtoObj::kIsTangible);
}
bool PhysicalContainerProto::canContainAt(
ObjectID dObj,
ObjectID item,
const TilePoint &where) {
if (canContain(dObj, item) == false) return false;
return true;
}
bool PhysicalContainerProto::useAction(ObjectID dObj, ObjectID enactor) {
bool result;
GameObject *dObjPtr = GameObject::objectAddress(dObj);
if (dObjPtr->_data.objectFlags & kObjectOpen)
result = close(dObj, enactor);
else
result = open(dObj, enactor);
return result;
}
// Determine if this object can be opened
bool PhysicalContainerProto::canOpen(ObjectID dObj, ObjectID) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
return !dObjPtr->isLocked() && !dObjPtr->isOpen();
}
// Open a physical container
bool PhysicalContainerProto::openAction(ObjectID dObj, ObjectID) {
ContainerNode *cn;
GameObject *dObjPtr = GameObject::objectAddress(dObj);
assert(!dObjPtr->isOpen() && !dObjPtr->isLocked());
cn = CreateContainerNode(dObj, false);
cn->markForShow(); // Deferred open
dObjPtr->_data.objectFlags |= kObjectOpen; // Set open bit;
g_vm->_cnm->setUpdate(dObjPtr->IDParent());
return true;
}
bool PhysicalContainerProto::closeAction(ObjectID dObj, ObjectID) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
ContainerNode *cn = g_vm->_cnm->find(dObj, ContainerNode::kPhysicalType);
assert(dObjPtr->isOpen());
assert(cn);
// Delete the container (lazy delete)
cn->markForDelete();
// Clear open bit
dObjPtr->_data.objectFlags &= ~kObjectOpen;
g_vm->_cnm->setUpdate(dObjPtr->IDParent());
return true;
}
// Determine if this object's lock can be toggled
bool PhysicalContainerProto::canToggleLock(
ObjectID dObj,
ObjectID enactor,
uint8 keyCode) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
return keyCode == lockType && !dObjPtr->isOpen();
}
// Unlock or lock the physical container
bool PhysicalContainerProto::acceptLockToggleAction(
ObjectID dObj,
ObjectID enactor, uint8) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
// Toggle locked bit
dObjPtr->_data.objectFlags ^= kObjectLocked;
return true;
}
// Insert another object into this object
bool PhysicalContainerProto::acceptInsertionAction(
ObjectID dObj,
ObjectID enactor,
ObjectID item,
int16 num) {
assert(isObject(dObj));
assert(isObject(item));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
GameObject *itemPtr = GameObject::objectAddress(item);
// Place the object in the container (if possible)
if ((dObjPtr->_data.objectFlags & kObjectLocked)
|| !dObjPtr->placeObject(enactor, item, true, num)) {
if (isWorld(dObjPtr->IDParent()))
dObjPtr->dropInventoryObject(itemPtr, num);
else {
if (!itemPtr->dropOn(enactor, dObjPtr->IDParent(), num))
return false;
}
}
return true;
}
// Insert another object into this object at the specified slot
bool PhysicalContainerProto::acceptInsertionAtAction(
ObjectID dObj,
ObjectID,
ObjectID item,
const TilePoint &where,
int16 num) {
assert(isObject(dObj));
assert(isObject(item));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
GameObject *itemPtr = GameObject::objectAddress(item);
GameObject *extractedObj = nullptr;
Location oldLoc(itemPtr->getLocation(), itemPtr->IDParent());
// Split the merged object if needed.
if (itemPtr->isMergeable() // If mergeable
&& num < itemPtr->getExtra()) { // And not dropping whole pile
if (num == 0) return false; // If mergeing zero, then do nothing
extractedObj = itemPtr->extractMerged(itemPtr->getExtra() - num);
if (extractedObj == nullptr)
return false;
extractedObj->move(oldLoc);
}
itemPtr->move(Location(0, 0, 0, ImportantLimbo));
if (dObjPtr->canFitBulkwise(itemPtr)
&& dObjPtr->canFitMasswise(itemPtr)) {
itemPtr->move(Location(where, dObj));
return true;
}
itemPtr->move(oldLoc);
if (extractedObj != nullptr)
GameObject::mergeWith(extractedObj, itemPtr, extractedObj->getExtra());
return false;
}
// ------------------------------------------------------------------------
// Determine if the specified object can fit within the specified container
// based upon bulk
bool PhysicalContainerProto::canFitBulkwise(GameObject *container, GameObject *obj) {
#if DEBUG
if (massAndBulkCount)
#endif
{
uint16 maxBulk = container->bulkCapacity();
uint16 totalBulk = container->totalContainedBulk();
return totalBulk + obj->totalBulk() <= maxBulk;
}
#if DEBUG
return true;
#endif
}
// ------------------------------------------------------------------------
// Determine if the specified object can fit within the specified container
// based upon mass
bool PhysicalContainerProto::canFitMasswise(GameObject *container, GameObject *obj) {
#if DEBUG
if (massAndBulkCount)
#endif
{
if (!isWorld(container->IDParent()))
return container->parent()->canFitMasswise(obj);
return true;
}
#if DEBUG
return true;
#endif
}
// ------------------------------------------------------------------------
// Return the maximum mass capacity for the specified container
uint16 PhysicalContainerProto::massCapacity(GameObject *container) {
if (!isWorld(container->IDParent()))
return container->parent()->massCapacity();
return unlimitedCapacity;
}
// ------------------------------------------------------------------------
// Return the maximum bulk capacity for the specified container
uint16 PhysicalContainerProto::bulkCapacity(GameObject *) {
return bulk * 4;
}
/* ==================================================================== *
KeyProto class
* ==================================================================== */
// Put key into mouse with intention to use
bool KeyProto::setUseCursor(ObjectID dObj) {
assert(g_vm->_mouseInfo->getObjectId() == Nothing);
g_vm->_mouseInfo->copyObject(GameObject::objectAddress(dObj), GrabInfo::kIntUse);
return true;
}
// Send acceptLockToggle message to container
bool KeyProto::useOnAction(ObjectID dObj, ObjectID enactor, ObjectID withObj) {
GameObject *container = GameObject::objectAddress(withObj);
if (!container->acceptLockToggle(enactor, lockType)) {
return false;
}
return true;
}
// Send acceptLockToggle message to active terrain
bool KeyProto::useOnAction(ObjectID dObj, ObjectID enactor, ActiveItem *withTAI) {
GameObject *thisKey = GameObject::objectAddress(dObj);
int16 keyID = thisKey->_data.massCount > 0 ? thisKey->_data.massCount : lockType;
if (!withTAI->acceptLockToggle(enactor, keyID)) {
// WriteStatusF( 3, "%s doesn't work", thisKey->objName() );
return false;
}
return true;
}
/* ==================================================================== *
BottleProto class
* ==================================================================== */
uint16 BottleProto::containmentSet() {
return InventoryProto::containmentSet() | kIsBottle;
}
bool BottleProto::useAction(ObjectID dObj, ObjectID enactor) {
//Set Up Empty Bottle Sprite
return true;
}
/* ==================================================================== *
FoodProto class
* ==================================================================== */
uint16 FoodProto::containmentSet() {
return InventoryProto::containmentSet() | kIsFood;
}
bool FoodProto::useAction(ObjectID dObj, ObjectID enactor) {
return true;
}
/* ==================================================================== *
WearableProto class
* ==================================================================== */
uint16 WearableProto::containmentSet() {
return InventoryProto::containmentSet() | kIsWearable;
}
/* ==================================================================== *
WeaponProto class
* ==================================================================== */
weaponID WeaponProto::getWeaponID() {
return weaponDamage;
}
uint16 WeaponProto::containmentSet() {
return InventoryProto::containmentSet() | kIsWeapon;
}
// return the address of the sprite when held in hand
Sprite *WeaponProto::getOrientedSprite(GameObject *obj, int16 offset) {
return weaponSprites[heldSpriteBase]->sprite(offset);
}
//-----------------------------------------------------------------------
// Returns true if object in continuous use.
bool WeaponProto::isObjectBeingUsed(GameObject *obj) {
ObjectID wielder = obj->possessor();
if (wielder != Nothing) {
Actor *a = (Actor *)GameObject::objectAddress(wielder);
if (a->_rightHandObject == obj->thisID()
|| a->_leftHandObject == obj->thisID())
return true;
}
return false;
}
/* ==================================================================== *
MeleeWeaponProto
* ==================================================================== */
// Place weapon into right hand
bool MeleeWeaponProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
assert(isActor(enactor));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
Actor *a = (Actor *)GameObject::objectAddress(enactor);
if (enactor != dObjPtr->IDParent()) return false;
if (dObj == a->_rightHandObject)
a->holdInRightHand(Nothing);
else {
GameObject *leftHandObjectPtr;
leftHandObjectPtr = a->_leftHandObject != Nothing
? GameObject::objectAddress(a->_leftHandObject)
: nullptr;
if (dObjPtr->proto()->isTwoHanded(enactor)
|| (leftHandObjectPtr != nullptr
&& leftHandObjectPtr->proto()->isTwoHanded(enactor)))
a->holdInLeftHand(Nothing);
a->holdInRightHand(dObj);
}
return true;
}
bool MeleeWeaponProto::useOnAction(
ObjectID dObj,
ObjectID enactor,
ObjectID item) {
if (item == enactor) //If Trying To Hurt Oneself Stop It!!!
return false;
return strike(dObj, enactor, item);
}
bool MeleeWeaponProto::strikeAction(
ObjectID dObj,
ObjectID enactor,
ObjectID item) {
assert(isObject(dObj));
assert(isActor(enactor));
assert(isObject(item) || isActor(item));
GameObject *itemPtr = GameObject::objectAddress(item);
ObjectSoundFXs *soundFXs;
Actor *a = (Actor *)GameObject::objectAddress(enactor);
Location ol = Location(a->getWorldLocation(), a->IDParent());
if (itemPtr->acceptStrike(enactor, dObj, getSkillValue(enactor)))
return true;
soundFXs = &objectSoundFXTable[soundFXClass];
makeCombatSound(soundFXs->soundFXMissed, ol);
return false;
}
bool MeleeWeaponProto::damageAction(
ObjectID dObj,
ObjectID enactor,
ObjectID target) {
assert(isObject(dObj));
assert(isActor(enactor));
assert(isObject(target) || isActor(target));
Actor *a = (Actor *)GameObject::objectAddress(enactor);
ActorAttributes *effStats = a->getStats();
WeaponStuff *ws = &getWeapon(getWeaponID());
GameObject *targetPtr = GameObject::objectAddress(target);
uint8 damageSoundID;
Location ol = Location(a->getWorldLocation(), a->IDParent());
damageSoundID = targetPtr->proto()->getDamageSound(
objectSoundFXTable[soundFXClass]);
if (damageSoundID != 0)
makeCombatSound(damageSoundID, ol);
ws->implement(
a,
targetPtr,
GameObject::objectAddress(dObj),
effStats->getSkillLevel(kSkillIDBrawn));
return true;
}
bool MeleeWeaponProto::acceptDamageAction(
ObjectID,
ObjectID,
int8,
effectDamageTypes dType,
int8,
uint8,
int8) {
return true;
}
// Determine if this type of weapon must be wielded with two hands
// for the specified actor
bool MeleeWeaponProto::isTwoHanded(ObjectID attackerID) {
assert(isActor(attackerID));
Actor *attackerPtr = (Actor *)GameObject::objectAddress(attackerID);
ActorProto *attackerProto = (ActorProto *)attackerPtr->proto();
// This weapon must be wielded in two hands if its bulk is greater
// than a quarter of the bulk of the wielder or if the actor does not
// have one handed fighing animation.
return !attackerPtr->isActionAvailable(kActionSwingHigh)
|| bulk > attackerProto->bulk / 4;
}
// Initiate a melee weapon attack motion
void MeleeWeaponProto::initiateAttack(ObjectID attacker, ObjectID target) {
assert(isActor(attacker));
assert(isObject(target) || isActor(target));
Actor *attackerPtr = (Actor *)GameObject::objectAddress(attacker);
GameObject *targetPtr = GameObject::objectAddress(target);
// Start the attack motion
if (isTwoHanded(attacker))
MotionTask::twoHandedSwing(*attackerPtr, *targetPtr);
else
MotionTask::oneHandedSwing(*attackerPtr, *targetPtr);
}
// Initiate a melee weapon parry motion
void MeleeWeaponProto::initiateDefense(
ObjectID defensiveObj,
ObjectID defender,
ObjectID attacker) {
assert(isObject(defensiveObj));
assert(isActor(defender));
assert(isActor(attacker));
GameObject *weapon = GameObject::objectAddress(defensiveObj);
Actor *defenderPtr = (Actor *)GameObject::objectAddress(defender),
*attackerPtr = (Actor *)GameObject::objectAddress(attacker);
if (isTwoHanded(defender))
MotionTask::twoHandedParry(*defenderPtr, *weapon, *attackerPtr);
else
MotionTask::oneHandedParry(*defenderPtr, *weapon, *attackerPtr);
}
// Melee weapons can block an attack
bool MeleeWeaponProto::canBlock() {
return true;
}
// Return a mask of bits indicating the directions relative to the
// wielders facing in which this object can defend
uint8 MeleeWeaponProto::defenseDirMask() {
return 1 << kDirUp;
}
//-----------------------------------------------------------------------
// Rate this weapon's goodness for a specified attack situation
uint8 MeleeWeaponProto::weaponRating(
ObjectID weaponID_,
ObjectID wielderID,
ObjectID targetID) {
assert(isActor(wielderID));
assert(isObject(targetID) || isActor(targetID));
Actor *wielder = (Actor *)GameObject::objectAddress(wielderID);
// If the wielder is on screen yet does not have the attack frames
// for this weapon then this weapon is useless
if (wielder->_appearance != nullptr
&& !wielder->isActionAvailable(fightStanceAction(wielderID)))
return 0;
GameObject *target = GameObject::objectAddress(targetID);
int16 dist = (target->getLocation() - wielder->getLocation()).quickHDistance();
uint8 rating = 0;
if (dist < maximumRange) rating += kInRangeRatingBonus;
// Add in the value of the appropriate skill1
rating += getSkillValue(wielderID);
return rating;
}
// ------------------------------------------------------------------------
// Return the fight stance approriate to this weapon
int16 MeleeWeaponProto::fightStanceAction(ObjectID actor) {
return isTwoHanded(actor) ? kActionTwoHandSwingHigh : kActionSwingHigh;
}
// ------------------------------------------------------------------------
// Given an object sound effect record, which sound should be made
// when this object is damaged
uint8 MeleeWeaponProto::getDamageSound(const ObjectSoundFXs &soundFXs) {
return soundFXs.soundFXParried;
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool MeleeWeaponProto::useSlotAvailable(GameObject *obj, Actor *a) {
assert(isObject(obj) && obj->proto() == this);
assert(isActor(a));
if (a->_rightHandObject == Nothing) {
if (a->_leftHandObject != Nothing) {
assert(isObject(a->_leftHandObject));
GameObject *leftHandObjectPtr;
leftHandObjectPtr = GameObject::objectAddress(a->_leftHandObject);
return !isTwoHanded(a->thisID())
&& !leftHandObjectPtr->proto()->isTwoHanded(a->thisID());
}
return true;
}
assert(isObject(a->_rightHandObject));
return false;
}
/* ==================================================================== *
BludgeoningWeaponProto
* ==================================================================== */
//-----------------------------------------------------------------------
// Get the value of the wielder's skill which applies to this weapon
uint8 BludgeoningWeaponProto::getSkillValue(ObjectID enactor) {
assert(isActor(enactor));
Actor *a;
ActorAttributes *effStats;
a = (Actor *)GameObject::objectAddress(enactor);
effStats = a->getStats();
return effStats->getSkillLevel(kSkillIDBludgeon);
}
// ------------------------------------------------------------------------
// Cause the user's associated skill to grow
void BludgeoningWeaponProto::applySkillGrowth(ObjectID enactor, uint8 points) {
assert(isActor(enactor));
PlayerActorID playerID;
if (actorIDToPlayerID(enactor, playerID)) {
PlayerActor *player = getPlayerActorAddress(playerID);
player->skillAdvance(kSkillIDBludgeon, points);
if (g_vm->_rnd->getRandomNumber(1))
player->skillAdvance(kSkillIDBrawn, points);
}
}
/* ==================================================================== *
SlashingWeaponProto
* ==================================================================== */
//-----------------------------------------------------------------------
// Get the value of the wielder's skill which applies to this weapon
uint8 SlashingWeaponProto::getSkillValue(ObjectID enactor) {
assert(isActor(enactor));
Actor *a;
ActorAttributes *effStats;
a = (Actor *)GameObject::objectAddress(enactor);
effStats = a->getStats();
return effStats->getSkillLevel(kSkillIDSwordcraft);
}
// ------------------------------------------------------------------------
// Cause the user's associated skill to grow
void SlashingWeaponProto::applySkillGrowth(ObjectID enactor, uint8 points) {
assert(isActor(enactor));
PlayerActorID playerID;
if (actorIDToPlayerID(enactor, playerID)) {
PlayerActor *player = getPlayerActorAddress(playerID);
player->skillAdvance(kSkillIDSwordcraft, points);
if (g_vm->_rnd->getRandomNumber(1))
player->skillAdvance(kSkillIDBrawn, points);
}
}
/* ==================================================================== *
BowProto
* ==================================================================== */
bool BowProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
assert(isActor(enactor));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
Actor *a = (Actor *)GameObject::objectAddress(enactor);
if (enactor != dObjPtr->IDParent()) return false;
// If this object is in the enactor's left hand remove it else
// place it into his left hand
if (a->_leftHandObject == dObj)
a->holdInLeftHand(Nothing);
else {
a->holdInRightHand(Nothing);
a->holdInLeftHand(dObj);
}
return true;
}
// Bows are two handed
bool BowProto::isTwoHanded(ObjectID) {
return true;
}
// Initiate the bow firing motion
void BowProto::initiateAttack(ObjectID attacker, ObjectID target) {
assert(isActor(attacker));
assert(isObject(target) || isActor(target));
Actor *attackerPtr = (Actor *)GameObject::objectAddress(attacker);
GameObject *targetPtr = GameObject::objectAddress(target);
MotionTask::fireBow(*attackerPtr, *targetPtr);
}
// Grab an arrow from the actor's inventory
GameObject *BowProto::getProjectile(ObjectID weapon, ObjectID enactor) {
assert(isObject(weapon));
assert(isActor(enactor));
GameObject *obj = nullptr,
*arrow = nullptr;
Actor *a = (Actor *)GameObject::objectAddress(enactor);
TilePoint bestSlot(maxint16, maxint16, 0);
ContainerIterator iter(a);
while (iter.next(&obj) != Nothing) {
// Look for objects which are arrows
if (obj->proto()->classType == protoClassArrow) {
TilePoint objSlot = obj->getLocation();
if (objSlot.u < bestSlot.u
|| (objSlot.u == bestSlot.u
&& objSlot.v < bestSlot.v)) {
bestSlot = objSlot;
arrow = obj;
}
}
}
return arrow;
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool BowProto::useSlotAvailable(GameObject *obj, Actor *a) {
assert(isObject(obj) && obj->proto() == this);
assert(isActor(a));
return a->_leftHandObject == Nothing && a->_rightHandObject == Nothing;
}
//-----------------------------------------------------------------------
// Rate this weapon's goodness for a specified attack situation
uint8 BowProto::weaponRating(
ObjectID weaponID_,
ObjectID wielderID,
ObjectID targetID) {
assert(isActor(wielderID));
assert(isObject(targetID) || isActor(targetID));
if (getProjectile(weaponID_, wielderID) == nullptr) return 0;
Actor *wielder = (Actor *)GameObject::objectAddress(wielderID);
// If the wielder is on screen yet does not have the attack frames
// for this weapon then this weapon is useless
if (wielder->_appearance != nullptr
&& !wielder->isActionAvailable(fightStanceAction(wielderID)))
return 0;
GameObject *target = GameObject::objectAddress(targetID);
int16 dist = (target->getLocation() - wielder->getLocation()).quickHDistance();
uint8 rating = 0;
if (dist < maximumRange && !wielder->inReach(target->getLocation()))
rating += kInRangeRatingBonus;
rating += wielder->getStats()->getSkillLevel(kSkillIDArchery);
return rating;
}
// ------------------------------------------------------------------------
// Return the fight stance approriate to this weapon
int16 BowProto::fightStanceAction(ObjectID actor) {
return kActionFireBow;
}
/* ==================================================================== *
WeaponWandProto
* ==================================================================== */
bool WeaponWandProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
assert(isActor(enactor));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
Actor *a = (Actor *)GameObject::objectAddress(enactor);
if (enactor != dObjPtr->IDParent()) return false;
// If this object is in the enactor's left hand remove it else
// place it into his left hand
if (a->_leftHandObject == dObj)
a->holdInLeftHand(Nothing);
else {
a->holdInRightHand(Nothing);
a->holdInLeftHand(dObj);
}
return true;
}
// Wands are two handed
bool WeaponWandProto::isTwoHanded(ObjectID) {
return true;
}
// Initiate the use wand motion
void WeaponWandProto::initiateAttack(ObjectID attacker, ObjectID target) {
assert(isActor(attacker));
assert(isObject(target) || isActor(target));
Actor *attackerPtr = (Actor *)GameObject::objectAddress(attacker);
GameObject *targetPtr = GameObject::objectAddress(target);
MotionTask::useWand(*attackerPtr, *targetPtr);
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool WeaponWandProto::useSlotAvailable(GameObject *obj, Actor *a) {
assert(isObject(obj) && obj->proto() == this);
assert(isActor(a));
return a->_leftHandObject == Nothing && a->_rightHandObject == Nothing;
}
//-----------------------------------------------------------------------
// Rate this weapon's goodness for a specified attack situation
uint8 WeaponWandProto::weaponRating(
ObjectID weaponID_,
ObjectID wielderID,
ObjectID targetID) {
assert(isObject(weaponID_) || isActor(weaponID_));
assert(isActor(wielderID));
assert(isObject(targetID) || isActor(targetID));
Actor *wielder = (Actor *)GameObject::objectAddress(wielderID);
// If the wielder is on screen yet does not have the attack frames
// for this weapon then this weapon is useless
if (wielder->_appearance != nullptr
&& !wielder->isActionAvailable(fightStanceAction(wielderID)))
return 0;
GameObject *weapon = GameObject::objectAddress(weaponID_),
*target = GameObject::objectAddress(targetID);
int16 dist = (target->getLocation() - wielder->getLocation()).quickHDistance();
uint8 rating = 0;
if (weapon->IDChild() != Nothing) {
SkillProto *spellProto = (SkillProto *)GameObject::protoAddress(weapon->IDChild());
SpellStuff *spellStuff = &spellBook[spellProto->getSpellID()];
ActorManaID manaType = (ActorManaID)spellStuff->getManaType();
uint16 manaAmount = spellStuff->getManaAmt();
if (!weapon->hasCharge(manaType, manaAmount)) return 0;
} else
return 0;
if (dist < maximumRange && !wielder->inReach(target->getLocation()))
rating += kInRangeRatingBonus;
rating += wielder->getStats()->getSkillLevel(kSkillIDSpellcraft);
return rating;
}
// ------------------------------------------------------------------------
// Return the fight stance approriate to this weapon
int16 WeaponWandProto::fightStanceAction(ObjectID actor) {
return kActionUseWand;
}
/* ==================================================================== *
ProjectileProto class
* ==================================================================== */
//-----------------------------------------------------------------------
// return the address of the sprite when held in hand
Sprite *ProjectileProto::getOrientedSprite(GameObject *, int16) {
return nullptr;
}
//-----------------------------------------------------------------------
// Returns true if object in continuous use.
bool ProjectileProto::isObjectBeingUsed(GameObject *) {
return false;
}
//-----------------------------------------------------------------------
// Rate this weapon's goodness for a specified attack situation
uint8 ProjectileProto::weaponRating(
ObjectID weaponID_,
ObjectID wielderID,
ObjectID targetID) {
// Projectiles are worthless as far as wieldable weapons
return 0;
}
// Projectiles are missiles
bool ProjectileProto::isMissile() {
return true;
}
/* ==================================================================== *
ArrowProto class
* ==================================================================== */
bool ArrowProto::useOnAction(ObjectID dObj, ObjectID enactor, ObjectID item) {
// Reuse the MeleeWeaponProto's useOnAction() function.
return ((MeleeWeaponProto *)this)->MeleeWeaponProto::useOnAction(
dObj, enactor, item);
}
bool ArrowProto::strikeAction(
ObjectID dObj,
ObjectID enactor,
ObjectID item) {
assert(isObject(dObj));
assert(isActor(enactor));
assert(isObject(item) || isActor(item));
Actor *a = (Actor *)GameObject::objectAddress(enactor);
GameObject *itemPtr = GameObject::objectAddress(item);
return itemPtr->acceptStrike(
enactor,
dObj,
a->getStats()->getSkillLevel(kSkillIDArchery));
}
bool ArrowProto::damageAction(
ObjectID dObj,
ObjectID enactor,
ObjectID target) {
assert(isObject(dObj));
assert(isActor(enactor));
assert(isObject(target) || isActor(target));
Actor *a = (Actor *)GameObject::objectAddress(enactor);
ActorAttributes *effStats = a->getStats();
WeaponStuff *ws = &getWeapon(getWeaponID());
GameObject *targetPtr = GameObject::objectAddress(target);
uint8 damageSoundID;
Location al = Location(a->getLocation(), a->IDParent());
damageSoundID = targetPtr->proto()->getDamageSound(
objectSoundFXTable[soundFXClass]);
if (damageSoundID != 0)
makeCombatSound(damageSoundID, al);
ws->implement(
a,
GameObject::objectAddress(target),
GameObject::objectAddress(dObj),
effStats->getSkillLevel(kSkillIDBrawn));
return true;
}
// ------------------------------------------------------------------------
// Cause the user's associated skill to grow
void ArrowProto::applySkillGrowth(ObjectID enactor, uint8 points) {
assert(isActor(enactor));
PlayerActorID playerID;
if (actorIDToPlayerID(enactor, playerID)) {
PlayerActor *player = getPlayerActorAddress(playerID);
player->skillAdvance(kSkillIDArchery, points);
if (g_vm->_rnd->getRandomNumber(1))
player->skillAdvance(kSkillIDBrawn, points);
}
}
/* ==================================================================== *
ArmorProto class
* ==================================================================== */
uint16 ArmorProto::containmentSet() {
return InventoryProto::containmentSet() | kIsWearable | kIsArmor;
}
// Compute how much damage this defensive object will absorb
uint8 ArmorProto::adjustDamage(uint8 damage) {
// Apply damage divider
if (damageDivider != 0) damage /= damageDivider;
// Apply damage absorption
if (damageAbsorbtion < damage)
damage -= damageAbsorbtion;
else
damage = 0;
return damage;
}
//-----------------------------------------------------------------------
// Returns true if object in continuous use.
bool ArmorProto::isObjectBeingUsed(GameObject *obj) {
ObjectID aID = obj->possessor();
if (aID != Nothing) {
Actor *a = (Actor *)GameObject::objectAddress(aID);
ObjectID id = obj->thisID();
for (int i = 0; i < ARMOR_COUNT; i++) {
if (a->_armorObjects[i] == id) return true;
}
}
return false;
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool ArmorProto::useSlotAvailable(GameObject *obj, Actor *a) {
assert(isObject(obj) || obj->proto() == this);
assert(isActor(a));
return a->_armorObjects[whereWearable] == Nothing;
}
//-----------------------------------------------------------------------
// "Wear" a piece of armor.
bool ArmorProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
assert(isActor(enactor));
Actor *a = (Actor *)GameObject::objectAddress(enactor);
GameObject *obj = GameObject::objectAddress(dObj);
assert(obj->proto() == this);
if (enactor != obj->IDParent()) return false;
int16 slot = whereWearable;
if (a->_armorObjects[slot] == dObj)
a->wear(Nothing, slot);
else
a->wear(dObj, slot);
g_vm->_cnm->setUpdate(obj->IDParent());
return true;
}
/* ==================================================================== *
ShieldProto class
* ==================================================================== */
uint16 ShieldProto::containmentSet() {
return InventoryProto::containmentSet() | kIsWearable | kIsArmor;
}
// Place shield into left hand
bool ShieldProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
assert(isActor(enactor));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
Actor *a = (Actor *)GameObject::objectAddress(enactor);
if (enactor != dObjPtr->IDParent()) return false;
if (a->_rightHandObject != Nothing) {
assert(isObject(a->_rightHandObject));
GameObject *rightHandObjectPtr =
GameObject::objectAddress(a->_rightHandObject);
if (rightHandObjectPtr->proto()->isTwoHanded(enactor))
return false;
}
a->holdInLeftHand(dObj != a->_leftHandObject ? dObj : Nothing);
return true;
}
bool ShieldProto::acceptDamageAction(
ObjectID,
ObjectID,
int8,
effectDamageTypes dType,
int8,
uint8,
int8) {
return true;
}
Sprite *ShieldProto::getOrientedSprite(GameObject *obj, int16 offset) {
return weaponSprites[heldSpriteBase]->sprite(offset);
}
// Initiate a shield parrying motion
void ShieldProto::initiateDefense(
ObjectID defensiveObj,
ObjectID defender,
ObjectID attacker) {
assert(isObject(defensiveObj));
assert(isActor(defender));
assert(isActor(attacker));
GameObject *shield = GameObject::objectAddress(defensiveObj);
Actor *defenderPtr = (Actor *)GameObject::objectAddress(defender),
*attackerPtr = (Actor *)GameObject::objectAddress(attacker);
MotionTask::shieldParry(*defenderPtr, *shield, *attackerPtr);
}
// Shields can block an attack
bool ShieldProto::canBlock() {
return true;
}
// Return a mask of bits indicating the directions relative to the
// wielders facing in which this object can defend
uint8 ShieldProto::defenseDirMask() {
return (1 << kDirUp) | (1 << kDirUpLeft);
}
//-----------------------------------------------------------------------
// Returns true if object in continuous use.
bool ShieldProto::isObjectBeingUsed(GameObject *obj) {
ObjectID wielder = obj->possessor();
if (wielder != Nothing) {
Actor *a = (Actor *)GameObject::objectAddress(wielder);
if (a->_rightHandObject == obj->thisID()
|| a->_leftHandObject == obj->thisID())
return true;
}
return false;
}
//-----------------------------------------------------------------------
// Determine if the specified object's 'use' slot is available within the
// specified actor
bool ShieldProto::useSlotAvailable(GameObject *obj, Actor *a) {
assert(isObject(obj) || obj->proto() == this);
assert(isActor(a));
if (a->_leftHandObject == Nothing) {
if (a->_rightHandObject != Nothing) {
assert(isObject(a->_rightHandObject));
GameObject *rightHandObjectPtr;
rightHandObjectPtr = GameObject::objectAddress(a->_rightHandObject);
return !rightHandObjectPtr->proto()->isTwoHanded(a->thisID());
}
return true;
}
return false;
}
//-----------------------------------------------------------------------
// Get the value of the user's skill which applies to this object
uint8 ShieldProto::getSkillValue(ObjectID enactor) {
assert(isActor(enactor));
Actor *a;
ActorAttributes *effStats;
a = (Actor *)GameObject::objectAddress(enactor);
effStats = a->getStats();
return effStats->getSkillLevel(kSkillIDShieldcraft);
}
// ------------------------------------------------------------------------
// Cause the user's associated skill to grow
void ShieldProto::applySkillGrowth(ObjectID enactor, uint8 points) {
assert(isActor(enactor));
PlayerActorID playerID;
if (actorIDToPlayerID(enactor, playerID)) {
PlayerActor *player = getPlayerActorAddress(playerID);
player->skillAdvance(kSkillIDShieldcraft, points);
if (g_vm->_rnd->getRandomNumber(1))
player->skillAdvance(kSkillIDBrawn, points);
}
}
// ------------------------------------------------------------------------
// Given an object sound effect record, which sound should be made
// when this object is damaged
uint8 ShieldProto::getDamageSound(const ObjectSoundFXs &soundFXs) {
return soundFXs.soundFXParried;
}
/* ==================================================================== *
ToolProto class
* ==================================================================== */
// Put tool into mouse with intention to use
bool ToolProto::setUseCursor(ObjectID dObj) {
assert(g_vm->_mouseInfo->getObjectId() == Nothing);
g_vm->_mouseInfo->copyObject(GameObject::objectAddress(dObj), GrabInfo::kIntUse);
return true;
}
bool ToolProto::useOnAction(ObjectID, ObjectID, ObjectID) {
return true;
}
/* ==================================================================== *
DocumentProto class
* ==================================================================== */
uint16 DocumentProto::containmentSet() {
return InventoryProto::containmentSet() | kIsDocument;
}
/* ==================================================================== *
BookProto class
* ==================================================================== */
bool BookProto::useAction(ObjectID dObj, ObjectID enactor) {
GameObject *bookObj = GameObject::objectAddress(dObj);
// open the modal book with text filled in with the previously run script
switch (appearanceType) {
case 0:
openBook(bookObj->getExtra());
break;
case 1:
openScroll(bookObj->getExtra());
break;
case 2:
openParchment(bookObj->getExtra());
break;
}
return true;
}
/* ==================================================================== *
ScrollProto class
* ==================================================================== */
bool ScrollProto::useAction(ObjectID dObj, ObjectID enactor) {
GameObject *bookObj = GameObject::objectAddress(dObj);
openScroll(bookObj->getExtra()); // open the modal book with text filled in with
// the previously run script
return true;
}
/* ==================================================================== *
AutoMapProto class
* ==================================================================== */
bool AutoMapProto::openAction(ObjectID, ObjectID) {
return false;
}
/* ==================================================================== *
IntangibleObjProto class
* ==================================================================== */
uint16 IntangibleObjProto::containmentSet() {
return kIsIntangible;
}
bool IntangibleObjProto::useAction(ObjectID dObj, ObjectID enactor) {
assert(isObject(dObj));
/* GameObject *obj = GameObject::objectAddress(dObj);
if(obj->isAlias()) //This Tells Whether Its A Copy Or Not
grabTangible(dObj);//Set To Use If Its The Copy
*/
return false;
}
bool IntangibleObjProto::takeAction(ObjectID dObj, ObjectID enactor, int16) {
assert(isObject(dObj));
assert(g_vm->_mouseInfo->getObjectId() == Nothing);
GameObject *dObjPtr = GameObject::objectAddress(dObj);
if (dObjPtr->isTrueSkill())
return false;
g_vm->_mouseInfo->copyObject(dObj);
return true;
}
bool IntangibleObjProto::canDropAt(
ObjectID,
ObjectID,
const Location &loc) {
// We can try dropping this object anywhere, except within a world
return !isWorld(loc._context);
}
bool IntangibleObjProto::dropAction(
ObjectID dObj,
ObjectID enactor,
const Location &loc,
int16) {
assert(isObject(dObj));
assert(loc._context != Nothing);
assert(!isWorld(loc._context));
GameObject *container = GameObject::objectAddress(loc._context);
if (container->canContain(dObj)) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
ObjectID newObj;
if ((newObj = dObjPtr->makeAlias(Location(dObjPtr->getLocation(), dObjPtr->IDParent()))) != Nothing) {
if (container->acceptInsertionAt(enactor, newObj, loc))
return true;
else
GameObject::objectAddress(newObj)->deleteObject();
}
}
return false;
}
bool IntangibleObjProto::acceptDropAction(
ObjectID dObj,
ObjectID enactor,
ObjectID droppedObj,
int) {
assert(isObject(dObj));
GameObject *dObjPtr = GameObject::objectAddress(dObj);
if (dObjPtr->isAlias()) {
GameObject *droppedObjPtr =
GameObject::objectAddress(droppedObj);
Location loc(dObjPtr->getLocation(), dObjPtr->IDParent());
dObjPtr->deleteObject();
return droppedObjPtr->drop(enactor, loc);
}
return false;
}
ObjectID IntangibleObjProto::placeObject() {
//return Container That It Inserted Itself Into
return 2;
}
// Builds an identity color translation table for intagible objects
void IntangibleObjProto::getColorTranslation(ColorTable map) {
memcpy(map, identityColors, sizeof(ColorTable));
/*
uint8 color = 0;
do {
map[color] = color;
} while ( ++color != 0 );
*/
}
// Return the sprite data
ObjectSpriteInfo IntangibleObjProto::getSprite(
GameObject *obj,
enum spriteTypes spr,
int16) {
ObjectSpriteInfo sprInfo = { nullptr, false };
switch (spr) {
case kObjOnGround:
sprInfo.sp = mentalSprites->sprite(groundSprite);
break;
case kObjInContainerView:
case kObjAsMousePtr:
sprInfo.sp = mentalSprites->sprite(iconSprite);
}
return sprInfo;
}
/* ==================================================================== *
IdeaProto class
* ==================================================================== */
uint16 IdeaProto::containmentSet() {
//Maybe I Could Use This ID And Call IntanobjProt For Setting IsIntangible
return kIsConcept | kIsIntangible;
}
/* ==================================================================== *
MemoryProto class
* ==================================================================== */
uint16 MemoryProto::containmentSet() {
//Maybe I Could Use This ID And Call IntanobjProt For Setting IsIntangible
return kIsConcept | kIsIntangible;
}
/* ==================================================================== *
PsychProto class
* ==================================================================== */
uint16 PsychProto::containmentSet() {
//Maybe I Could Use This ID And Call IntanobjProt For Setting IsIntangible
return kIsPsych | kIsIntangible;
}
/* ==================================================================== *
SkillProto class
* ==================================================================== */
uint16 SkillProto::containmentSet() {
//Maybe I Could Use This ID And Call IntanobjProt For Setting IsIntangible
return kIsSkill | kIsIntangible;
}
bool SkillProto::useAction(ObjectID dObj, ObjectID enactor) {
if (nonUsable(this))
return false;
if (nonTargeted(this)) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
return castUntargetedSpell(attackerPtr, this);
}
g_vm->_mouseInfo->copyObject(dObj, GrabInfo::kIntUse);
return true;
}
// cast a skill at various things
bool SkillProto::useOnAction(ObjectID dObj, ObjectID enactor, ObjectID withObj) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
GameObject *targetPtr = GameObject::objectAddress(withObj);
// *spellPtr = GameObject::objectAddress( dObj );
return castSpell(attackerPtr, targetPtr, this);
}
bool SkillProto::useOnAction(ObjectID dObj, ObjectID enactor, ActiveItem *item) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
//GameObject *spellPtr = GameObject::objectAddress( dObj );
return castSpell(attackerPtr, item, this);
}
bool SkillProto::useOnAction(ObjectID dObj, ObjectID enactor, const Location &loc) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
Location l = loc;
//GameObject *spellPtr = GameObject::objectAddress( dObj );
return castSpell(attackerPtr, l, this);
}
bool SkillProto::canDropAt(ObjectID, ObjectID, const Location &) {
return true;
}
bool SkillProto::dropAction(ObjectID dObj, ObjectID enactor, const Location &loc, int16 num) {
assert(isActor(enactor));
if (isWorld(loc._context)) {
Actor *enactorPtr = (Actor *)GameObject::objectAddress(enactor);
if (validTarget(enactorPtr, nullptr, nullptr, this))
return useOn(dObj, enactor, loc);
return false;
}
return IntangibleObjProto::dropAction(dObj, enactor, loc, num);
}
bool SkillProto::dropOnAction(ObjectID dObj, ObjectID enactor, ObjectID target, int count) {
assert(isActor(enactor));
assert(isObject(target) || isActor(target));
GameObject *targetPtr = GameObject::objectAddress(target);
if (isWorld(targetPtr->IDParent())) {
Actor *enactorPtr = (Actor *)GameObject::objectAddress(enactor);
if (validTarget(enactorPtr, targetPtr, nullptr, this))
return useOn(dObj, enactor, target);
}
return IntangibleObjProto::dropOnAction(dObj, enactor, target, count);
}
bool SkillProto::dropOnAction(
ObjectID dObj,
ObjectID enactor,
ActiveItem *target,
const Location &loc,
int16 num) {
assert(isActor(enactor));
if (target != nullptr) {
Actor *enactorPtr = (Actor *)GameObject::objectAddress(enactor);
if (validTarget(enactorPtr, nullptr, target, this))
return useOn(dObj, enactor, target);
}
return IntangibleObjProto::dropOnAction(dObj, enactor, target, loc, num);
}
bool SkillProto::implementAction(SpellID dObj, ObjectID enactor, ObjectID withObj) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
GameObject *targetPtr = GameObject::objectAddress(withObj);
// *spellPtr = GameObject::objectAddress( dObj );
return implementSpell(attackerPtr, targetPtr, this);
}
bool SkillProto::implementAction(SpellID dObj, ObjectID enactor, ActiveItem *item) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
//GameObject *spellPtr = GameObject::objectAddress( dObj );
return implementSpell(attackerPtr, item, this);
}
bool SkillProto::implementAction(SpellID dObj, ObjectID enactor, Location &loc) {
Actor *attackerPtr = (Actor *) GameObject::objectAddress(enactor);
//GameObject *spellPtr = GameObject::objectAddress( dObj );
return implementSpell(attackerPtr, loc, this);
}
/* ==================================================================== *
EnchantmentProto class
* ==================================================================== */
uint16 EnchantmentProto::containmentSet() {
return kIsEnchantment;
}
// ------------------------------------------------------------------------
// Background update function, called once every few seconds
void EnchantmentProto::doBackgroundUpdate(GameObject *obj) {
int16 hitPoints = obj->getHitPoints(); // get hitpoints of enchant
GameObject *parentObj = obj->parent(); // get parent of enchantment
assert(parentObj);
// if this is a poison enchantment
// then hurt the victim
if (parentObj && isActor(parentObj)) {
// get the enchantment type
uint16 flgs = obj->getExtra();
uint16 type = getEnchantmentType(flgs),
subType = getEnchantmentSubType(flgs);
if (type == kEffectOthers && subType == kActorPoisoned) {
// get the damage amount for this poison
int16 damage = getEnchantmentAmount(flgs);
// apply the damage
parentObj->acceptDamage(obj->thisID(), damage, kDamagePoison);
}
}
// if the enchantment does not hemorage
// away mana, it does'nt go away ( oh no mr.bill! )
if (hitPoints == Forever) return;
hitPoints -= 1;
if (hitPoints <= 0) {
// delete the now dead enchantment
obj->deleteObject();
if (parentObj) {
parentObj->evalEnchantments();
}
} else {
obj->setHitPoints(hitPoints);
}
}
/* ======================================================================== *
GeneratorProto
* ======================================================================== */
uint16 GeneratorProto::containmentSet() {
return kIsIntangible;
}
/* ======================================================================== *
MonsterGeneratorProto
* ======================================================================== */
/* ======================================================================== *
EncounterGeneratorProto
* ======================================================================== */
// Generate an encounter at approx. 10-second intervals
void EncounterGeneratorProto::doBackgroundUpdate(GameObject *obj) {
Actor *centerActor = getCenterActor();
PlayerActor *playerActor;
Location generatorLoc; // World position of this object
LivingPlayerActorIterator iter;
// REM: Add code to disable this generator if there are too many
// hostiles on the screen
// Get the world that the object is in, and the location of the object
// within the world.
obj->getWorldLocation(generatorLoc);
// Iterate through all living player actors
for (playerActor = iter.first(); playerActor; playerActor = iter.next()) {
Actor *a = playerActor->getActor();
Location actorLoc;
a->getWorldLocation(actorLoc);
// If actor is in the same world as the generator
if (actorLoc._context == generatorLoc._context) {
int32 dist,
mtRadius = obj->getHitPoints(),// Radius in metatiles
ptRadius = mtRadius * kTileUVSize * kPlatformWidth,
prob = obj->getExtra() * (256 * 256) / 100;
TilePoint diff = (TilePoint)actorLoc - (TilePoint)generatorLoc;
// Compute the distance between the actor and the generator
dist = diff.quickHDistance();
// Determine distance from center of
// prob = (ptRadius - dist) * prob / (ptRadius * mtRadius);
// avoid divde by zeros
if (ptRadius == 0) {
return;
}
// prevent negtive distance from generator
if (dist > ptRadius) {
return;
}
prob = (ptRadius - dist) * prob / ptRadius;
if (a == centerActor) prob /= 2;
else prob /= 4;
// Now, roll to see if we got an encounter!
if (g_vm->_rnd->getRandomNumber(0xffff) < (uint)prob) {
scriptCallFrame scf;
#if DEBUG
WriteStatusF(8, "Encounter %d Triggered!", obj->thisID());
#endif
scf.invokedObject = obj->thisID();
scf.enactor = a->thisID();
scf.directObject = scf.invokedObject;
scf.indirectObject = Nothing;
scf.value = dist / kTileUVSize;
// Call the SAGA script, if there is one.
runObjectMethod(obj->thisID(), Method_GameObject_onTrigger, scf);
return;
}
}
}
}
/* ======================================================================== *
MissionGeneratorProto
* ======================================================================== */
// Generate an encounter at approx. 10-second intervals
void MissionGeneratorProto::doBackgroundUpdate(GameObject *obj) {
}
/* ==================================================================== *
IntangibleContainerProto class
* ==================================================================== */
bool IntangibleContainerProto::canContain(ObjectID dObj, ObjectID item) {
assert(isObject(item));
GameObject *itemPtr = GameObject::objectAddress(item);
return (itemPtr->containmentSet() & (kIsSkill | kIsConcept)) != 0;
}
bool IntangibleContainerProto::useAction(ObjectID dObj, ObjectID enactor) {
bool result;
GameObject *dObjPtr = GameObject::objectAddress(dObj);
if (dObjPtr->_data.objectFlags & kObjectOpen)
result = close(dObj, enactor);
else
result = open(dObj, enactor);
return result;
}
// Determine if this intagible container can be opened
bool IntangibleContainerProto::canOpen(ObjectID dObj, ObjectID) {
GameObject *dObjPtr = GameObject::objectAddress(dObj);
return !dObjPtr->isOpen();
}
// Open a intangible container
bool IntangibleContainerProto::openAction(ObjectID dObj, ObjectID enactor) {
ContainerNode *cn;
// Perform appropriate opening tasks
cn = CreateContainerNode(enactor, false);
cn->markForShow();
return true;
}
bool IntangibleContainerProto::closeAction(ObjectID dObj, ObjectID) {
ContainerNode *cn = g_vm->_cnm->find(dObj, ContainerNode::kMentalType);
assert(cn);
// Mark container for lazy deletion
cn->markForDelete();
return true;
}
uint16 IntangibleContainerProto::containmentSet() {
return kIsContainer | kIsIntangible;
}
/* ==================================================================== *
IdeaContainerProto class
* ==================================================================== */
/* ==================================================================== *
MemoryContainerProto class
* ==================================================================== */
/* ==================================================================== *
PsychContainerProto class
* ==================================================================== */
/* ==================================================================== *
SkillContainerProto class
* ==================================================================== */
/* ==================================================================== *
MindContainerProto class
* ==================================================================== */
} // end of namespace Saga2