diff --git a/Engine/source/T3D/camera.h b/Engine/source/T3D/camera.h index 5e760e61f..484cb2c92 100644 --- a/Engine/source/T3D/camera.h +++ b/Engine/source/T3D/camera.h @@ -20,6 +20,11 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// + #ifndef _CAMERA_H_ #define _CAMERA_H_ @@ -246,6 +251,8 @@ class Camera: public ShapeBase DECLARE_CONOBJECT( Camera ); DECLARE_CATEGORY( "Game" ); DECLARE_DESCRIPTION( "Represents a position, direction and field of view to render a scene from." ); + static F32 getMovementSpeed() { return smMovementSpeed; } + bool isCamera() const { return true; } }; typedef Camera::CameraMotionMode CameraMotionMode; diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index 989c9fd56..d39b5c249 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -61,6 +61,7 @@ #include "core/stream/fileStream.h" #endif +#include "afx/arcaneFX.h" //---------------------------------------------------------------------------- #define MAX_MOVE_PACKET_SENDS 4 @@ -191,6 +192,12 @@ bool GameConnection::client_cache_on = false; //---------------------------------------------------------------------------- GameConnection::GameConnection() { + mRolloverObj = NULL; + mPreSelectedObj = NULL; + mSelectedObj = NULL; + mChangedSelectedObj = false; + mPreSelectTimestamp = 0; + zoned_in = false; #ifdef AFX_CAP_DATABLOCK_CACHE client_db_stream = new InfiniteBitStream; @@ -1168,6 +1175,17 @@ void GameConnection::readPacket(BitStream *bstream) { mMoveList->clientReadMovePacket(bstream); + // selected object - do we have a change in status? + if (bstream->readFlag()) + { + if (bstream->readFlag()) + { + S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize); + setSelectedObj(static_cast(resolveGhost(gIndex))); + } + else + setSelectedObj(NULL); + } bool hadFlash = mDamageFlash > 0 || mWhiteOut > 0; mDamageFlash = 0; mWhiteOut = 0; @@ -1411,6 +1429,35 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note) // all the damage flash & white out S32 gIndex = -1; + if (mChangedSelectedObj) + { + S32 gidx; + // send NULL player + if ((mSelectedObj == NULL) || mSelectedObj.isNull()) + { + bstream->writeFlag(true); + bstream->writeFlag(false); + mChangedSelectedObj = false; + } + // send ghost-idx + else if ((gidx = getGhostIndex(mSelectedObj)) != -1) + { + Con::printf("SEND OBJECT SELECTION"); + bstream->writeFlag(true); + bstream->writeFlag(true); + bstream->writeInt(gidx, NetConnection::GhostIdBitSize); + mChangedSelectedObj = false; + } + // not fully changed yet + else + { + bstream->writeFlag(false); + mChangedSelectedObj = true; + } + } + else + bstream->writeFlag(false); + if (!mControlObject.isNull()) { gIndex = getGhostIndex(mControlObject); @@ -2405,6 +2452,135 @@ DefineEngineMethod( GameConnection, getVisibleGhostDistance, F32, (),, return object->getVisibleGhostDistance(); } +// The object selection code here is, in part, based, on functionality described +// in the following resource: +// Object Selection in Torque by Dave Myers +// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7335 + +ConsoleMethod(GameConnection, setSelectedObj, bool, 3, 4, "(object, [propagate_to_client])") +{ + SceneObject* pending_selection; + if (!Sim::findObject(argv[2], pending_selection)) + return false; + + bool propagate_to_client = (argc > 3) ? dAtob(argv[3]) : false; + object->setSelectedObj(pending_selection, propagate_to_client); + + return true; +} + +ConsoleMethod(GameConnection, getSelectedObj, S32, 2, 2, "()") +{ + SimObject* selected = object->getSelectedObj(); + return (selected) ? selected->getId(): -1; +} + +ConsoleMethod(GameConnection, clearSelectedObj, void, 2, 3, "([propagate_to_client])") +{ + bool propagate_to_client = (argc > 2) ? dAtob(argv[2]) : false; + object->setSelectedObj(NULL, propagate_to_client); +} + +ConsoleMethod(GameConnection, setPreSelectedObjFromRollover, void, 2, 2, "()") +{ + object->setPreSelectedObjFromRollover(); +} + +ConsoleMethod(GameConnection, clearPreSelectedObj, void, 2, 2, "()") +{ + object->clearPreSelectedObj(); +} + +ConsoleMethod(GameConnection, setSelectedObjFromPreSelected, void, 2, 2, "()") +{ + object->setSelectedObjFromPreSelected(); +} + +void GameConnection::setSelectedObj(SceneObject* so, bool propagate_to_client) +{ + if (!isConnectionToServer()) + { + // clear previously selected object + if (mSelectedObj) + clearNotify(mSelectedObj); + + // save new selection + mSelectedObj = so; + + // mark selected object + if (mSelectedObj) + deleteNotify(mSelectedObj); + + // mark selection dirty + if (propagate_to_client) + mChangedSelectedObj = true; + + return; + } + + // clear previously selected object + if (mSelectedObj) + { + mSelectedObj->setSelectionFlags(mSelectedObj->getSelectionFlags() & ~SceneObject::SELECTED); + clearNotify(mSelectedObj); + Con::executef(this, "onObjectDeselected", mSelectedObj->getIdString()); + } + + // save new selection + mSelectedObj = so; + + // mark selected object + if (mSelectedObj) + { + mSelectedObj->setSelectionFlags(mSelectedObj->getSelectionFlags() | SceneObject::SELECTED); + deleteNotify(mSelectedObj); + } + + // mark selection dirty + //mChangedSelectedObj = true; + + // notify appropriate script of the change + if (mSelectedObj) + Con::executef(this, "onObjectSelected", mSelectedObj->getIdString()); +} + +void GameConnection::setRolloverObj(SceneObject* so) +{ + // save new selection + mRolloverObj = so; + + // notify appropriate script of the change + Con::executef(this, "onObjectRollover", (mRolloverObj) ? mRolloverObj->getIdString() : ""); +} + +void GameConnection::setPreSelectedObjFromRollover() +{ + mPreSelectedObj = mRolloverObj; + mPreSelectTimestamp = Platform::getRealMilliseconds(); +} + +void GameConnection::clearPreSelectedObj() +{ + mPreSelectedObj = 0; + mPreSelectTimestamp = 0; +} + +void GameConnection::setSelectedObjFromPreSelected() +{ + U32 now = Platform::getRealMilliseconds(); + if (now - mPreSelectTimestamp < arcaneFX::sTargetSelectionTimeoutMS) + setSelectedObj(mPreSelectedObj); + mPreSelectedObj = 0; +} + +void GameConnection::onDeleteNotify(SimObject* obj) +{ + if (obj == mSelectedObj) + setSelectedObj(NULL); + + Parent::onDeleteNotify(obj); +} + #ifdef AFX_CAP_DATABLOCK_CACHE void GameConnection::tempDisableStringBuffering(BitStream* bs) const diff --git a/Engine/source/T3D/gameBase/gameConnection.h b/Engine/source/T3D/gameBase/gameConnection.h index a234960b0..427c8ce8d 100644 --- a/Engine/source/T3D/gameBase/gameConnection.h +++ b/Engine/source/T3D/gameBase/gameConnection.h @@ -386,6 +386,36 @@ protected: DECLARE_CALLBACK( void, onDataBlocksDone, (U32 sequence) ); DECLARE_CALLBACK( void, onFlash, (bool state) ); + // GameConnection is modified to keep track of object selections which are used in + // spell targeting. This code stores the current object selection as well as the + // current rollover object beneath the cursor. The rollover object is treated as a + // pending object selection and actual object selection is usually made by promoting + // the rollover object to the current object selection. +private: + SimObjectPtr mRolloverObj; + SimObjectPtr mPreSelectedObj; + SimObjectPtr mSelectedObj; + bool mChangedSelectedObj; + U32 mPreSelectTimestamp; +protected: + virtual void onDeleteNotify(SimObject*); +public: + void setRolloverObj(SceneObject*); + SceneObject* getRolloverObj() { return mRolloverObj; } + void setSelectedObj(SceneObject*, bool propagate_to_client=false); + SceneObject* getSelectedObj() { return mSelectedObj; } + void setPreSelectedObjFromRollover(); + void clearPreSelectedObj(); + void setSelectedObjFromPreSelected(); + // Flag is added to indicate when a client is fully connected or "zoned-in". + // This information determines when AFX will startup active effects on a newly + // added client. +private: + bool zoned_in; +public: + bool isZonedIn() const { return zoned_in; } + void setZonedIn() { zoned_in = true; } + #ifdef AFX_CAP_DATABLOCK_CACHE private: static StringTableEntry server_cache_filename; diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 832937f0e..6b4d4ae0d 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -5885,7 +5885,12 @@ void Player::applyImpulse(const Point3F&,const VectorF& vec) bool Player::castRay(const Point3F &start, const Point3F &end, RayInfo* info) { - if (getDamageState() != Enabled) + // In standard Torque there's a rather brute force culling of all + // non-enabled players (corpses) from the ray cast. But, to + // demonstrate a resurrection spell, we need corpses to be + // selectable, so this code change allows consideration of corpses + // in the ray cast if corpsesHiddenFromRayCast is set to false. + if (sCorpsesHiddenFromRayCast && getDamageState() != Enabled) return false; // Collide against bounding box. Need at least this for the editor. diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index a49f3baf4..367536058 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -790,6 +790,9 @@ private: void afx_init(); U32 afx_packUpdate(NetConnection*, U32 mask, BitStream*, U32 retMask); void afx_unpackUpdate(NetConnection*, BitStream*); +private: + static bool sCorpsesHiddenFromRayCast; + }; typedef Player::Pose PlayerPose; diff --git a/Engine/source/scene/sceneObject.cpp b/Engine/source/scene/sceneObject.cpp index 6bd796607..550ee80b4 100644 --- a/Engine/source/scene/sceneObject.cpp +++ b/Engine/source/scene/sceneObject.cpp @@ -20,6 +20,11 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// + #include "platform/platform.h" #include "scene/sceneObject.h" @@ -144,6 +149,7 @@ SceneObject::SceneObject() mIsScopeAlways = false; mAccuTex = NULL; + mSelectionFlags = 0; mPathfindingIgnore = false; } diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h index 3985d372c..bf04ea4c4 100644 --- a/Engine/source/scene/sceneObject.h +++ b/Engine/source/scene/sceneObject.h @@ -20,6 +20,11 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// + #ifndef _SCENEOBJECT_H_ #define _SCENEOBJECT_H_ @@ -778,6 +783,23 @@ class SceneObject : public NetObject, private SceneContainer::Link, public Proce // Note: This was placed in SceneObject to both ShapeBase and TSStatic could support it. public: GFXTextureObject* mAccuTex; + // mSelectionFlags field keeps track of flags related to object selection. + // PRE_SELECTED marks an object as pre-selected (object under cursor) + // SELECTED marks an object as selected (a target) + protected: + U8 mSelectionFlags; + public: + enum { + SELECTED = BIT(0), + PRE_SELECTED = BIT(1), + }; + virtual void setSelectionFlags(U8 flags) { mSelectionFlags = flags; } + U8 getSelectionFlags() const { return mSelectionFlags; } + bool needsSelectionHighlighting() const { return (mSelectionFlags != 0); } + // This should only return true if the object represents an independent camera + // as opposed to something like a Player that has a built-in camera that requires + // special calculations to determine the view transform. + virtual bool isCamera() const { return false; } }; #endif // _SCENEOBJECT_H_