diff --git a/Engine/source/T3D/assets/ShapeAsset.h b/Engine/source/T3D/assets/ShapeAsset.h index afa39ca57..2d186346d 100644 --- a/Engine/source/T3D/assets/ShapeAsset.h +++ b/Engine/source/T3D/assets/ShapeAsset.h @@ -370,7 +370,7 @@ public: \ #pragma region Arrayed Asset Macros -#define DECLARE_SHAPEASSET_ARRAY(className,name,max) public: \ +#define DECLARE_SHAPEASSET_ARRAY(className,name,max,changeFunc) public: \ static const U32 sm##name##Count = max;\ Resourcem##name[max];\ StringTableEntry m##name##Name[max]; \ @@ -384,6 +384,10 @@ public: \ \ bool _set##name(StringTableEntry _in, const U32& index)\ {\ + if (m##name##Asset[index].notNull())\ + {\ + m##name##Asset[index]->getChangedSignal().remove(this, &className::changeFunc);\ + }\ if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\ {\ if(index >= sm##name##Count || index < 0)\ @@ -430,6 +434,8 @@ public: \ if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\ {\ m##name[index] = m##name##Asset[index]->getShapeResource();\ + \ + m##name##Asset[index]->getChangedSignal().notify(this, &className::changeFunc);\ }\ else\ {\ diff --git a/Engine/source/T3D/camera.cpp b/Engine/source/T3D/camera.cpp index 41056f833..3e2408583 100644 --- a/Engine/source/T3D/camera.cpp +++ b/Engine/source/T3D/camera.cpp @@ -345,7 +345,7 @@ bool Camera::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 7c05e3ec5..78a1e53cc 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -601,7 +601,7 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload ) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/debris.h b/Engine/source/T3D/debris.h index a0ff99520..1d1116031 100644 --- a/Engine/source/T3D/debris.h +++ b/Engine/source/T3D/debris.h @@ -111,7 +111,10 @@ public: void onPerformSubstitutions() override; bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; //************************************************************************** diff --git a/Engine/source/T3D/fps/guiHealthBarHud.cpp b/Engine/source/T3D/fps/guiHealthBarHud.cpp index 602d242b1..79b420e3a 100644 --- a/Engine/source/T3D/fps/guiHealthBarHud.cpp +++ b/Engine/source/T3D/fps/guiHealthBarHud.cpp @@ -147,6 +147,11 @@ void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect) if (!conn) return; ShapeBase* control = dynamic_cast(conn->getControlObject()); + + //cover the case of a connection controling an object in turn controlling another + if (control && control->getControlObject()) + control = control->getControlObject(); + if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType))) return; diff --git a/Engine/source/T3D/fps/guiHealthTextHud.cpp b/Engine/source/T3D/fps/guiHealthTextHud.cpp index 1c202906c..faa798253 100644 --- a/Engine/source/T3D/fps/guiHealthTextHud.cpp +++ b/Engine/source/T3D/fps/guiHealthTextHud.cpp @@ -148,7 +148,12 @@ void GuiHealthTextHud::onRender(Point2I offset, const RectI &updateRect) GameConnection* conn = GameConnection::getConnectionToServer(); if (!conn) return; - ShapeBase* control = dynamic_cast(conn->getControlObject()); + ShapeBase* control = dynamic_cast(conn->getControlObject()); + + //cover the case of a connection controling an object in turn controlling another + if (control && control->getControlObject()) + control = control->getControlObject(); + if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType))) return; diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index f10543198..02676be26 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -1127,7 +1127,7 @@ bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload ) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/explosion.h b/Engine/source/T3D/fx/explosion.h index 447b7747d..0ea4c63d7 100644 --- a/Engine/source/T3D/fx/explosion.h +++ b/Engine/source/T3D/fx/explosion.h @@ -143,7 +143,10 @@ public: ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0); bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; diff --git a/Engine/source/T3D/fx/groundCover.cpp b/Engine/source/T3D/fx/groundCover.cpp index 377f8bb63..072ef6463 100644 --- a/Engine/source/T3D/fx/groundCover.cpp +++ b/Engine/source/T3D/fx/groundCover.cpp @@ -852,8 +852,11 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream ) // It's sloppy, but it works for now. _freeCells(); - if ( isProperlyAdded() ) + if (isProperlyAdded()) + { _initMaterial(); + _initShapes(); + } } } diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h index b2ef23e05..af5d02858 100644 --- a/Engine/source/T3D/fx/groundCover.h +++ b/Engine/source/T3D/fx/groundCover.h @@ -341,7 +341,7 @@ protected: RectF mBillboardRects[MAX_COVERTYPES]; /// The cover shape filenames. - DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES); + DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES, onShapeChanged); DECLARE_ASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1); /// The cover shape instances. @@ -409,6 +409,12 @@ protected: S32 randSeed ); void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void onShapeChanged() + { + _initShapes(); + setMaskBits(U32(-1)); + } }; #endif // _GROUNDCOVER_H_ diff --git a/Engine/source/T3D/fx/lightning.cpp b/Engine/source/T3D/fx/lightning.cpp index 10447fa6f..44c94b1a8 100644 --- a/Engine/source/T3D/fx/lightning.cpp +++ b/Engine/source/T3D/fx/lightning.cpp @@ -474,7 +474,7 @@ bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/particle.h b/Engine/source/T3D/fx/particle.h index 12e6a242a..0f328801a 100644 --- a/Engine/source/T3D/fx/particle.h +++ b/Engine/source/T3D/fx/particle.h @@ -92,7 +92,10 @@ class ParticleData : public SimDataBlock static bool protectedSetSizes(void* object, const char* index, const char* data); static bool protectedSetTimes(void* object, const char* index, const char* data); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: ParticleData(); diff --git a/Engine/source/T3D/fx/particleEmitter.cpp b/Engine/source/T3D/fx/particleEmitter.cpp index c9887f619..00470eb8a 100644 --- a/Engine/source/T3D/fx/particleEmitter.cpp +++ b/Engine/source/T3D/fx/particleEmitter.cpp @@ -1131,7 +1131,7 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload ) return true; } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/particleEmitterNode.cpp b/Engine/source/T3D/fx/particleEmitterNode.cpp index 26e8a71b4..5197872a2 100644 --- a/Engine/source/T3D/fx/particleEmitterNode.cpp +++ b/Engine/source/T3D/fx/particleEmitterNode.cpp @@ -254,7 +254,7 @@ bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/precipitation.cpp b/Engine/source/T3D/fx/precipitation.cpp index 805e87e43..b56893ea1 100644 --- a/Engine/source/T3D/fx/precipitation.cpp +++ b/Engine/source/T3D/fx/precipitation.cpp @@ -614,7 +614,7 @@ bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload ) initMaterials(); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/precipitation.h b/Engine/source/T3D/fx/precipitation.h index 25aad8e60..a3ca97c40 100644 --- a/Engine/source/T3D/fx/precipitation.h +++ b/Engine/source/T3D/fx/precipitation.h @@ -69,8 +69,14 @@ class PrecipitationData : public GameBaseData void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; - void onDropChanged() {} - void onSplashChanged() {} + void onDropChanged() + { + reloadOnLocalClient(); + } + void onSplashChanged() + { + reloadOnLocalClient(); + } }; struct Raindrop diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp index adc846017..fb8af2db6 100644 --- a/Engine/source/T3D/fx/ribbonNode.cpp +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -159,7 +159,7 @@ bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 68bbee0c3..77edaae76 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/Engine/source/T3D/fx/splash.cpp @@ -451,7 +451,7 @@ bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload ) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/splash.h b/Engine/source/T3D/fx/splash.h index de98a5bca..0a055ee2f 100644 --- a/Engine/source/T3D/fx/splash.h +++ b/Engine/source/T3D/fx/splash.h @@ -124,7 +124,10 @@ public: DECLARE_IMAGEASSET_ARRAY(SplashData, Texture, NUM_TEX, onTextureChanged); DECLARE_IMAGEASSET_ARRAY_SETGET(SplashData, Texture) - void onTextureChanged() {} + void onTextureChanged() + { + reloadOnLocalClient(); + } ExplosionData* explosion; S32 explosionId; diff --git a/Engine/source/T3D/gameBase/gameBase.cpp b/Engine/source/T3D/gameBase/gameBase.cpp index 8775f94b3..f16dafc06 100644 --- a/Engine/source/T3D/gameBase/gameBase.cpp +++ b/Engine/source/T3D/gameBase/gameBase.cpp @@ -95,7 +95,7 @@ IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ), "}\n\n" "@endtsexample\n" ); -IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj ), ( obj ), +IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj, bool reload), ( obj, reload), "@brief Called when the object has a new datablock assigned.\n\n" "@param obj the GameBase object\n\n" "@see onAdd for an example\n" ); @@ -512,12 +512,12 @@ void GameBase::scriptOnAdd() mDataBlock->onAdd_callback( this ); } -void GameBase::scriptOnNewDataBlock() +void GameBase::scriptOnNewDataBlock(bool reload) { // Script onNewDataBlock() must be called by the leaf class // after everything is loaded. if (mDataBlock && !isGhost()) - mDataBlock->onNewDataBlock_callback( this ); + mDataBlock->onNewDataBlock_callback( this, reload); } void GameBase::scriptOnRemove() diff --git a/Engine/source/T3D/gameBase/gameBase.h b/Engine/source/T3D/gameBase/gameBase.h index 7c175f71e..c18b30196 100644 --- a/Engine/source/T3D/gameBase/gameBase.h +++ b/Engine/source/T3D/gameBase/gameBase.h @@ -115,7 +115,7 @@ public: /// @{ DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) ); DECLARE_CALLBACK( void, onRemove, ( GameBase* obj ) ); - DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj ) ); + DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj, bool reload) ); DECLARE_CALLBACK( void, onMount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) ); DECLARE_CALLBACK( void, onUnmount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) ); /// @} @@ -299,7 +299,7 @@ public: /// Executes the 'onNewDataBlock' script function for this object. /// /// @note This must be called after everything is loaded. - void scriptOnNewDataBlock(); + void scriptOnNewDataBlock(bool reload = false); /// Executes the 'onRemove' script function for this object. /// @note This must be called while the object is still valid diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp index 6c5395452..3681774da 100644 --- a/Engine/source/T3D/item.cpp +++ b/Engine/source/T3D/item.cpp @@ -422,7 +422,7 @@ bool Item::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; if (!mSubclassItemHandlesScene) - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); if ( isProperlyAdded() ) _updatePhysics(); diff --git a/Engine/source/T3D/lightFlareData.h b/Engine/source/T3D/lightFlareData.h index 5239bf3c1..baa74c39b 100644 --- a/Engine/source/T3D/lightFlareData.h +++ b/Engine/source/T3D/lightFlareData.h @@ -106,7 +106,10 @@ protected: void _makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count ); void _renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } protected: diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index 5fac601ce..a18523ef7 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -142,7 +142,7 @@ bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload ) mDataBlock = dynamic_cast( dptr ); if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return(false); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return(true); } diff --git a/Engine/source/T3D/pathCamera.cpp b/Engine/source/T3D/pathCamera.cpp index 3a4ccd578..bb14db972 100644 --- a/Engine/source/T3D/pathCamera.cpp +++ b/Engine/source/T3D/pathCamera.cpp @@ -181,7 +181,7 @@ bool PathCamera::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/pathShape.cpp b/Engine/source/T3D/pathShape.cpp index 846e7bf18..1a903e555 100644 --- a/Engine/source/T3D/pathShape.cpp +++ b/Engine/source/T3D/pathShape.cpp @@ -134,7 +134,7 @@ bool PathShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/physics/physicsDebris.h b/Engine/source/T3D/physics/physicsDebris.h index 2626300e9..9b9ae6861 100644 --- a/Engine/source/T3D/physics/physicsDebris.h +++ b/Engine/source/T3D/physics/physicsDebris.h @@ -97,7 +97,10 @@ public: void packData( BitStream *stream ) override; void unpackData( BitStream *stream ) override; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } DECLARE_CONOBJECT( PhysicsDebrisData ); diff --git a/Engine/source/T3D/physics/physicsShape.h b/Engine/source/T3D/physics/physicsShape.h index 144580c31..466e39ebf 100644 --- a/Engine/source/T3D/physics/physicsShape.h +++ b/Engine/source/T3D/physics/physicsShape.h @@ -135,7 +135,10 @@ public: SimObjectRef< ExplosionData > explosion; SimObjectRef< PhysicsShapeData > destroyedShape; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; typedef PhysicsShapeData::SimType PhysicsSimType; diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 801df58df..fe560e739 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -460,6 +460,7 @@ PlayerData::PlayerData() jumpTowardsNormal = true; physicsPlayerType = StringTable->EmptyString(); + mControlMap = StringTable->EmptyString(); dMemset( actionList, 0, sizeof(actionList) ); } @@ -739,7 +740,9 @@ void PlayerData::initPersistFields() endGroup( "Camera" ); addGroup( "Movement" ); - + addField("controlMap", TypeString, Offset(mControlMap, PlayerData), + "@brief movemap used by these types of objects.\n\n"); + addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat, "@brief Maximum height the player can step up.\n\n" "The player will automatically step onto changes in ground height less " @@ -1923,7 +1926,7 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload ) onScaleChanged(); resetWorldBox(); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index b80b3f0e5..0b8f44df2 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -76,7 +76,7 @@ struct PlayerData: public ShapeBaseData { /// that we don't create a TSThread on the player if we don't /// need to. - DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages); ///< Used to render with mounted images in first person [optional] + DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages, onShapeChanged); ///< Used to render with mounted images in first person [optional] DECLARE_ASSET_ARRAY_SETGET(PlayerData, ShapeFP); StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify @@ -346,7 +346,7 @@ struct PlayerData: public ShapeBaseData { // Jump off surfaces at their normal rather than straight up bool jumpTowardsNormal; - + StringTableEntry mControlMap; // For use if/when mPhysicsPlayer is created StringTableEntry physicsPlayerType; @@ -366,6 +366,11 @@ struct PlayerData: public ShapeBaseData { void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; + void onShapeChanged() + { + reloadOnLocalClient(); + } + /// @name Callbacks /// @{ DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) ); diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 56f8af638..58c6f3aa6 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -56,6 +56,7 @@ #include "T3D/decal/decalData.h" #include "T3D/lightDescription.h" #include "console/engineAPI.h" +#include "T3D/rigidShape.h" IMPLEMENT_CO_DATABLOCK_V1(ProjectileData); @@ -163,6 +164,7 @@ ProjectileData::ProjectileData() scale.set( 1.0f, 1.0f, 1.0f ); isBallistic = false; + mExplodeOnTmeout = false; velInheritFactor = 1.0f; muzzleVelocity = 50; @@ -203,6 +205,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G muzzleVelocity = other.muzzleVelocity; impactForce = other.impactForce; isBallistic = other.isBallistic; + mExplodeOnTmeout = other.mExplodeOnTmeout; bounceElasticity = other.bounceElasticity; bounceFriction = other.bounceFriction; gravityMod = other.gravityMod; @@ -285,6 +288,8 @@ void ProjectileData::initPersistFields() addProtectedFieldV("fadeDelay", TypeRangedS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, &CommonValidators::NaturalNumber, "@brief Amount of time, in milliseconds, before the projectile begins to fade out.\n\n" "This value must be smaller than the projectile's lifetime to have an affect."); + addField("explodeOnTmeout", TypeBool, Offset(mExplodeOnTmeout, ProjectileData), + "@brief Detetmines if the projectile should explode on timeout"); addField("isBallistic", TypeBool, Offset(isBallistic, ProjectileData), "@brief Detetmines if the projectile should be affected by gravity and whether or not " "it bounces before exploding.\n\n"); @@ -455,13 +460,14 @@ void ProjectileData::packData(BitStream* stream) stream->write(armingDelay); stream->write(fadeDelay); + stream->writeFlag(mExplodeOnTmeout); if(stream->writeFlag(isBallistic)) { stream->write(gravityMod); stream->write(bounceElasticity); stream->write(bounceFriction); } - + } void ProjectileData::unpackData(BitStream* stream) @@ -514,6 +520,7 @@ void ProjectileData::unpackData(BitStream* stream) stream->read(&armingDelay); stream->read(&fadeDelay); + mExplodeOnTmeout = stream->readFlag(); isBallistic = stream->readFlag(); if(isBallistic) { @@ -611,6 +618,7 @@ Projectile::Projectile() mProjectileShape( NULL ), mActivateThread( NULL ), mMaintainThread( NULL ), + mHasHit(false), mHasExploded( false ), mFadeValue( 1.0f ) { @@ -1128,10 +1136,18 @@ void Projectile::processTick( const Move *move ) void Projectile::simulate( F32 dt ) { - if ( isServerObject() && mCurrTick >= mDataBlock->lifetime ) + if ( isServerObject() ) { - deleteObject(); - return; + if (mCurrTick >= (mDataBlock->lifetime - TickMs)) + { + if (mDataBlock->mExplodeOnTmeout) + explode(mCurrPosition, Point3F::UnitZ, VehicleObjectType); + } + if (mCurrTick >= mDataBlock->lifetime || (mHasHit && mCurrTick < mDataBlock->armingDelay)) + { + deleteObject(); + return; + } } if ( mHasExploded ) @@ -1167,9 +1183,16 @@ void Projectile::simulate( F32 dt ) if ( mPhysicsWorld ) hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce ); - else + else + { hit = getContainer()->castRay(oldPosition, newPosition, dynamicCollisionMask | staticCollisionMask, &rInfo); - + if (hit && rInfo.object->getTypeMask() & VehicleObjectType) + { + RigidShape* aRigid = dynamic_cast(rInfo.object); + if (aRigid) + aRigid->applyImpulse(rInfo.point, Point3F(newPosition - oldPosition) * mDataBlock->impactForce); + } + } if ( hit ) { // make sure the client knows to bounce @@ -1237,6 +1260,8 @@ void Projectile::simulate( F32 dt ) else { mCurrVelocity = Point3F::Zero; + newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f; + mHasHit = true; } } diff --git a/Engine/source/T3D/projectile.h b/Engine/source/T3D/projectile.h index 671c3f98f..2ca827ea7 100644 --- a/Engine/source/T3D/projectile.h +++ b/Engine/source/T3D/projectile.h @@ -87,9 +87,9 @@ public: /// Force imparted on a hit object. F32 impactForce; + bool mExplodeOnTmeout; /// Should it arc? bool isBallistic; - /// How HIGH should it bounce (parallel to normal), [0,1] F32 bounceElasticity; /// How much momentum should be lost when it bounces (perpendicular to normal), [0,1] @@ -154,7 +154,10 @@ public: ProjectileData(const ProjectileData&, bool = false); bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; @@ -274,7 +277,7 @@ protected: LightInfo *mLight; LightState mLightState; - + bool mHasHit; bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions. F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime ///< in data block diff --git a/Engine/source/T3D/proximityMine.cpp b/Engine/source/T3D/proximityMine.cpp index 8ce924ea1..fd02fc091 100644 --- a/Engine/source/T3D/proximityMine.cpp +++ b/Engine/source/T3D/proximityMine.cpp @@ -350,7 +350,7 @@ bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp index e216e7473..8a453b065 100644 --- a/Engine/source/T3D/rigidShape.cpp +++ b/Engine/source/T3D/rigidShape.cpp @@ -906,7 +906,7 @@ bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload) else mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 7f2389bd8..b3d2e1db1 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -349,7 +349,7 @@ bool ShapeBaseData::preload(bool server, String &errorStr) S32 i; U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset); - if (assetStatus == AssetBase::Ok|| assetStatus == AssetBase::UsingFallback) + if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback) { if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded()) shapeError = true; @@ -912,7 +912,17 @@ void ShapeBaseData::unpackData(BitStream* stream) silent_bbox_check = stream->readFlag(); } +// +// +void ShapeBaseData::onShapeChanged() +{ + reloadOnLocalClient(); +} +void ShapeBaseData::onDebrisChanged() +{ + reloadOnLocalClient(); +} //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 594b709bd..b34e544bf 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -378,7 +378,7 @@ struct ShapeBaseImageData: public GameBaseData { F32 scriptAnimTransitionTime; ///< The amount of time to transition between the previous sequence and new sequence ///< when the script prefix has changed. - DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes); ///< Name of shape to render. + DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes, onShapeChanged); ///< Name of shape to render. DECLARE_ASSET_ARRAY_SETGET(ShapeBaseImageData, Shape); //DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP); ///< Name of shape to render in first person (optional). @@ -505,6 +505,11 @@ struct ShapeBaseImageData: public GameBaseData { void handleStateSoundTrack(const U32& stateId); + void onShapeChanged() + { + reloadOnLocalClient(); + } + /// @} /// @name Callbacks @@ -683,8 +688,8 @@ public: Vector txr_tag_remappings; bool silent_bbox_check; - void onShapeChanged() {} - void onDebrisChanged() {} + void onShapeChanged(); + void onDebrisChanged(); public: ShapeBaseData(const ShapeBaseData&, bool = false); }; diff --git a/Engine/source/T3D/staticShape.cpp b/Engine/source/T3D/staticShape.cpp index de47194f5..66e9ec454 100644 --- a/Engine/source/T3D/staticShape.cpp +++ b/Engine/source/T3D/staticShape.cpp @@ -219,7 +219,7 @@ bool StaticShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index dcccd0778..9df293ba5 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -465,7 +465,7 @@ bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/turret/aiTurretShape.cpp b/Engine/source/T3D/turret/aiTurretShape.cpp index 0e5ce0248..5ad3df020 100644 --- a/Engine/source/T3D/turret/aiTurretShape.cpp +++ b/Engine/source/T3D/turret/aiTurretShape.cpp @@ -545,7 +545,7 @@ bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) mShapeInstance->setTimeScale(mStateAnimThread,0); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/turret/turretShape.cpp b/Engine/source/T3D/turret/turretShape.cpp index 8c523588e..e155663aa 100644 --- a/Engine/source/T3D/turret/turretShape.cpp +++ b/Engine/source/T3D/turret/turretShape.cpp @@ -127,6 +127,7 @@ TurretShapeData::TurretShapeData() recoilSequence[i] = -1; pitchSequence = -1; headingSequence = -1; + mControlMap = StringTable->EmptyString(); } void TurretShapeData::initPersistFields() @@ -134,6 +135,8 @@ void TurretShapeData::initPersistFields() docsURL; Parent::initPersistFields(); addGroup("Steering"); + addField("controlMap", TypeString, Offset(mControlMap, TurretShapeData), + "@brief movemap used by these types of objects.\n\n"); addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData), "@brief Should the turret allow only z rotations.\n\n" "True indicates that the turret may only be rotated on its z axis, just like the Item class. " @@ -440,7 +443,7 @@ bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mSubclassTurretShapeHandlesScene) { - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); } return true; diff --git a/Engine/source/T3D/turret/turretShape.h b/Engine/source/T3D/turret/turretShape.h index b31024e07..a086d79d7 100644 --- a/Engine/source/T3D/turret/turretShape.h +++ b/Engine/source/T3D/turret/turretShape.h @@ -80,6 +80,7 @@ public: bool startLoaded; ///< Should the turret's mounted weapon(s) start in a loaded state? bool zRotOnly; ///< Should the turret allow only z rotations (like an item)? + StringTableEntry mControlMap; public: TurretShapeData(); diff --git a/Engine/source/T3D/vehicles/flyingVehicle.cpp b/Engine/source/T3D/vehicles/flyingVehicle.cpp index efe611b9a..24cf33490 100644 --- a/Engine/source/T3D/vehicles/flyingVehicle.cpp +++ b/Engine/source/T3D/vehicles/flyingVehicle.cpp @@ -407,7 +407,7 @@ bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) mJetThread[i] = 0; } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/vehicles/hoverVehicle.cpp b/Engine/source/T3D/vehicles/hoverVehicle.cpp index 7e7f748a5..482364bd3 100644 --- a/Engine/source/T3D/vehicles/hoverVehicle.cpp +++ b/Engine/source/T3D/vehicles/hoverVehicle.cpp @@ -549,7 +549,7 @@ bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) } // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index a0ba43208..aa191be29 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -141,6 +141,7 @@ VehicleData::VehicleData() dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) ); dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) ); dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) ); + mControlMap = StringTable->EmptyString(); numDmgEmitterAreas = 0; @@ -321,6 +322,8 @@ void VehicleData::initPersistFields() endGroup("Collision"); addGroup("Steering"); + addField("controlMap", TypeString, Offset(mControlMap, VehicleData), + "@brief movemap used by these types of objects.\n\n"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, "@brief Additional force applied to the vehicle when it is jetting.\n\n" "For WheeledVehicles, the force is applied in the forward direction. For " @@ -726,6 +729,7 @@ void Vehicle::updateMove(const Move* move) if (mDamageState == Enabled) { setImageTriggerState(0,move->trigger[0]); setImageTriggerState(1,move->trigger[1]); + //legacy code has trigger 2 and 3 reserved setImageTriggerState(2, move->trigger[4]); setImageTriggerState(3, move->trigger[5]); } diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 402a3109a..746e99a16 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -69,6 +69,7 @@ struct VehicleData : public RigidShapeData F32 damageLevelTolerance[ VC_NUM_DAMAGE_LEVELS ]; F32 numDmgEmitterAreas; + StringTableEntry mControlMap; bool enablePhysicsRep; // diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.cpp b/Engine/source/T3D/vehicles/wheeledVehicle.cpp index 88d7f67ce..9f3febd13 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.cpp +++ b/Engine/source/T3D/vehicles/wheeledVehicle.cpp @@ -711,7 +711,7 @@ bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() ); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.h b/Engine/source/T3D/vehicles/wheeledVehicle.h index 45161db12..f208fbf5b 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.h +++ b/Engine/source/T3D/vehicles/wheeledVehicle.h @@ -74,7 +74,10 @@ struct WheeledVehicleTire: public SimDataBlock void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; diff --git a/Engine/source/afx/afxMagicMissile.h b/Engine/source/afx/afxMagicMissile.h index e3f6ee755..8aa8f8084 100644 --- a/Engine/source/afx/afxMagicMissile.h +++ b/Engine/source/afx/afxMagicMissile.h @@ -66,7 +66,10 @@ protected: public: enum { MaxLifetimeTicks = 4095 }; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } public: // variables set in datablock definition: diff --git a/Engine/source/afx/afxSpellBook.cpp b/Engine/source/afx/afxSpellBook.cpp index 35d207c57..d902d36ad 100644 --- a/Engine/source/afx/afxSpellBook.cpp +++ b/Engine/source/afx/afxSpellBook.cpp @@ -206,7 +206,7 @@ bool afxSpellBook::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/afx/ce/afxBillboard.h b/Engine/source/afx/ce/afxBillboard.h index 8918e2f26..81542f716 100644 --- a/Engine/source/afx/ce/afxBillboard.h +++ b/Engine/source/afx/ce/afxBillboard.h @@ -71,7 +71,10 @@ public: static void initPersistFields(); - void onChangeTexture() {} + void onChangeTexture() + { + reloadOnLocalClient(); + } DECLARE_CONOBJECT(afxBillboardData); }; diff --git a/Engine/source/afx/ce/afxModel.h b/Engine/source/afx/ce/afxModel.h index 521409735..de2656b8f 100644 --- a/Engine/source/afx/ce/afxModel.h +++ b/Engine/source/afx/ce/afxModel.h @@ -94,7 +94,10 @@ public: static void initPersistFields(); - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } void onSequenceChanged() {} DECLARE_CONOBJECT(afxModelData); diff --git a/Engine/source/afx/ce/afxParticleEmitter.cpp b/Engine/source/afx/ce/afxParticleEmitter.cpp index cadf0d3ed..a3bb9e448 100644 --- a/Engine/source/afx/ce/afxParticleEmitter.cpp +++ b/Engine/source/afx/ce/afxParticleEmitter.cpp @@ -1064,7 +1064,7 @@ bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1108,7 +1108,7 @@ bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1177,7 +1177,7 @@ bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1294,7 +1294,7 @@ bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/afx/ce/afxZodiac.h b/Engine/source/afx/ce/afxZodiac.h index a7d14c2f1..16f4a8d79 100644 --- a/Engine/source/afx/ce/afxZodiac.h +++ b/Engine/source/afx/ce/afxZodiac.h @@ -56,7 +56,10 @@ public: static void convertGradientRangeFromDegrees(Point2F& gradrange, const Point2F& gradrange_deg); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: DECLARE_IMAGEASSET(afxZodiacData, Texture, onImageChanged, AFX_GFXZodiacTextureProfile); diff --git a/Engine/source/afx/ce/afxZodiacPlane.h b/Engine/source/afx/ce/afxZodiacPlane.h index 13c9879b5..a32a14d26 100644 --- a/Engine/source/afx/ce/afxZodiacPlane.h +++ b/Engine/source/afx/ce/afxZodiacPlane.h @@ -56,7 +56,10 @@ public: FACES_BITS = 3 }; - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: DECLARE_IMAGEASSET(afxZodiacPlaneData, Texture, onImageChanged, AFX_GFXZodiacTextureProfile); diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index f782df2b4..69bdc09f6 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -162,6 +162,69 @@ namespace PropertyInfo bool hex_print(String & string, const S8 & hex); bool default_print(String & result, SimObjectType * const & data); + + template + char* FormatPropertyBuffer(const void* dataPtr, char* buffer, U32 bufSize) + { + const T* values = reinterpret_cast(dataPtr); + char* ptr = buffer; + + for (size_t i = 0; i < count; ++i) + { + S32 written = 0; + + if constexpr (std::is_same_v || std::is_same_v) + written = dSprintf(ptr, bufSize - (ptr - buffer), "%d", values[i]); + else if constexpr (std::is_same_v || std::is_same_v) + written = dSprintf(ptr, bufSize - (ptr - buffer), "%g", values[i]); + else + AssertFatal(sizeof(T) == 0, "Unsupported type in FormatProperty"); + + ptr += written; + if (i < count - 1) + *ptr++ = ' '; + } + + return ptr; // return end of written string for chaining + } + + template + const char* FormatProperty(const void* dataPtr) + { + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer(bufSize); + FormatPropertyBuffer(dataPtr, buffer, bufSize); + return buffer; + } + + template + inline bool ParseProperty(char* str, T(&out)[count]) + { + + size_t index = 0; + char* tok = dStrtok(str, " \t"); + + while (tok && index < count) + { + if constexpr (std::is_same_v || std::is_same_v) + { + out[index++] = mRound(dAtof(tok)); + } + else if constexpr (std::is_same_v || std::is_same_v) + { + out[index++] = dAtof(tok); + } + else + { + AssertFatal(sizeof(T) == 0, "Unsupported type in PropertyInfo::ParseProperty"); + } + + tok = dStrtok(nullptr, " \t"); + } + + return index == count; + } + } // Default Scan/print definition diff --git a/Engine/source/console/simDatablock.cpp b/Engine/source/console/simDatablock.cpp index d3969a069..fc5070224 100644 --- a/Engine/source/console/simDatablock.cpp +++ b/Engine/source/console/simDatablock.cpp @@ -425,39 +425,42 @@ void SimDataBlock::write(Stream &stream, U32 tabStop, U32 flags) // MARK: ---- API ---- //----------------------------------------------------------------------------- - -DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),, - "Reload the datablock. This can only be used with a local client configuration." ) +void SimDataBlock::reloadOnLocalClient() { // Make sure we're running a local client. GameConnection* localClient = GameConnection::getLocalClientConnection(); - if( !localClient ) + if (!localClient) return; // Do an in-place pack/unpack/preload. - if( !object->preload( true, NetConnection::getErrorBuffer() ) ) + if (!preload(true, NetConnection::getErrorBuffer())) { - Con::errorf( NetConnection::getErrorBuffer() ); + Con::errorf(NetConnection::getErrorBuffer()); return; } - U8 buffer[ 16384 ]; - BitStream stream( buffer, 16384 ); + U8 buffer[16384]; + BitStream stream(buffer, 16384); - object->packData( &stream ); + packData(&stream); stream.setPosition(0); - object->unpackData( &stream ); + unpackData(&stream); - if( !object->preload( false, NetConnection::getErrorBuffer() ) ) + if (!preload(false, NetConnection::getErrorBuffer())) { - Con::errorf( NetConnection::getErrorBuffer() ); + Con::errorf(NetConnection::getErrorBuffer()); return; } // Trigger a post-apply so that change notifications respond. - object->inspectPostApply(); + inspectPostApply(); +} +DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),, + "Reload the datablock. This can only be used with a local client configuration." ) +{ + object->reloadOnLocalClient(); } //----------------------------------------------------------------------------- diff --git a/Engine/source/console/simDatablock.h b/Engine/source/console/simDatablock.h index 1b61822d7..2f5c8efcc 100644 --- a/Engine/source/console/simDatablock.h +++ b/Engine/source/console/simDatablock.h @@ -176,6 +176,8 @@ public: /// Used by the console system to automatically tell datablock classes apart /// from non-datablock classes. static const bool __smIsDatablock = true; + + void reloadOnLocalClient(); protected: struct SubstitutionStatement { diff --git a/Engine/source/forest/forestItem.h b/Engine/source/forest/forestItem.h index 42bf56a5f..64e6eb611 100644 --- a/Engine/source/forest/forestItem.h +++ b/Engine/source/forest/forestItem.h @@ -143,7 +143,10 @@ public: return theSignal; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; typedef Vector ForestItemDataVector; diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index ef7616340..18526267c 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -24,6 +24,7 @@ #include "console/consoleTypes.h" #include "console/console.h" #include "console/engineAPI.h" +#include "console/propertyParsing.h" #include "math/mPoint2.h" #include "math/mPoint3.h" #include "math/mMatrix.h" @@ -169,21 +170,27 @@ ImplementConsoleTypeCasters( TypePoint2I, Point2I ) ConsoleGetType( TypePoint2I ) { - Point2I *pt = (Point2I *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d", pt->x, pt->y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint2I ) { - if(argc == 1) - dSscanf(argv[0], "%d %d", &((Point2I *) dptr)->x, &((Point2I *) dptr)->y); - else if(argc == 2) - *((Point2I *) dptr) = Point2I(dAtoi(argv[0]), dAtoi(argv[1])); - else - Con::printf("Point2I must be set as { x, y } or \"x y\""); + if (argc >= 1) + { + S32 parsed[2]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point2I*)dptr) = Point2I(parsed[0], parsed[1]); + return; + } + } + + Con::warnf("Point2I must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -194,21 +201,27 @@ ImplementConsoleTypeCasters( TypePoint2F, Point2F ) ConsoleGetType( TypePoint2F ) { - Point2F *pt = (Point2F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g", pt->x, pt->y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint2F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g", &((Point2F *) dptr)->x, &((Point2F *) dptr)->y); - else if(argc == 2) - *((Point2F *) dptr) = Point2F(dAtof(argv[0]), dAtof(argv[1])); - else - Con::printf("Point2F must be set as { x, y } or \"x y\""); + if (argc >= 1) + { + F32 parsed[2]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point2F*)dptr) = Point2F(parsed[0], parsed[1]); + return; + } + } + + Con::warnf("Point2F must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -219,21 +232,27 @@ ImplementConsoleTypeCasters(TypePoint3I, Point3I) ConsoleGetType( TypePoint3I ) { - Point3I *pt = (Point3I *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %d", pt->x, pt->y, pt->z); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint3I ) { - if(argc == 1) - dSscanf(argv[0], "%d %d %d", &((Point3I *) dptr)->x, &((Point3I *) dptr)->y, &((Point3I *) dptr)->z); - else if(argc == 3) - *((Point3I *) dptr) = Point3I(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2])); - else - Con::printf("Point3I must be set as { x, y, z } or \"x y z\""); + if (argc >= 1) + { + S32 parsed[3]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point3I*)dptr) = Point3I(parsed[0], parsed[1], parsed[2]); + return; + } + } + + Con::warnf("Point3I must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -244,21 +263,27 @@ ImplementConsoleTypeCasters(TypePoint3F, Point3F) ConsoleGetType( TypePoint3F ) { - Point3F *pt = (Point3F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g", pt->x, pt->y, pt->z); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint3F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g", &((Point3F *) dptr)->x, &((Point3F *) dptr)->y, &((Point3F *) dptr)->z); - else if(argc == 3) - *((Point3F *) dptr) = Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])); - else - Con::printf("Point3F must be set as { x, y, z } or \"x y z\""); + if (argc >= 1) + { + F32 parsed[3]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point3F*)dptr) = Point3F(parsed[0], parsed[1], parsed[2]); + return; + } + } + + Con::warnf("Point3F must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -269,21 +294,27 @@ ImplementConsoleTypeCasters( TypePoint4F, Point4F ) ConsoleGetType( TypePoint4F ) { - Point4F *pt = (Point4F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint4F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g %g", &((Point4F *) dptr)->x, &((Point4F *) dptr)->y, &((Point4F *) dptr)->z, &((Point4F *) dptr)->w); - else if(argc == 4) - *((Point4F *) dptr) = Point4F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); - else - Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\""); + if (argc >= 1) + { + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point4F*)dptr) = Point4F(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::warnf("Point4F must be set as { x, y, z, w } or \"x y z w\""); } //----------------------------------------------------------------------------- @@ -294,23 +325,27 @@ ImplementConsoleTypeCasters( TypeRectI, RectI ) ConsoleGetType( TypeRectI ) { - RectI *rect = (RectI *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %d %d", rect->point.x, rect->point.y, - rect->extent.x, rect->extent.y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeRectI ) { - if(argc == 1) - dSscanf(argv[0], "%d %d %d %d", &((RectI *) dptr)->point.x, &((RectI *) dptr)->point.y, - &((RectI *) dptr)->extent.x, &((RectI *) dptr)->extent.y); - else if(argc == 4) - *((RectI *) dptr) = RectI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3])); - else - Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\""); + if (argc >= 1) + { + S32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((RectI*)dptr) = RectI(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::warnf("RectI must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- @@ -321,23 +356,27 @@ ImplementConsoleTypeCasters( TypeRectF, RectF ) ConsoleGetType( TypeRectF ) { - RectF *rect = (RectF *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", rect->point.x, rect->point.y, - rect->extent.x, rect->extent.y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeRectF ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g %g", &((RectF *) dptr)->point.x, &((RectF *) dptr)->point.y, - &((RectF *) dptr)->extent.x, &((RectF *) dptr)->extent.y); - else if(argc == 4) - *((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); - else - Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\""); + if (argc >= 1) + { + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((RectF*)dptr) = RectF(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::warnf("RectF must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- @@ -351,36 +390,44 @@ ImplementConsoleTypeCasters( TypeMatrixF, MatrixF ) ConsoleGetType( TypeMatrixF ) { - MatrixF* mat = ( MatrixF* ) dptr; + MatrixF* mat = (MatrixF*)dptr; Point3F col0, col1, col2; mat->getColumn(0, &col0); mat->getColumn(1, &col1); mat->getColumn(2, &col2); static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g %g %g %g %g %g", - col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z); - return returnBuffer; + char* buffer = Con::getReturnBuffer(bufSize); + + PropertyInfo::FormatPropertyBuffer(col0, buffer, bufSize); + *buffer++ = ' '; + PropertyInfo::FormatPropertyBuffer(col1, buffer, bufSize); + *buffer++ = ' '; + PropertyInfo::FormatPropertyBuffer(col2, buffer, bufSize); + + return buffer; } ConsoleSetType( TypeMatrixF ) { - if( argc != 1 ) + if (argc == 1) { - Con::errorf( "MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"" ); - return; - } - - Point3F col0, col1, col2; - dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g %g %g", - &col0.x, &col0.y, &col0.z, &col1.x, &col1.y, &col1.z, &col2.x, &col2.y, &col2.z ); + F32 parsed[9]; - MatrixF* mat = ( MatrixF* ) dptr; - - mat->setColumn( 0, col0 ); - mat->setColumn( 1, col1 ); - mat->setColumn( 2, col2 ); + char* buffer = new char[dStrlen(argv[0])]; + dStrcpy(buffer, argv[0], sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + MatrixF* mat = (MatrixF*)dptr; + + mat->setColumn(0, Point3F(parsed[0], parsed[1], parsed[2])); + mat->setColumn(1, Point3F(parsed[3], parsed[4], parsed[5])); + mat->setColumn(2, Point3F(parsed[6], parsed[7], parsed[8])); + return; + } + } + + Con::warnf("MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\""); } //----------------------------------------------------------------------------- @@ -390,32 +437,40 @@ ConsoleMappedType(MatrixPosition, TypeMatrixPosition, Point3F, MatrixF, "") ConsoleGetType( TypeMatrixPosition ) { - F32 *col = (F32 *) dptr + 3; + F32* col = (F32*)dptr + 3; static const U32 bufSize = 256; char* returnBuffer = Con::getReturnBuffer(bufSize); - if(col[12] == 1.f) - dSprintf(returnBuffer, bufSize, "%g %g %g", col[0], col[4], col[8]); + Point4F pos(col[0], col[4], col[8], col[12]); + + if (col[12] == 1.0f) + PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); else - dSprintf(returnBuffer, bufSize, "%g %g %g %g", col[0], col[4], col[8], col[12]); + PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); + return returnBuffer; } ConsoleSetType( TypeMatrixPosition ) { - F32 *col = ((F32 *) dptr) + 3; - if (argc == 1) + if (argc >= 1) { - col[0] = col[4] = col[8] = 0.f; - col[12] = 1.f; - dSscanf(argv[0], "%g %g %g %g", &col[0], &col[4], &col[8], &col[12]); + F32 parsed[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + // we dont want to hard fail based on the count. + // this will allow any number of properties to be set. + PropertyInfo::ParseProperty(buffer, parsed); + { + Point4F temp(parsed[0], parsed[1], parsed[2], parsed[3]); + MatrixF* mat = (MatrixF*)dptr; + mat->setColumn(3, temp); + return; + } } - else if (argc <= 4) - { - for (S32 i = 0; i < argc; i++) - col[i << 2] = dAtof(argv[i]); - } - else - Con::printf("Matrix position must be set as { x, y, z, w } or \"x y z w\""); + + Con::warnf("Matrix position must be set as { x, y, z, w } or \"x y z w\""); + } //----------------------------------------------------------------------------- @@ -425,42 +480,38 @@ ConsoleMappedType(MatrixRotation, TypeMatrixRotation, AngAxisF, MatrixF, "") ConsoleGetType( TypeMatrixRotation ) { - AngAxisF aa(*(MatrixF *) dptr); + AngAxisF aa(*(MatrixF*)dptr); aa.axis.normalize(); - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle)); - return returnBuffer; + aa.angle = mRadToDeg(aa.angle); + const char* buff = PropertyInfo::FormatProperty(&aa); + return buff; } ConsoleSetType( TypeMatrixRotation ) { - // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. - // - AngAxisF aa(Point3F(0,0,0),0); - if (argc == 1) + if (argc >= 1) { - dSscanf(argv[0], "%g %g %g %g", &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle); - aa.angle = mDegToRad(aa.angle); - } - else if (argc == 4) - { - for (S32 i = 0; i < argc; i++) - ((F32*)&aa)[i] = dAtof(argv[i]); - aa.angle = mDegToRad(aa.angle); - } - else - Con::printf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\""); + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - // - MatrixF temp; - aa.setMatrix(&temp); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF aa(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3])); + MatrixF temp; + aa.setMatrix(&temp); - F32* pDst = *(MatrixF *)dptr; - const F32* pSrc = temp; - for (U32 i = 0; i < 3; i++) - for (U32 j = 0; j < 3; j++) - pDst[i*4 + j] = pSrc[i*4 + j]; + F32* pDst = *(MatrixF*)dptr; + const F32* pSrc = temp; + for (U32 i = 0; i < 3; i++) + for (U32 j = 0; j < 3; j++) + pDst[i * 4 + j] = pSrc[i * 4 + j]; + return; + } + } + + Con::warnf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\""); } //----------------------------------------------------------------------------- @@ -472,30 +523,29 @@ ImplementConsoleTypeCasters( TypeAngAxisF, AngAxisF ) ConsoleGetType( TypeAngAxisF ) { AngAxisF* aa = ( AngAxisF* ) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle)); - return returnBuffer; + aa->angle = mRadToDeg(aa->angle); + const char* buff = PropertyInfo::FormatProperty(aa); + return buff; } ConsoleSetType( TypeAngAxisF ) { - // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. - // - AngAxisF* aa = ( AngAxisF* ) dptr; - if (argc == 1) + if (argc >= 1) { - dSscanf(argv[0], "%g %g %g %g", &aa->axis.x, &aa->axis.y, &aa->axis.z, &aa->angle); - aa->angle = mDegToRad(aa->angle); + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + + if(PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF* aa = (AngAxisF*)dptr; + aa->set(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3])); + return; + } } - else if (argc == 4) - { - for (S32 i = 0; i < argc; i++) - ((F32*)&aa)[i] = dAtof(argv[i]); - aa->angle = mDegToRad(aa->angle); - } - else - Con::printf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\""); + + Con::warnf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\""); } @@ -510,38 +560,35 @@ ImplementConsoleTypeCasters( TypeTransformF, TransformF ) ConsoleGetType( TypeTransformF ) { - TransformF* aa = ( TransformF* ) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf( returnBuffer, bufSize, "%g %g %g %g %g %g %g", - aa->mPosition.x, aa->mPosition.y, aa->mPosition.z, - aa->mOrientation.axis.x, aa->mOrientation.axis.y, aa->mOrientation.axis.z, aa->mOrientation.angle ); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeTransformF ) { - TransformF* aa = ( TransformF* ) dptr; - if( argc == 1 ) + if(argc >= 1) { - U32 count = dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g", - &aa->mPosition.x, &aa->mPosition.y, &aa->mPosition.z, - &aa->mOrientation.axis.x, &aa->mOrientation.axis.y, &aa->mOrientation.axis.z, &aa->mOrientation.angle ); + F32 parsed[7]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - aa->mHasRotation = ( count == 7 ); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + TransformF* aa = (TransformF*)dptr; + aa->mPosition.x = parsed[0]; + aa->mPosition.y = parsed[1]; + aa->mPosition.z = parsed[2]; + aa->mOrientation.axis.x = parsed[3]; + aa->mOrientation.axis.y = parsed[4]; + aa->mOrientation.axis.z = parsed[5]; + aa->mOrientation.angle = parsed[6]; + aa->mHasRotation = true; + return; + } } - else if( argc == 7 ) - { - aa->mPosition.x = dAtof( argv[ 0 ] ); - aa->mPosition.y = dAtof( argv[ 1 ] ); - aa->mPosition.z = dAtof( argv[ 2 ] ); - aa->mOrientation.axis.x = dAtof( argv[ 3 ] ); - aa->mOrientation.axis.y = dAtof( argv[ 4 ] ); - aa->mOrientation.axis.z = dAtof( argv[ 5 ] ); - aa->mOrientation.angle = dAtof( argv[ 6 ] ); - } - else - Con::errorf( "TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\""); + + Con::warnf("TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\""); } @@ -554,32 +601,33 @@ ImplementConsoleTypeCasters( TypeBox3F, Box3F ) ConsoleGetType( TypeBox3F ) { - const Box3F* pBox = (const Box3F*)dptr; - - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g %g %g", - pBox->minExtents.x, pBox->minExtents.y, pBox->minExtents.z, - pBox->maxExtents.x, pBox->maxExtents.y, pBox->maxExtents.z); - - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeBox3F ) { - Box3F* pDst = (Box3F*)dptr; + if (argc >= 1) + { + F32 parsed[6]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - if (argc == 1) - { - U32 args = dSscanf(argv[0], "%g %g %g %g %g %g", - &pDst->minExtents.x, &pDst->minExtents.y, &pDst->minExtents.z, - &pDst->maxExtents.x, &pDst->maxExtents.y, &pDst->maxExtents.z); - AssertWarn(args == 6, "Warning, box probably not read properly"); - } - else - { - Con::printf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\""); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + Box3F* pDst = (Box3F*)dptr; + pDst->minExtents.x = parsed[0]; + pDst->minExtents.y = parsed[1]; + pDst->minExtents.z = parsed[2]; + pDst->maxExtents.x = parsed[3]; + pDst->maxExtents.y = parsed[4]; + pDst->maxExtents.z = parsed[5]; + return; + } } + + Con::warnf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\""); } @@ -591,31 +639,39 @@ ImplementConsoleTypeCasters( TypeEaseF, EaseF ) ConsoleGetType( TypeEaseF ) { - const EaseF* pEase = (const EaseF*)dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %g %g", - pEase->mDir, pEase->mType, pEase->mParam[0], pEase->mParam[1]); + char* buffer = Con::getReturnBuffer(bufSize); - return returnBuffer; + EaseF* pEase = (EaseF*)dptr; + PropertyInfo::FormatPropertyBuffer(pEase + 0, buffer, bufSize); + *buffer++ = ' '; + PropertyInfo::FormatPropertyBuffer(pEase + 2, buffer, bufSize); + + return buffer; } ConsoleSetType( TypeEaseF ) { - EaseF* pDst = (EaseF*)dptr; + if (argc >= 1) + { + F32 parsed[4]; + parsed[2] = -1.0f; + parsed[3] = -1.0f; - // defaults... - pDst->mParam[0] = -1.0f; - pDst->mParam[1] = -1.0f; - if (argc == 1) { - U32 args = dSscanf(argv[0], "%d %d %f %f", // the two params are optional and assumed -1 if not present... - &pDst->mDir, &pDst->mType, &pDst->mParam[0],&pDst->mParam[1]); - if( args < 2 ) - Con::warnf( "Warning, EaseF probably not read properly" ); - } else { - Con::printf("EaseF must be set as \"dir type [param0 param1]\""); + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + // same as matrix do not hard fail based on count! + PropertyInfo::ParseProperty(buffer, parsed); + { + ((EaseF*)dptr)->set(mRound(parsed[0]), mRound(parsed[1]), parsed[2], parsed[3]); + return; + } } + + Con::warnf("EaseF must be set as \"dir type [param0 param1]\""); } //----------------------------------------------------------------------------- @@ -633,12 +689,12 @@ ConsoleGetType(TypeRotationF) if (pt->mRotationType == RotationF::Euler) { EulerF out = pt->asEulerF(RotationF::Degrees); - dSprintf(returnBuffer, bufSize, "%g %g %g", out.x, out.y, out.z); + PropertyInfo::FormatPropertyBuffer(out, returnBuffer, bufSize); } else if (pt->mRotationType == RotationF::AxisAngle) { AngAxisF out = pt->asAxisAngle(RotationF::Degrees); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", out.axis.x, out.axis.y, out.axis.z, out.angle); + PropertyInfo::FormatPropertyBuffer(&out, returnBuffer, bufSize); } return returnBuffer; @@ -646,34 +702,36 @@ ConsoleGetType(TypeRotationF) ConsoleSetType(TypeRotationF) { - if (argc == 1) + if (argc >= 1) { - U32 elements = StringUnit::getUnitCount(argv[0], " \t\n"); + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + + U32 elements = StringUnit::getUnitCount(buffer, " \t\n"); if (elements == 3) { - EulerF in; - dSscanf(argv[0], "%g %g %g", &in.x, &in.y, &in.z); - ((RotationF *)dptr)->set(in, RotationF::Degrees); + F32 parsed[3]; + if(PropertyInfo::ParseProperty(buffer, parsed)) + { + EulerF in(parsed[0], parsed[1], parsed[2]); + ((RotationF*)dptr)->set(in, RotationF::Degrees); + return; + } } - else + else if (elements == 4) { - AngAxisF in; - dSscanf(argv[0], "%g %g %g %g", &in.axis.x, &in.axis.y, &in.axis.z, &in.angle); - ((RotationF *)dptr)->set(in, RotationF::Degrees); + F32 parsed[4]; + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF in(Point3F(parsed[0], parsed[1], parsed[2]), parsed[3]); + ((RotationF*)dptr)->set(in, RotationF::Degrees); + return; + } } } - else if (argc == 3) - { - EulerF in(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])); - ((RotationF *)dptr)->set(in, RotationF::Degrees); - } - else if (argc == 4) - { - AngAxisF in(Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])), dAtof(argv[3])); - ((RotationF *)dptr)->set(in, RotationF::Degrees); - } - else - Con::printf("RotationF must be set as { x, y, z, w } or \"x y z w\""); + + Con::warnf("RotationF must be set as { x, y, z, w } or \"x y z w\""); } //----------------------------------------------------------------------------- diff --git a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript index cbd4b46cc..c9cebaa2b 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript @@ -111,8 +111,12 @@ function handleConnectionErrorMessage(%msgType, %msgString, %msgError) //----------------------------------------------------------------------------- // Disconnect //----------------------------------------------------------------------------- - function disconnect() +{ + callOnModules("disconnect"); +} + +function Core_ClientServer::disconnect(%this) { // We need to stop the client side simulation // else physics resources will not cleanup properly. @@ -158,3 +162,16 @@ function disconnectedCleanup() moduleExec("onDestroyClientConnection", "Game"); } + +function clientCmdsetMoveMap(%movemap) +{ + if (!isObject(%movemap)) return; + if(isObject(ServerConnection) && isObject(ServerConnection.curMoveMap)) + ServerConnection.curMoveMap.pop(); + + // clear movement + $mvForwardAction = 0; + $mvBackwardAction = 0; + %movemap.push(); + ServerConnection.curMoveMap = %movemap; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript index b804ac046..0e2a11e28 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript @@ -275,7 +275,9 @@ function GameConnection::onPostSpawn( %this ) if (%this.numModsNeedingLoaded) callOnObjectList("onPostSpawn", %modulesIdList, %this); else - %this.listener.onPostSpawnComplete(%this); + %this.listener.onPostSpawnComplete(%this); + if (isObject(%this.player.getDatablock().controlMap)) + commandToClient(%this, 'setMoveMap', %this.player.getDatablock().controlMap); } function GameConnectionListener::onPostSpawnComplete(%this, %client) diff --git a/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript b/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript index 64b75e331..51a4566fa 100644 --- a/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript +++ b/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript @@ -78,17 +78,15 @@ function spawnGameObject(%name, %addToScene) return 0; } -function GameBaseData::onNewDataBlock(%this, %obj) +function GameBaseData::onNewDataBlock(%this, %obj, %reload) { - if (%obj.firstDataCheck) + if (%reload) { if(%this.isMethod("onRemove")) %this.onRemove(%obj); if(%this.isMethod("onAdd")) %this.onAdd(%obj); } - else - %obj.firstDataCheck = true; } function saveGameObject(%name, %tamlPath, %scriptPath) diff --git a/Templates/BaseGame/game/tools/tools.tscript b/Templates/BaseGame/game/tools/tools.tscript index 47101f55a..04ce56cad 100644 --- a/Templates/BaseGame/game/tools/tools.tscript +++ b/Templates/BaseGame/game/tools/tools.tscript @@ -23,4 +23,13 @@ function ToolsModule::onCreate(%this) function ToolsModule::onDestroy(%this) { +} + +function ToolsModule::disconnect(%this) +{ + if ( isObject( Editor ) && Editor.isEditorEnabled() ) + { + EditorGui.saveAs = false; //whatever edits we were doing are irrelevent now + Editor.close(MainMenuGui); + } } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript index ff5696dd5..9f0bc5b77 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript @@ -164,40 +164,3 @@ function toggleEditor(%make) //------------------------------------------------------------------------------ // The editor action maps are defined in editor.bind.tscript GlobalActionMap.bind(keyboard, "f11", fastLoadWorldEdit); - - -// The scenario: -// The editor is open and the user closes the level by any way other than -// the file menu ( exit level ), eg. typing disconnect() in the console. -// -// The problem: -// Editor::close() is not called in this scenario which means onEditorDisable -// is not called on objects which hook into it and also gEditingMission will no -// longer be valid. -// -// The solution: -// Override the stock disconnect() function which is in game scripts from here -// in tools so we avoid putting our code in there. -// -// Disclaimer: -// If you think of a better way to do this feel free. The thing which could -// be dangerous about this is that no one will ever realize this code overriding -// a fairly standard and core game script from a somewhat random location. -// If it 'did' have unforscene sideeffects who would ever find it? - -package EditorDisconnectOverride -{ - function disconnect() - { - if ( isObject( Editor ) && Editor.isEditorEnabled() ) - { - EditorGui.saveAs = false; //whatever edits we were doing are irrelevent now - %mainMenuGUI = ProjectSettings.value("UI/mainMenuName"); - if (isObject( %mainMenuGUI )) - Editor.close( %mainMenuGUI ); - } - - Parent::disconnect(); - } -}; -activatePackage( EditorDisconnectOverride );