tge/engine/game/gameBase.cc
2017-04-17 06:17:10 -06:00

519 lines
14 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "game/gameBase.h"
#include "console/consoleTypes.h"
#include "console/consoleInternal.h"
#include "core/bitStream.h"
#include "sim/netConnection.h"
#include "game/gameConnection.h"
#include "math/mathIO.h"
#include "dgl/dgl.h"
//----------------------------------------------------------------------------
// Ghost update relative priority values
static F32 sUpFov = 1.0;
static F32 sUpDistance = 0.4;
static F32 sUpVelocity = 0.4;
static F32 sUpSkips = 0.2;
static F32 sUpOwnership = 0.2;
static F32 sUpInterest = 0.2;
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
IMPLEMENT_CO_DATABLOCK_V1(GameBaseData);
GameBaseData::GameBaseData()
{
category = "";
className = "";
packed = false;
}
bool GameBaseData::onAdd()
{
if (!Parent::onAdd())
return false;
// Link our object name to the datablock class name and
// then onto our C++ class name.
const char* name = getName();
if (name && name[0] && getClassRep()) {
bool linkSuccess = false;
Namespace *parent = getClassRep()->getNameSpace();
if (className && className[0] && dStricmp(className, parent->mName)) {
linkSuccess = Con::linkNamespaces(parent->mName,className);
if(linkSuccess)
linkSuccess = Con::linkNamespaces(className,name);
}
else
linkSuccess = Con::linkNamespaces(parent->mName,name);
if(linkSuccess)
mNameSpace = Con::lookupNamespace(name);
}
// If no className was specified, set it to our C++ class name
if (!className || !className[0])
className = getClassRep()->getClassName();
return true;
}
void GameBaseData::initPersistFields()
{
Parent::initPersistFields();
addField("category", TypeCaseString, Offset(category, GameBaseData));
addField("className", TypeString, Offset(className, GameBaseData));
}
bool GameBaseData::preload(bool server, char errorBuffer[256])
{
if (!Parent::preload(server, errorBuffer))
return false;
packed = false;
return true;
}
void GameBaseData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
packed = true;
}
//----------------------------------------------------------------------------
bool UNPACK_DB_ID(BitStream * stream, U32 & id)
{
if (stream->readFlag())
{
id = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
return true;
}
return false;
}
bool PACK_DB_ID(BitStream * stream, U32 id)
{
if (stream->writeFlag(id))
{
stream->writeRangedU32(id,DataBlockObjectIdFirst,DataBlockObjectIdLast);
return true;
}
return false;
}
bool PRELOAD_DB(U32 & id, SimDataBlock ** data, bool server, const char * clientMissing, const char * serverMissing)
{
if (server)
{
if (*data)
id = (*data)->getId();
else if (server && serverMissing)
{
Con::errorf(ConsoleLogEntry::General,serverMissing);
return false;
}
}
else
{
if (id && !Sim::findObject(id,*data) && clientMissing)
{
Con::errorf(ConsoleLogEntry::General,clientMissing);
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
bool GameBase::gShowBoundingBox;
//----------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1(GameBase);
GameBase::GameBase()
{
mNetFlags.set(Ghostable);
mTypeMask |= GameBaseObjectType;
mProcessLink.next = mProcessLink.prev = this;
mAfterObject = 0;
mProcessTag = 0;
mLastDelta = 0;
mDataBlock = 0;
mProcessTick = true;
mNameTag = "";
mControllingClient = 0;
}
GameBase::~GameBase()
{
plUnlink();
}
//----------------------------------------------------------------------------
bool GameBase::onAdd()
{
if (!Parent::onAdd() || !mDataBlock)
return false;
if (isClientObject()) {
// Client datablock are initialized by the initial update
gClientProcessList.addObject(this);
}
else {
// Datablock must be initialized on the server
if (!onNewDataBlock(mDataBlock))
return false;
gServerProcessList.addObject(this);
}
return true;
}
void GameBase::onRemove()
{
plUnlink();
Parent::onRemove();
}
bool GameBase::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dptr;
if (!mDataBlock)
return false;
setMaskBits(DataBlockMask);
return true;
}
void GameBase::inspectPostApply()
{
Parent::inspectPostApply();
setMaskBits(ExtendedInfoMask);
}
//----------------------------------------------------------------------------
void GameBase::processTick(const Move*)
{
mLastDelta = 0;
}
void GameBase::interpolateTick(F32 delta)
{
mLastDelta = delta;
}
void GameBase::advanceTime(F32)
{
}
//----------------------------------------------------------------------------
F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
{
updateMask;
// Calculate a priority used to decide if this object
// will be updated on the client. All the weights
// are calculated 0 -> 1 Then weighted together at the
// end to produce a priority.
Point3F pos;
getWorldBox().getCenter(&pos);
pos -= camInfo->pos;
F32 dist = pos.len();
if (dist == 0.0f) dist = 0.001f;
pos *= 1.0f / dist;
// Weight based on linear distance, the basic stuff.
F32 wDistance = (dist < camInfo->visibleDistance)?
1.0f - (dist / camInfo->visibleDistance): 0.0f;
// Weight by field of view, objects directly in front
// will be weighted 1, objects behind will be 0
F32 dot = mDot(pos,camInfo->orientation);
bool inFov = dot > camInfo->cosFov;
F32 wFov = inFov? 1.0f: 0;
// Weight by linear velocity parallel to the viewing plane
// (if it's the field of view, 0 if it's not).
F32 wVelocity = 0.0f;
if (inFov)
{
Point3F vec;
mCross(camInfo->orientation,getVelocity(),&vec);
wVelocity = (vec.len() * camInfo->fov) /
(camInfo->fov * camInfo->visibleDistance);
if (wVelocity > 1.0f)
wVelocity = 1.0f;
}
// Weight by interest.
F32 wInterest;
if (getType() & PlayerObjectType)
wInterest = 0.75f;
else if (getType() & ProjectileObjectType)
{
// Projectiles are more interesting if they
// are heading for us.
wInterest = 0.30f;
F32 dot = -mDot(pos,getVelocity());
if (dot > 0.0f)
wInterest += 0.20 * dot;
}
else
{
if (getType() & ItemObjectType)
wInterest = 0.25f;
else
// Everything else is less interesting.
wInterest = 0.0f;
}
// Weight by updateSkips
F32 wSkips = updateSkips * 0.5;
// Calculate final priority, should total to about 1.0f
//
return
wFov * sUpFov +
wDistance * sUpDistance +
wVelocity * sUpVelocity +
wSkips * sUpSkips +
wInterest * sUpInterest;
}
void GameBase::drawBoundingBox(bool useRenderTransform)
{
const MatrixF & mat = useRenderTransform ? getRenderTransform() : getTransform();
Box3F wbox = useRenderTransform ? getRenderWorldBox() : mWorldBox;
glDisable(GL_DEPTH_TEST);
Point3F box;
glPushMatrix();
dglMultMatrix(&mat);
box = (mObjBox.min + mObjBox.max) * 0.5;
glTranslatef(box.x,box.y,box.z);
box = (mObjBox.max - mObjBox.min) * 0.5;
glScalef(box.x,box.y,box.z);
glColor3f(1, 0, 1);
dglWireCube(Point3F(1,1,1),Point3F(0,0,0));
glPopMatrix();
glPushMatrix();
box = (wbox.min + wbox.max) * 0.5;
glTranslatef(box.x,box.y,box.z);
box = (wbox.max - wbox.min) * 0.5;
glScalef(box.x,box.y,box.z);
glColor3f(0, 1, 1);
dglWireCube(Point3F(1,1,1),Point3F(0,0,0));
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
//----------------------------------------------------------------------------
bool GameBase::setDataBlock(GameBaseData* dptr)
{
if (isGhost() || isProperlyAdded()) {
if (mDataBlock != dptr)
return onNewDataBlock(dptr);
}
else
mDataBlock = dptr;
return true;
}
//--------------------------------------------------------------------------
void GameBase::scriptOnAdd()
{
// Script onAdd() must be called by the leaf class after
// everything is ready.
if (!isGhost())
Con::executef(mDataBlock,2,"onAdd",scriptThis());
}
void GameBase::scriptOnNewDataBlock()
{
// Script onNewDataBlock() must be called by the leaf class
// after everything is loaded.
if (!isGhost())
Con::executef(mDataBlock,2,"onNewDataBlock",scriptThis());
}
void GameBase::scriptOnRemove()
{
// Script onRemove() must be called by leaf class while
// the object state is still valid.
if (!isGhost() && mDataBlock)
Con::executef(mDataBlock,2,"onRemove",scriptThis());
}
//--------------------------------------------------------------------------
void GameBase::plUnlink()
{
mProcessLink.next->mProcessLink.prev = mProcessLink.prev;
mProcessLink.prev->mProcessLink.next = mProcessLink.next;
mProcessLink.next = mProcessLink.prev = this;
}
void GameBase::plLinkAfter(GameBase* obj)
{
// Link this after obj
mProcessLink.next = obj->mProcessLink.next;
mProcessLink.prev = obj;
obj->mProcessLink.next = this;
mProcessLink.next->mProcessLink.prev = this;
}
void GameBase::plLinkBefore(GameBase* obj)
{
// Link this before obj
mProcessLink.next = obj;
mProcessLink.prev = obj->mProcessLink.prev;
obj->mProcessLink.prev = this;
mProcessLink.prev->mProcessLink.next = this;
}
void GameBase::plJoin(GameBase* head)
{
GameBase *tail1 = head->mProcessLink.prev;
GameBase *tail2 = mProcessLink.prev;
tail1->mProcessLink.next = this;
mProcessLink.prev = tail1;
tail2->mProcessLink.next = head;
head->mProcessLink.prev = tail2;
}
//----------------------------------------------------------------------------
void GameBase::processAfter(GameBase* obj)
{
mAfterObject = obj;
if ((const GameBase*)obj->mAfterObject == this)
obj->mAfterObject = 0;
if (isGhost())
gClientProcessList.markDirty();
else
gServerProcessList.markDirty();
}
void GameBase::clearProcessAfter()
{
mAfterObject = 0;
}
//----------------------------------------------------------------------------
void GameBase::setControllingClient(GameConnection* client)
{
if (isClientObject())
{
if (mControllingClient)
Con::executef(this, 3, "setControl","0");
if (client)
Con::executef(this, 3, "setControl","1");
}
mControllingClient = client;
setMaskBits(ControlMask);
}
U32 GameBase::getPacketDataChecksum(GameConnection*)
{
return 0;
}
void GameBase::writePacketData(GameConnection*, BitStream*)
{
}
void GameBase::readPacketData(GameConnection*, BitStream*)
{
}
U32 GameBase::packUpdate(NetConnection *, U32 mask, BitStream *stream)
{
// Check the mask for the ScaleMask; if it's true, pass that in.
if (stream->writeFlag( mask & ScaleMask ) ) {
mathWrite( *stream, Parent::getScale() );
}
if (stream->writeFlag((mask & DataBlockMask) && mDataBlock != NULL)) {
stream->writeRangedU32(mDataBlock->getId(),
DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
// cafTODO: ControlMask
return 0;
}
void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
if (stream->readFlag()) {
VectorF scale;
mathRead( *stream, &scale );
setScale( scale );
}
if (stream->readFlag()) {
GameBaseData* dptr = 0;
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (!Sim::findObject(id,dptr) || !setDataBlock(dptr))
con->setLastError("Invalid packet GameBase::unpackUpdate()");
}
}
//----------------------------------------------------------------------------
ConsoleMethod( GameBase, getDataBlock, S32, 2, 2, "()"
"Return the datablock this GameBase is using.")
{
return object->getDataBlock()? object->getDataBlock()->getId(): 0;
}
//----------------------------------------------------------------------------
ConsoleMethod(GameBase, setDataBlock, bool, 3, 3, "(DataBlock db)"
"Assign this GameBase to use the specified datablock.")
{
GameBaseData* data;
if (Sim::findObject(argv[2],data)) {
return object->setDataBlock(data);
}
Con::errorf("Could not find data block \"%s\"",argv[2]);
return false;
}
//----------------------------------------------------------------------------
IMPLEMENT_CONSOLETYPE(GameBaseData)
IMPLEMENT_GETDATATYPE(GameBaseData)
IMPLEMENT_SETDATATYPE(GameBaseData)
void GameBase::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField("nameTag", TypeCaseString, Offset(mNameTag, GameBase));
addField("dataBlock", TypeGameBaseDataPtr, Offset(mDataBlock, GameBase));
endGroup("Misc");
}
void GameBase::consoleInit()
{
#ifdef TORQUE_DEBUG
Con::addVariable("GameBase::boundingBox", TypeBool, &gShowBoundingBox);
#endif
}