mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 11:51:52 +00:00
996 lines
35 KiB
C++
996 lines
35 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/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006-2010 - Frictional Games
|
|
*
|
|
* This file is part of Penumbra Overture.
|
|
*/
|
|
|
|
#include "hpl1/penumbra-overture/GameObject.h"
|
|
#include "hpl1/engine/engine.h"
|
|
|
|
#include "hpl1/penumbra-overture/AttackHandler.h"
|
|
#include "hpl1/penumbra-overture/EffectHandler.h"
|
|
#include "hpl1/penumbra-overture/GameEnemy.h"
|
|
#include "hpl1/penumbra-overture/GameStickArea.h"
|
|
#include "hpl1/penumbra-overture/Init.h"
|
|
#include "hpl1/penumbra-overture/MapHandler.h"
|
|
#include "hpl1/penumbra-overture/Player.h"
|
|
#include "hpl1/penumbra-overture/PlayerHelper.h"
|
|
|
|
#include "hpl1/penumbra-overture/GlobalInit.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CALLBACK
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cGameObjectBodyCallback::cGameObjectBodyCallback(cInit *apInit, cGameObject *apObject) {
|
|
mpInit = apInit;
|
|
|
|
mpObject = apObject;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameObjectBodyCallback::OnBeginCollision(iPhysicsBody *apBody, iPhysicsBody *apCollideBody) {
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObjectBodyCallback::OnCollide(iPhysicsBody *apBody, iPhysicsBody *apCollideBody,
|
|
cPhysicsContactData *apContactData) {
|
|
// Log("OnCollide %s vs %s\n",apBody->GetName().c_str(),apCollideBody->GetName().c_str());
|
|
|
|
/////////////////////////////////////////
|
|
// Damage on enemies
|
|
if (apCollideBody->IsCharacter()) {
|
|
// Check if there is an enemy.
|
|
iGameEnemy *pEnemy = (iGameEnemy *)apCollideBody->GetUserData();
|
|
|
|
if (pEnemy && apBody->GetMass() > 4) {
|
|
float fSpeed = apBody->GetLinearVelocity().Length();
|
|
float fImpulseSize = fSpeed * apBody->GetMass();
|
|
|
|
if (fSpeed > 4.5f && fImpulseSize > 25 && ABS(apContactData->mfMaxContactNormalSpeed) >= 1.0f) {
|
|
pEnemy->Damage(fImpulseSize * 0.1f, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
// Check if the object breaks
|
|
if (mpObject->mBreakProps.mbActive) {
|
|
float fImpulseSize = apBody->GetLinearVelocity().Length() * apBody->GetMass() +
|
|
apCollideBody->GetLinearVelocity().Length() * apCollideBody->GetMass();
|
|
|
|
if (fImpulseSize >= mpObject->mBreakProps.mfMinImpulse &&
|
|
ABS(apContactData->mfMaxContactNormalSpeed) >= mpObject->mBreakProps.mfMinNormalSpeed) {
|
|
if (mpInit->mbDebugInteraction) {
|
|
Log("------ Breakage ----------\n");
|
|
Log(" Body '%s' by Body '%s'\n", apBody->GetName().c_str(), apCollideBody->GetName().c_str());
|
|
Log(" Impulse: %f (%fm/s * %fkg) + (%fm/s * %fkg)\n", fImpulseSize,
|
|
apBody->GetLinearVelocity().Length(), apBody->GetMass(),
|
|
apCollideBody->GetLinearVelocity().Length(), apCollideBody->GetMass());
|
|
Log("-------------------------\n");
|
|
}
|
|
|
|
mpObject->Break();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// Do Some Damage
|
|
if (mpObject->mDamageProps.mbActive) {
|
|
float fDamage = 0;
|
|
|
|
// Damage by linear velocity.
|
|
float fSpeed = apBody->GetLinearVelocity().Length();
|
|
if (fSpeed > mpObject->mDamageProps.mfMinLinearDamageSpeed) {
|
|
if (fSpeed > mpObject->mDamageProps.mfMaxLinearDamageSpeed)
|
|
fSpeed = mpObject->mDamageProps.mfMaxLinearDamageSpeed;
|
|
|
|
fDamage = mpObject->mDamageProps.mfMinDamage +
|
|
(mpObject->mDamageProps.mfMaxDamage - mpObject->mDamageProps.mfMinDamage) *
|
|
((fSpeed - mpObject->mDamageProps.mfMinLinearDamageSpeed) /
|
|
(mpObject->mDamageProps.mfMaxLinearDamageSpeed - mpObject->mDamageProps.mfMinLinearDamageSpeed));
|
|
}
|
|
|
|
// Damage by angular velocity.
|
|
fSpeed = apBody->GetAngularVelocity().Length();
|
|
if (fSpeed > mpObject->mDamageProps.mfMinAngularDamageSpeed) {
|
|
if (fSpeed > mpObject->mDamageProps.mfMaxAngularDamageSpeed)
|
|
fSpeed = mpObject->mDamageProps.mfMaxAngularDamageSpeed;
|
|
|
|
float fTempDamage = mpObject->mDamageProps.mfMinDamage +
|
|
(mpObject->mDamageProps.mfMaxDamage - mpObject->mDamageProps.mfMinDamage) *
|
|
((fSpeed - mpObject->mDamageProps.mfMinAngularDamageSpeed) /
|
|
(mpObject->mDamageProps.mfMaxAngularDamageSpeed - mpObject->mDamageProps.mfMinAngularDamageSpeed));
|
|
|
|
if (fTempDamage > fDamage)
|
|
fDamage = fTempDamage;
|
|
}
|
|
|
|
//////////////////////////
|
|
// Do the damage
|
|
if (fDamage > 0) {
|
|
// Player
|
|
if (apCollideBody == mpInit->mpPlayer->GetCharacterBody()->GetBody()) {
|
|
mpInit->mpPlayer->Damage(fDamage, ePlayerDamageType_BloodSplash);
|
|
}
|
|
// Entity
|
|
else {
|
|
iGameEntity *pEntity = (iGameEntity *)apCollideBody->GetUserData();
|
|
if (pEntity) {
|
|
pEntity->Damage(fDamage, mpObject->mDamageProps.mlDamageStrength);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*if(apCollideBody->IsCharacter()==false) return;
|
|
|
|
//Check if there is an enemy.
|
|
iGameEnemy *pEnemy = NULL;
|
|
if(apCollideBody->GetUserData())
|
|
{
|
|
pEnemy = (iGameEnemy*)apCollideBody->GetUserData();
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// Stun
|
|
if(pEnemy && mpObject->GetInteractMode() != eObjectInteractMode_Push)
|
|
{
|
|
float fSpeed = apBody->GetLinearVelocity().Length();
|
|
float fMass = apBody->GetMass();
|
|
float fImpulse = fSpeed * fMass;
|
|
|
|
if(pEnemy->mfMinStunSpeed <= fSpeed && pEnemy->mfMinStunImpulse <= fImpulse)
|
|
{
|
|
//Log("Stun by %s speed: %f mass: %f\n",apBody->GetName().c_str(),fSpeed,fMass);
|
|
pEnemy->Stun(pEnemy->mfObjectCollideStun, true);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// LOADER
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cEntityLoader_GameObject::cEntityLoader_GameObject(const tString &asName, cInit *apInit)
|
|
: cEntityLoader_Object(asName) {
|
|
mpInit = apInit;
|
|
}
|
|
|
|
cEntityLoader_GameObject::~cEntityLoader_GameObject() {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cEntityLoader_GameObject::BeforeLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
|
|
cWorld3D *apWorld) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cEntityLoader_GameObject::AfterLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
|
|
cWorld3D *apWorld) {
|
|
cGameObject *pObject = hplNew(cGameObject, (mpInit, mpEntity->GetName()));
|
|
|
|
pObject->msFileName = msFileName;
|
|
pObject->m_mtxOnLoadTransform = a_mtxTransform;
|
|
|
|
// Set the engine objects to the object
|
|
pObject->SetBodies(mvBodies);
|
|
pObject->SetBeams(mvBeams);
|
|
pObject->SetMeshEntity(mpEntity);
|
|
pObject->SetParticleSystems(mvParticleSystems);
|
|
pObject->SetSoundEntities(mvSoundEntities);
|
|
pObject->SetLights(mvLights);
|
|
|
|
///////////////////////////////////
|
|
// Load game properties
|
|
TiXmlElement *pGameElem = apRootElem->FirstChildElement("GAME");
|
|
if (pGameElem) {
|
|
////////////////////////////////////////////
|
|
// General
|
|
pObject->mInteractMode = ToInteractMode(pGameElem->Attribute("InteractMode"));
|
|
|
|
pObject->mfHealth = cString::ToFloat(pGameElem->Attribute("Health"), 0);
|
|
pObject->mlToughness = cString::ToInt(pGameElem->Attribute("Toughness"), 0);
|
|
|
|
pObject->mfForwardUpMul = cString::ToFloat(pGameElem->Attribute("ForwardUpMul"), 1);
|
|
pObject->mfForwardRightMul = cString::ToFloat(pGameElem->Attribute("ForwardRightMul"), 1);
|
|
|
|
pObject->mfUpMul = cString::ToFloat(pGameElem->Attribute("UpMul"), 1);
|
|
pObject->mfRightMul = cString::ToFloat(pGameElem->Attribute("RightMul"), 1);
|
|
|
|
pObject->mbPickAtPoint = cString::ToBool(pGameElem->Attribute("PickAtPoint"), false);
|
|
pObject->mbRotateWithPlayer = cString::ToBool(pGameElem->Attribute("RotateWithPlayer"), true);
|
|
pObject->mbUseNormalMass = cString::ToBool(pGameElem->Attribute("UseNormalMass"), false);
|
|
pObject->mfGrabMassMul = cString::ToFloat(pGameElem->Attribute("GrabMassMul"), 1.0f);
|
|
|
|
pObject->mbCanBeThrown = cString::ToBool(pGameElem->Attribute("CanBeThrown"), true);
|
|
pObject->mbCanBePulled = cString::ToBool(pGameElem->Attribute("CanBePulled"), true);
|
|
|
|
pObject->mbDestroyable = cString::ToBool(pGameElem->Attribute("Destroyable"), false);
|
|
pObject->mfDestroyStrength = cString::ToFloat(pGameElem->Attribute("DestroyStrength"), 0.0f);
|
|
pObject->msDestoySound = cString::ToString(pGameElem->Attribute("DestoySound"), "");
|
|
|
|
pObject->mbPauseControllers = cString::ToBool(pGameElem->Attribute("PauseControllers"), true);
|
|
pObject->mbPauseGravity = cString::ToBool(pGameElem->Attribute("PauseGravity"), true);
|
|
|
|
pObject->mbForceLightOffset = cString::ToBool(pGameElem->Attribute("ForceLightOffset"), false);
|
|
pObject->mvLightOffset = cString::ToVector3f(pGameElem->Attribute("LightOffset"), 0);
|
|
|
|
pObject->mfHapticTorqueMul = cString::ToFloat(pGameElem->Attribute("HapticTorqueMul"), 1.0f);
|
|
|
|
////////////////////////////////////////////
|
|
// Disappear
|
|
pObject->mDisappearProps.mbActive = cString::ToBool(pGameElem->Attribute("Disappear"), false);
|
|
if (pObject->mDisappearProps.mbActive) {
|
|
pObject->mDisappearProps.mfMinTime = cString::ToFloat(pGameElem->Attribute("DisappearMinTime"), 0);
|
|
pObject->mDisappearProps.mfMaxTime = cString::ToFloat(pGameElem->Attribute("DisappearMaxTime"), 0);
|
|
pObject->mDisappearProps.mfMinDistance = cString::ToFloat(pGameElem->Attribute("DisappearMinDist"), 0);
|
|
|
|
pObject->mDisappearProps.mfMinCloseDistance = cString::ToFloat(pGameElem->Attribute("DisappearMinCloseDist"), 0);
|
|
|
|
pObject->mDisappearProps.mfTime = cMath::RandRectf(pObject->mDisappearProps.mfMinTime,
|
|
pObject->mDisappearProps.mfMaxTime);
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// Breakable
|
|
pObject->mBreakProps.mbActive = cString::ToBool(pGameElem->Attribute("Breakable"), false);
|
|
if (pObject->mBreakProps.mbActive || pObject->mDisappearProps.mbActive) {
|
|
pObject->mBreakProps.msSound = cString::ToString(pGameElem->Attribute("BreakSound"), "");
|
|
pObject->mBreakProps.msEntity = cString::ToString(pGameElem->Attribute("BreakEntity"), "");
|
|
pObject->mBreakProps.msPS = cString::ToString(pGameElem->Attribute("BreakPS"), "");
|
|
|
|
pObject->mBreakProps.mfMinImpulse = cString::ToFloat(pGameElem->Attribute("BreakImpulse"), 99999);
|
|
pObject->mBreakProps.mfMinNormalSpeed = cString::ToFloat(pGameElem->Attribute("BreakNormalSpeed"), 99999);
|
|
pObject->mBreakProps.mfMinPlayerImpulse = cString::ToFloat(pGameElem->Attribute("BreakPlayerImpulse"), 99999);
|
|
pObject->mBreakProps.mfCenterForce = cString::ToFloat(pGameElem->Attribute("BreakCenterForce"), 0);
|
|
|
|
pObject->mBreakProps.mbExplosion = cString::ToBool(pGameElem->Attribute("BreakExplosion"), false);
|
|
pObject->mBreakProps.mfExpl_Radius = cString::ToFloat(pGameElem->Attribute("BreakExpl_Radius"), 0);
|
|
pObject->mBreakProps.mfExpl_MinDamage = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinDamage"), 0);
|
|
pObject->mBreakProps.mfExpl_MaxDamage = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxDamage"), 0);
|
|
pObject->mBreakProps.mfExpl_MinForce = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinForce"), 0);
|
|
pObject->mBreakProps.mfExpl_MaxForce = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxForce"), 0);
|
|
pObject->mBreakProps.mfExpl_MaxImpulse = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxImpulse"), 0);
|
|
pObject->mBreakProps.mfExpl_MinMass = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinMass"), 0);
|
|
pObject->mBreakProps.mlExpl_Strength = cString::ToInt(pGameElem->Attribute("BreakExpl_Strength"), 0);
|
|
|
|
pObject->mBreakProps.mbLightFlash = cString::ToBool(pGameElem->Attribute("LightFlash"), false);
|
|
pObject->mBreakProps.mLight_Color = cString::ToColor(pGameElem->Attribute("LightFlash_Color"), cColor(0, 0));
|
|
pObject->mBreakProps.mfLight_Radius = cString::ToFloat(pGameElem->Attribute("LightFlash_Radius"), 0);
|
|
pObject->mBreakProps.mfLight_AddTime = cString::ToFloat(pGameElem->Attribute("LightFlash_AddTime"), 0);
|
|
pObject->mBreakProps.mfLight_NegTime = cString::ToFloat(pGameElem->Attribute("LightFlash_NegTime"), 0);
|
|
pObject->mBreakProps.mvLight_Offset = cString::ToVector3f(pGameElem->Attribute("LightFlash_Offset"), 0);
|
|
|
|
pObject->mBreakProps.mbEarRing = cString::ToBool(pGameElem->Attribute("EarRing"), false);
|
|
pObject->mBreakProps.mfEarRing_MaxDist = cString::ToFloat(pGameElem->Attribute("EarRing_MaxDist"), 0);
|
|
pObject->mBreakProps.mfEarRing_Time = cString::ToFloat(pGameElem->Attribute("EarRing_Time"), 0);
|
|
|
|
// Set all bodies as volatile
|
|
for (size_t i = 0; i < mvBodies.size(); ++i) {
|
|
mvBodies[i]->SetVolatile(true);
|
|
}
|
|
}
|
|
////////////////////////////////////////////
|
|
// Attracts Enemies
|
|
pObject->mAttractProps.mbActive = cString::ToBool(pGameElem->Attribute("AttractEnemies"), false);
|
|
if (pObject->mAttractProps.mbActive) {
|
|
pObject->mAttractProps.mfDistance = cString::ToFloat(pGameElem->Attribute("AttractDistance"), 0);
|
|
tString sSubTypes = cString::ToString(pGameElem->Attribute("AttractSubtypes"), "");
|
|
cString::GetStringVec(sSubTypes, pObject->mAttractProps.mvSubtypes, NULL);
|
|
|
|
pObject->mAttractProps.mbIsEaten = cString::ToBool(pGameElem->Attribute("AttractIsEaten"), false);
|
|
pObject->mAttractProps.mfEatLength = cString::ToFloat(pGameElem->Attribute("AttractEatLength"), 0);
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// Mode specific
|
|
|
|
// Push mode
|
|
if (pObject->mInteractMode == eObjectInteractMode_Push) {
|
|
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"), mpInit->mpPlayer->GetMaxPushDist());
|
|
pObject->mbHasInteraction = true;
|
|
}
|
|
// Move Mode
|
|
else if (pObject->mInteractMode == eObjectInteractMode_Move) {
|
|
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"), mpInit->mpPlayer->GetMaxMoveDist());
|
|
pObject->mbHasInteraction = true;
|
|
}
|
|
// Grab Mode
|
|
else if (pObject->mInteractMode == eObjectInteractMode_Grab) {
|
|
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"), mpInit->mpPlayer->GetMaxGrabDist());
|
|
pObject->mbHasInteraction = true;
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
// Damage
|
|
pObject->mDamageProps.mbActive = cString::ToBool(pGameElem->Attribute("DamageCharacter"), false);
|
|
if (pObject->mDamageProps.mbActive) {
|
|
pObject->mDamageProps.mfMinLinearDamageSpeed = cString::ToFloat(pGameElem->Attribute("MinLinearDamageSpeed"), 0);
|
|
pObject->mDamageProps.mfMinAngularDamageSpeed = cString::ToFloat(pGameElem->Attribute("MinAngularDamageSpeed"), 0);
|
|
pObject->mDamageProps.mfMaxLinearDamageSpeed = cString::ToFloat(pGameElem->Attribute("MaxLinearDamageSpeed"), 0);
|
|
pObject->mDamageProps.mfMaxAngularDamageSpeed = cString::ToFloat(pGameElem->Attribute("MaxAngularDamageSpeed"), 0);
|
|
pObject->mDamageProps.mfMinDamage = cString::ToFloat(pGameElem->Attribute("MinDamage"), 0);
|
|
pObject->mDamageProps.mfMaxDamage = cString::ToFloat(pGameElem->Attribute("MaxDamage"), 0);
|
|
pObject->mDamageProps.mlDamageStrength = cString::ToInt(pGameElem->Attribute("DamageStrength"), 0);
|
|
}
|
|
|
|
// Add callback for all bodies
|
|
pObject->mpBodyCallback = hplNew(cGameObjectBodyCallback, (mpInit, pObject));
|
|
for (size_t i = 0; i < mvBodies.size(); ++i)
|
|
mvBodies[i]->AddBodyCallback(pObject->mpBodyCallback);
|
|
} else {
|
|
Error("Couldn't find game element for entity '%s'\n", mpEntity->GetName().c_str());
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// Add a the object as user data to the body, to get the obejct later on.
|
|
for (size_t i = 0; i < mvBodies.size(); ++i) {
|
|
if (mvBodies[i]) {
|
|
mvBodies[i]->SetUserData((void *)pObject);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// Add to map handler
|
|
mpInit->mpMapHandler->AddGameEntity(pObject);
|
|
|
|
pObject->SetUpTransMaterials();
|
|
pObject->SetupBreakObject();
|
|
pObject->SetupForceOffset();
|
|
|
|
/////////////////////////////////
|
|
// Add to map handler
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
eObjectInteractMode cEntityLoader_GameObject::ToInteractMode(const char *apString) {
|
|
if (apString == NULL)
|
|
return eObjectInteractMode_Static;
|
|
|
|
tString sName = cString::ToLowerCase(apString);
|
|
|
|
if (sName == "grab")
|
|
return eObjectInteractMode_Grab;
|
|
if (sName == "move")
|
|
return eObjectInteractMode_Move;
|
|
if (sName == "push")
|
|
return eObjectInteractMode_Push;
|
|
if (sName == "static")
|
|
return eObjectInteractMode_Static;
|
|
|
|
Warning("Invalid object interact mode '%s'\n", apString);
|
|
|
|
return eObjectInteractMode_Static;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CONSTRUCTORS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cGameObject::cGameObject(cInit *apInit, const tString &asName) : iGameEntity(apInit, asName) {
|
|
mType = eGameEntityType_Object;
|
|
mInteractMode = eObjectInteractMode_Static;
|
|
|
|
mpBodyCallback = NULL;
|
|
|
|
mpCurrentAttraction = NULL;
|
|
mfAttractCount = 4.0f;
|
|
|
|
mbIsMover = false;
|
|
|
|
mfCloseToSameCount = cMath::RandRectf(0, 5.5f);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cGameObject::~cGameObject(void) {
|
|
if (mpBodyCallback)
|
|
hplDelete(mpBodyCallback);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::OnPlayerPick() {
|
|
if (mvCallbackScripts[eGameEntityScriptType_PlayerInteract] &&
|
|
mpInit->mpPlayer->GetPickedDist() < mfMaxInteractDist &&
|
|
mpInit->mpPlayer->mbProxyTouching) {
|
|
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Active);
|
|
} else if (mInteractMode == eObjectInteractMode_Static && msDescription == _W("")) {
|
|
if (mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
|
|
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Inactive);
|
|
else
|
|
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
|
|
} else {
|
|
// mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::OnPlayerInteract() {
|
|
iPhysicsBody *pBody = mpInit->mpPlayer->GetPickedBody();
|
|
|
|
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(pBody);
|
|
if ((pBody->GetMass() == 0 && pStickArea == NULL) ||
|
|
(pStickArea && pStickArea->GetCanDeatch() == false)) {
|
|
return;
|
|
}
|
|
|
|
switch (mInteractMode) {
|
|
case eObjectInteractMode_Push:
|
|
PushObject();
|
|
break;
|
|
case eObjectInteractMode_Move:
|
|
MoveObject();
|
|
break;
|
|
case eObjectInteractMode_Grab:
|
|
GrabObject();
|
|
break;
|
|
case eObjectInteractMode_Static:
|
|
break;
|
|
case eObjectInteractMode_LastEnum:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::Update(float afTimeStep) {
|
|
UpdateAttraction(afTimeStep);
|
|
|
|
////////////////////////////////
|
|
// Disappear
|
|
if (mDisappearProps.mbActive) {
|
|
// TODO: Check per body!
|
|
float fMinDist = cMath::Vector3Dist(mpInit->mpPlayer->GetCamera()->GetPosition(),
|
|
mvBodies[0]->GetWorldPosition());
|
|
|
|
for (size_t i = 1; i < mvBodies.size(); ++i) {
|
|
if (mvBodies[i] == NULL)
|
|
continue;
|
|
|
|
float fDist = cMath::Vector3Dist(mpInit->mpPlayer->GetCamera()->GetPosition(),
|
|
mvBodies[i]->GetWorldPosition());
|
|
if (fDist < fMinDist)
|
|
fMinDist = fDist;
|
|
}
|
|
|
|
if (fMinDist >= mDisappearProps.mfMinDistance) {
|
|
mDisappearProps.mfTime -= afTimeStep;
|
|
if (mDisappearProps.mfTime <= 0) {
|
|
Break();
|
|
}
|
|
}
|
|
|
|
if (mDisappearProps.mfMinCloseDistance > 0) {
|
|
cVector3f vPos = mpMeshEntity->GetBoundingVolume()->GetWorldCenter();
|
|
float fMinSqrDist = mDisappearProps.mfMinCloseDistance *
|
|
mDisappearProps.mfMinCloseDistance;
|
|
if (mfCloseToSameCount <= 0) {
|
|
mfCloseToSameCount = 5.5f;
|
|
|
|
tGameEntityIterator it = mpInit->mpMapHandler->GetGameEntityIterator();
|
|
while (it.HasNext()) {
|
|
iGameEntity *pEntity = it.Next();
|
|
if (pEntity == this)
|
|
continue;
|
|
if (pEntity->GetType() != eGameEntityType_Object)
|
|
continue;
|
|
if (pEntity->GetFileName() != GetFileName())
|
|
continue;
|
|
|
|
cGameObject *pObject = static_cast<cGameObject *>(pEntity);
|
|
|
|
float fSqrDist = cMath::Vector3DistSqr(
|
|
pObject->GetMeshEntity()->GetBoundingVolume()->GetWorldCenter(),
|
|
vPos);
|
|
if (fSqrDist < fMinSqrDist) {
|
|
mDisappearProps.mfTime = 0;
|
|
Break();
|
|
}
|
|
}
|
|
} else {
|
|
mfCloseToSameCount -= afTimeStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////
|
|
// Force offset
|
|
if (mbForceLightOffset) {
|
|
for (size_t i = 0; i < mvLights.size(); ++i) {
|
|
iLight3D *pLight = mvLights[i];
|
|
|
|
pLight->SetMatrix(cMath::MatrixMul(mpMeshEntity->GetWorldMatrix(),
|
|
mvLightLocalOffsets[i]));
|
|
|
|
pLight->SetPosition(pLight->GetWorldPosition() + mvLightOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::OnPlayerGravityCollide(iCharacterBody *apCharBody, cCollideData *apCollideData) {
|
|
if (mBreakProps.mbActive) {
|
|
///////////////////////////////////////////////
|
|
// Check so that the player is really on top of the
|
|
// object and not just sliding
|
|
bool bPushDown = false;
|
|
for (int i = 0; i < apCollideData->mlNumOfPoints; i++) {
|
|
cCollidePoint &point = apCollideData->mvContactPoints[i];
|
|
if (point.mvNormal.y > 0.001f &&
|
|
point.mvNormal.y > ABS(point.mvNormal.x) &&
|
|
point.mvNormal.y > ABS(point.mvNormal.z)) {
|
|
bPushDown = true;
|
|
}
|
|
}
|
|
|
|
if (bPushDown) {
|
|
////////////////////////////////////
|
|
// Check the impulse created by the player
|
|
float fImpulse = apCharBody->GetMass() * ABS(apCharBody->GetForceVelocity().y);
|
|
|
|
if (mBreakProps.mfMinPlayerImpulse <= fImpulse) {
|
|
if (mpInit->mbDebugInteraction) {
|
|
Log("------ Breakage ----------\n");
|
|
Log(" Body '%s' by Player\n", mvBodies[0]->GetName().c_str());
|
|
Log(" Impulse: %f : %fm/s * %fkg (from Player)\n", fImpulse,
|
|
ABS(apCharBody->GetForceVelocity().y), apCharBody->GetMass());
|
|
Log("-------------------------\n");
|
|
}
|
|
|
|
Break();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::SetInteractMode(eObjectInteractMode aInteractMode) {
|
|
mInteractMode = aInteractMode;
|
|
|
|
if (mInteractMode == eObjectInteractMode_Static) {
|
|
mbHasInteraction = false;
|
|
} else {
|
|
mbHasInteraction = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::BreakAction() {
|
|
if (mvBodies.empty())
|
|
return;
|
|
|
|
iPhysicsBody *pParentBody = mvBodies[0];
|
|
|
|
for (size_t i = 1; i < mvBodies.size(); ++i) {
|
|
if (pParentBody->GetMass() != 0) {
|
|
if (mvBodies[i]->GetMass() > pParentBody->GetMass() || mvBodies[i]->GetMass() == 0)
|
|
pParentBody = mvBodies[i];
|
|
}
|
|
}
|
|
|
|
//////////////////
|
|
// Script
|
|
if (mvCallbackScripts[eGameEntityScriptType_OnBreak]) {
|
|
tString sCommand = GetScriptCommand(eGameEntityScriptType_OnBreak);
|
|
mpInit->RunScriptCommand(sCommand);
|
|
}
|
|
|
|
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
|
|
|
|
//////////////////
|
|
// Check if player os holding object
|
|
if ((mpInit->mpPlayer->GetState() == ePlayerState_Grab ||
|
|
mpInit->mpPlayer->GetState() == ePlayerState_Move ||
|
|
mpInit->mpPlayer->GetState() == ePlayerState_Push) &&
|
|
mpInit->mpPlayer->GetPushBody() == pParentBody) {
|
|
mpInit->mpPlayer->ChangeState(ePlayerState_Normal);
|
|
}
|
|
|
|
//////////////////
|
|
// Sound
|
|
if (mBreakProps.msSound != "") {
|
|
cSoundEntity *pSound = pWorld->CreateSoundEntity("Break", mBreakProps.msSound, true);
|
|
if (pSound)
|
|
pSound->SetPosition(mpMeshEntity->GetWorldPosition());
|
|
}
|
|
|
|
//////////////////
|
|
// Particle System
|
|
if (mBreakProps.msPS != "") {
|
|
/*cParticleSystem3D *pPS = */pWorld->CreateParticleSystem("Break", mBreakProps.msPS, cVector3f(1, 1, 1),
|
|
pParentBody->GetWorldMatrix());
|
|
}
|
|
|
|
//////////////////
|
|
// Entity
|
|
if (mBreakProps.msEntity != "") {
|
|
iEntity3D *pEntity = pWorld->CreateEntity(mpMeshEntity->GetName() + "_broken",
|
|
pParentBody->GetWorldMatrix(),
|
|
mBreakProps.msEntity, true);
|
|
if (pEntity) {
|
|
iGameEntity *pGameEntity = mpInit->mpMapHandler->GetLatestEntity();
|
|
|
|
for (int i = 0; i < pGameEntity->GetBodyNum(); ++i) {
|
|
// Add the object velocity
|
|
iPhysicsBody *pBody = pGameEntity->GetBody(i);
|
|
pBody->SetLinearVelocity(pParentBody->GetLinearVelocity());
|
|
// TODO: Add torque
|
|
|
|
// Force from center
|
|
cVector3f vBodyCentre = cMath::MatrixMul(pBody->GetWorldMatrix(), pBody->GetMassCentre());
|
|
|
|
cVector3f vForceDir = vBodyCentre - pParentBody->GetLocalPosition();
|
|
vForceDir.Normalise();
|
|
|
|
pBody->AddForce(vForceDir * mBreakProps.mfCenterForce);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////
|
|
// Explosion
|
|
if (mBreakProps.mbExplosion) {
|
|
mpInit->mpAttackHandler->CreateSplashDamage(
|
|
pParentBody->GetWorldPosition(),
|
|
mBreakProps.mfExpl_Radius,
|
|
mBreakProps.mfExpl_MinDamage, mBreakProps.mfExpl_MaxDamage,
|
|
mBreakProps.mfExpl_MinForce, mBreakProps.mfExpl_MaxForce,
|
|
mBreakProps.mfExpl_MaxImpulse,
|
|
eAttackTargetFlag_Bodies | eAttackTargetFlag_Player | eAttackTargetFlag_Enemy,
|
|
mBreakProps.mfExpl_MinMass,
|
|
mBreakProps.mlExpl_Strength);
|
|
}
|
|
|
|
//////////////////
|
|
// Light flash
|
|
if (mBreakProps.mbLightFlash) {
|
|
mpInit->mpMapHandler->AddLightFlash(pParentBody->GetWorldPosition() + mBreakProps.mvLight_Offset,
|
|
mBreakProps.mfLight_Radius,
|
|
mBreakProps.mLight_Color,
|
|
mBreakProps.mfLight_AddTime,
|
|
mBreakProps.mfLight_NegTime);
|
|
}
|
|
|
|
//////////////////
|
|
// Ear ring
|
|
if (mBreakProps.mbEarRing) {
|
|
cPlayer *pPlayer = mpInit->mpPlayer;
|
|
float fDist = cMath::Vector3Dist(pParentBody->GetWorldPosition(),
|
|
pPlayer->GetCamera()->GetPosition());
|
|
|
|
// Log("Ear ring dist: %f max: %f\n",fDist, mBreakProps.mfEarRing_MaxDist);
|
|
|
|
if (fDist <= mBreakProps.mfEarRing_MaxDist) {
|
|
pPlayer->GetEarRing()->Start(mBreakProps.mfEarRing_Time);
|
|
}
|
|
}
|
|
|
|
mpInit->mpGame->ResetLogicTimer();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::OnDeath(float afDamage) {
|
|
if (mBreakProps.mbActive) {
|
|
Break();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::SetupBreakObject() {
|
|
if (mBreakProps.mbActive == false)
|
|
return;
|
|
|
|
if (mBreakProps.msEntity != "")
|
|
PreloadModel(mBreakProps.msEntity);
|
|
if (mBreakProps.msPS != "") {
|
|
cParticleSystem3D *pPS = mpInit->mpGame->GetResources()->GetParticleManager()->CreatePS3D(
|
|
"", mBreakProps.msPS, 1, cMatrixf::Identity);
|
|
hplDelete(pPS);
|
|
}
|
|
if (mBreakProps.msSound != "") {
|
|
mpInit->PreloadSoundEntityData(mBreakProps.msSound);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::SetupForceOffset() {
|
|
if (mbForceLightOffset == false)
|
|
return;
|
|
|
|
mvLightLocalOffsets.resize(mvLights.size());
|
|
for (size_t i = 0; i < mvLights.size(); ++i) {
|
|
mvLightLocalOffsets[i] = mvLights[i]->GetLocalMatrix();
|
|
|
|
if (mvLights[i]->GetParent())
|
|
mvLights[i]->GetParent()->RemoveEntity(mvLights[i]);
|
|
if (mvLights[i]->GetEntityParent())
|
|
mvLights[i]->GetEntityParent()->RemoveChild(mvLights[i]);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PRIVATE METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::GrabObject() {
|
|
if (mpInit->mpPlayer->GetPickedDist() > mfMaxInteractDist) {
|
|
if (mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
|
|
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"), 2.0f, true);
|
|
|
|
return;
|
|
}
|
|
|
|
// Set some properties
|
|
mpInit->mpPlayer->mbGrabbingMoveBody = mbIsMover;
|
|
mpInit->mpPlayer->mfHapticTorqueMul = mfHapticTorqueMul;
|
|
|
|
mpInit->mpPlayer->mbPickAtPoint = mbPickAtPoint;
|
|
mpInit->mpPlayer->mbRotateWithPlayer = mbRotateWithPlayer;
|
|
mpInit->mpPlayer->mbUseNormalMass = mbUseNormalMass;
|
|
mpInit->mpPlayer->mfGrabMassMul = mfGrabMassMul;
|
|
|
|
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
|
|
|
|
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
|
|
|
|
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
|
|
mpInit->mpPlayer->ChangeState(ePlayerState_Grab);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::MoveObject() {
|
|
float fDist = GetMoveDist();
|
|
|
|
if (fDist > mfMaxInteractDist) {
|
|
if (mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
|
|
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"), 2.0f, true);
|
|
|
|
return;
|
|
}
|
|
|
|
// Set some properties
|
|
mpInit->mpPlayer->mfForwardUpMul = mfForwardUpMul;
|
|
mpInit->mpPlayer->mfForwardRightMul = mfForwardRightMul;
|
|
|
|
mpInit->mpPlayer->mfUpMul = mfUpMul;
|
|
mpInit->mpPlayer->mfRightMul = mfRightMul;
|
|
|
|
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
|
|
|
|
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
|
|
|
|
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
|
|
mpInit->mpPlayer->ChangeState(ePlayerState_Move);
|
|
}
|
|
|
|
float cGameObject::GetMoveDist() {
|
|
/*cVector3f vBodyPos = mpInit->mpPlayer->GetPickedPos();
|
|
cVector3f vPlayerPos = mpInit->mpPlayer->GetCamera()->GetPosition();
|
|
|
|
float fDist = cMath::Vector3Dist(vBodyPos, vPlayerPos);
|
|
|
|
return fDist;*/
|
|
return mpInit->mpPlayer->GetPickedDist();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::PushObject() {
|
|
float fDist = GetPushDist();
|
|
|
|
if (fDist > mfMaxInteractDist) {
|
|
if (mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
|
|
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"), 2.0f, true);
|
|
|
|
return;
|
|
}
|
|
|
|
mpInit->mpPlayer->mbPickAtPoint = mbPickAtPoint;
|
|
|
|
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
|
|
mpInit->mpPlayer->mbCanBePulled = mbCanBePulled;
|
|
|
|
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
|
|
|
|
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
|
|
mpInit->mpPlayer->ChangeState(ePlayerState_Push);
|
|
}
|
|
|
|
float cGameObject::GetPushDist() {
|
|
/*cVector3f vBodyPos = mpInit->mpPlayer->GetPickedPos();
|
|
cVector3f vPlayerPos = mpInit->mpPlayer->GetCamera()->GetPosition();
|
|
|
|
float fDist = cMath::Dist2D(cVector2f(vBodyPos.x,vBodyPos.z),cVector2f(vPlayerPos.x,vPlayerPos.z));
|
|
*/
|
|
return mpInit->mpPlayer->GetPickedDist();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::UpdateAttraction(float afTimeStep) {
|
|
if (mAttractProps.mbActive == false)
|
|
return;
|
|
|
|
///////////////////////////////////////////////
|
|
// Check if the current attraction is near enough
|
|
if (mpCurrentAttraction) {
|
|
////////////////////////////////////////
|
|
// Check for the attracted enemy
|
|
iCharacterBody *pBody = mpCurrentAttraction->GetMover()->GetCharBody();
|
|
float fDist = cMath::Vector3Dist(pBody->GetFeetPosition(),
|
|
mvBodies[0]->GetWorldPosition());
|
|
|
|
// Check if the enemy have become busy.
|
|
if ((mpCurrentAttraction->GetCurrentStateId() != STATE_MOVETO && fDist < pBody->GetSize().x * 0.75f) ||
|
|
(mAttractProps.mbIsEaten && fDist < pBody->GetSize().x * 0.75f)) {
|
|
// Use half the width here.
|
|
if (mAttractProps.mbIsEaten) {
|
|
mpCurrentAttraction->SetTempFloat(mAttractProps.mfEatLength);
|
|
mpCurrentAttraction->ChangeState(STATE_EAT);
|
|
}
|
|
|
|
mpCurrentAttraction->SetAttracted(false);
|
|
mpCurrentAttraction = NULL;
|
|
|
|
mbDestroyMe = true;
|
|
return;
|
|
} else if (mpCurrentAttraction->GetCurrentStateId() != STATE_MOVETO) {
|
|
mpCurrentAttraction->SetAttracted(false);
|
|
mpCurrentAttraction = NULL;
|
|
return;
|
|
}
|
|
}
|
|
///////////////////////////////////////////////
|
|
// Check if any enemy is near enough to be attracted
|
|
else {
|
|
if (mfAttractCount <= 0) {
|
|
float fClosestDist = 10000.0f;
|
|
iGameEnemy *pChosenEnemy = NULL;
|
|
|
|
tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator();
|
|
while (enemyIt.HasNext()) {
|
|
iGameEnemy *pEnemy = enemyIt.Next();
|
|
|
|
if (pEnemy->GetHealth() <= 0 || pEnemy->IsActive() == false ||
|
|
pEnemy->IsAttracted()) {
|
|
continue;
|
|
}
|
|
|
|
bool bCorrectSub = false;
|
|
for (size_t i = 0; i < mAttractProps.mvSubtypes.size(); ++i) {
|
|
if (mAttractProps.mvSubtypes[i] == pEnemy->GetSubType())
|
|
bCorrectSub = true;
|
|
}
|
|
|
|
if (bCorrectSub == false)
|
|
continue;
|
|
|
|
// Check if the enemy has already been attracted by this object
|
|
if (m_setAttractedEnemies.find(pEnemy) != m_setAttractedEnemies.end())
|
|
continue;
|
|
|
|
float fDist = cMath::Vector3Dist(pEnemy->GetMover()->GetCharBody()->GetFeetPosition(),
|
|
mvBodies[0]->GetWorldPosition());
|
|
|
|
if (fDist < mAttractProps.mfDistance && fDist < fClosestDist) {
|
|
fClosestDist = fDist;
|
|
pChosenEnemy = pEnemy;
|
|
}
|
|
}
|
|
|
|
if (pChosenEnemy) {
|
|
if (pChosenEnemy->MoveToPos(mvBodies[0]->GetWorldPosition() + cVector3f(0, 0.2f, 0))) {
|
|
mpCurrentAttraction = pChosenEnemy;
|
|
pChosenEnemy->SetAttracted(true);
|
|
m_setAttractedEnemies.insert(pChosenEnemy);
|
|
}
|
|
}
|
|
|
|
mfAttractCount = 0.2f;
|
|
} else {
|
|
mfAttractCount -= afTimeStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SAVE OBJECT STUFF
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
kBeginSerialize(cGameObject_SaveData, iGameEntity_SaveData)
|
|
kSerializeVar(mInteractMode, eSerializeType_Int32)
|
|
kSerializeVar(mfMaxInteractDist, eSerializeType_Float32)
|
|
kEndSerialize()
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
iGameEntity *cGameObject_SaveData::CreateEntity() {
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
iGameEntity_SaveData *cGameObject::CreateSaveData() {
|
|
return hplNew(cGameObject_SaveData, ());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::SaveToSaveData(iGameEntity_SaveData *apSaveData) {
|
|
super::SaveToSaveData(apSaveData);
|
|
cGameObject_SaveData *pData = static_cast<cGameObject_SaveData *>(apSaveData);
|
|
|
|
kCopyToVar(pData, mInteractMode);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameObject::LoadFromSaveData(iGameEntity_SaveData *apSaveData) {
|
|
super::LoadFromSaveData(apSaveData);
|
|
cGameObject_SaveData *pData = static_cast<cGameObject_SaveData *>(apSaveData);
|
|
|
|
kCopyFromVar(pData, mInteractMode);
|
|
}
|
|
//-----------------------------------------------------------------------
|