Prechádzať zdrojové kódy

Merge branch 'development' of https://github.com/TorqueGameEngines/Torque3D into alpha41/DamageModel

# Conflicts:
#	Engine/source/T3D/vehicles/vehicle.cpp
AzaezelX 5 mesiacov pred
rodič
commit
be35c27411
64 zmenil súbory, kde vykonal 619 pridanie a 371 odobranie
  1. 7 1
      Engine/source/T3D/assets/ShapeAsset.h
  2. 1 1
      Engine/source/T3D/camera.cpp
  3. 1 1
      Engine/source/T3D/debris.cpp
  4. 4 1
      Engine/source/T3D/debris.h
  5. 5 0
      Engine/source/T3D/fps/guiHealthBarHud.cpp
  6. 6 1
      Engine/source/T3D/fps/guiHealthTextHud.cpp
  7. 1 1
      Engine/source/T3D/fx/explosion.cpp
  8. 4 1
      Engine/source/T3D/fx/explosion.h
  9. 4 1
      Engine/source/T3D/fx/groundCover.cpp
  10. 7 1
      Engine/source/T3D/fx/groundCover.h
  11. 1 1
      Engine/source/T3D/fx/lightning.cpp
  12. 4 1
      Engine/source/T3D/fx/particle.h
  13. 1 1
      Engine/source/T3D/fx/particleEmitter.cpp
  14. 1 1
      Engine/source/T3D/fx/particleEmitterNode.cpp
  15. 1 1
      Engine/source/T3D/fx/precipitation.cpp
  16. 8 2
      Engine/source/T3D/fx/precipitation.h
  17. 1 1
      Engine/source/T3D/fx/ribbonNode.cpp
  18. 1 1
      Engine/source/T3D/fx/splash.cpp
  19. 4 1
      Engine/source/T3D/fx/splash.h
  20. 3 3
      Engine/source/T3D/gameBase/gameBase.cpp
  21. 2 2
      Engine/source/T3D/gameBase/gameBase.h
  22. 1 1
      Engine/source/T3D/item.cpp
  23. 4 1
      Engine/source/T3D/lightFlareData.h
  24. 1 1
      Engine/source/T3D/missionMarker.cpp
  25. 1 1
      Engine/source/T3D/pathCamera.cpp
  26. 1 1
      Engine/source/T3D/pathShape.cpp
  27. 4 1
      Engine/source/T3D/physics/physicsDebris.h
  28. 4 1
      Engine/source/T3D/physics/physicsShape.h
  29. 5 2
      Engine/source/T3D/player.cpp
  30. 7 2
      Engine/source/T3D/player.h
  31. 31 6
      Engine/source/T3D/projectile.cpp
  32. 6 3
      Engine/source/T3D/projectile.h
  33. 1 1
      Engine/source/T3D/proximityMine.cpp
  34. 1 1
      Engine/source/T3D/rigidShape.cpp
  35. 11 1
      Engine/source/T3D/shapeBase.cpp
  36. 8 3
      Engine/source/T3D/shapeBase.h
  37. 1 1
      Engine/source/T3D/staticShape.cpp
  38. 1 1
      Engine/source/T3D/trigger.cpp
  39. 1 1
      Engine/source/T3D/turret/aiTurretShape.cpp
  40. 4 1
      Engine/source/T3D/turret/turretShape.cpp
  41. 1 0
      Engine/source/T3D/turret/turretShape.h
  42. 1 1
      Engine/source/T3D/vehicles/flyingVehicle.cpp
  43. 1 1
      Engine/source/T3D/vehicles/hoverVehicle.cpp
  44. 4 0
      Engine/source/T3D/vehicles/vehicle.cpp
  45. 1 0
      Engine/source/T3D/vehicles/vehicle.h
  46. 1 1
      Engine/source/T3D/vehicles/wheeledVehicle.cpp
  47. 4 1
      Engine/source/T3D/vehicles/wheeledVehicle.h
  48. 4 1
      Engine/source/afx/afxMagicMissile.h
  49. 1 1
      Engine/source/afx/afxSpellBook.cpp
  50. 4 1
      Engine/source/afx/ce/afxBillboard.h
  51. 4 1
      Engine/source/afx/ce/afxModel.h
  52. 4 4
      Engine/source/afx/ce/afxParticleEmitter.cpp
  53. 4 1
      Engine/source/afx/ce/afxZodiac.h
  54. 4 1
      Engine/source/afx/ce/afxZodiacPlane.h
  55. 63 0
      Engine/source/console/propertyParsing.h
  56. 16 13
      Engine/source/console/simDatablock.cpp
  57. 2 0
      Engine/source/console/simDatablock.h
  58. 4 1
      Engine/source/forest/forestItem.h
  59. 304 246
      Engine/source/math/mathTypes.cpp
  60. 18 1
      Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript
  61. 3 1
      Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript
  62. 2 4
      Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript
  63. 9 0
      Templates/BaseGame/game/tools/tools.tscript
  64. 0 37
      Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript

+ 7 - 1
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;\
    Resource<TSShape>m##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\
       {\

+ 1 - 1
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;
 }

+ 1 - 1
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;
 
 }

+ 4 - 1
Engine/source/T3D/debris.h

@@ -111,7 +111,10 @@ public:
    void onPerformSubstitutions() override;
    bool allowSubstitutions() const override { return true; }
 
-   void onShapeChanged() {}
+   void onShapeChanged()
+   {
+      reloadOnLocalClient();
+   }
 };
 
 //**************************************************************************

+ 5 - 0
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<ShapeBase*>(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;
 

+ 6 - 1
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<ShapeBase*>(conn->getControlObject());  
+   ShapeBase* control = dynamic_cast<ShapeBase*>(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;  
   

+ 1 - 1
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;
 }
 

+ 4 - 1
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();
+   }
 };
 
 

+ 4 - 1
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();
+      }
    }
 }
 

+ 7 - 1
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_

+ 1 - 1
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;
 }
 

+ 4 - 1
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();

+ 1 - 1
Engine/source/T3D/fx/particleEmitter.cpp

@@ -1131,7 +1131,7 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload )
      return true;
    }
 
-   scriptOnNewDataBlock();
+   scriptOnNewDataBlock(reload);
    return true;
 }
 

+ 1 - 1
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;
 }
 

+ 1 - 1
Engine/source/T3D/fx/precipitation.cpp

@@ -614,7 +614,7 @@ bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload )
       initMaterials();
    }
 
-   scriptOnNewDataBlock();
+   scriptOnNewDataBlock(reload);
    return true;
 }
 

+ 8 - 2
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

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }
 

+ 4 - 1
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;

+ 3 - 3
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()

+ 2 - 2
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

+ 1 - 1
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();

+ 4 - 1
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:
    

+ 1 - 1
Engine/source/T3D/missionMarker.cpp

@@ -142,7 +142,7 @@ bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload )
    mDataBlock = dynamic_cast<MissionMarkerData*>( dptr );
    if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
       return(false);
-   scriptOnNewDataBlock();
+   scriptOnNewDataBlock(reload);
    return(true);
 }
 

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }
 

+ 4 - 1
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 );
 

+ 4 - 1
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;

+ 5 - 2
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;
 }
 

+ 7 - 2
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 ) );

+ 31 - 6
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<RigidShape*>(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;
       }
    }
 

+ 6 - 3
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

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }

+ 11 - 1
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();
+}
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 

+ 8 - 3
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<TextureTagRemapping> txr_tag_remappings;
    bool silent_bbox_check;
 
-   void onShapeChanged() {}
-   void onDebrisChanged() {}
+   void onShapeChanged();
+   void onDebrisChanged();
 public:
    ShapeBaseData(const ShapeBaseData&, bool = false);
 };

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }
 

+ 4 - 1
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;

+ 1 - 0
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();

+ 1 - 1
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;
 }
 

+ 1 - 1
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;
 }

+ 4 - 0
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]);
    }

+ 1 - 0
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;
 
    //

+ 1 - 1
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;
 }
 

+ 4 - 1
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();
+   }
 };
 
 

+ 4 - 1
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:

+ 1 - 1
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;
 }

+ 4 - 1
Engine/source/afx/ce/afxBillboard.h

@@ -71,7 +71,10 @@ public:
 
   static void       initPersistFields();
 
-  void onChangeTexture() {}
+  void onChangeTexture()
+  {
+     reloadOnLocalClient();
+  }
 
   DECLARE_CONOBJECT(afxBillboardData);
 };

+ 4 - 1
Engine/source/afx/ce/afxModel.h

@@ -94,7 +94,10 @@ public:
 
   static void           initPersistFields();
 
-  void onShapeChanged() {}
+  void onShapeChanged()
+  {
+     reloadOnLocalClient();
+  }
   void onSequenceChanged() {}
 
   DECLARE_CONOBJECT(afxModelData);

+ 4 - 4
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;
 }
 

+ 4 - 1
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);

+ 4 - 1
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);

+ 63 - 0
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<typename T, size_t count>
+   char* FormatPropertyBuffer(const void* dataPtr, char* buffer, U32 bufSize)
+   {
+      const T* values = reinterpret_cast<const T*>(dataPtr);
+      char* ptr = buffer;
+
+      for (size_t i = 0; i < count; ++i)
+      {
+         S32 written = 0;
+
+         if constexpr (std::is_same_v<T, int> || std::is_same_v<T, S32>)
+            written = dSprintf(ptr, bufSize - (ptr - buffer), "%d", values[i]);
+         else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, F32>)
+            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<typename T, size_t count>
+   const char* FormatProperty(const void* dataPtr)
+   {
+      static const U32 bufSize = 256;
+      char* buffer = Con::getReturnBuffer(bufSize);
+      FormatPropertyBuffer<T,count>(dataPtr, buffer, bufSize);
+      return buffer;
+   }
+
+   template<typename T, size_t count>
+   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<T, int> || std::is_same_v<T, S32>)
+         {
+            out[index++] = mRound(dAtof(tok));
+         }
+         else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, F32>)
+         {
+            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

+ 16 - 13
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();
 }
 
 //-----------------------------------------------------------------------------

+ 2 - 0
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
    {

+ 4 - 1
Engine/source/forest/forestItem.h

@@ -143,7 +143,10 @@ public:
       return theSignal;
    }
 
-   void onShapeChanged() {}
+   void onShapeChanged()
+   {
+      reloadOnLocalClient();
+   }
 };
 
 typedef Vector<ForestItemData*> ForestItemDataVector;

+ 304 - 246
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<S32, 2>(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<S32, 2>(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<F32, 2>(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<F32, 2>(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<S32, 3>(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<S32, 3>(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<F32, 3>(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<F32, 3>(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<F32, 4>(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<F32, 4>(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<S32, 4>(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<S32, 4>(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<F32, 4>(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<F32, 4>(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<F32, 3>(col0, buffer, bufSize);
+   *buffer++ = ' ';
+   PropertyInfo::FormatPropertyBuffer<F32, 3>(col1, buffer, bufSize);
+   *buffer++ = ' ';
+   PropertyInfo::FormatPropertyBuffer<F32, 3>(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;
+      F32 parsed[9];
+
+      char* buffer = new char[dStrlen(argv[0])];
+      dStrcpy(buffer, argv[0], sizeof(buffer));
+
+      if (PropertyInfo::ParseProperty<F32, 9>(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;
+      }
    }
-   
-   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 );
 
-   MatrixF* mat = ( MatrixF* ) dptr;
-   
-   mat->setColumn( 0, col0 );
-   mat->setColumn( 1, col1 );
-   mat->setColumn( 2, col2 );
+   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<F32, 3>(&pos, returnBuffer, bufSize);
    else
-      dSprintf(returnBuffer, bufSize, "%g %g %g %g", col[0], col[4], col[8], col[12]);
+      PropertyInfo::FormatPropertyBuffer<F32, 4>(&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]);
-   }
-   else if (argc <= 4) 
-   {
-      for (S32 i = 0; i < argc; i++)
-         col[i << 2] = dAtof(argv[i]);
+      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<F32, 4>(buffer, parsed);
+      {
+         Point4F temp(parsed[0], parsed[1], parsed[2], parsed[3]);
+         MatrixF* mat = (MatrixF*)dptr;
+         mat->setColumn(3, temp);
+         return;
+      }
    }
-   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<F32, 4>(&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)
-   {
-      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) 
+   if (argc >= 1)
    {
-         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<F32, 4>(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];
+         return;
+      }
+   }
 
-   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];
+   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<F32, 4>(aa);
+   return buff;
 }
 
 ConsoleSetType( TypeAngAxisF )
 {
-   // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix.
-   //
-   AngAxisF* aa = ( AngAxisF* ) dptr;
-   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) 
+   if (argc >= 1)
    {
-      for (S32 i = 0; i < argc; i++)
-         ((F32*)&aa)[i] = dAtof(argv[i]);
-      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<F32, 4>(buffer, parsed))
+      {
+         AngAxisF* aa = (AngAxisF*)dptr;
+         aa->set(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3]));
+         return;
+      }
    }
-   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<F32, 7>(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 );
-   }
-   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 ] );
+      if (PropertyInfo::ParseProperty<F32, 7>(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
-      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<F32, 6>(dptr);
+   return buff;
 }
 
 ConsoleSetType( TypeBox3F )
 {
-   Box3F* pDst = (Box3F*)dptr;
-
-   if (argc == 1) 
+   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\"");
+      F32 parsed[6];
+      // Combine argv into a single space-separated string if argc > 1
+      char buffer[256] = { 0 };
+      dStrncpy(buffer, *argv, sizeof(buffer));
+
+      if (PropertyInfo::ParseProperty<F32, 6>(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<S32, 2>(pEase + 0, buffer, bufSize);
+   *buffer++ = ' ';
+   PropertyInfo::FormatPropertyBuffer<F32, 2>(pEase + 2, buffer, bufSize);
+
+   return buffer;
 }
 
 ConsoleSetType( TypeEaseF )
 {
-   EaseF* pDst = (EaseF*)dptr;
-
-   // 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]\"");
+   if (argc >= 1)
+   {
+      F32 parsed[4];
+      parsed[2] = -1.0f;
+      parsed[3] = -1.0f;
+
+      // 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<F32, 4>(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<F32, 3>(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<F32, 4>(&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<F32, 3>(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<F32, 4>(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\"");
 }
 
 //-----------------------------------------------------------------------------

+ 18 - 1
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;
+}

+ 3 - 1
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)

+ 2 - 4
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)

+ 9 - 0
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);
+   }
 }

+ 0 - 37
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 );