mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 00:24:40 +00:00
Merge remote-tracking branch 'upstream/development' into imageAsset_refactor_rev3
This commit is contained in:
commit
e1c01cd49a
152 changed files with 7312 additions and 4132 deletions
|
|
@ -58,7 +58,7 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async"
|
||||||
|
|
||||||
torqueAddSourceDirectories("platform/nativeDialogs")
|
torqueAddSourceDirectories("platform/nativeDialogs")
|
||||||
# Handle T3D
|
# Handle T3D
|
||||||
torqueAddSourceDirectories( "T3D" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx"
|
torqueAddSourceDirectories( "T3D" "T3D/AI" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx"
|
||||||
"T3D/gameBase" "T3D/gameBase/std"
|
"T3D/gameBase" "T3D/gameBase/std"
|
||||||
"T3D/lighting"
|
"T3D/lighting"
|
||||||
"T3D/physics"
|
"T3D/physics"
|
||||||
|
|
|
||||||
226
Engine/source/T3D/AI/AIAimTarget.cpp
Normal file
226
Engine/source/T3D/AI/AIAimTarget.cpp
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "AIAimTarget.h"
|
||||||
|
#include "AIController.h"
|
||||||
|
|
||||||
|
static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
|
||||||
|
|
||||||
|
F32 AIAimTarget::getTargetDistance(SceneObject* target, bool _checkEnabled)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = mObj.getPointer();
|
||||||
|
if (!target)
|
||||||
|
return F32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_checkEnabled)
|
||||||
|
{
|
||||||
|
if (target->getTypeMask() & ShapeBaseObjectType)
|
||||||
|
{
|
||||||
|
ShapeBase* shapeBaseCheck = static_cast<ShapeBase*>(target);
|
||||||
|
if (shapeBaseCheck)
|
||||||
|
if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return F32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (getPosition() - target->getPosition()).len();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIAimTarget::checkInLos(SceneObject* target, bool _useMuzzle, bool _checkEnabled)
|
||||||
|
{
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(getCtrl()->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = dynamic_cast<ShapeBase*>(mObj.getPointer());
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_checkEnabled)
|
||||||
|
{
|
||||||
|
if (target->getTypeMask() & ShapeBaseObjectType)
|
||||||
|
{
|
||||||
|
ShapeBase* shapeBaseCheck = static_cast<ShapeBase*>(target);
|
||||||
|
if (shapeBaseCheck)
|
||||||
|
if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RayInfo ri;
|
||||||
|
|
||||||
|
sbo->disableCollision();
|
||||||
|
|
||||||
|
S32 mountCount = target->getMountedObjectCount();
|
||||||
|
for (S32 i = 0; i < mountCount; i++)
|
||||||
|
{
|
||||||
|
target->getMountedObject(i)->disableCollision();
|
||||||
|
}
|
||||||
|
|
||||||
|
Point3F checkPoint;
|
||||||
|
if (_useMuzzle)
|
||||||
|
sbo->getMuzzlePoint(0, &checkPoint);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MatrixF eyeMat;
|
||||||
|
sbo->getEyeTransform(&eyeMat);
|
||||||
|
eyeMat.getColumn(3, &checkPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAILoSMask, &ri);
|
||||||
|
sbo->enableCollision();
|
||||||
|
|
||||||
|
for (S32 i = 0; i < mountCount; i++)
|
||||||
|
{
|
||||||
|
target->getMountedObject(i)->enableCollision();
|
||||||
|
}
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIAimTarget::checkInFoV(SceneObject* target, F32 camFov, bool _checkEnabled)
|
||||||
|
{
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(getCtrl()->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = dynamic_cast<ShapeBase*>(mObj.getPointer());
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_checkEnabled)
|
||||||
|
{
|
||||||
|
if (target->getTypeMask() & ShapeBaseObjectType)
|
||||||
|
{
|
||||||
|
ShapeBase* shapeBaseCheck = static_cast<ShapeBase*>(target);
|
||||||
|
if (shapeBaseCheck)
|
||||||
|
if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixF cam = sbo->getTransform();
|
||||||
|
Point3F camPos;
|
||||||
|
VectorF camDir;
|
||||||
|
|
||||||
|
cam.getColumn(3, &camPos);
|
||||||
|
cam.getColumn(1, &camDir);
|
||||||
|
|
||||||
|
camFov = mDegToRad(camFov) / 2;
|
||||||
|
|
||||||
|
Point3F shapePos = target->getBoxCenter();
|
||||||
|
VectorF shapeDir = shapePos - camPos;
|
||||||
|
// Test to see if it's within our viewcone, this test doesn't
|
||||||
|
// actually match the viewport very well, should consider
|
||||||
|
// projection and box test.
|
||||||
|
shapeDir.normalize();
|
||||||
|
F32 dot = mDot(shapeDir, camDir);
|
||||||
|
return (dot > mCos(camFov));
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setAimLocation, void, (Point3F target), ,
|
||||||
|
"@brief Tells the AIPlayer to aim at the location provided.\n\n"
|
||||||
|
|
||||||
|
"@param target An \"x y z\" position in the game world to target.\n\n"
|
||||||
|
|
||||||
|
"@see getAimLocation()\n")
|
||||||
|
{
|
||||||
|
object->setAim(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getAimLocation, Point3F, (), ,
|
||||||
|
"@brief Returns the point the AIPlayer is aiming at.\n\n"
|
||||||
|
|
||||||
|
"This will reflect the position set by setAimLocation(), "
|
||||||
|
"or the position of the object that the bot is now aiming at. "
|
||||||
|
"If the bot is not aiming at anything, this value will "
|
||||||
|
"change to whatever point the bot's current line-of-sight intercepts."
|
||||||
|
|
||||||
|
"@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n"
|
||||||
|
|
||||||
|
"@see setAimLocation()\n"
|
||||||
|
"@see setAimObject()\n")
|
||||||
|
{
|
||||||
|
return object->getAim()->getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setAimObject, void, (const char* objName, Point3F offset), (Point3F::Zero), "( GameBase obj, [Point3F offset] )"
|
||||||
|
"Sets the bot's target object. Optionally set an offset from target location."
|
||||||
|
"@hide")
|
||||||
|
{
|
||||||
|
// Find the target
|
||||||
|
SceneObject* targetObject;
|
||||||
|
if (Sim::findObject(objName, targetObject))
|
||||||
|
{
|
||||||
|
|
||||||
|
object->setAim(targetObject, 0.0f, offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
object->setAim(0, 0.0f, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, clearAim, void, (), , "clears the bot's target.")
|
||||||
|
{
|
||||||
|
object->clearAim();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getAimObject, S32, (), ,
|
||||||
|
"@brief Gets the object the AIPlayer is targeting.\n\n"
|
||||||
|
|
||||||
|
"@return Returns -1 if no object is being aimed at, "
|
||||||
|
"or the SimObjectID of the object the AIPlayer is aiming at.\n\n"
|
||||||
|
|
||||||
|
"@see setAimObject()\n")
|
||||||
|
{
|
||||||
|
SceneObject* obj = dynamic_cast<GameBase*>(object->getAim()->mObj.getPointer());
|
||||||
|
return obj ? obj->getId() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getTargetDistance, F32, (SceneObject* obj, bool checkEnabled), (nullAsType<SceneObject*>(), false),
|
||||||
|
"@brief The distance to a given target.\n"
|
||||||
|
"@obj Object to check. (If blank, it will check the current target).\n"
|
||||||
|
"@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n")
|
||||||
|
{
|
||||||
|
return object->getAim()->getTargetDistance(obj, checkEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, checkInLos, bool, (SceneObject* obj, bool useMuzzle, bool checkEnabled), (nullAsType<ShapeBase*>(), false, false),
|
||||||
|
"@brief Check whether an object is in line of sight.\n"
|
||||||
|
"@obj Object to check. (If blank, it will check the current target).\n"
|
||||||
|
"@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n"
|
||||||
|
"@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n")
|
||||||
|
{
|
||||||
|
return object->getAim()->checkInLos(obj, useMuzzle, checkEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, checkInFoV, bool, (SceneObject* obj, F32 fov, bool checkEnabled), (nullAsType<ShapeBase*>(), 45.0f, false),
|
||||||
|
"@brief Check whether an object is within a specified veiw cone.\n"
|
||||||
|
"@obj Object to check. (If blank, it will check the current target).\n"
|
||||||
|
"@fov view angle in degrees.(Defaults to 45)\n"
|
||||||
|
"@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n")
|
||||||
|
{
|
||||||
|
return object->getAim()->checkInFoV(obj, fov, checkEnabled);
|
||||||
|
}
|
||||||
41
Engine/source/T3D/AI/AIAimTarget.h
Normal file
41
Engine/source/T3D/AI/AIAimTarget.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AIAIMTARGET_H_
|
||||||
|
#define _AIAIMTARGET_H_
|
||||||
|
|
||||||
|
#include "AIInfo.h"
|
||||||
|
struct AIAimTarget : public AIInfo
|
||||||
|
{
|
||||||
|
typedef AIInfo Parent;
|
||||||
|
Point3F mAimOffset;
|
||||||
|
bool mTargetInLOS; // Is target object visible?
|
||||||
|
Point3F getPosition() { return ((mObj.isValid()) ? mObj->getPosition() : mPosition) + mAimOffset; }
|
||||||
|
bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false);
|
||||||
|
bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false);
|
||||||
|
F32 getTargetDistance(SceneObject* target, bool _checkEnabled);
|
||||||
|
AIAimTarget() = delete;
|
||||||
|
AIAimTarget(AIController* controller) : Parent(controller) { mTargetInLOS = false; };
|
||||||
|
AIAimTarget(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) { mTargetInLOS = false; };
|
||||||
|
AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mTargetInLOS = false; };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
916
Engine/source/T3D/AI/AIController.cpp
Normal file
916
Engine/source/T3D/AI/AIController.cpp
Normal file
|
|
@ -0,0 +1,916 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "AIController.h"
|
||||||
|
#include "T3D/player.h"
|
||||||
|
#include "T3D/rigidShape.h"
|
||||||
|
#include "T3D/vehicles/wheeledVehicle.h"
|
||||||
|
#include "T3D/vehicles/flyingVehicle.h"
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(AIController);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void AIController::throwCallback(const char* name)
|
||||||
|
{
|
||||||
|
//Con::warnf("throwCallback: %s", name);
|
||||||
|
Con::executef(mControllerData, name, getIdString()); //controller data callbacks
|
||||||
|
|
||||||
|
GameBase* gbo = dynamic_cast<GameBase*>(getAIInfo()->mObj.getPointer());
|
||||||
|
if (!gbo) return;
|
||||||
|
Con::executef(gbo->getDataBlock(), name, getAIInfo()->mObj->getIdString()); //legacy support for object db callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::initPersistFields()
|
||||||
|
{
|
||||||
|
addProtectedField("ControllerData", TYPEID< AIControllerData >(), Offset(mControllerData, AIController),
|
||||||
|
&setControllerDataProperty, &defaultProtectedGetFn,
|
||||||
|
"Script datablock used for game objects.");
|
||||||
|
addFieldV("MoveSpeed", TypeRangedF32, Offset(mMovement.mMoveSpeed, AIController), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief default move sepeed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIController::setControllerDataProperty(void* obj, const char* index, const char* db)
|
||||||
|
{
|
||||||
|
if (db == NULL || !db[0])
|
||||||
|
{
|
||||||
|
Con::errorf("AIController::setControllerDataProperty - Can't unset ControllerData on AIController objects");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AIController* object = static_cast<AIController*>(obj);
|
||||||
|
AIControllerData* data;
|
||||||
|
if (Sim::findObject(db, data))
|
||||||
|
{
|
||||||
|
object->mControllerData = data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Con::errorf("AIController::setControllerDataProperty - Could not find ControllerData \"%s\"", db);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::setGoal(AIInfo* targ)
|
||||||
|
{
|
||||||
|
if (mGoal) { delete(mGoal); mGoal = NULL; }
|
||||||
|
|
||||||
|
if (targ->mObj.isValid())
|
||||||
|
{
|
||||||
|
delete(mGoal);
|
||||||
|
mGoal = new AIGoal(this, targ->mObj, targ->mRadius);
|
||||||
|
}
|
||||||
|
else if (targ->mPosSet)
|
||||||
|
{
|
||||||
|
delete(mGoal);
|
||||||
|
mGoal = new AIGoal(this, targ->mPosition, targ->mRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::setGoal(Point3F loc, F32 rad)
|
||||||
|
{
|
||||||
|
if (mGoal) delete(mGoal);
|
||||||
|
mGoal = new AIGoal(this, loc, rad);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::setGoal(SimObjectPtr<SceneObject> objIn, F32 rad)
|
||||||
|
{
|
||||||
|
if (mGoal) delete(mGoal);
|
||||||
|
mGoal = new AIGoal(this, objIn, rad);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::setAim(Point3F loc, F32 rad, Point3F offset)
|
||||||
|
{
|
||||||
|
if (mAimTarget) delete(mAimTarget);
|
||||||
|
mAimTarget = new AIAimTarget(this, loc, rad);
|
||||||
|
mAimTarget->mAimOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::setAim(SimObjectPtr<SceneObject> objIn, F32 rad, Point3F offset)
|
||||||
|
{
|
||||||
|
if (mAimTarget) delete(mAimTarget);
|
||||||
|
mAimTarget = new AIAimTarget(this, objIn, rad);
|
||||||
|
mAimTarget->mAimOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIController::getAIMove(Move* movePtr)
|
||||||
|
{
|
||||||
|
*movePtr = NullMove;
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(getAIInfo()->mObj.getPointer());
|
||||||
|
if (!sbo) return false;
|
||||||
|
|
||||||
|
// Use the eye as the current position.
|
||||||
|
MatrixF eye;
|
||||||
|
sbo->getEyeTransform(&eye);
|
||||||
|
Point3F location = eye.getPosition();
|
||||||
|
Point3F rotation = sbo->getTransform().toEuler();
|
||||||
|
|
||||||
|
// Test for target location in sight if it's an object. The LOS is
|
||||||
|
// run from the eye position to the center of the object's bounding,
|
||||||
|
// which is not very accurate.
|
||||||
|
if (getAim() && getAim()->mObj)
|
||||||
|
{
|
||||||
|
GameBase* gbo = dynamic_cast<GameBase*>(getAIInfo()->mObj.getPointer());
|
||||||
|
if (getAim()->checkInLos(gbo))
|
||||||
|
{
|
||||||
|
if (!getAim()->mTargetInLOS)
|
||||||
|
{
|
||||||
|
throwCallback("onTargetEnterLOS");
|
||||||
|
getAim()->mTargetInLOS = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (getAim()->mTargetInLOS)
|
||||||
|
{
|
||||||
|
throwCallback("onTargetExitLOS");
|
||||||
|
getAim()->mTargetInLOS = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sbo->getDamageState() == ShapeBase::Enabled && getGoal())
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (mMovement.mMoveState != ModeStop)
|
||||||
|
getNav()->updateNavMesh();
|
||||||
|
|
||||||
|
if (getNav()->mPathData.path.isNull())
|
||||||
|
{
|
||||||
|
if (getGoal()->getDist() > mControllerData->mFollowTolerance)
|
||||||
|
{
|
||||||
|
if (getGoal()->mObj.isValid())
|
||||||
|
getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance);
|
||||||
|
else if (getGoal()->mPosSet)
|
||||||
|
getNav()->setPathDestination(getGoal()->getPosition(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (getGoal()->getDist() > mControllerData->mFollowTolerance)
|
||||||
|
{
|
||||||
|
SceneObject* obj = getAIInfo()->mObj->getObjectMount();
|
||||||
|
if (!obj)
|
||||||
|
{
|
||||||
|
obj = getAIInfo()->mObj;
|
||||||
|
}
|
||||||
|
RayInfo info;
|
||||||
|
if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, mControllerData->mHeightTolerance), StaticShapeObjectType, &info))
|
||||||
|
{
|
||||||
|
getNav()->repath();
|
||||||
|
}
|
||||||
|
getGoal()->mInRange = false;
|
||||||
|
}
|
||||||
|
if (getGoal()->getDist() < mControllerData->mFollowTolerance )
|
||||||
|
{
|
||||||
|
getNav()->clearPath();
|
||||||
|
mMovement.mMoveState = ModeStop;
|
||||||
|
|
||||||
|
if (!getGoal()->mInRange)
|
||||||
|
{
|
||||||
|
getGoal()->mInRange = true;
|
||||||
|
throwCallback("onTargetInRange");
|
||||||
|
}
|
||||||
|
else getGoal()->mInRange = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (getGoal()->getDist() < mControllerData->mAttackRadius )
|
||||||
|
{
|
||||||
|
if (!getGoal()->mInFiringRange)
|
||||||
|
{
|
||||||
|
getGoal()->mInFiringRange = true;
|
||||||
|
throwCallback("onTargetInFiringRange");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else getGoal()->mInFiringRange = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (getGoal()->getDist() > mControllerData->mFollowTolerance)
|
||||||
|
{
|
||||||
|
if (getGoal()->mObj.isValid())
|
||||||
|
getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance);
|
||||||
|
else if (getGoal()->mPosSet)
|
||||||
|
getNav()->setPathDestination(getGoal()->getPosition(true));
|
||||||
|
|
||||||
|
getGoal()->mInRange = false;
|
||||||
|
}
|
||||||
|
if (getGoal()->getDist() < mControllerData->mFollowTolerance)
|
||||||
|
{
|
||||||
|
mMovement.mMoveState = ModeStop;
|
||||||
|
|
||||||
|
if (!getGoal()->mInRange)
|
||||||
|
{
|
||||||
|
getGoal()->mInRange = true;
|
||||||
|
throwCallback("onTargetInRange");
|
||||||
|
}
|
||||||
|
else getGoal()->mInRange = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (getGoal()->getDist() < mControllerData->mAttackRadius)
|
||||||
|
{
|
||||||
|
if (!getGoal()->mInFiringRange)
|
||||||
|
{
|
||||||
|
getGoal()->mInFiringRange = true;
|
||||||
|
throwCallback("onTargetInFiringRange");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else getGoal()->mInFiringRange = false;
|
||||||
|
}
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
}
|
||||||
|
// Orient towards the aim point, aim object, or towards
|
||||||
|
// our destination.
|
||||||
|
if (getAim() || mMovement.mMoveState != ModeStop)
|
||||||
|
{
|
||||||
|
// Update the aim position if we're aiming for an object or explicit position
|
||||||
|
if (getAim())
|
||||||
|
mMovement.mAimLocation = getAim()->getPosition();
|
||||||
|
else
|
||||||
|
mMovement.mAimLocation = getNav()->getMoveDestination();
|
||||||
|
|
||||||
|
mControllerData->resolveYawPtr(this, location, movePtr);
|
||||||
|
mControllerData->resolvePitchPtr(this, location, movePtr);
|
||||||
|
mControllerData->resolveRollPtr(this, location, movePtr);
|
||||||
|
|
||||||
|
if (mMovement.mMoveState != AIController::ModeStop)
|
||||||
|
{
|
||||||
|
F32 xDiff = getNav()->getMoveDestination().x - location.x;
|
||||||
|
F32 yDiff = getNav()->getMoveDestination().y - location.y;
|
||||||
|
if (mFabs(xDiff) < mControllerData->mMoveTolerance && mFabs(yDiff) < mControllerData->mMoveTolerance)
|
||||||
|
{
|
||||||
|
getNav()->onReachDestination();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mControllerData->resolveSpeedPtr(this, location, movePtr);
|
||||||
|
mControllerData->resolveStuckPtr(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mControllerData->resolveTriggerStatePtr(this, movePtr);
|
||||||
|
|
||||||
|
getAIInfo()->mLastPos = getAIInfo()->getPosition();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::clearCover()
|
||||||
|
{
|
||||||
|
// Notify cover that we are no longer on our way.
|
||||||
|
if (getCover() && !getCover()->mCoverPoint.isNull())
|
||||||
|
getCover()->mCoverPoint->setOccupied(false);
|
||||||
|
SAFE_DELETE(mCover);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIController::Movement::stopMove()
|
||||||
|
{
|
||||||
|
mMoveState = ModeStop;
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
getCtrl()->getNav()->clearPath();
|
||||||
|
getCtrl()->clearCover();
|
||||||
|
getCtrl()->getNav()->clearFollow();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void AIController::Movement::onStuck()
|
||||||
|
{
|
||||||
|
mMoveState = AIController::ModeStuck;
|
||||||
|
getCtrl()->throwCallback("onMoveStuck");
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (!getCtrl()->getNav()->getPath().isNull())
|
||||||
|
getCtrl()->getNav()->repath();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), ,
|
||||||
|
"@brief Sets the move speed for an AI object.\n\n"
|
||||||
|
|
||||||
|
"@param speed A speed multiplier between 0.0 and 1.0. "
|
||||||
|
"This is multiplied by the AIController controlled object's base movement rates (as defined in "
|
||||||
|
"its PlayerData datablock)\n\n"
|
||||||
|
|
||||||
|
"@see getMoveDestination()\n")
|
||||||
|
{
|
||||||
|
object->mMovement.setMoveSpeed(speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getMoveSpeed, F32, (), ,
|
||||||
|
"@brief Gets the move speed of an AI object.\n\n"
|
||||||
|
|
||||||
|
"@return A speed multiplier between 0.0 and 1.0.\n\n"
|
||||||
|
|
||||||
|
"@see setMoveSpeed()\n")
|
||||||
|
{
|
||||||
|
return object->mMovement.getMoveSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, stop, void, (), ,
|
||||||
|
"@brief Tells the AIController controlled object to stop moving.\n\n")
|
||||||
|
{
|
||||||
|
object->mMovement.stopMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of a movement trigger.
|
||||||
|
*
|
||||||
|
* @param slot The trigger slot to set
|
||||||
|
* @param isSet set/unset the trigger
|
||||||
|
*/
|
||||||
|
void AIController::TriggerState::setMoveTrigger(U32 slot, const bool isSet)
|
||||||
|
{
|
||||||
|
if (slot >= MaxTriggerKeys)
|
||||||
|
{
|
||||||
|
Con::errorf("Attempting to set an invalid trigger slot (%i)", slot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mMoveTriggers[slot] = isSet; // set the trigger
|
||||||
|
mControllerRef->getAIInfo()->mObj->setMaskBits(ShapeBase::NoWarpMask); // force the client to updateMove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the state of a movement trigger.
|
||||||
|
*
|
||||||
|
* @param slot The trigger slot to query
|
||||||
|
* @return True if the trigger is set, false if it is not set
|
||||||
|
*/
|
||||||
|
bool AIController::TriggerState::getMoveTrigger(U32 slot) const
|
||||||
|
{
|
||||||
|
if (slot >= MaxTriggerKeys)
|
||||||
|
{
|
||||||
|
Con::errorf("Attempting to get an invalid trigger slot (%i)", slot);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return mMoveTriggers[slot];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the trigger state for all movement triggers.
|
||||||
|
*/
|
||||||
|
void AIController::TriggerState::clearMoveTriggers()
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < MaxTriggerKeys; i++)
|
||||||
|
setMoveTrigger(i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CO_DATABLOCK_V1(AIControllerData);
|
||||||
|
void AIControllerData::resolveYaw(AIController* obj, Point3F location, Move* move)
|
||||||
|
{
|
||||||
|
F32 xDiff = obj->mMovement.mAimLocation.x - location.x;
|
||||||
|
F32 yDiff = obj->mMovement.mAimLocation.y - location.y;
|
||||||
|
Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler();
|
||||||
|
|
||||||
|
if (!mIsZero(xDiff) || !mIsZero(yDiff))
|
||||||
|
{
|
||||||
|
// First do Yaw
|
||||||
|
// use the cur yaw between -Pi and Pi
|
||||||
|
F32 curYaw = rotation.z;
|
||||||
|
while (curYaw > M_2PI_F)
|
||||||
|
curYaw -= M_2PI_F;
|
||||||
|
while (curYaw < -M_2PI_F)
|
||||||
|
curYaw += M_2PI_F;
|
||||||
|
|
||||||
|
// find the yaw offset
|
||||||
|
F32 newYaw = mAtan2(xDiff, yDiff);
|
||||||
|
F32 yawDiff = newYaw - curYaw;
|
||||||
|
|
||||||
|
// make it between 0 and 2PI
|
||||||
|
if (yawDiff < 0.0f)
|
||||||
|
yawDiff += M_2PI_F;
|
||||||
|
else if (yawDiff >= M_2PI_F)
|
||||||
|
yawDiff -= M_2PI_F;
|
||||||
|
|
||||||
|
// now make sure we take the short way around the circle
|
||||||
|
if (yawDiff > M_PI_F)
|
||||||
|
yawDiff -= M_2PI_F;
|
||||||
|
else if (yawDiff < -M_PI_F)
|
||||||
|
yawDiff += M_2PI_F;
|
||||||
|
|
||||||
|
move->yaw = yawDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
F32 xDiff = obj->getNav()->getMoveDestination().x - location.x;
|
||||||
|
F32 yDiff = obj->getNav()->getMoveDestination().y - location.y;
|
||||||
|
Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler();
|
||||||
|
|
||||||
|
// Build move direction in world space
|
||||||
|
if (mIsZero(xDiff))
|
||||||
|
movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mIsZero(yDiff))
|
||||||
|
movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mFabs(xDiff) > mFabs(yDiff))
|
||||||
|
{
|
||||||
|
F32 value = mFabs(yDiff / xDiff);
|
||||||
|
movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value;
|
||||||
|
movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
F32 value = mFabs(xDiff / yDiff);
|
||||||
|
movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value;
|
||||||
|
movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rotate the move into object space (this really only needs
|
||||||
|
// a 2D matrix)
|
||||||
|
Point3F newMove;
|
||||||
|
MatrixF moveMatrix;
|
||||||
|
moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw)));
|
||||||
|
moveMatrix.mulV(Point3F(movePtr->x, movePtr->y, 0.0f), &newMove);
|
||||||
|
movePtr->x = newMove.x;
|
||||||
|
movePtr->y = newMove.y;
|
||||||
|
|
||||||
|
// Set movement speed. We'll slow down once we get close
|
||||||
|
// to try and stop on the spot...
|
||||||
|
if (obj->mMovement.mMoveSlowdown)
|
||||||
|
{
|
||||||
|
F32 speed = obj->mMovement.mMoveSpeed;
|
||||||
|
F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff);
|
||||||
|
F32 maxDist = mMoveTolerance * 2;
|
||||||
|
if (dist < maxDist)
|
||||||
|
speed *= dist / maxDist;
|
||||||
|
movePtr->x *= speed;
|
||||||
|
movePtr->y *= speed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
movePtr->x *= obj->mMovement.mMoveSpeed;
|
||||||
|
movePtr->y *= obj->mMovement.mMoveSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr)
|
||||||
|
{
|
||||||
|
//check for scripted overides
|
||||||
|
for (U32 slot = 0; slot < MaxTriggerKeys; slot++)
|
||||||
|
{
|
||||||
|
movePtr->trigger[slot] = obj->mTriggerState.mMoveTriggers[slot];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIControllerData::resolveStuck(AIController* obj)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
if (!obj->getGoal()) return;
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
// Don't check for ai stuckness if animation during
|
||||||
|
// an anim-clip effect override.
|
||||||
|
if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked())
|
||||||
|
{
|
||||||
|
// We should check to see if we are stuck...
|
||||||
|
F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len();
|
||||||
|
if (locationDelta < mMoveStuckTolerance)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveStuckTestCountdown > 0)
|
||||||
|
--obj->mMovement.mMoveStuckTestCountdown;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we are slowing down, then it's likely that our location delta will be less than
|
||||||
|
// our move stuck tolerance. Because we can be both slowing and stuck
|
||||||
|
// we should TRY to check if we've moved. This could use better detection.
|
||||||
|
if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0)
|
||||||
|
{
|
||||||
|
obj->mMovement.onStuck();
|
||||||
|
obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AIControllerData::AIControllerData()
|
||||||
|
{
|
||||||
|
mMoveTolerance = 0.25f;
|
||||||
|
mAttackRadius = 2.0f;
|
||||||
|
mMoveStuckTolerance = 0.01f;
|
||||||
|
mMoveStuckTestDelay = 30;
|
||||||
|
mHeightTolerance = 0.001f;
|
||||||
|
mFollowTolerance = 1.0f;
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
mLinkTypes = LinkData(AllFlags);
|
||||||
|
mNavSize = AINavigation::Regular;
|
||||||
|
mFlocking.mChance = 90;
|
||||||
|
mFlocking.mMin = 1.0f;
|
||||||
|
mFlocking.mMax = 3.0f;
|
||||||
|
mFlocking.mSideStep = 0.01f;
|
||||||
|
#endif
|
||||||
|
resolveYawPtr.bind(this, &AIControllerData::resolveYaw);
|
||||||
|
resolvePitchPtr.bind(this, &AIControllerData::resolvePitch);
|
||||||
|
resolveRollPtr.bind(this, &AIControllerData::resolveRoll);
|
||||||
|
resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed);
|
||||||
|
resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState);
|
||||||
|
resolveStuckPtr.bind(this, &AIControllerData::resolveStuck);
|
||||||
|
}
|
||||||
|
|
||||||
|
AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clone) : SimDataBlock(other, temp_clone)
|
||||||
|
{
|
||||||
|
mMoveTolerance = other.mMoveTolerance;
|
||||||
|
mAttackRadius = other.mAttackRadius;
|
||||||
|
mMoveStuckTolerance = other.mMoveStuckTolerance;
|
||||||
|
mMoveStuckTestDelay = other.mMoveStuckTestDelay;
|
||||||
|
mHeightTolerance = other.mHeightTolerance;
|
||||||
|
mFollowTolerance = other.mFollowTolerance;
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
mLinkTypes = other.mLinkTypes;
|
||||||
|
mNavSize = other.mNavSize;
|
||||||
|
mFlocking.mChance = other.mFlocking.mChance;
|
||||||
|
mFlocking.mMin = other.mFlocking.mMin;
|
||||||
|
mFlocking.mMax = other.mFlocking.mMax;
|
||||||
|
mFlocking.mSideStep = other.mFlocking.mSideStep;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
resolveYawPtr.bind(this, &AIControllerData::resolveYaw);
|
||||||
|
resolvePitchPtr.bind(this, &AIControllerData::resolvePitch);
|
||||||
|
resolveRollPtr.bind(this, &AIControllerData::resolveRoll);
|
||||||
|
resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed);
|
||||||
|
resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState);
|
||||||
|
resolveStuckPtr.bind(this, &AIControllerData::resolveStuck);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIControllerData::initPersistFields()
|
||||||
|
{
|
||||||
|
docsURL;
|
||||||
|
addGroup("AI");
|
||||||
|
|
||||||
|
addFieldV("moveTolerance", TypeRangedF32, Offset(mMoveTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance from destination before stopping.\n\n"
|
||||||
|
"When the AIController controlled object is moving to a given destination it will move to within "
|
||||||
|
"this distance of the destination and then stop. By providing this tolerance "
|
||||||
|
"it helps the AIController controlled object from never reaching its destination due to minor obstacles, "
|
||||||
|
"rounding errors on its position calculation, etc. By default it is set to 0.25.\n");
|
||||||
|
|
||||||
|
addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance from destination before stopping.\n\n"
|
||||||
|
"When the AIController controlled object is moving to a given destination it will move to within "
|
||||||
|
"this distance of the destination and then stop. By providing this tolerance "
|
||||||
|
"it helps the AIController controlled object from never reaching its destination due to minor obstacles, "
|
||||||
|
"rounding errors on its position calculation, etc. By default it is set to 0.25.\n");
|
||||||
|
|
||||||
|
addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance considered in firing range for callback purposes.");
|
||||||
|
|
||||||
|
addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance tolerance on stuck check.\n\n"
|
||||||
|
"When the AIController controlled object controlled object is moving to a given destination, if it ever moves less than "
|
||||||
|
"this tolerance during a single tick, the AIController controlled object is considered stuck. At this point "
|
||||||
|
"the onMoveStuck() callback is called on the datablock.\n");
|
||||||
|
|
||||||
|
addFieldV("HeightTolerance", TypeRangedF32, Offset(mHeightTolerance, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance from destination before stopping.\n\n"
|
||||||
|
"When the AIController controlled object is moving to a given destination it will move to within "
|
||||||
|
"this distance of the destination and then stop. By providing this tolerance "
|
||||||
|
"it helps the AIController controlled object from never reaching its destination due to minor obstacles, "
|
||||||
|
"rounding errors on its position calculation, etc. By default it is set to 0.25.\n");
|
||||||
|
|
||||||
|
addFieldV("moveStuckTestDelay", TypeRangedS32, Offset(mMoveStuckTestDelay, AIControllerData), &CommonValidators::PositiveInt,
|
||||||
|
"@brief The number of ticks to wait before testing if the AIController controlled object is stuck.\n\n"
|
||||||
|
"When the AIController controlled object is asked to move, this property is the number of ticks to wait "
|
||||||
|
"before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object "
|
||||||
|
"to accelerate to full speed without its initial slow start being considered as stuck.\n"
|
||||||
|
"@note Set to zero to have the stuck test start immediately.\n");
|
||||||
|
endGroup("AI");
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
addGroup("Pathfinding");
|
||||||
|
addFieldV("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent,
|
||||||
|
"@brief chance of flocking.");
|
||||||
|
addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief min flocking separation distance.");
|
||||||
|
addFieldV("FlockMax", TypeRangedF32, Offset(mFlocking.mMax, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief max flocking clustering distance.");
|
||||||
|
addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat,
|
||||||
|
"@brief Distance from destination before we stop moving out of the way.");
|
||||||
|
|
||||||
|
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData),
|
||||||
|
"Allow the character to walk on dry land.");
|
||||||
|
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIControllerData),
|
||||||
|
"Allow the character to use jump links.");
|
||||||
|
addField("allowDrop", TypeBool, Offset(mLinkTypes.drop, AIControllerData),
|
||||||
|
"Allow the character to use drop links.");
|
||||||
|
addField("allowSwim", TypeBool, Offset(mLinkTypes.swim, AIControllerData),
|
||||||
|
"Allow the character to move in water.");
|
||||||
|
addField("allowLedge", TypeBool, Offset(mLinkTypes.ledge, AIControllerData),
|
||||||
|
"Allow the character to jump ledges.");
|
||||||
|
addField("allowClimb", TypeBool, Offset(mLinkTypes.climb, AIControllerData),
|
||||||
|
"Allow the character to use climb links.");
|
||||||
|
addField("allowTeleport", TypeBool, Offset(mLinkTypes.teleport, AIControllerData),
|
||||||
|
"Allow the character to use teleporters.");
|
||||||
|
|
||||||
|
endGroup("Pathfinding");
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIControllerData::packData(BitStream* stream)
|
||||||
|
{
|
||||||
|
Parent::packData(stream);
|
||||||
|
stream->write(mMoveTolerance);
|
||||||
|
stream->write(mAttackRadius);
|
||||||
|
stream->write(mMoveStuckTolerance);
|
||||||
|
stream->write(mMoveStuckTestDelay);
|
||||||
|
stream->write(mHeightTolerance);
|
||||||
|
stream->write(mFollowTolerance);
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
//enums
|
||||||
|
stream->write(mLinkTypes.getFlags());
|
||||||
|
stream->write((U32)mNavSize);
|
||||||
|
// end enums
|
||||||
|
stream->write(mFlocking.mChance);
|
||||||
|
stream->write(mFlocking.mMin);
|
||||||
|
stream->write(mFlocking.mMax);
|
||||||
|
stream->write(mFlocking.mSideStep);
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
void AIControllerData::unpackData(BitStream* stream)
|
||||||
|
{
|
||||||
|
Parent::unpackData(stream);
|
||||||
|
|
||||||
|
stream->read(&mMoveTolerance);
|
||||||
|
stream->read(&mAttackRadius);
|
||||||
|
stream->read(&mMoveStuckTolerance);
|
||||||
|
stream->read(&mMoveStuckTestDelay);
|
||||||
|
stream->read(&mHeightTolerance);
|
||||||
|
stream->read(&mFollowTolerance);
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
//enums
|
||||||
|
U16 linkFlags;
|
||||||
|
stream->read(&linkFlags);
|
||||||
|
mLinkTypes = LinkData(linkFlags);
|
||||||
|
U32 navSize;
|
||||||
|
stream->read(&navSize);
|
||||||
|
mNavSize = (AINavigation::NavSize)(navSize);
|
||||||
|
// end enums
|
||||||
|
stream->read(&(mFlocking.mChance));
|
||||||
|
stream->read(&(mFlocking.mMin));
|
||||||
|
stream->read(&(mFlocking.mMax));
|
||||||
|
stream->read(&(mFlocking.mSideStep));
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
};
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
IMPLEMENT_CO_DATABLOCK_V1(AIPlayerControllerData);
|
||||||
|
void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
Player* po = dynamic_cast<Player*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!po) return;//not a player
|
||||||
|
|
||||||
|
if (obj->getAim() || obj->mMovement.mMoveState != AIController::ModeStop)
|
||||||
|
{
|
||||||
|
// Next do pitch.
|
||||||
|
if (!obj->getAim())
|
||||||
|
{
|
||||||
|
// Level out if were just looking at our next way point.
|
||||||
|
Point3F headRotation = po->getHeadRotation();
|
||||||
|
movePtr->pitch = -headRotation.x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
F32 xDiff = obj->mMovement.mAimLocation.x - location.x;
|
||||||
|
F32 yDiff = obj->mMovement.mAimLocation.y - location.y;
|
||||||
|
// This should be adjusted to run from the
|
||||||
|
// eye point to the object's center position. Though this
|
||||||
|
// works well enough for now.
|
||||||
|
F32 vertDist = obj->mMovement.mAimLocation.z - location.z;
|
||||||
|
F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
|
||||||
|
F32 newPitch = mAtan2(horzDist, vertDist) - (M_PI_F / 2.0f);
|
||||||
|
if (mFabs(newPitch) > 0.01f)
|
||||||
|
{
|
||||||
|
Point3F headRotation = po->getHeadRotation();
|
||||||
|
movePtr->pitch = newPitch - headRotation.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Level out if we're not doing anything else
|
||||||
|
Point3F headRotation = po->getHeadRotation();
|
||||||
|
movePtr->pitch = -headRotation.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePtr)
|
||||||
|
{
|
||||||
|
Parent::resolveTriggerState(obj, movePtr);
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (obj->getNav()->mJump == AINavigation::Now)
|
||||||
|
{
|
||||||
|
movePtr->trigger[2] = true;
|
||||||
|
obj->getNav()->mJump = AINavigation::None;
|
||||||
|
}
|
||||||
|
else if (obj->getNav()->mJump == AINavigation::Ledge)
|
||||||
|
{
|
||||||
|
// If we're not touching the ground, jump!
|
||||||
|
RayInfo info;
|
||||||
|
if (!obj->getAIInfo()->mObj->getContainer()->castRay(obj->getAIInfo()->getPosition(), obj->getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info))
|
||||||
|
{
|
||||||
|
movePtr->trigger[2] = true;
|
||||||
|
obj->getNav()->mJump = AINavigation::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData);
|
||||||
|
|
||||||
|
void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
WheeledVehicle* wvo = dynamic_cast<WheeledVehicle*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!wvo)
|
||||||
|
{
|
||||||
|
//cover the case of a connection controling an object in turn controlling another
|
||||||
|
if (obj->getAIInfo()->mObj->getObjectMount())
|
||||||
|
wvo = dynamic_cast<WheeledVehicle*>(obj->getAIInfo()->mObj->getObjectMount());
|
||||||
|
}
|
||||||
|
if (!wvo) return;//not a WheeledVehicle
|
||||||
|
|
||||||
|
F32 lastYaw = wvo->getSteering().x;
|
||||||
|
|
||||||
|
Point3F right = wvo->getTransform().getRightVector();
|
||||||
|
right.normalize();
|
||||||
|
Point3F aimLoc = obj->mMovement.mAimLocation;
|
||||||
|
|
||||||
|
// Get the AI to Target vector and normalize it.
|
||||||
|
Point3F toTarg = (location + wvo->getVelocity() * TickSec) - aimLoc;
|
||||||
|
toTarg.normalize();
|
||||||
|
|
||||||
|
F32 dotYaw = -mDot(right, toTarg);
|
||||||
|
movePtr->yaw = -lastYaw;
|
||||||
|
|
||||||
|
VehicleData* vd = (VehicleData*)(wvo->getDataBlock());
|
||||||
|
F32 maxSteeringAngle = vd->maxSteeringAngle;
|
||||||
|
|
||||||
|
if (mFabs(dotYaw) > maxSteeringAngle*1.5f)
|
||||||
|
dotYaw *= -1.0f;
|
||||||
|
|
||||||
|
if (dotYaw > maxSteeringAngle) dotYaw = maxSteeringAngle;
|
||||||
|
if (dotYaw < -maxSteeringAngle) dotYaw = -maxSteeringAngle;
|
||||||
|
|
||||||
|
if (mFabs(dotYaw) > 0.05f)
|
||||||
|
movePtr->yaw = dotYaw - lastYaw;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
WheeledVehicle* wvo = dynamic_cast<WheeledVehicle*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!wvo)
|
||||||
|
{
|
||||||
|
//cover the case of a connection controling an object in turn controlling another
|
||||||
|
if (obj->getAIInfo()->mObj->getObjectMount())
|
||||||
|
wvo = dynamic_cast<WheeledVehicle*>(obj->getAIInfo()->mObj->getObjectMount());
|
||||||
|
}
|
||||||
|
if (!wvo) return;//not a WheeledVehicle
|
||||||
|
|
||||||
|
Parent::resolveSpeed(obj, location, movePtr);
|
||||||
|
|
||||||
|
VehicleData* db = static_cast<VehicleData *>(wvo->getDataBlock());
|
||||||
|
movePtr->x = 0;
|
||||||
|
movePtr->y *= mMax((db->maxSteeringAngle-mFabs(movePtr->yaw) / db->maxSteeringAngle),0.75f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData);
|
||||||
|
|
||||||
|
void AIFlyingVehicleControllerData::initPersistFields()
|
||||||
|
{
|
||||||
|
docsURL;
|
||||||
|
addGroup("AI");
|
||||||
|
|
||||||
|
addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::F32Range,
|
||||||
|
"@brief Min height we can target.");
|
||||||
|
|
||||||
|
addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::F32Range,
|
||||||
|
"@brief Max height we can target.");
|
||||||
|
|
||||||
|
endGroup("AI");
|
||||||
|
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
AIFlyingVehicleControllerData::AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData& other, bool temp_clone) : AIControllerData(other, temp_clone)
|
||||||
|
{
|
||||||
|
mFlightCeiling = other.mFlightCeiling;
|
||||||
|
mFlightFloor = other.mFlightFloor;
|
||||||
|
resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw);
|
||||||
|
resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch);
|
||||||
|
resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
FlyingVehicle* fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!fvo)
|
||||||
|
{
|
||||||
|
//cover the case of a connection controling an object in turn controlling another
|
||||||
|
if (obj->getAIInfo()->mObj->getObjectMount())
|
||||||
|
fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj->getObjectMount());
|
||||||
|
}
|
||||||
|
if (!fvo) return;//not a FlyingVehicle
|
||||||
|
|
||||||
|
Point3F right = fvo->getTransform().getRightVector();
|
||||||
|
right.normalize();
|
||||||
|
|
||||||
|
// Get the Target to AI vector and normalize it.
|
||||||
|
Point3F aimLoc = obj->mMovement.mAimLocation;
|
||||||
|
aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling);
|
||||||
|
Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc;
|
||||||
|
toTarg.normalize();
|
||||||
|
|
||||||
|
movePtr->yaw = 0;
|
||||||
|
F32 dotYaw = -mDot(right, toTarg);
|
||||||
|
if (mFabs(dotYaw) > 0.05f)
|
||||||
|
movePtr->yaw = dotYaw;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
FlyingVehicle* fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!fvo)
|
||||||
|
{
|
||||||
|
//cover the case of a connection controling an object in turn controlling another
|
||||||
|
if (obj->getAIInfo()->mObj->getObjectMount())
|
||||||
|
fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj->getObjectMount());
|
||||||
|
}
|
||||||
|
if (!fvo) return;//not a FlyingVehicle
|
||||||
|
|
||||||
|
Point3F up = fvo->getTransform().getUpVector();
|
||||||
|
up.normalize();
|
||||||
|
|
||||||
|
|
||||||
|
// Get the Target to AI vector and normalize it.
|
||||||
|
Point3F aimLoc = obj->mMovement.mAimLocation;
|
||||||
|
aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling);
|
||||||
|
Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc;
|
||||||
|
toTarg.normalize();
|
||||||
|
|
||||||
|
movePtr->pitch = 0.0f;
|
||||||
|
F32 dotPitch = mDot(up, toTarg);
|
||||||
|
if (mFabs(dotPitch) > 0.05f)
|
||||||
|
movePtr->pitch = dotPitch * M_2PI_F;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr)
|
||||||
|
{
|
||||||
|
if (obj->mMovement.mMoveState < AIController::ModeSlowing) return;
|
||||||
|
FlyingVehicle* fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj.getPointer());
|
||||||
|
if (!fvo)
|
||||||
|
{
|
||||||
|
//cover the case of a connection controling an object in turn controlling another
|
||||||
|
if (obj->getAIInfo()->mObj->getObjectMount())
|
||||||
|
fvo = dynamic_cast<FlyingVehicle*>(obj->getAIInfo()->mObj->getObjectMount());
|
||||||
|
}
|
||||||
|
if (!fvo) return;//not a FlyingVehicle
|
||||||
|
|
||||||
|
movePtr->x = 0;
|
||||||
|
movePtr->y = obj->mMovement.mMoveSpeed;
|
||||||
|
}
|
||||||
247
Engine/source/T3D/AI/AIController.h
Normal file
247
Engine/source/T3D/AI/AIController.h
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AICONTROLLER_H_
|
||||||
|
#define _AICONTROLLER_H_
|
||||||
|
#include "navigation/coverPoint.h"
|
||||||
|
#include "AIInfo.h"
|
||||||
|
#include "AIGoal.h"
|
||||||
|
#include "AIAimTarget.h"
|
||||||
|
#include "AICover.h"
|
||||||
|
#include "AINavigation.h"
|
||||||
|
class AIControllerData;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
class AIController : public SimObject {
|
||||||
|
|
||||||
|
typedef SimObject Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AIControllerData* mControllerData;
|
||||||
|
protected:
|
||||||
|
static bool setControllerDataProperty(void* object, const char* index, const char* data);
|
||||||
|
public:
|
||||||
|
enum MoveState {
|
||||||
|
ModeStop, // AI has stopped moving.
|
||||||
|
ModeStuck, // AI is stuck, but wants to move.
|
||||||
|
ModeSlowing, // AI is slowing down as it reaches it's destination.
|
||||||
|
ModeMove, // AI is currently moving.
|
||||||
|
ModeReverse // AI is reversing
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
AIInfo*mAIInfo;
|
||||||
|
public:
|
||||||
|
void setAIInfo(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f) { delete(mAIInfo); mAIInfo = new AIInfo(this, objIn, rad); }
|
||||||
|
AIInfo* getAIInfo() { return mAIInfo; }
|
||||||
|
private:
|
||||||
|
AIGoal* mGoal;
|
||||||
|
public:
|
||||||
|
void setGoal(AIInfo* targ);
|
||||||
|
void setGoal(Point3F loc, F32 rad = 0.0f);
|
||||||
|
void setGoal(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f);
|
||||||
|
AIGoal* getGoal() { return mGoal; }
|
||||||
|
void clearGoal() { SAFE_DELETE(mGoal); }
|
||||||
|
private:
|
||||||
|
AIAimTarget* mAimTarget;
|
||||||
|
public:
|
||||||
|
void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f));
|
||||||
|
void setAim(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f));
|
||||||
|
AIAimTarget* getAim() { return mAimTarget; }
|
||||||
|
void clearAim() { SAFE_DELETE(mAimTarget); }
|
||||||
|
private:
|
||||||
|
AICover* mCover;
|
||||||
|
public:
|
||||||
|
void setCover(Point3F loc, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, loc, rad); }
|
||||||
|
void setCover(SimObjectPtr<SceneObject> objIn, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, objIn, rad); }
|
||||||
|
AICover* getCover() { return mCover; }
|
||||||
|
bool findCover(const Point3F& from, F32 radius);
|
||||||
|
void clearCover();
|
||||||
|
|
||||||
|
// Utility Methods
|
||||||
|
void throwCallback(const char* name);
|
||||||
|
AINavigation* mNav;
|
||||||
|
AINavigation* getNav() { return mNav; };
|
||||||
|
struct Movement
|
||||||
|
{
|
||||||
|
AIController* mControllerRef;
|
||||||
|
AIController* getCtrl() { return mControllerRef; };
|
||||||
|
MoveState mMoveState;
|
||||||
|
F32 mMoveSpeed = 1.0;
|
||||||
|
void setMoveSpeed(F32 speed) { mMoveSpeed = speed; };
|
||||||
|
F32 getMoveSpeed() { return mMoveSpeed; };
|
||||||
|
bool mMoveSlowdown; // Slowdown as we near the destination
|
||||||
|
Point3F mLastLocation; // For stuck check
|
||||||
|
S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck
|
||||||
|
Point3F mAimLocation;
|
||||||
|
// move triggers
|
||||||
|
bool mMoveTriggers[MaxTriggerKeys];
|
||||||
|
void stopMove();
|
||||||
|
void onStuck();
|
||||||
|
} mMovement;
|
||||||
|
|
||||||
|
struct TriggerState
|
||||||
|
{
|
||||||
|
AIController* mControllerRef;
|
||||||
|
AIController* getCtrl() { return mControllerRef; };
|
||||||
|
bool mMoveTriggers[MaxTriggerKeys];
|
||||||
|
// Trigger sets/gets
|
||||||
|
void setMoveTrigger(U32 slot, const bool isSet = true);
|
||||||
|
bool getMoveTrigger(U32 slot) const;
|
||||||
|
void clearMoveTriggers();
|
||||||
|
} mTriggerState;
|
||||||
|
bool getAIMove(Move* move);
|
||||||
|
|
||||||
|
static void initPersistFields();
|
||||||
|
AIController()
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < MaxTriggerKeys; i++)
|
||||||
|
mTriggerState.mMoveTriggers[i] = false;
|
||||||
|
|
||||||
|
mMovement.mControllerRef = this;
|
||||||
|
mTriggerState.mControllerRef = this;
|
||||||
|
mControllerData = NULL;
|
||||||
|
mAIInfo = new AIInfo(this);
|
||||||
|
mNav = new AINavigation(this);
|
||||||
|
mGoal = NULL;
|
||||||
|
mAimTarget = NULL;
|
||||||
|
mCover = NULL;
|
||||||
|
mMovement.mMoveState = ModeStop;
|
||||||
|
};
|
||||||
|
~AIController()
|
||||||
|
{
|
||||||
|
SAFE_DELETE(mAIInfo);
|
||||||
|
SAFE_DELETE(mNav);
|
||||||
|
clearGoal();
|
||||||
|
clearAim();
|
||||||
|
clearCover();
|
||||||
|
}
|
||||||
|
DECLARE_CONOBJECT(AIController);
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
class AIControllerData : public SimDataBlock {
|
||||||
|
|
||||||
|
typedef SimDataBlock Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AIControllerData();
|
||||||
|
AIControllerData(const AIControllerData&, bool = false);
|
||||||
|
~AIControllerData() {};
|
||||||
|
void packData(BitStream* stream) override;
|
||||||
|
void unpackData(BitStream* stream) override;
|
||||||
|
static void initPersistFields();
|
||||||
|
DECLARE_CONOBJECT(AIControllerData);
|
||||||
|
|
||||||
|
F32 mMoveTolerance; // Distance from destination point before we stop
|
||||||
|
F32 mFollowTolerance; // Distance from destination object before we stop
|
||||||
|
F32 mAttackRadius; // Distance to trigger weaponry calcs
|
||||||
|
S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck
|
||||||
|
F32 mMoveStuckTolerance; // Distance tolerance on stuck check
|
||||||
|
F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
struct Flocking {
|
||||||
|
U32 mChance; // chance of flocking
|
||||||
|
F32 mMin; // min flocking separation distance
|
||||||
|
F32 mMax; // max flocking clustering distance
|
||||||
|
F32 mSideStep; // Distance from destination before we stop moving out of the way
|
||||||
|
} mFlocking;
|
||||||
|
|
||||||
|
/// Types of link we can use.
|
||||||
|
LinkData mLinkTypes;
|
||||||
|
AINavigation::NavSize mNavSize;
|
||||||
|
#endif
|
||||||
|
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolveYawPtr;
|
||||||
|
void resolveYaw(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
|
||||||
|
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolvePitchPtr;
|
||||||
|
void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {};
|
||||||
|
|
||||||
|
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolveRollPtr;
|
||||||
|
void resolveRoll(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
|
||||||
|
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolveSpeedPtr;
|
||||||
|
void resolveSpeed(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
|
||||||
|
Delegate<void(AIController* obj, Move* movePtr)> resolveTriggerStatePtr;
|
||||||
|
void resolveTriggerState(AIController* obj, Move* movePtr);
|
||||||
|
|
||||||
|
Delegate<void(AIController* obj)> resolveStuckPtr;
|
||||||
|
void resolveStuck(AIController* obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AIPlayerControllerData : public AIControllerData
|
||||||
|
{
|
||||||
|
typedef AIControllerData Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AIPlayerControllerData()
|
||||||
|
{
|
||||||
|
resolvePitchPtr.bind(this, &AIPlayerControllerData::resolvePitch);
|
||||||
|
resolveTriggerStatePtr.bind(this, &AIPlayerControllerData::resolveTriggerState);
|
||||||
|
}
|
||||||
|
void resolvePitch(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
void resolveTriggerState(AIController* obj, Move* movePtr);
|
||||||
|
DECLARE_CONOBJECT(AIPlayerControllerData);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AIWheeledVehicleControllerData : public AIControllerData
|
||||||
|
{
|
||||||
|
typedef AIControllerData Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AIWheeledVehicleControllerData()
|
||||||
|
{
|
||||||
|
resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw);
|
||||||
|
resolveSpeedPtr.bind(this, &AIWheeledVehicleControllerData::resolveSpeed);
|
||||||
|
mHeightTolerance = 2.0f;
|
||||||
|
}
|
||||||
|
void resolveYaw(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
void resolveSpeed(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
DECLARE_CONOBJECT(AIWheeledVehicleControllerData);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AIFlyingVehicleControllerData : public AIControllerData
|
||||||
|
{
|
||||||
|
typedef AIControllerData Parent;
|
||||||
|
|
||||||
|
F32 mFlightFloor;
|
||||||
|
F32 mFlightCeiling;
|
||||||
|
public:
|
||||||
|
AIFlyingVehicleControllerData()
|
||||||
|
{
|
||||||
|
resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw);
|
||||||
|
resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch);
|
||||||
|
resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed);
|
||||||
|
mHeightTolerance = 200.0f;
|
||||||
|
mFlightCeiling = 200.0f;
|
||||||
|
mFlightFloor = 1.0;
|
||||||
|
}
|
||||||
|
AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData&, bool = false);
|
||||||
|
static void initPersistFields();
|
||||||
|
void resolveYaw(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
void resolveSpeed(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
void resolvePitch(AIController* obj, Point3F location, Move* movePtr);
|
||||||
|
|
||||||
|
DECLARE_CONOBJECT(AIFlyingVehicleControllerData);
|
||||||
|
};
|
||||||
|
#endif //_AICONTROLLER_H_
|
||||||
108
Engine/source/T3D/AI/AICover.cpp
Normal file
108
Engine/source/T3D/AI/AICover.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "AICover.h"
|
||||||
|
#include "AIController.h"
|
||||||
|
|
||||||
|
struct CoverSearch
|
||||||
|
{
|
||||||
|
Point3F loc;
|
||||||
|
Point3F from;
|
||||||
|
F32 dist;
|
||||||
|
F32 best;
|
||||||
|
CoverPoint* point;
|
||||||
|
CoverSearch() : loc(0, 0, 0), from(0, 0, 0)
|
||||||
|
{
|
||||||
|
best = -FLT_MAX;
|
||||||
|
point = NULL;
|
||||||
|
dist = FLT_MAX;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void findCoverCallback(SceneObject* obj, void* key)
|
||||||
|
{
|
||||||
|
CoverPoint* p = dynamic_cast<CoverPoint*>(obj);
|
||||||
|
if (!p || p->isOccupied())
|
||||||
|
return;
|
||||||
|
CoverSearch* s = static_cast<CoverSearch*>(key);
|
||||||
|
Point3F dir = s->from - p->getPosition();
|
||||||
|
dir.normalizeSafe();
|
||||||
|
// Score first based on angle of cover point to enemy.
|
||||||
|
F32 score = mDot(p->getNormal(), dir);
|
||||||
|
// Score also based on distance from seeker.
|
||||||
|
score -= (p->getPosition() - s->loc).len() / s->dist;
|
||||||
|
// Finally, consider cover size.
|
||||||
|
score += (p->getSize() + 1) / CoverPoint::NumSizes;
|
||||||
|
score *= p->getQuality();
|
||||||
|
if (score > s->best)
|
||||||
|
{
|
||||||
|
s->best = score;
|
||||||
|
s->point = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIController::findCover(const Point3F& from, F32 radius)
|
||||||
|
{
|
||||||
|
if (radius <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create a search state.
|
||||||
|
CoverSearch s;
|
||||||
|
s.loc = getAIInfo()->getPosition();
|
||||||
|
s.dist = radius;
|
||||||
|
// Direction we seek cover FROM.
|
||||||
|
s.from = from;
|
||||||
|
|
||||||
|
// Find cover points.
|
||||||
|
Box3F box(radius * 2.0f);
|
||||||
|
box.setCenter(getAIInfo()->getPosition());
|
||||||
|
getAIInfo()->mObj->getContainer()->findObjects(box, MarkerObjectType, findCoverCallback, &s);
|
||||||
|
|
||||||
|
// Go to cover!
|
||||||
|
if (s.point)
|
||||||
|
{
|
||||||
|
// Calling setPathDestination clears cover...
|
||||||
|
bool foundPath = getNav()->setPathDestination(s.point->getPosition());
|
||||||
|
setCover(s.point);
|
||||||
|
s.point->setOccupied(true);
|
||||||
|
return foundPath;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, findCover, S32, (Point3F from, F32 radius), ,
|
||||||
|
"@brief Tells the AI to find cover nearby.\n\n"
|
||||||
|
|
||||||
|
"@param from Location to find cover from (i.e., enemy position).\n"
|
||||||
|
"@param radius Distance to search for cover.\n"
|
||||||
|
"@return Cover point ID if cover was found, -1 otherwise.\n\n")
|
||||||
|
{
|
||||||
|
if (object->findCover(from, radius))
|
||||||
|
{
|
||||||
|
CoverPoint* cover = object->getCover()->mCoverPoint.getObject();
|
||||||
|
return cover ? cover->getId() : -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Engine/source/T3D/AI/AICover.h
Normal file
41
Engine/source/T3D/AI/AICover.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AICOVER_H_
|
||||||
|
#define _AICOVER_H_
|
||||||
|
|
||||||
|
#include "AIInfo.h"
|
||||||
|
#include "navigation/coverPoint.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct AICover : public AIInfo
|
||||||
|
{
|
||||||
|
typedef AIInfo Parent;
|
||||||
|
/// Pointer to a cover point.
|
||||||
|
SimObjectPtr<CoverPoint> mCoverPoint;
|
||||||
|
AICover() = delete;
|
||||||
|
AICover(AIController* controller) : Parent(controller) {};
|
||||||
|
AICover(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) { mCoverPoint = dynamic_cast<CoverPoint*>(objIn.getPointer());};
|
||||||
|
AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
24
Engine/source/T3D/AI/AIGoal.cpp
Normal file
24
Engine/source/T3D/AI/AIGoal.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "AIGoal.h"
|
||||||
|
#include "AIController.h"
|
||||||
36
Engine/source/T3D/AI/AIGoal.h
Normal file
36
Engine/source/T3D/AI/AIGoal.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AIGOAL_H_
|
||||||
|
#define _AIGOAL_H_
|
||||||
|
|
||||||
|
#include "AIInfo.h"
|
||||||
|
|
||||||
|
struct AIGoal : public AIInfo
|
||||||
|
{
|
||||||
|
typedef AIInfo Parent;
|
||||||
|
bool mInRange, mInFiringRange;
|
||||||
|
AIGoal() = delete;
|
||||||
|
AIGoal(AIController* controller) : Parent(controller) { mInRange = mInFiringRange = false; };
|
||||||
|
AIGoal(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn) : Parent(controller, objIn, radIn) { mInRange = mInFiringRange = false; };
|
||||||
|
AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mInRange = mInFiringRange = false; };
|
||||||
|
};
|
||||||
|
#endif
|
||||||
81
Engine/source/T3D/AI/AIInfo.cpp
Normal file
81
Engine/source/T3D/AI/AIInfo.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "AIInfo.h"
|
||||||
|
#include "AIController.h"
|
||||||
|
|
||||||
|
AIInfo::AIInfo(AIController* controller)
|
||||||
|
{
|
||||||
|
mControllerRef = controller;
|
||||||
|
mObj = NULL;
|
||||||
|
mPosition = mLastPos = Point3F(0.0f, 0.0f, 0.0f);
|
||||||
|
mRadius = 0.0f;
|
||||||
|
mPosSet = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
AIInfo::AIInfo(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn)
|
||||||
|
{
|
||||||
|
mControllerRef = controller;
|
||||||
|
mObj = objIn;
|
||||||
|
mPosition = mLastPos = objIn->getPosition();
|
||||||
|
mRadius = radIn;
|
||||||
|
mPosSet = false;
|
||||||
|
if (radIn == 0.0f)
|
||||||
|
mRadius = mMax(objIn->getObjBox().len_x(), objIn->getObjBox().len_y()) * 0.5;
|
||||||
|
};
|
||||||
|
|
||||||
|
AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn)
|
||||||
|
{
|
||||||
|
mControllerRef = controller;
|
||||||
|
mObj = NULL;
|
||||||
|
mPosition = mLastPos = pointIn;
|
||||||
|
mRadius = radIn;
|
||||||
|
mPosSet = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Point3F AIInfo::getPosition(bool doCastray)
|
||||||
|
{
|
||||||
|
Point3F pos = (mObj.isValid()) ? mObj->getPosition() : mPosition;
|
||||||
|
if (doCastray)
|
||||||
|
{
|
||||||
|
RayInfo info;
|
||||||
|
if (gServerContainer.castRay(pos, pos - Point3F(0, 0, getCtrl()->mControllerData->mHeightTolerance), StaticShapeObjectType, &info))
|
||||||
|
{
|
||||||
|
pos = info.point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 AIInfo::getDist()
|
||||||
|
{
|
||||||
|
AIInfo* controlObj = getCtrl()->getAIInfo();
|
||||||
|
Point3F targPos = getPosition();
|
||||||
|
|
||||||
|
if (mFabs(targPos.z - controlObj->getPosition().z) < getCtrl()->mControllerData->mHeightTolerance)
|
||||||
|
targPos.z = controlObj->getPosition().z;
|
||||||
|
|
||||||
|
F32 ret = VectorF(controlObj->getPosition() - targPos).len();
|
||||||
|
ret -= controlObj->mRadius + mRadius;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
46
Engine/source/T3D/AI/AIInfo.h
Normal file
46
Engine/source/T3D/AI/AIInfo.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AIINFO_H_
|
||||||
|
#define _AIINFO_H_
|
||||||
|
|
||||||
|
#ifndef _SCENEOBJECT_H_
|
||||||
|
#include "scene/sceneObject.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AIController;
|
||||||
|
struct AIInfo
|
||||||
|
{
|
||||||
|
AIController* mControllerRef;
|
||||||
|
AIController* getCtrl() { return mControllerRef; };
|
||||||
|
void setCtrl(AIController* controller) { mControllerRef = controller; };
|
||||||
|
SimObjectPtr<SceneObject> mObj;
|
||||||
|
Point3F mPosition, mLastPos;
|
||||||
|
bool mPosSet;
|
||||||
|
F32 mRadius;
|
||||||
|
Point3F getPosition(bool doCastray = false);
|
||||||
|
F32 getDist();
|
||||||
|
AIInfo() = delete;
|
||||||
|
AIInfo(AIController* controller);
|
||||||
|
AIInfo(AIController* controller, SimObjectPtr<SceneObject> objIn, F32 radIn = 0.0f);
|
||||||
|
AIInfo(AIController* controller,Point3F pointIn, F32 radIn = 0.0f);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
574
Engine/source/T3D/AI/AINavigation.cpp
Normal file
574
Engine/source/T3D/AI/AINavigation.cpp
Normal file
|
|
@ -0,0 +1,574 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "AINavigation.h"
|
||||||
|
#include "AIController.h"
|
||||||
|
#include "T3D/shapeBase.h"
|
||||||
|
|
||||||
|
static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType | AIObjectType;
|
||||||
|
|
||||||
|
AINavigation::AINavigation(AIController* controller)
|
||||||
|
{
|
||||||
|
mControllerRef = controller;
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
mJump = None;
|
||||||
|
mNavSize = Regular;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AINavigation::~AINavigation()
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
clearPath();
|
||||||
|
clearFollow();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::setMoveDestination(const Point3F& location, bool slowdown)
|
||||||
|
{
|
||||||
|
mMoveDestination = location;
|
||||||
|
getCtrl()->mMovement.mMoveState = AIController::ModeMove;
|
||||||
|
getCtrl()->mMovement.mMoveSlowdown = slowdown;
|
||||||
|
getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AINavigation::setPathDestination(const Point3F& pos, bool replace)
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (replace)
|
||||||
|
getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance);
|
||||||
|
|
||||||
|
if (!mNavMesh)
|
||||||
|
updateNavMesh();
|
||||||
|
|
||||||
|
// If we can't find a mesh, just move regularly.
|
||||||
|
if (!mNavMesh)
|
||||||
|
{
|
||||||
|
//setMoveDestination(pos);
|
||||||
|
getCtrl()->throwCallback("onPathFailed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new path.
|
||||||
|
NavPath* path = new NavPath();
|
||||||
|
|
||||||
|
path->mMesh = mNavMesh;
|
||||||
|
path->mFrom = getCtrl()->getAIInfo()->getPosition(true);
|
||||||
|
path->mTo = getCtrl()->getGoal()->getPosition(true);
|
||||||
|
path->mFromSet = path->mToSet = true;
|
||||||
|
path->mAlwaysRender = true;
|
||||||
|
path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes;
|
||||||
|
path->mXray = true;
|
||||||
|
// Paths plan automatically upon being registered.
|
||||||
|
if (!path->registerObject())
|
||||||
|
{
|
||||||
|
delete path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path->success())
|
||||||
|
{
|
||||||
|
// Clear any current path we might have.
|
||||||
|
clearPath();
|
||||||
|
getCtrl()->clearCover();
|
||||||
|
// Store new path.
|
||||||
|
mPathData.path = path;
|
||||||
|
mPathData.owned = true;
|
||||||
|
// Skip node 0, which we are currently standing on.
|
||||||
|
moveToNode(1);
|
||||||
|
getCtrl()->throwCallback("onPathSuccess");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Just move normally if we can't path.
|
||||||
|
//setMoveDestination(pos, true);
|
||||||
|
//return;
|
||||||
|
getCtrl()->throwCallback("onPathFailed");
|
||||||
|
path->deleteObject();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
setMoveDestination(pos, false);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Point3F AINavigation::getPathDestination() const
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (!mPathData.path.isNull())
|
||||||
|
return mPathData.path->mTo;
|
||||||
|
return Point3F(0, 0, 0);
|
||||||
|
#else
|
||||||
|
return getMoveDestination();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void AINavigation::onReachDestination()
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
if (!getPath().isNull())
|
||||||
|
{
|
||||||
|
if (mPathData.index == getPath()->size() - 1)
|
||||||
|
{
|
||||||
|
// Handle looping paths.
|
||||||
|
if (getPath()->mIsLooping)
|
||||||
|
moveToNode(0);
|
||||||
|
// Otherwise end path.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clearPath();
|
||||||
|
getCtrl()->throwCallback("onReachDestination");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moveToNode(mPathData.index + 1);
|
||||||
|
// Throw callback every time if we're on a looping path.
|
||||||
|
//if(mPathData.path->mIsLooping)
|
||||||
|
//throwCallback("onReachDestination");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
getCtrl()->throwCallback("onReachDestination");
|
||||||
|
getCtrl()->mMovement.mMoveState = AIController::ModeStop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::followObject()
|
||||||
|
{
|
||||||
|
if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (setPathDestination(getCtrl()->getGoal()->getPosition(true)))
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
getCtrl()->clearCover();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::followObject(SceneObject* obj, F32 radius)
|
||||||
|
{
|
||||||
|
getCtrl()->setGoal(obj, radius);
|
||||||
|
followObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::clearFollow()
|
||||||
|
{
|
||||||
|
getCtrl()->clearGoal();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true),
|
||||||
|
"@brief Tells the AI to move to the location provided\n\n"
|
||||||
|
|
||||||
|
"@param goal Coordinates in world space representing location to move to.\n"
|
||||||
|
"@param slowDown A boolean value. If set to true, the bot will slow down "
|
||||||
|
"when it gets within 5-meters of its move destination. If false, the bot "
|
||||||
|
"will stop abruptly when it reaches the move destination. By default, this is true.\n\n"
|
||||||
|
|
||||||
|
"@note Upon reaching a move destination, the bot will clear its move destination and "
|
||||||
|
"calls to getMoveDestination will return \"0 0 0\"."
|
||||||
|
|
||||||
|
"@see getMoveDestination()\n")
|
||||||
|
{
|
||||||
|
object->getNav()->setMoveDestination(goal, slowDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getMoveDestination, Point3F, (), ,
|
||||||
|
"@brief Get the AIPlayer's current destination.\n\n"
|
||||||
|
|
||||||
|
"@return Returns a point containing the \"x y z\" position "
|
||||||
|
"of the AIPlayer's current move destination. If no move destination "
|
||||||
|
"has yet been set, this returns \"0 0 0\"."
|
||||||
|
|
||||||
|
"@see setMoveDestination()\n")
|
||||||
|
{
|
||||||
|
return object->getNav()->getMoveDestination();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), ,
|
||||||
|
"@brief Tells the AI to find a path to the location provided\n\n"
|
||||||
|
|
||||||
|
"@param goal Coordinates in world space representing location to move to.\n"
|
||||||
|
"@return True if a path was found.\n\n"
|
||||||
|
|
||||||
|
"@see getPathDestination()\n"
|
||||||
|
"@see setMoveDestination()\n")
|
||||||
|
{
|
||||||
|
return object->getNav()->setPathDestination(goal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getPathDestination, Point3F, (), ,
|
||||||
|
"@brief Get the AIPlayer's current pathfinding destination.\n\n"
|
||||||
|
|
||||||
|
"@return Returns a point containing the \"x y z\" position "
|
||||||
|
"of the AIPlayer's current path destination. If no path destination "
|
||||||
|
"has yet been set, this returns \"0 0 0\"."
|
||||||
|
|
||||||
|
"@see setPathDestination()\n")
|
||||||
|
{
|
||||||
|
return object->getNav()->getPathDestination();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), ,
|
||||||
|
"@brief Tell the AIPlayer to follow another object.\n\n"
|
||||||
|
|
||||||
|
"@param obj ID of the object to follow.\n"
|
||||||
|
"@param radius Maximum distance we let the target escape to.")
|
||||||
|
{
|
||||||
|
SceneObject* follow;
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
object->getNav()->clearPath();
|
||||||
|
object->clearCover();
|
||||||
|
#endif
|
||||||
|
object->getNav()->clearFollow();
|
||||||
|
|
||||||
|
if (Sim::findObject(obj, follow))
|
||||||
|
object->getNav()->followObject(follow, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
NavMesh* AINavigation::findNavMesh() const
|
||||||
|
{
|
||||||
|
GameBase* gbo = dynamic_cast<GameBase*>(mControllerRef->getAIInfo()->mObj.getPointer());
|
||||||
|
// Search for NavMeshes that contain us entirely with the smallest possible
|
||||||
|
// volume.
|
||||||
|
NavMesh* mesh = NULL;
|
||||||
|
SimSet* set = NavMesh::getServerSet();
|
||||||
|
for (U32 i = 0; i < set->size(); i++)
|
||||||
|
{
|
||||||
|
NavMesh* m = static_cast<NavMesh*>(set->at(i));
|
||||||
|
if (m->getWorldBox().isContained(gbo->getWorldBox()))
|
||||||
|
{
|
||||||
|
// Check that mesh size is appropriate.
|
||||||
|
if (gbo->isMounted())
|
||||||
|
{
|
||||||
|
if (!m->mVehicles)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((getNavSize() == Small && !m->mSmallCharacters) ||
|
||||||
|
(getNavSize() == Regular && !m->mRegularCharacters) ||
|
||||||
|
(getNavSize() == Large && !m->mLargeCharacters))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!mesh || m->getWorldBox().getVolume() < mesh->getWorldBox().getVolume())
|
||||||
|
mesh = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::updateNavMesh()
|
||||||
|
{
|
||||||
|
GameBase* gbo = dynamic_cast<GameBase*>(mControllerRef->getAIInfo()->mObj.getPointer());
|
||||||
|
NavMesh* old = mNavMesh;
|
||||||
|
if (mNavMesh.isNull())
|
||||||
|
mNavMesh = findNavMesh();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!mNavMesh->getWorldBox().isContained(gbo->getWorldBox()))
|
||||||
|
mNavMesh = findNavMesh();
|
||||||
|
}
|
||||||
|
// See if we need to update our path.
|
||||||
|
if (mNavMesh != old && !mPathData.path.isNull())
|
||||||
|
{
|
||||||
|
setPathDestination(mPathData.path->mTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::moveToNode(S32 node)
|
||||||
|
{
|
||||||
|
if (mPathData.path.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// -1 is shorthand for 'last path node'.
|
||||||
|
if (node == -1)
|
||||||
|
node = mPathData.path->size() - 1;
|
||||||
|
|
||||||
|
// Consider slowing down on the last path node.
|
||||||
|
setMoveDestination(mPathData.path->getNode(node), false);
|
||||||
|
|
||||||
|
// Check flags for this segment.
|
||||||
|
if (mPathData.index)
|
||||||
|
{
|
||||||
|
U16 flags = mPathData.path->getFlags(node - 1);
|
||||||
|
// Jump if we must.
|
||||||
|
if (flags & LedgeFlag)
|
||||||
|
mJump = Ledge;
|
||||||
|
else if (flags & JumpFlag)
|
||||||
|
mJump = Now;
|
||||||
|
else
|
||||||
|
// Catch pathing errors.
|
||||||
|
mJump = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current index.
|
||||||
|
mPathData.index = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::repath()
|
||||||
|
{
|
||||||
|
// Ineffectual if we don't have a path, or are using someone else's.
|
||||||
|
if (mPathData.path.isNull() || !mPathData.owned)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance && flock())
|
||||||
|
{
|
||||||
|
mPathData.path->mTo = mMoveDestination;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we're following, get their position.
|
||||||
|
mPathData.path->mTo = getCtrl()->getGoal()->getPosition(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update from position and replan.
|
||||||
|
mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(true);
|
||||||
|
mPathData.path->plan();
|
||||||
|
|
||||||
|
// Move to first node (skip start pos).
|
||||||
|
moveToNode(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AINavigation::followNavPath(NavPath* path)
|
||||||
|
{
|
||||||
|
// Get rid of our current path.
|
||||||
|
clearPath();
|
||||||
|
getCtrl()->clearCover();
|
||||||
|
|
||||||
|
// Follow new path.
|
||||||
|
mPathData.path = path;
|
||||||
|
mPathData.owned = false;
|
||||||
|
// Start from 0 since we might not already be there.
|
||||||
|
moveToNode(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINavigation::clearPath()
|
||||||
|
{
|
||||||
|
// Only delete if we own the path.
|
||||||
|
if (!mPathData.path.isNull() && mPathData.owned)
|
||||||
|
mPathData.path->deleteObject();
|
||||||
|
// Reset path data.
|
||||||
|
mPathData = PathData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AINavigation::flock()
|
||||||
|
{
|
||||||
|
AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking;
|
||||||
|
SimObjectPtr<SceneObject> obj = getCtrl()->getAIInfo()->mObj;
|
||||||
|
|
||||||
|
obj->disableCollision();
|
||||||
|
Point3F pos = obj->getBoxCenter();
|
||||||
|
Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2);
|
||||||
|
|
||||||
|
F32 maxFlocksq = flockingData.mMax * flockingData.mMax;
|
||||||
|
bool flocking = false;
|
||||||
|
U32 found = 0;
|
||||||
|
if (getCtrl()->getGoal())
|
||||||
|
{
|
||||||
|
Point3F dest = mMoveDestination;
|
||||||
|
|
||||||
|
if (getCtrl()->mMovement.mMoveState == AIController::ModeStuck)
|
||||||
|
{
|
||||||
|
Point3F shuffle = Point3F(mRandF() - 0.5, mRandF() - 0.5, 0);
|
||||||
|
shuffle.normalize();
|
||||||
|
dest += shuffle * flockingData.mMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.z = pos.z;
|
||||||
|
if ((pos - dest).len() > flockingData.mSideStep)
|
||||||
|
{
|
||||||
|
//find closest object
|
||||||
|
SimpleQueryList sql;
|
||||||
|
Box3F queryBox = Box3F(pos - searchArea, pos + searchArea);
|
||||||
|
obj->getContainer()->findObjects(queryBox, AIObjectType, SimpleQueryList::insertionCallback, &sql);
|
||||||
|
sql.mList.remove(obj);
|
||||||
|
|
||||||
|
Point3F avoidanceOffset = Point3F::Zero;
|
||||||
|
|
||||||
|
//avoid objects in the way
|
||||||
|
RayInfo info;
|
||||||
|
if (obj->getContainer()->castRay(pos, dest + Point3F(0, 0, obj->getObjBox().len_z() / 2), sAILoSMask, &info))
|
||||||
|
{
|
||||||
|
Point3F blockerOffset = (info.point - dest);
|
||||||
|
blockerOffset.z = 0;
|
||||||
|
avoidanceOffset += blockerOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//avoid bots that are too close
|
||||||
|
for (U32 i = 0; i < sql.mList.size(); i++)
|
||||||
|
{
|
||||||
|
ShapeBase* other = dynamic_cast<ShapeBase*>(sql.mList[i]);
|
||||||
|
Point3F objectCenter = other->getBoxCenter();
|
||||||
|
|
||||||
|
F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin;
|
||||||
|
F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius;
|
||||||
|
sumRad += separation;
|
||||||
|
|
||||||
|
Point3F offset = (pos - objectCenter);
|
||||||
|
F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares
|
||||||
|
if ((flockingData.mMin > 0) && (offsetLensq < (sumRad * sumRad)))
|
||||||
|
{
|
||||||
|
other->disableCollision();
|
||||||
|
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info))
|
||||||
|
{
|
||||||
|
found++;
|
||||||
|
offset.normalizeSafe();
|
||||||
|
offset *= sumRad + separation;
|
||||||
|
avoidanceOffset += offset; //accumulate total group, move away from that
|
||||||
|
}
|
||||||
|
other->enableCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if we don't have to worry about bumping into one another (nothing found lower than minFLock), see about grouping up
|
||||||
|
if (found == 0)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < sql.mList.size(); i++)
|
||||||
|
{
|
||||||
|
ShapeBase* other = static_cast<ShapeBase*>(sql.mList[i]);
|
||||||
|
Point3F objectCenter = other->getBoxCenter();
|
||||||
|
|
||||||
|
F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin;
|
||||||
|
F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius;
|
||||||
|
sumRad += separation;
|
||||||
|
|
||||||
|
Point3F offset = (pos - objectCenter);
|
||||||
|
if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq)))
|
||||||
|
{
|
||||||
|
other->disableCollision();
|
||||||
|
if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info))
|
||||||
|
{
|
||||||
|
found++;
|
||||||
|
offset.normalizeSafe();
|
||||||
|
offset *= sumRad + separation;
|
||||||
|
avoidanceOffset -= offset; // subtract total group, move toward it
|
||||||
|
}
|
||||||
|
other->enableCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found > 0)
|
||||||
|
{
|
||||||
|
avoidanceOffset.z = 0;
|
||||||
|
avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75;
|
||||||
|
avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75;
|
||||||
|
if (avoidanceOffset.lenSquared() < (maxFlocksq))
|
||||||
|
{
|
||||||
|
dest += avoidanceOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we're not jumping...
|
||||||
|
if (mJump == None)
|
||||||
|
{
|
||||||
|
dest.z = obj->getPosition().z;
|
||||||
|
//make sure we don't run off a cliff
|
||||||
|
Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance);
|
||||||
|
if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info))
|
||||||
|
{
|
||||||
|
if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance)
|
||||||
|
{
|
||||||
|
mMoveDestination = dest;
|
||||||
|
flocking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj->enableCollision();
|
||||||
|
return flocking;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), ,
|
||||||
|
"@brief Tell the AIPlayer to follow a path.\n\n"
|
||||||
|
|
||||||
|
"@param obj ID of a NavPath object for the character to follow.")
|
||||||
|
{
|
||||||
|
NavPath* path;
|
||||||
|
if (Sim::findObject(obj, path))
|
||||||
|
object->getNav()->followNavPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, repath, void, (), ,
|
||||||
|
"@brief Tells the AI to re-plan its path. Does nothing if the character "
|
||||||
|
"has no path, or if it is following a mission path.\n\n")
|
||||||
|
{
|
||||||
|
object->getNav()->repath();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, findNavMesh, S32, (), ,
|
||||||
|
"@brief Get the NavMesh object this AIPlayer is currently using.\n\n"
|
||||||
|
|
||||||
|
"@return The ID of the NavPath object this character is using for "
|
||||||
|
"pathfinding. This is determined by the character's location, "
|
||||||
|
"navigation type and other factors. Returns -1 if no NavMesh is "
|
||||||
|
"found.")
|
||||||
|
{
|
||||||
|
NavMesh* mesh = object->getNav()->getNavMesh();
|
||||||
|
return mesh ? mesh->getId() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getNavMesh, S32, (), ,
|
||||||
|
"@brief Return the NavMesh this AIPlayer is using to navigate.\n\n")
|
||||||
|
{
|
||||||
|
NavMesh* m = object->getNav()->getNavMesh();
|
||||||
|
return m ? m->getId() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, setNavSize, void, (const char* size), ,
|
||||||
|
"@brief Set the size of NavMesh this character uses. One of \"Small\", \"Regular\" or \"Large\".")
|
||||||
|
{
|
||||||
|
if (!String::compare(size, "Small"))
|
||||||
|
object->getNav()->setNavSize(AINavigation::Small);
|
||||||
|
else if (!String::compare(size, "Regular"))
|
||||||
|
object->getNav()->setNavSize(AINavigation::Regular);
|
||||||
|
else if (!String::compare(size, "Large"))
|
||||||
|
object->getNav()->setNavSize(AINavigation::Large);
|
||||||
|
else
|
||||||
|
Con::errorf("AIPlayer::setNavSize: no such size '%s'.", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(AIController, getNavSize, const char*, (), ,
|
||||||
|
"@brief Return the size of NavMesh this character uses for pathfinding.")
|
||||||
|
{
|
||||||
|
switch (object->getNav()->getNavSize())
|
||||||
|
{
|
||||||
|
case AINavigation::Small:
|
||||||
|
return "Small";
|
||||||
|
case AINavigation::Regular:
|
||||||
|
return "Regular";
|
||||||
|
case AINavigation::Large:
|
||||||
|
return "Large";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
105
Engine/source/T3D/AI/AINavigation.h
Normal file
105
Engine/source/T3D/AI/AINavigation.h
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2012 GarageGames, LLC
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef _AINAVIGATION_H_
|
||||||
|
#define _AINAVIGATION_H_
|
||||||
|
|
||||||
|
#include "AIInfo.h"
|
||||||
|
|
||||||
|
#include "navigation/navPath.h"
|
||||||
|
#include "navigation/navMesh.h"
|
||||||
|
|
||||||
|
class AIController;
|
||||||
|
struct AINavigation
|
||||||
|
{
|
||||||
|
AIController* mControllerRef;
|
||||||
|
AIController* getCtrl() { return mControllerRef; };
|
||||||
|
|
||||||
|
AINavigation() = delete;
|
||||||
|
AINavigation(AIController* controller);
|
||||||
|
~AINavigation();
|
||||||
|
Point3F mMoveDestination;
|
||||||
|
void setMoveDestination(const Point3F& location, bool slowdown);
|
||||||
|
Point3F getMoveDestination() const { return mMoveDestination; };
|
||||||
|
bool setPathDestination(const Point3F& pos, bool replace = false);
|
||||||
|
Point3F getPathDestination() const;
|
||||||
|
|
||||||
|
void onReachDestination();
|
||||||
|
|
||||||
|
void followObject();
|
||||||
|
void followObject(SceneObject* obj, F32 radius);
|
||||||
|
void clearFollow();
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
/// Stores information about a path.
|
||||||
|
struct PathData {
|
||||||
|
/// Pointer to path object.
|
||||||
|
SimObjectPtr<NavPath> path;
|
||||||
|
/// Do we own our path? If so, we will delete it when finished.
|
||||||
|
bool owned;
|
||||||
|
/// Path node we're at.
|
||||||
|
U32 index;
|
||||||
|
/// Default constructor.
|
||||||
|
PathData() : path(NULL)
|
||||||
|
{
|
||||||
|
owned = false;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Should we jump?
|
||||||
|
enum JumpStates {
|
||||||
|
None, ///< No, don't jump.
|
||||||
|
Now, ///< Jump immediately.
|
||||||
|
Ledge, ///< Jump when we walk off a ledge.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NavSize {
|
||||||
|
Small,
|
||||||
|
Regular,
|
||||||
|
Large
|
||||||
|
} mNavSize;
|
||||||
|
void setNavSize(NavSize size) { mNavSize = size; updateNavMesh(); }
|
||||||
|
NavSize getNavSize() const { return mNavSize; }
|
||||||
|
|
||||||
|
/// NavMesh we pathfind on.
|
||||||
|
SimObjectPtr<NavMesh> mNavMesh;
|
||||||
|
NavMesh* findNavMesh() const;
|
||||||
|
void updateNavMesh();
|
||||||
|
NavMesh* getNavMesh() const { return mNavMesh; }
|
||||||
|
PathData mPathData;
|
||||||
|
JumpStates mJump;
|
||||||
|
|
||||||
|
/// Clear out the current path.
|
||||||
|
void clearPath();
|
||||||
|
void repath();
|
||||||
|
|
||||||
|
/// Get the current path we're following.
|
||||||
|
SimObjectPtr<NavPath> getPath() { return mPathData.path; };
|
||||||
|
void followNavPath(NavPath* path);
|
||||||
|
|
||||||
|
/// Move to the specified node in the current path.
|
||||||
|
void moveToNode(S32 node);
|
||||||
|
bool flock();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -24,7 +24,7 @@ IMPLEMENT_CALLBACK(SubScene, onUnloaded, void, (), (),
|
||||||
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
|
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
|
||||||
|
|
||||||
SubScene::SubScene() :
|
SubScene::SubScene() :
|
||||||
mLevelAssetId(StringTable->EmptyString()),
|
mSubSceneAssetId(StringTable->EmptyString()),
|
||||||
mGameModesNames(StringTable->EmptyString()),
|
mGameModesNames(StringTable->EmptyString()),
|
||||||
mScopeDistance(-1),
|
mScopeDistance(-1),
|
||||||
mLoaded(false),
|
mLoaded(false),
|
||||||
|
|
@ -67,7 +67,7 @@ void SubScene::initPersistFields()
|
||||||
{
|
{
|
||||||
addGroup("SubScene");
|
addGroup("SubScene");
|
||||||
addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), "");
|
addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), "");
|
||||||
INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load.");
|
INITPERSISTFIELD_SUBSCENEASSET(SubScene, SubScene, "The subscene asset to load.");
|
||||||
addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
|
addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
|
||||||
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
|
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
|
||||||
endGroup("SubScene");
|
endGroup("SubScene");
|
||||||
|
|
@ -265,13 +265,13 @@ void SubScene::processTick(const Move* move)
|
||||||
|
|
||||||
void SubScene::_onFileChanged(const Torque::Path& path)
|
void SubScene::_onFileChanged(const Torque::Path& path)
|
||||||
{
|
{
|
||||||
if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
|
if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mSaving)
|
if (mSaving)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename.");
|
AssertFatal(path == mSubSceneAsset->getLevelPath(), "SubScene::_onFileChanged - path does not match filename.");
|
||||||
|
|
||||||
_closeFile(false);
|
_closeFile(false);
|
||||||
_loadFile(false);
|
_loadFile(false);
|
||||||
|
|
@ -307,9 +307,9 @@ void SubScene::_closeFile(bool removeFileNotify)
|
||||||
|
|
||||||
_removeContents(SimGroupIterator(this));
|
_removeContents(SimGroupIterator(this));
|
||||||
|
|
||||||
if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
|
if (removeFileNotify && mSubSceneAsset.notNull() && mSubSceneAsset->getLevelPath() != StringTable->EmptyString())
|
||||||
{
|
{
|
||||||
Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
Torque::FS::RemoveChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
mGameModesList.clear();
|
mGameModesList.clear();
|
||||||
|
|
@ -319,18 +319,18 @@ void SubScene::_loadFile(bool addFileNotify)
|
||||||
{
|
{
|
||||||
AssertFatal(isServerObject(), "Trying to load a SubScene file on the client is bad!");
|
AssertFatal(isServerObject(), "Trying to load a SubScene file on the client is bad!");
|
||||||
|
|
||||||
if(mLevelAsset.isNull() || mLevelAsset->getLevelPath() == StringTable->EmptyString())
|
if(mSubSceneAsset.isNull() || mSubSceneAsset->getLevelPath() == StringTable->EmptyString())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
|
String evalCmd = String::ToString("exec(\"%s\");", mSubSceneAsset->getLevelPath());
|
||||||
|
|
||||||
String instantGroup = Con::getVariable("InstantGroup");
|
String instantGroup = Con::getVariable("InstantGroup");
|
||||||
Con::setIntVariable("InstantGroup", this->getId());
|
Con::setIntVariable("InstantGroup", this->getId());
|
||||||
Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath());
|
Con::evaluate((const char*)evalCmd.c_str(), false, mSubSceneAsset->getLevelPath());
|
||||||
Con::setVariable("InstantGroup", instantGroup.c_str());
|
Con::setVariable("InstantGroup", instantGroup.c_str());
|
||||||
|
|
||||||
if (addFileNotify)
|
if (addFileNotify)
|
||||||
Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
Torque::FS::AddChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubScene::load()
|
void SubScene::load()
|
||||||
|
|
@ -432,7 +432,7 @@ bool SubScene::save()
|
||||||
if (size() == 0 || !isLoaded())
|
if (size() == 0 || !isLoaded())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mLevelAsset.isNull())
|
if (mSubSceneAsset.isNull())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mSaving)
|
if (mSaving)
|
||||||
|
|
@ -446,7 +446,7 @@ bool SubScene::save()
|
||||||
|
|
||||||
PersistenceManager prMger;
|
PersistenceManager prMger;
|
||||||
|
|
||||||
StringTableEntry levelPath = mLevelAsset->getLevelPath();
|
StringTableEntry levelPath = mSubSceneAsset->getLevelPath();
|
||||||
|
|
||||||
FileStream fs;
|
FileStream fs;
|
||||||
fs.open(levelPath, Torque::FS::File::Write);
|
fs.open(levelPath, Torque::FS::File::Write);
|
||||||
|
|
@ -460,7 +460,7 @@ bool SubScene::save()
|
||||||
{
|
{
|
||||||
if ((*itr)->isMethod("onSaving"))
|
if ((*itr)->isMethod("onSaving"))
|
||||||
{
|
{
|
||||||
Con::executef((*itr), "onSaving", mLevelAssetId);
|
Con::executef((*itr), "onSaving", mSubSceneAssetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childObj->getGroup() == this)
|
if (childObj->getGroup() == this)
|
||||||
|
|
@ -481,14 +481,14 @@ bool SubScene::save()
|
||||||
bool saveSuccess = false;
|
bool saveSuccess = false;
|
||||||
|
|
||||||
//Get the level asset
|
//Get the level asset
|
||||||
if (mLevelAsset.isNull())
|
if (mSubSceneAsset.isNull())
|
||||||
return saveSuccess;
|
return saveSuccess;
|
||||||
|
|
||||||
//update the gamemode list as well
|
//update the gamemode list as well
|
||||||
mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
|
mSubSceneAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
|
||||||
|
|
||||||
//Finally, save
|
//Finally, save
|
||||||
saveSuccess = mLevelAsset->saveAsset();
|
saveSuccess = mSubSceneAsset->saveAsset();
|
||||||
|
|
||||||
mSaving = false;
|
mSaving = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
#ifndef SCENE_GROUP_H
|
#ifndef SCENE_GROUP_H
|
||||||
#include "SceneGroup.h"
|
#include "SceneGroup.h"
|
||||||
#endif
|
#endif
|
||||||
#ifndef LEVEL_ASSET_H
|
#ifndef SUBSCENE_ASSET_H
|
||||||
#include "assets/LevelAsset.h"
|
#include "assets/SubSceneAsset.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class GameMode;
|
class GameMode;
|
||||||
|
|
@ -21,13 +21,13 @@ public:
|
||||||
NextFreeMask = Parent::NextFreeMask << 0
|
NextFreeMask = Parent::NextFreeMask << 0
|
||||||
};
|
};
|
||||||
|
|
||||||
void onLevelChanged() {}
|
void onSubSceneChanged() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool smTransformChildren;
|
static bool smTransformChildren;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_LEVELASSET(SubScene, Level, onLevelChanged);
|
DECLARE_SUBSCENEASSET(SubScene, SubScene, onSubSceneChanged);
|
||||||
|
|
||||||
StringTableEntry mGameModesNames;
|
StringTableEntry mGameModesNames;
|
||||||
Vector<GameMode*> mGameModesList;
|
Vector<GameMode*> mGameModesList;
|
||||||
|
|
@ -61,7 +61,7 @@ public:
|
||||||
|
|
||||||
static void initPersistFields();
|
static void initPersistFields();
|
||||||
static void consoleInit();
|
static void consoleInit();
|
||||||
StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); }
|
StringTableEntry getTypeHint() const override { return (getSubSceneAsset()) ? getSubSceneAsset()->getAssetName() : StringTable->EmptyString(); }
|
||||||
|
|
||||||
// SimObject
|
// SimObject
|
||||||
bool onAdd() override;
|
bool onAdd() override;
|
||||||
|
|
@ -122,6 +122,6 @@ public:
|
||||||
|
|
||||||
DECLARE_CALLBACK(void, onLoaded, ());
|
DECLARE_CALLBACK(void, onLoaded, ());
|
||||||
DECLARE_CALLBACK(void, onUnloaded, ());
|
DECLARE_CALLBACK(void, onUnloaded, ());
|
||||||
DECLARE_ASSET_SETGET(SubScene, Level);
|
DECLARE_ASSET_SETGET(SubScene, SubScene);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -178,3 +178,17 @@ void CppAsset::onAssetRefresh(void)
|
||||||
|
|
||||||
mHeaderPath = getOwned() ? expandAssetFilePath(mHeaderFile) : mHeaderPath;
|
mHeaderPath = getOwned() ? expandAssetFilePath(mHeaderFile) : mHeaderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CppAsset, getCodePath, const char*, (), ,
|
||||||
|
"Gets the code file filepath of this asset.\n"
|
||||||
|
"@return File path of the code file.")
|
||||||
|
{
|
||||||
|
return object->getCppFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(CppAsset, getHeaderPath, const char*, (), ,
|
||||||
|
"Gets the header file filepath of this asset.\n"
|
||||||
|
"@return File path of the header file.")
|
||||||
|
{
|
||||||
|
return object->getHeaderFilePath();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ public:
|
||||||
void setHeaderFile(const char* pHeaderFile);
|
void setHeaderFile(const char* pHeaderFile);
|
||||||
inline StringTableEntry getHeaderFile(void) const { return mHeaderFile; };
|
inline StringTableEntry getHeaderFile(void) const { return mHeaderFile; };
|
||||||
|
|
||||||
|
inline StringTableEntry getCppFilePath(void) const { return mCodePath; };
|
||||||
|
inline StringTableEntry getHeaderFilePath(void) const { return mHeaderPath; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initializeAsset(void) override;
|
void initializeAsset(void) override;
|
||||||
void onAssetRefresh(void) override;
|
void onAssetRefresh(void) override;
|
||||||
|
|
@ -73,9 +76,6 @@ protected:
|
||||||
static bool setCppFile(void *obj, const char *index, const char *data) { static_cast<CppAsset*>(obj)->setCppFile(data); return false; }
|
static bool setCppFile(void *obj, const char *index, const char *data) { static_cast<CppAsset*>(obj)->setCppFile(data); return false; }
|
||||||
static const char* getCppFile(void* obj, const char* data) { return static_cast<CppAsset*>(obj)->getCppFile(); }
|
static const char* getCppFile(void* obj, const char* data) { return static_cast<CppAsset*>(obj)->getCppFile(); }
|
||||||
|
|
||||||
inline StringTableEntry getCppFilePath(void) const { return mCodePath; };
|
|
||||||
inline StringTableEntry getHeaderFilePath(void) const { return mHeaderPath; };
|
|
||||||
|
|
||||||
static bool setHeaderFile(void *obj, const char *index, const char *data) { static_cast<CppAsset*>(obj)->setHeaderFile(data); return false; }
|
static bool setHeaderFile(void *obj, const char *index, const char *data) { static_cast<CppAsset*>(obj)->setHeaderFile(data); return false; }
|
||||||
static const char* getHeaderFile(void* obj, const char* data) { return static_cast<CppAsset*>(obj)->getHeaderFile(); }
|
static const char* getHeaderFile(void* obj, const char* data) { return static_cast<CppAsset*>(obj)->getHeaderFile(); }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ ConsoleSetType(TypeLevelAssetId)
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
|
LevelAsset::LevelAsset() : AssetBase()
|
||||||
{
|
{
|
||||||
mLevelName = StringTable->EmptyString();
|
mLevelName = StringTable->EmptyString();
|
||||||
mLevelFile = StringTable->EmptyString();
|
mLevelFile = StringTable->EmptyString();
|
||||||
|
|
@ -117,7 +117,6 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
|
||||||
mNavmeshPath = StringTable->EmptyString();
|
mNavmeshPath = StringTable->EmptyString();
|
||||||
|
|
||||||
mGameModesNames = StringTable->EmptyString();
|
mGameModesNames = StringTable->EmptyString();
|
||||||
mMainLevelAsset = StringTable->EmptyString();
|
|
||||||
|
|
||||||
mEditorFile = StringTable->EmptyString();
|
mEditorFile = StringTable->EmptyString();
|
||||||
mBakedSceneFile = StringTable->EmptyString();
|
mBakedSceneFile = StringTable->EmptyString();
|
||||||
|
|
@ -158,7 +157,6 @@ void LevelAsset::initPersistFields()
|
||||||
addProtectedField("BakedSceneFile", TypeAssetLooseFilePath, Offset(mBakedSceneFile, LevelAsset),
|
addProtectedField("BakedSceneFile", TypeAssetLooseFilePath, Offset(mBakedSceneFile, LevelAsset),
|
||||||
&setBakedSceneFile, &getBakedSceneFile, "Path to the level file with the objects generated as part of the baking process");
|
&setBakedSceneFile, &getBakedSceneFile, "Path to the level file with the objects generated as part of the baking process");
|
||||||
|
|
||||||
addField("isSubScene", TypeBool, Offset(mIsSubLevel, LevelAsset), "Is this a sublevel to another Scene");
|
|
||||||
addField("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level");
|
addField("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,15 +479,8 @@ GuiControl* GuiInspectorTypeLevelAssetPtr::constructEditControl()
|
||||||
// Create "Open in Editor" button
|
// Create "Open in Editor" button
|
||||||
mEditButton = new GuiBitmapButtonCtrl();
|
mEditButton = new GuiBitmapButtonCtrl();
|
||||||
|
|
||||||
String setSubSceneValue = "$createLevelAssetIsSubScene = \"\";";
|
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
|
||||||
if(dynamic_cast<SubScene*>(mInspector->getInspectObject()) != NULL)
|
getIdString());
|
||||||
{
|
|
||||||
setSubSceneValue = "$createLevelAssetIsSubScene = true;";
|
|
||||||
}
|
|
||||||
|
|
||||||
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; %s AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
|
|
||||||
getIdString(),
|
|
||||||
setSubSceneValue.c_str());
|
|
||||||
mEditButton->setField("Command", szBuffer);
|
mEditButton->setField("Command", szBuffer);
|
||||||
|
|
||||||
char bitmapName[512] = "ToolsModule:iconAdd_image";
|
char bitmapName[512] = "ToolsModule:iconAdd_image";
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,6 @@ class LevelAsset : public AssetBase
|
||||||
StringTableEntry mEditorFile;
|
StringTableEntry mEditorFile;
|
||||||
StringTableEntry mBakedSceneFile;
|
StringTableEntry mBakedSceneFile;
|
||||||
|
|
||||||
bool mIsSubLevel;
|
|
||||||
StringTableEntry mMainLevelAsset;
|
|
||||||
|
|
||||||
StringTableEntry mGameModesNames;
|
StringTableEntry mGameModesNames;
|
||||||
|
|
||||||
Vector<AssetBase*> mAssetDependencies;
|
Vector<AssetBase*> mAssetDependencies;
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ public: \
|
||||||
|
|
||||||
#pragma region Arrayed Asset Macros
|
#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;\
|
static const U32 sm##name##Count = max;\
|
||||||
Resource<TSShape>m##name[max];\
|
Resource<TSShape>m##name[max];\
|
||||||
StringTableEntry m##name##Name[max]; \
|
StringTableEntry m##name##Name[max]; \
|
||||||
|
|
@ -384,6 +384,10 @@ public: \
|
||||||
\
|
\
|
||||||
bool _set##name(StringTableEntry _in, const U32& index)\
|
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(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\
|
||||||
{\
|
{\
|
||||||
if(index >= sm##name##Count || index < 0)\
|
if(index >= sm##name##Count || index < 0)\
|
||||||
|
|
@ -430,6 +434,8 @@ public: \
|
||||||
if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\
|
if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\
|
||||||
{\
|
{\
|
||||||
m##name[index] = m##name##Asset[index]->getShapeResource();\
|
m##name[index] = m##name##Asset[index]->getShapeResource();\
|
||||||
|
\
|
||||||
|
m##name##Asset[index]->getChangedSignal().notify(this, &className::changeFunc);\
|
||||||
}\
|
}\
|
||||||
else\
|
else\
|
||||||
{\
|
{\
|
||||||
|
|
|
||||||
166
Engine/source/T3D/assets/SubSceneAsset.cpp
Normal file
166
Engine/source/T3D/assets/SubSceneAsset.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
#include "SubSceneAsset.h"
|
||||||
|
|
||||||
|
#include "T3D/SubScene.h"
|
||||||
|
IMPLEMENT_CONOBJECT(SubSceneAsset);
|
||||||
|
|
||||||
|
|
||||||
|
ConsoleType(SubSceneAssetPtr, TypeSubSceneAssetPtr, const char*, "")
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleGetType(TypeSubSceneAssetPtr)
|
||||||
|
{
|
||||||
|
// Fetch asset Id.
|
||||||
|
return *((const char**)(dptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleSetType(TypeSubSceneAssetPtr)
|
||||||
|
{
|
||||||
|
// Was a single argument specified?
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
// Yes, so fetch field value.
|
||||||
|
*((const char**)dptr) = StringTable->insert(argv[0]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn.
|
||||||
|
Con::warnf("(TypeSubSceneAssetPtr) - Cannot set multiple args to a single asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
ConsoleType(assetIdString, TypeSubSceneAssetId, const char*, "")
|
||||||
|
|
||||||
|
ConsoleGetType(TypeSubSceneAssetId)
|
||||||
|
{
|
||||||
|
// Fetch asset Id.
|
||||||
|
return *((const char**)(dptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleSetType(TypeSubSceneAssetId)
|
||||||
|
{
|
||||||
|
// Was a single argument specified?
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
*((const char**)dptr) = StringTable->insert(argv[0]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn.
|
||||||
|
Con::warnf("(TypeSubSceneAssetId) - Cannot set multiple args to a single asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SubSceneAsset::SubSceneAsset() : LevelAsset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SubSceneAsset::~SubSceneAsset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SubSceneAsset::initPersistFields()
|
||||||
|
{
|
||||||
|
docsURL;
|
||||||
|
Parent::initPersistFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// GuiInspectorTypeAssetId
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr);
|
||||||
|
|
||||||
|
ConsoleDocClass(GuiInspectorTypeSubSceneAssetPtr,
|
||||||
|
"@brief Inspector field type for Shapes\n\n"
|
||||||
|
"Editor use only.\n\n"
|
||||||
|
"@internal"
|
||||||
|
);
|
||||||
|
|
||||||
|
void GuiInspectorTypeSubSceneAssetPtr::consoleInit()
|
||||||
|
{
|
||||||
|
Parent::consoleInit();
|
||||||
|
|
||||||
|
ConsoleBaseType::getType(TypeSubSceneAssetPtr)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetPtr");
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiControl* GuiInspectorTypeSubSceneAssetPtr::constructEditControl()
|
||||||
|
{
|
||||||
|
// Create base filename edit controls
|
||||||
|
GuiControl* retCtrl = Parent::constructEditControl();
|
||||||
|
if (retCtrl == NULL)
|
||||||
|
return retCtrl;
|
||||||
|
|
||||||
|
// Change filespec
|
||||||
|
char szBuffer[512];
|
||||||
|
dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"SubSceneAsset\", \"AssetBrowser.changeAsset\", %s, \"\");",
|
||||||
|
getIdString());
|
||||||
|
mBrowseButton->setField("Command", szBuffer);
|
||||||
|
|
||||||
|
setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString());
|
||||||
|
|
||||||
|
// Create "Open in Editor" button
|
||||||
|
mEditButton = new GuiBitmapButtonCtrl();
|
||||||
|
|
||||||
|
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"SubSceneAsset\", AssetBrowser.selectedModule);",
|
||||||
|
getIdString());
|
||||||
|
mEditButton->setField("Command", szBuffer);
|
||||||
|
|
||||||
|
char bitmapName[512] = "ToolsModule:iconAdd_image";
|
||||||
|
mEditButton->setBitmap(StringTable->insert(bitmapName));
|
||||||
|
|
||||||
|
mEditButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
|
||||||
|
mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
|
||||||
|
mEditButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
|
||||||
|
mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Test play this sound");
|
||||||
|
|
||||||
|
mEditButton->registerObject();
|
||||||
|
addObject(mEditButton);
|
||||||
|
|
||||||
|
return retCtrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GuiInspectorTypeSubSceneAssetPtr::updateRects()
|
||||||
|
{
|
||||||
|
S32 dividerPos, dividerMargin;
|
||||||
|
mInspector->getDivider(dividerPos, dividerMargin);
|
||||||
|
Point2I fieldExtent = getExtent();
|
||||||
|
Point2I fieldPos = getPosition();
|
||||||
|
|
||||||
|
mCaptionRect.set(0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
|
||||||
|
mEditCtrlRect.set(fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 34, fieldExtent.y);
|
||||||
|
|
||||||
|
bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
|
||||||
|
if (mBrowseButton != NULL)
|
||||||
|
{
|
||||||
|
mBrowseRect.set(fieldExtent.x - 32, 2, 14, fieldExtent.y - 4);
|
||||||
|
resized |= mBrowseButton->resize(mBrowseRect.point, mBrowseRect.extent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEditButton != NULL)
|
||||||
|
{
|
||||||
|
RectI shapeEdRect(fieldExtent.x - 16, 2, 14, fieldExtent.y - 4);
|
||||||
|
resized |= mEditButton->resize(shapeEdRect.point, shapeEdRect.extent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resized;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_CONOBJECT(GuiInspectorTypeSubSceneAssetId);
|
||||||
|
|
||||||
|
ConsoleDocClass(GuiInspectorTypeSubSceneAssetId,
|
||||||
|
"@brief Inspector field type for SubScene\n\n"
|
||||||
|
"Editor use only.\n\n"
|
||||||
|
"@internal"
|
||||||
|
);
|
||||||
|
|
||||||
|
void GuiInspectorTypeSubSceneAssetId::consoleInit()
|
||||||
|
{
|
||||||
|
Parent::consoleInit();
|
||||||
|
|
||||||
|
ConsoleBaseType::getType(TypeSubSceneAssetId)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetId");
|
||||||
|
}
|
||||||
113
Engine/source/T3D/assets/SubSceneAsset.h
Normal file
113
Engine/source/T3D/assets/SubSceneAsset.h
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
#pragma once
|
||||||
|
#ifndef SUBSCENE_ASSET_H
|
||||||
|
#define SUBSCENE_ASSET_H
|
||||||
|
|
||||||
|
#ifndef LEVEL_ASSET_H
|
||||||
|
#include "LevelAsset.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ASSET_DEFINITION_H_
|
||||||
|
#include "assets/assetDefinition.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _GUI_INSPECTOR_TYPES_H_
|
||||||
|
#include "gui/editor/guiInspectorTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SubSceneAsset : public LevelAsset
|
||||||
|
{
|
||||||
|
typedef LevelAsset Parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SubSceneAsset();
|
||||||
|
virtual ~SubSceneAsset();
|
||||||
|
|
||||||
|
/// Engine.
|
||||||
|
static void initPersistFields();
|
||||||
|
|
||||||
|
/// Declare Console Object.
|
||||||
|
DECLARE_CONOBJECT(SubSceneAsset);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef TORQUE_TOOLS
|
||||||
|
class GuiInspectorTypeSubSceneAssetPtr : public GuiInspectorTypeFileName
|
||||||
|
{
|
||||||
|
typedef GuiInspectorTypeFileName Parent;
|
||||||
|
public:
|
||||||
|
|
||||||
|
GuiBitmapButtonCtrl* mEditButton;
|
||||||
|
|
||||||
|
DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr);
|
||||||
|
static void consoleInit();
|
||||||
|
|
||||||
|
GuiControl* constructEditControl() override;
|
||||||
|
bool updateRects() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GuiInspectorTypeSubSceneAssetId : public GuiInspectorTypeSubSceneAssetPtr
|
||||||
|
{
|
||||||
|
typedef GuiInspectorTypeSubSceneAssetPtr Parent;
|
||||||
|
public:
|
||||||
|
|
||||||
|
DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetId);
|
||||||
|
static void consoleInit();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DefineConsoleType(TypeSubSceneAssetPtr, SubSceneAsset)
|
||||||
|
DefineConsoleType(TypeSubSceneAssetId, String)
|
||||||
|
|
||||||
|
#pragma region Singular Asset Macros
|
||||||
|
|
||||||
|
//Singular assets
|
||||||
|
/// <Summary>
|
||||||
|
/// Declares an SubScene asset
|
||||||
|
/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
|
||||||
|
/// </Summary>
|
||||||
|
#define DECLARE_SUBSCENEASSET(className, name, changeFunc) public: \
|
||||||
|
StringTableEntry m##name##AssetId;\
|
||||||
|
AssetPtr<SubSceneAsset> m##name##Asset;\
|
||||||
|
public: \
|
||||||
|
const AssetPtr<SubSceneAsset> & get##name##Asset() const { return m##name##Asset; }\
|
||||||
|
void set##name##Asset(const AssetPtr<SubSceneAsset> &_in) { m##name##Asset = _in;}\
|
||||||
|
\
|
||||||
|
bool _set##name(StringTableEntry _in)\
|
||||||
|
{\
|
||||||
|
if(m##name##AssetId != _in)\
|
||||||
|
{\
|
||||||
|
if (m##name##Asset.notNull())\
|
||||||
|
{\
|
||||||
|
m##name##Asset->getChangedSignal().remove(this, &className::changeFunc);\
|
||||||
|
}\
|
||||||
|
if (_in == NULL || _in == StringTable->EmptyString())\
|
||||||
|
{\
|
||||||
|
m##name##AssetId = StringTable->EmptyString();\
|
||||||
|
m##name##Asset = NULL;\
|
||||||
|
return true;\
|
||||||
|
}\
|
||||||
|
if (AssetDatabase.isDeclaredAsset(_in))\
|
||||||
|
{\
|
||||||
|
m##name##AssetId = _in;\
|
||||||
|
m##name##Asset = _in;\
|
||||||
|
return true;\
|
||||||
|
}\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
if(get##name() == StringTable->EmptyString())\
|
||||||
|
return true;\
|
||||||
|
\
|
||||||
|
return false;\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
const StringTableEntry get##name() const\
|
||||||
|
{\
|
||||||
|
return m##name##AssetId;\
|
||||||
|
}\
|
||||||
|
bool name##Valid() {return (get##name() != StringTable->EmptyString() && m##name##Asset->getStatus() == AssetBase::Ok); }
|
||||||
|
|
||||||
|
#define INITPERSISTFIELD_SUBSCENEASSET(name, consoleClass, docs) \
|
||||||
|
addProtectedField(assetText(name, Asset), TypeSubSceneAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#endif // SUBSCENE_ASSET_H
|
||||||
|
|
@ -345,7 +345,7 @@ bool Camera::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -601,7 +601,7 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
|
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,10 @@ public:
|
||||||
void onPerformSubstitutions() override;
|
void onPerformSubstitutions() override;
|
||||||
bool allowSubstitutions() const override { return true; }
|
bool allowSubstitutions() const override { return true; }
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,11 @@ void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect)
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return;
|
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)))
|
if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,11 @@ void GuiHealthTextHud::onRender(Point2I offset, const RectI &updateRect)
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return;
|
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)))
|
if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1127,7 +1127,7 @@ bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
|
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,10 @@ public:
|
||||||
ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
|
ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
|
||||||
bool allowSubstitutions() const override { return true; }
|
bool allowSubstitutions() const override { return true; }
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -852,8 +852,11 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
|
||||||
// It's sloppy, but it works for now.
|
// It's sloppy, but it works for now.
|
||||||
_freeCells();
|
_freeCells();
|
||||||
|
|
||||||
if ( isProperlyAdded() )
|
if (isProperlyAdded())
|
||||||
|
{
|
||||||
_initMaterial();
|
_initMaterial();
|
||||||
|
_initShapes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ protected:
|
||||||
RectF mBillboardRects[MAX_COVERTYPES];
|
RectF mBillboardRects[MAX_COVERTYPES];
|
||||||
|
|
||||||
/// The cover shape filenames.
|
/// 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);
|
DECLARE_ASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1);
|
||||||
|
|
||||||
/// The cover shape instances.
|
/// The cover shape instances.
|
||||||
|
|
@ -409,6 +409,12 @@ protected:
|
||||||
S32 randSeed );
|
S32 randSeed );
|
||||||
|
|
||||||
void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
||||||
|
|
||||||
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
_initShapes();
|
||||||
|
setMaskBits(U32(-1));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _GROUNDCOVER_H_
|
#endif // _GROUNDCOVER_H_
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,7 @@ bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1134,7 +1134,7 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Todo: Uncomment if this is a "leaf" class
|
// Todo: Uncomment if this is a "leaf" class
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -598,7 +598,7 @@ bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
initMaterials();
|
initMaterials();
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Todo: Uncomment if this is a "leaf" class
|
// Todo: Uncomment if this is a "leaf" class
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -440,7 +440,7 @@ bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ),
|
||||||
"}\n\n"
|
"}\n\n"
|
||||||
"@endtsexample\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"
|
"@brief Called when the object has a new datablock assigned.\n\n"
|
||||||
"@param obj the GameBase object\n\n"
|
"@param obj the GameBase object\n\n"
|
||||||
"@see onAdd for an example\n" );
|
"@see onAdd for an example\n" );
|
||||||
|
|
@ -512,12 +512,12 @@ void GameBase::scriptOnAdd()
|
||||||
mDataBlock->onAdd_callback( this );
|
mDataBlock->onAdd_callback( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBase::scriptOnNewDataBlock()
|
void GameBase::scriptOnNewDataBlock(bool reload)
|
||||||
{
|
{
|
||||||
// Script onNewDataBlock() must be called by the leaf class
|
// Script onNewDataBlock() must be called by the leaf class
|
||||||
// after everything is loaded.
|
// after everything is loaded.
|
||||||
if (mDataBlock && !isGhost())
|
if (mDataBlock && !isGhost())
|
||||||
mDataBlock->onNewDataBlock_callback( this );
|
mDataBlock->onNewDataBlock_callback( this, reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBase::scriptOnRemove()
|
void GameBase::scriptOnRemove()
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) );
|
DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) );
|
||||||
DECLARE_CALLBACK( void, onRemove, ( 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, onMount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) );
|
||||||
DECLARE_CALLBACK( void, onUnmount, ( 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.
|
/// Executes the 'onNewDataBlock' script function for this object.
|
||||||
///
|
///
|
||||||
/// @note This must be called after everything is loaded.
|
/// @note This must be called after everything is loaded.
|
||||||
void scriptOnNewDataBlock();
|
void scriptOnNewDataBlock(bool reload = false);
|
||||||
|
|
||||||
/// Executes the 'onRemove' script function for this object.
|
/// Executes the 'onRemove' script function for this object.
|
||||||
/// @note This must be called while the object is still valid
|
/// @note This must be called while the object is still valid
|
||||||
|
|
|
||||||
|
|
@ -669,6 +669,7 @@ static void RegisterGameFunctions()
|
||||||
Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType);
|
Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType);
|
||||||
// PATHSHAPE END
|
// PATHSHAPE END
|
||||||
Con::setIntVariable("$TypeMasks::TurretObjectType", TurretObjectType);
|
Con::setIntVariable("$TypeMasks::TurretObjectType", TurretObjectType);
|
||||||
|
Con::setIntVariable("$TypeMasks::AIObjectType", AIObjectType);
|
||||||
|
|
||||||
Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut,
|
Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut,
|
||||||
"InOut ease for curve movement.\n"
|
"InOut ease for curve movement.\n"
|
||||||
|
|
|
||||||
|
|
@ -422,7 +422,7 @@ bool Item::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!mSubclassItemHandlesScene)
|
if (!mSubclassItemHandlesScene)
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
|
|
||||||
if ( isProperlyAdded() )
|
if ( isProperlyAdded() )
|
||||||
_updatePhysics();
|
_updatePhysics();
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
mDataBlock = dynamic_cast<MissionMarkerData*>( dptr );
|
mDataBlock = dynamic_cast<MissionMarkerData*>( dptr );
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return(false);
|
return(false);
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ enum SceneObjectTypes
|
||||||
/// @see TurretShape
|
/// @see TurretShape
|
||||||
TurretObjectType = BIT(29),
|
TurretObjectType = BIT(29),
|
||||||
N_A_31 = BIT(30),
|
N_A_31 = BIT(30),
|
||||||
N_A_32 = BIT(31),
|
AIObjectType = BIT(31),
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ bool PathCamera::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ bool PathShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,10 @@ public:
|
||||||
void packData( BitStream *stream ) override;
|
void packData( BitStream *stream ) override;
|
||||||
void unpackData( BitStream *stream ) override;
|
void unpackData( BitStream *stream ) override;
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
|
|
||||||
DECLARE_CONOBJECT( PhysicsDebrisData );
|
DECLARE_CONOBJECT( PhysicsDebrisData );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,10 @@ public:
|
||||||
SimObjectRef< ExplosionData > explosion;
|
SimObjectRef< ExplosionData > explosion;
|
||||||
SimObjectRef< PhysicsShapeData > destroyedShape;
|
SimObjectRef< PhysicsShapeData > destroyedShape;
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef PhysicsShapeData::SimType PhysicsSimType;
|
typedef PhysicsShapeData::SimType PhysicsSimType;
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,7 @@ PlayerData::PlayerData()
|
||||||
jumpTowardsNormal = true;
|
jumpTowardsNormal = true;
|
||||||
|
|
||||||
physicsPlayerType = StringTable->EmptyString();
|
physicsPlayerType = StringTable->EmptyString();
|
||||||
|
mControlMap = StringTable->EmptyString();
|
||||||
dMemset( actionList, 0, sizeof(actionList) );
|
dMemset( actionList, 0, sizeof(actionList) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -739,6 +739,8 @@ void PlayerData::initPersistFields()
|
||||||
endGroup( "Camera" );
|
endGroup( "Camera" );
|
||||||
|
|
||||||
addGroup( "Movement" );
|
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,
|
addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat,
|
||||||
"@brief Maximum height the player can step up.\n\n"
|
"@brief Maximum height the player can step up.\n\n"
|
||||||
|
|
@ -1640,7 +1642,6 @@ Player::Player()
|
||||||
mLastAbsoluteYaw = 0.0f;
|
mLastAbsoluteYaw = 0.0f;
|
||||||
mLastAbsolutePitch = 0.0f;
|
mLastAbsolutePitch = 0.0f;
|
||||||
mLastAbsoluteRoll = 0.0f;
|
mLastAbsoluteRoll = 0.0f;
|
||||||
|
|
||||||
afx_init();
|
afx_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1738,7 +1739,6 @@ bool Player::onAdd()
|
||||||
world );
|
world );
|
||||||
mPhysicsRep->setTransform( getTransform() );
|
mPhysicsRep->setTransform( getTransform() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1923,7 +1923,7 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
onScaleChanged();
|
onScaleChanged();
|
||||||
resetWorldBox();
|
resetWorldBox();
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2255,12 +2255,6 @@ void Player::advanceTime(F32 dt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::getAIMove(Move* move)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::setState(ActionState state, U32 recoverTicks)
|
void Player::setState(ActionState state, U32 recoverTicks)
|
||||||
{
|
{
|
||||||
if (state != mState) {
|
if (state != mState) {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ class Player;
|
||||||
class OpenVRTrackedObject;
|
class OpenVRTrackedObject;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
#include "navigation/navPath.h"
|
||||||
|
#include "navigation/navMesh.h"
|
||||||
|
#include "navigation/coverPoint.h"
|
||||||
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
struct PlayerData: public ShapeBaseData {
|
struct PlayerData: public ShapeBaseData {
|
||||||
|
|
@ -76,7 +82,7 @@ struct PlayerData: public ShapeBaseData {
|
||||||
/// that we don't create a TSThread on the player if we don't
|
/// that we don't create a TSThread on the player if we don't
|
||||||
/// need to.
|
/// 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);
|
DECLARE_ASSET_ARRAY_SETGET(PlayerData, ShapeFP);
|
||||||
|
|
||||||
StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify
|
StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify
|
||||||
|
|
@ -346,7 +352,7 @@ struct PlayerData: public ShapeBaseData {
|
||||||
|
|
||||||
// Jump off surfaces at their normal rather than straight up
|
// Jump off surfaces at their normal rather than straight up
|
||||||
bool jumpTowardsNormal;
|
bool jumpTowardsNormal;
|
||||||
|
StringTableEntry mControlMap;
|
||||||
// For use if/when mPhysicsPlayer is created
|
// For use if/when mPhysicsPlayer is created
|
||||||
StringTableEntry physicsPlayerType;
|
StringTableEntry physicsPlayerType;
|
||||||
|
|
||||||
|
|
@ -366,6 +372,11 @@ struct PlayerData: public ShapeBaseData {
|
||||||
void packData(BitStream* stream) override;
|
void packData(BitStream* stream) override;
|
||||||
void unpackData(BitStream* stream) override;
|
void unpackData(BitStream* stream) override;
|
||||||
|
|
||||||
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
|
|
||||||
/// @name Callbacks
|
/// @name Callbacks
|
||||||
/// @{
|
/// @{
|
||||||
DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) );
|
DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) );
|
||||||
|
|
@ -753,8 +764,6 @@ public:
|
||||||
Point3F getMomentum() const override;
|
Point3F getMomentum() const override;
|
||||||
void setMomentum(const Point3F &momentum) override;
|
void setMomentum(const Point3F &momentum) override;
|
||||||
bool displaceObject(const Point3F& displaceVector) override;
|
bool displaceObject(const Point3F& displaceVector) override;
|
||||||
virtual bool getAIMove(Move*);
|
|
||||||
|
|
||||||
bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here?
|
bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here?
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
#include "T3D/decal/decalData.h"
|
#include "T3D/decal/decalData.h"
|
||||||
#include "T3D/lightDescription.h"
|
#include "T3D/lightDescription.h"
|
||||||
#include "console/engineAPI.h"
|
#include "console/engineAPI.h"
|
||||||
|
#include "T3D/rigidShape.h"
|
||||||
|
|
||||||
|
|
||||||
IMPLEMENT_CO_DATABLOCK_V1(ProjectileData);
|
IMPLEMENT_CO_DATABLOCK_V1(ProjectileData);
|
||||||
|
|
@ -163,6 +164,7 @@ ProjectileData::ProjectileData()
|
||||||
scale.set( 1.0f, 1.0f, 1.0f );
|
scale.set( 1.0f, 1.0f, 1.0f );
|
||||||
|
|
||||||
isBallistic = false;
|
isBallistic = false;
|
||||||
|
mExplodeOnTmeout = false;
|
||||||
|
|
||||||
velInheritFactor = 1.0f;
|
velInheritFactor = 1.0f;
|
||||||
muzzleVelocity = 50;
|
muzzleVelocity = 50;
|
||||||
|
|
@ -203,6 +205,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G
|
||||||
muzzleVelocity = other.muzzleVelocity;
|
muzzleVelocity = other.muzzleVelocity;
|
||||||
impactForce = other.impactForce;
|
impactForce = other.impactForce;
|
||||||
isBallistic = other.isBallistic;
|
isBallistic = other.isBallistic;
|
||||||
|
mExplodeOnTmeout = other.mExplodeOnTmeout;
|
||||||
bounceElasticity = other.bounceElasticity;
|
bounceElasticity = other.bounceElasticity;
|
||||||
bounceFriction = other.bounceFriction;
|
bounceFriction = other.bounceFriction;
|
||||||
gravityMod = other.gravityMod;
|
gravityMod = other.gravityMod;
|
||||||
|
|
@ -285,6 +288,8 @@ void ProjectileData::initPersistFields()
|
||||||
addProtectedFieldV("fadeDelay", TypeRangedS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, &CommonValidators::NaturalNumber,
|
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"
|
"@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.");
|
"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),
|
addField("isBallistic", TypeBool, Offset(isBallistic, ProjectileData),
|
||||||
"@brief Detetmines if the projectile should be affected by gravity and whether or not "
|
"@brief Detetmines if the projectile should be affected by gravity and whether or not "
|
||||||
"it bounces before exploding.\n\n");
|
"it bounces before exploding.\n\n");
|
||||||
|
|
@ -455,6 +460,7 @@ void ProjectileData::packData(BitStream* stream)
|
||||||
stream->write(armingDelay);
|
stream->write(armingDelay);
|
||||||
stream->write(fadeDelay);
|
stream->write(fadeDelay);
|
||||||
|
|
||||||
|
stream->writeFlag(mExplodeOnTmeout);
|
||||||
if(stream->writeFlag(isBallistic))
|
if(stream->writeFlag(isBallistic))
|
||||||
{
|
{
|
||||||
stream->write(gravityMod);
|
stream->write(gravityMod);
|
||||||
|
|
@ -514,6 +520,7 @@ void ProjectileData::unpackData(BitStream* stream)
|
||||||
stream->read(&armingDelay);
|
stream->read(&armingDelay);
|
||||||
stream->read(&fadeDelay);
|
stream->read(&fadeDelay);
|
||||||
|
|
||||||
|
mExplodeOnTmeout = stream->readFlag();
|
||||||
isBallistic = stream->readFlag();
|
isBallistic = stream->readFlag();
|
||||||
if(isBallistic)
|
if(isBallistic)
|
||||||
{
|
{
|
||||||
|
|
@ -611,6 +618,7 @@ Projectile::Projectile()
|
||||||
mProjectileShape( NULL ),
|
mProjectileShape( NULL ),
|
||||||
mActivateThread( NULL ),
|
mActivateThread( NULL ),
|
||||||
mMaintainThread( NULL ),
|
mMaintainThread( NULL ),
|
||||||
|
mHasHit(false),
|
||||||
mHasExploded( false ),
|
mHasExploded( false ),
|
||||||
mFadeValue( 1.0f )
|
mFadeValue( 1.0f )
|
||||||
{
|
{
|
||||||
|
|
@ -1128,10 +1136,18 @@ void Projectile::processTick( const Move *move )
|
||||||
|
|
||||||
void Projectile::simulate( F32 dt )
|
void Projectile::simulate( F32 dt )
|
||||||
{
|
{
|
||||||
if ( isServerObject() && mCurrTick >= mDataBlock->lifetime )
|
if ( isServerObject() )
|
||||||
{
|
{
|
||||||
deleteObject();
|
if (mCurrTick >= (mDataBlock->lifetime - TickMs))
|
||||||
return;
|
{
|
||||||
|
if (mDataBlock->mExplodeOnTmeout)
|
||||||
|
explode(mCurrPosition, Point3F::UnitZ, VehicleObjectType);
|
||||||
|
}
|
||||||
|
if (mCurrTick >= mDataBlock->lifetime || (mHasHit && mCurrTick < mDataBlock->armingDelay))
|
||||||
|
{
|
||||||
|
deleteObject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mHasExploded )
|
if ( mHasExploded )
|
||||||
|
|
@ -1168,8 +1184,15 @@ void Projectile::simulate( F32 dt )
|
||||||
if ( mPhysicsWorld )
|
if ( mPhysicsWorld )
|
||||||
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
|
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
|
||||||
else
|
else
|
||||||
|
{
|
||||||
hit = getContainer()->castRay(oldPosition, newPosition, dynamicCollisionMask | staticCollisionMask, &rInfo);
|
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 )
|
if ( hit )
|
||||||
{
|
{
|
||||||
// make sure the client knows to bounce
|
// make sure the client knows to bounce
|
||||||
|
|
@ -1237,6 +1260,8 @@ void Projectile::simulate( F32 dt )
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mCurrVelocity = Point3F::Zero;
|
mCurrVelocity = Point3F::Zero;
|
||||||
|
newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f;
|
||||||
|
mHasHit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ public:
|
||||||
/// Force imparted on a hit object.
|
/// Force imparted on a hit object.
|
||||||
F32 impactForce;
|
F32 impactForce;
|
||||||
|
|
||||||
|
bool mExplodeOnTmeout;
|
||||||
/// Should it arc?
|
/// Should it arc?
|
||||||
bool isBallistic;
|
bool isBallistic;
|
||||||
|
|
||||||
/// How HIGH should it bounce (parallel to normal), [0,1]
|
/// How HIGH should it bounce (parallel to normal), [0,1]
|
||||||
F32 bounceElasticity;
|
F32 bounceElasticity;
|
||||||
/// How much momentum should be lost when it bounces (perpendicular to normal), [0,1]
|
/// How much momentum should be lost when it bounces (perpendicular to normal), [0,1]
|
||||||
|
|
@ -154,7 +154,10 @@ public:
|
||||||
ProjectileData(const ProjectileData&, bool = false);
|
ProjectileData(const ProjectileData&, bool = false);
|
||||||
bool allowSubstitutions() const override { return true; }
|
bool allowSubstitutions() const override { return true; }
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -274,7 +277,7 @@ protected:
|
||||||
|
|
||||||
LightInfo *mLight;
|
LightInfo *mLight;
|
||||||
LightState mLightState;
|
LightState mLightState;
|
||||||
|
bool mHasHit;
|
||||||
bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions.
|
bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions.
|
||||||
F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime
|
F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime
|
||||||
///< in data block
|
///< in data block
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload )
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -906,7 +906,7 @@ bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
else
|
else
|
||||||
mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
|
mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
#include "core/stream/fileStream.h"
|
#include "core/stream/fileStream.h"
|
||||||
#include "T3D/accumulationVolume.h"
|
#include "T3D/accumulationVolume.h"
|
||||||
#include "console/persistenceManager.h"
|
#include "console/persistenceManager.h"
|
||||||
|
#include "AI/AIController.h"
|
||||||
|
|
||||||
IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
|
IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
|
||||||
|
|
||||||
|
|
@ -172,6 +173,8 @@ ShapeBaseData::ShapeBaseData()
|
||||||
density( 1.0f ),
|
density( 1.0f ),
|
||||||
maxEnergy( 0.0f ),
|
maxEnergy( 0.0f ),
|
||||||
maxDamage( 1.0f ),
|
maxDamage( 1.0f ),
|
||||||
|
mCollisionMul(0.0f),
|
||||||
|
mImpactMul(0.0f),
|
||||||
repairRate( 0.0033f ),
|
repairRate( 0.0033f ),
|
||||||
disabledLevel( 1.0f ),
|
disabledLevel( 1.0f ),
|
||||||
destroyedLevel( 1.0f ),
|
destroyedLevel( 1.0f ),
|
||||||
|
|
@ -195,7 +198,8 @@ ShapeBaseData::ShapeBaseData()
|
||||||
useEyePoint( false ),
|
useEyePoint( false ),
|
||||||
isInvincible( false ),
|
isInvincible( false ),
|
||||||
renderWhenDestroyed( true ),
|
renderWhenDestroyed( true ),
|
||||||
inheritEnergyFromMount( false )
|
inheritEnergyFromMount( false ),
|
||||||
|
mAIControllData(NULL)
|
||||||
{
|
{
|
||||||
INIT_ASSET(Shape);
|
INIT_ASSET(Shape);
|
||||||
INIT_ASSET(DebrisShape);
|
INIT_ASSET(DebrisShape);
|
||||||
|
|
@ -229,6 +233,8 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game
|
||||||
density = other.density;
|
density = other.density;
|
||||||
maxEnergy = other.maxEnergy;
|
maxEnergy = other.maxEnergy;
|
||||||
maxDamage = other.maxDamage;
|
maxDamage = other.maxDamage;
|
||||||
|
mCollisionMul = other.mCollisionMul;
|
||||||
|
mImpactMul = other.mImpactMul;
|
||||||
repairRate = other.repairRate;
|
repairRate = other.repairRate;
|
||||||
disabledLevel = other.disabledLevel;
|
disabledLevel = other.disabledLevel;
|
||||||
destroyedLevel = other.destroyedLevel;
|
destroyedLevel = other.destroyedLevel;
|
||||||
|
|
@ -345,7 +351,7 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
|
||||||
|
|
||||||
S32 i;
|
S32 i;
|
||||||
U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
|
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())
|
if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
|
||||||
shapeError = true;
|
shapeError = true;
|
||||||
|
|
@ -544,6 +550,10 @@ void ShapeBaseData::initPersistFields()
|
||||||
addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
|
addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
|
||||||
INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional.");
|
INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional.");
|
||||||
endGroup( "Shapes" );
|
endGroup( "Shapes" );
|
||||||
|
addGroup("Movement");
|
||||||
|
addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, ShapeBaseData),
|
||||||
|
"@brief ai controller used by these types of objects.\n\n");
|
||||||
|
endGroup("Movement");
|
||||||
|
|
||||||
addGroup("Particle Effects");
|
addGroup("Particle Effects");
|
||||||
addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData),
|
addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData),
|
||||||
|
|
@ -585,6 +595,10 @@ void ShapeBaseData::initPersistFields()
|
||||||
addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData),
|
addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData),
|
||||||
"Invincible flag; when invincible, the object cannot be damaged or "
|
"Invincible flag; when invincible, the object cannot be damaged or "
|
||||||
"repaired." );
|
"repaired." );
|
||||||
|
addFieldV("collisionMul", TypeRangedF32, Offset(mCollisionMul, ShapeBaseData), &CommonValidators::PositiveFloat,
|
||||||
|
"collision damage multiplier");
|
||||||
|
addFieldV("impactMul", TypeRangedF32, Offset(mImpactMul, ShapeBaseData), &CommonValidators::PositiveFloat,
|
||||||
|
"impact damage multiplier");
|
||||||
endGroup( "Damage/Energy" );
|
endGroup( "Damage/Energy" );
|
||||||
|
|
||||||
addGroup( "Camera", "The settings used by the shape when it is the camera." );
|
addGroup( "Camera", "The settings used by the shape when it is the camera." );
|
||||||
|
|
@ -904,7 +918,17 @@ void ShapeBaseData::unpackData(BitStream* stream)
|
||||||
silent_bbox_check = stream->readFlag();
|
silent_bbox_check = stream->readFlag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
void ShapeBaseData::onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeBaseData::onDebrisChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -981,7 +1005,8 @@ ShapeBase::ShapeBase()
|
||||||
mCameraFov( 90.0f ),
|
mCameraFov( 90.0f ),
|
||||||
mIsControlled( false ),
|
mIsControlled( false ),
|
||||||
mLastRenderFrame( 0 ),
|
mLastRenderFrame( 0 ),
|
||||||
mLastRenderDistance( 0.0f )
|
mLastRenderDistance( 0.0f ),
|
||||||
|
mAIController(NULL)
|
||||||
{
|
{
|
||||||
mTypeMask |= ShapeBaseObjectType | LightObjectType;
|
mTypeMask |= ShapeBaseObjectType | LightObjectType;
|
||||||
|
|
||||||
|
|
@ -1032,6 +1057,7 @@ ShapeBase::~ShapeBase()
|
||||||
cur->next = sFreeTimeoutList;
|
cur->next = sFreeTimeoutList;
|
||||||
sFreeTimeoutList = cur;
|
sFreeTimeoutList = cur;
|
||||||
}
|
}
|
||||||
|
if (mAIController) mAIController->deleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeBase::initPersistFields()
|
void ShapeBase::initPersistFields()
|
||||||
|
|
@ -5449,3 +5475,44 @@ DefineEngineMethod(ShapeBase, getNodePoint, Point3F, (const char* nodeName), ,
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShapeBase::setAIController(SimObjectId controller)
|
||||||
|
{
|
||||||
|
if (Sim::findObject(controller, mAIController) && mAIController->mControllerData)
|
||||||
|
{
|
||||||
|
mAIController->setAIInfo(this);
|
||||||
|
mTypeMask |= AIObjectType;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Con::errorf("unable to find AIController : %i", controller);
|
||||||
|
mAIController = NULL;
|
||||||
|
mTypeMask |= ~AIObjectType;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeBase::getAIMove(Move* move)
|
||||||
|
{
|
||||||
|
if (!isServerObject()) return false;
|
||||||
|
if (isControlled()) return false; //something else is steering us, so use that one's controller
|
||||||
|
if (!(mTypeMask & VehicleObjectType || mTypeMask & PlayerObjectType)) return false; //only support players and vehicles for now
|
||||||
|
if (mAIController)
|
||||||
|
{
|
||||||
|
mAIController->getAIMove(move); //actual result
|
||||||
|
mTypeMask |= AIObjectType;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mAIController = NULL;
|
||||||
|
mTypeMask &= ~AIObjectType;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DefineEngineMethod(ShapeBase, setAIController, bool, (S32 controller), , "")
|
||||||
|
{
|
||||||
|
return object->setAIController(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefineEngineMethod(ShapeBase, getAIController, AIController*, (), , "")
|
||||||
|
{
|
||||||
|
return object->getAIController();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ class ShapeBase;
|
||||||
class SFXSource;
|
class SFXSource;
|
||||||
class SFXTrack;
|
class SFXTrack;
|
||||||
class SFXProfile;
|
class SFXProfile;
|
||||||
|
struct AIController;
|
||||||
|
struct AIControllerData;
|
||||||
|
|
||||||
typedef void* Light;
|
typedef void* Light;
|
||||||
|
|
||||||
|
|
@ -378,7 +380,7 @@ struct ShapeBaseImageData: public GameBaseData {
|
||||||
F32 scriptAnimTransitionTime; ///< The amount of time to transition between the previous sequence and new sequence
|
F32 scriptAnimTransitionTime; ///< The amount of time to transition between the previous sequence and new sequence
|
||||||
///< when the script prefix has changed.
|
///< 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_ASSET_ARRAY_SETGET(ShapeBaseImageData, Shape);
|
||||||
|
|
||||||
//DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP); ///< Name of shape to render in first person (optional).
|
//DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP); ///< Name of shape to render in first person (optional).
|
||||||
|
|
@ -505,6 +507,11 @@ struct ShapeBaseImageData: public GameBaseData {
|
||||||
|
|
||||||
void handleStateSoundTrack(const U32& stateId);
|
void handleStateSoundTrack(const U32& stateId);
|
||||||
|
|
||||||
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// @name Callbacks
|
/// @name Callbacks
|
||||||
|
|
@ -555,6 +562,7 @@ public:
|
||||||
U32 cubeDescId;
|
U32 cubeDescId;
|
||||||
ReflectorDesc *reflectorDesc;
|
ReflectorDesc *reflectorDesc;
|
||||||
|
|
||||||
|
AIControllerData* mAIControllData;
|
||||||
/// @name Destruction
|
/// @name Destruction
|
||||||
///
|
///
|
||||||
/// Everyone likes to blow things up!
|
/// Everyone likes to blow things up!
|
||||||
|
|
@ -579,6 +587,8 @@ public:
|
||||||
F32 density;
|
F32 density;
|
||||||
F32 maxEnergy;
|
F32 maxEnergy;
|
||||||
F32 maxDamage;
|
F32 maxDamage;
|
||||||
|
F32 mCollisionMul;
|
||||||
|
F32 mImpactMul;
|
||||||
F32 repairRate; ///< Rate per tick.
|
F32 repairRate; ///< Rate per tick.
|
||||||
|
|
||||||
F32 disabledLevel;
|
F32 disabledLevel;
|
||||||
|
|
@ -681,8 +691,8 @@ public:
|
||||||
Vector<TextureTagRemapping> txr_tag_remappings;
|
Vector<TextureTagRemapping> txr_tag_remappings;
|
||||||
bool silent_bbox_check;
|
bool silent_bbox_check;
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged();
|
||||||
void onDebrisChanged() {}
|
void onDebrisChanged();
|
||||||
public:
|
public:
|
||||||
ShapeBaseData(const ShapeBaseData&, bool = false);
|
ShapeBaseData(const ShapeBaseData&, bool = false);
|
||||||
};
|
};
|
||||||
|
|
@ -1754,6 +1764,11 @@ public:
|
||||||
/// Returns true if this object is controlling by something
|
/// Returns true if this object is controlling by something
|
||||||
bool isControlled() { return(mIsControlled); }
|
bool isControlled() { return(mIsControlled); }
|
||||||
|
|
||||||
|
AIController* mAIController;
|
||||||
|
bool setAIController(SimObjectId controller);
|
||||||
|
AIController* getAIController() { return mAIController; };
|
||||||
|
virtual bool getAIMove(Move* move);
|
||||||
|
|
||||||
/// Returns true if this object is being used as a camera in first person
|
/// Returns true if this object is being used as a camera in first person
|
||||||
bool isFirstPerson() const;
|
bool isFirstPerson() const;
|
||||||
|
|
||||||
|
|
@ -1895,7 +1910,6 @@ public:
|
||||||
void registerCollisionCallback(CollisionEventCallback*);
|
void registerCollisionCallback(CollisionEventCallback*);
|
||||||
void unregisterCollisionCallback(CollisionEventCallback*);
|
void unregisterCollisionCallback(CollisionEventCallback*);
|
||||||
|
|
||||||
protected:
|
|
||||||
enum {
|
enum {
|
||||||
ANIM_OVERRIDDEN = BIT(0),
|
ANIM_OVERRIDDEN = BIT(0),
|
||||||
BLOCK_USER_CONTROL = BIT(1),
|
BLOCK_USER_CONTROL = BIT(1),
|
||||||
|
|
@ -1903,6 +1917,8 @@ protected:
|
||||||
BAD_ANIM_ID = 999999999,
|
BAD_ANIM_ID = 999999999,
|
||||||
BLENDED_CLIP = 0x80000000,
|
BLENDED_CLIP = 0x80000000,
|
||||||
};
|
};
|
||||||
|
U8 anim_clip_flags;
|
||||||
|
protected:
|
||||||
struct BlendThread
|
struct BlendThread
|
||||||
{
|
{
|
||||||
TSThread* thread;
|
TSThread* thread;
|
||||||
|
|
@ -1910,7 +1926,6 @@ protected:
|
||||||
};
|
};
|
||||||
Vector<BlendThread> blend_clips;
|
Vector<BlendThread> blend_clips;
|
||||||
static U32 unique_anim_tag_counter;
|
static U32 unique_anim_tag_counter;
|
||||||
U8 anim_clip_flags;
|
|
||||||
S32 last_anim_id;
|
S32 last_anim_id;
|
||||||
U32 last_anim_tag;
|
U32 last_anim_tag;
|
||||||
U32 last_anim_lock_tag;
|
U32 last_anim_lock_tag;
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ bool StaticShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,7 @@ bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -545,7 +545,7 @@ bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
mShapeInstance->setTimeScale(mStateAnimThread,0);
|
mShapeInstance->setTimeScale(mStateAnimThread,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ TurretShapeData::TurretShapeData()
|
||||||
recoilSequence[i] = -1;
|
recoilSequence[i] = -1;
|
||||||
pitchSequence = -1;
|
pitchSequence = -1;
|
||||||
headingSequence = -1;
|
headingSequence = -1;
|
||||||
|
mControlMap = StringTable->EmptyString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TurretShapeData::initPersistFields()
|
void TurretShapeData::initPersistFields()
|
||||||
|
|
@ -134,6 +135,8 @@ void TurretShapeData::initPersistFields()
|
||||||
docsURL;
|
docsURL;
|
||||||
Parent::initPersistFields();
|
Parent::initPersistFields();
|
||||||
addGroup("Steering");
|
addGroup("Steering");
|
||||||
|
addField("controlMap", TypeString, Offset(mControlMap, TurretShapeData),
|
||||||
|
"@brief movemap used by these types of objects.\n\n");
|
||||||
addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData),
|
addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData),
|
||||||
"@brief Should the turret allow only z rotations.\n\n"
|
"@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. "
|
"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)
|
if (!mSubclassTurretShapeHandlesScene)
|
||||||
{
|
{
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ public:
|
||||||
bool startLoaded; ///< Should the turret's mounted weapon(s) start in a loaded state?
|
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)?
|
bool zRotOnly; ///< Should the turret allow only z rotations (like an item)?
|
||||||
|
StringTableEntry mControlMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TurretShapeData();
|
TurretShapeData();
|
||||||
|
|
|
||||||
|
|
@ -407,7 +407,7 @@ bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
mJetThread[i] = 0;
|
mJetThread[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Uncomment if this is a "leaf" class
|
// Todo: Uncomment if this is a "leaf" class
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,12 +141,14 @@ VehicleData::VehicleData()
|
||||||
dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) );
|
dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) );
|
||||||
dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) );
|
dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) );
|
||||||
dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) );
|
dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) );
|
||||||
|
mControlMap = StringTable->EmptyString();
|
||||||
|
|
||||||
numDmgEmitterAreas = 0;
|
numDmgEmitterAreas = 0;
|
||||||
|
|
||||||
collDamageThresholdVel = 20;
|
collDamageThresholdVel = 20;
|
||||||
collDamageMultiplier = 0.05f;
|
collDamageMultiplier = 0.05f;
|
||||||
enablePhysicsRep = true;
|
enablePhysicsRep = true;
|
||||||
|
mControlMap = StringTable->EmptyString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -320,7 +322,14 @@ void VehicleData::initPersistFields()
|
||||||
"velocity).\n\nCurrently unused." );
|
"velocity).\n\nCurrently unused." );
|
||||||
endGroup("Collision");
|
endGroup("Collision");
|
||||||
|
|
||||||
|
addGroup("Movement");
|
||||||
|
addField("controlMap", TypeString, Offset(mControlMap, VehicleData),
|
||||||
|
"@brief movemap used by these types of objects.\n\n");
|
||||||
|
endGroup("Movement");
|
||||||
|
|
||||||
addGroup("Steering");
|
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,
|
addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat,
|
||||||
"@brief Additional force applied to the vehicle when it is jetting.\n\n"
|
"@brief Additional force applied to the vehicle when it is jetting.\n\n"
|
||||||
"For WheeledVehicles, the force is applied in the forward direction. For "
|
"For WheeledVehicles, the force is applied in the forward direction. For "
|
||||||
|
|
@ -471,7 +480,6 @@ bool Vehicle::onAdd()
|
||||||
void Vehicle::onRemove()
|
void Vehicle::onRemove()
|
||||||
{
|
{
|
||||||
SAFE_DELETE(mPhysicsRep);
|
SAFE_DELETE(mPhysicsRep);
|
||||||
|
|
||||||
U32 i=0;
|
U32 i=0;
|
||||||
|
|
||||||
for( i=0; i<VehicleData::VC_NUM_DAMAGE_EMITTERS; i++ )
|
for( i=0; i<VehicleData::VC_NUM_DAMAGE_EMITTERS; i++ )
|
||||||
|
|
@ -496,10 +504,17 @@ void Vehicle::processTick(const Move* move)
|
||||||
{
|
{
|
||||||
PROFILE_SCOPE( Vehicle_ProcessTick );
|
PROFILE_SCOPE( Vehicle_ProcessTick );
|
||||||
|
|
||||||
|
// If we're not being controlled by a client, let the
|
||||||
|
// AI sub-module get a chance at producing a move.
|
||||||
|
Move aiMove;
|
||||||
|
if (!move && isServerObject() && getAIMove(&aiMove))
|
||||||
|
move = &aiMove;
|
||||||
|
|
||||||
ShapeBase::processTick(move);
|
ShapeBase::processTick(move);
|
||||||
if ( isMounted() )
|
if ( isMounted() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// Warp to catch up to server
|
// Warp to catch up to server
|
||||||
if (mDelta.warpCount < mDelta.warpTicks)
|
if (mDelta.warpCount < mDelta.warpTicks)
|
||||||
{
|
{
|
||||||
|
|
@ -726,6 +741,9 @@ void Vehicle::updateMove(const Move* move)
|
||||||
if (mDamageState == Enabled) {
|
if (mDamageState == Enabled) {
|
||||||
setImageTriggerState(0,move->trigger[0]);
|
setImageTriggerState(0,move->trigger[0]);
|
||||||
setImageTriggerState(1,move->trigger[1]);
|
setImageTriggerState(1,move->trigger[1]);
|
||||||
|
//legacy code has trigger 2 and 3 reserved
|
||||||
|
setImageTriggerState(2, move->trigger[4]);
|
||||||
|
setImageTriggerState(3, move->trigger[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throttle
|
// Throttle
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
#include "T3D/rigidShape.h"
|
#include "T3D/rigidShape.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "T3D/AI/AIController.h"
|
||||||
|
|
||||||
class ParticleEmitter;
|
class ParticleEmitter;
|
||||||
class ParticleEmitterData;
|
class ParticleEmitterData;
|
||||||
class ClippedPolyList;
|
class ClippedPolyList;
|
||||||
|
|
@ -70,6 +72,7 @@ struct VehicleData : public RigidShapeData
|
||||||
F32 numDmgEmitterAreas;
|
F32 numDmgEmitterAreas;
|
||||||
|
|
||||||
bool enablePhysicsRep;
|
bool enablePhysicsRep;
|
||||||
|
StringTableEntry mControlMap;
|
||||||
|
|
||||||
//
|
//
|
||||||
VehicleData();
|
VehicleData();
|
||||||
|
|
@ -98,7 +101,6 @@ class Vehicle : public RigidShape
|
||||||
Point2F mSteering;
|
Point2F mSteering;
|
||||||
F32 mThrottle;
|
F32 mThrottle;
|
||||||
bool mJetting;
|
bool mJetting;
|
||||||
|
|
||||||
GFXStateBlockRef mSolidSB;
|
GFXStateBlockRef mSolidSB;
|
||||||
|
|
||||||
SimObjectPtr<ParticleEmitter> mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS];
|
SimObjectPtr<ParticleEmitter> mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS];
|
||||||
|
|
@ -146,6 +148,9 @@ public:
|
||||||
bool onAdd() override;
|
bool onAdd() override;
|
||||||
void onRemove() override;
|
void onRemove() override;
|
||||||
|
|
||||||
|
Point2F getSteering() { return mSteering; };
|
||||||
|
F32 getThrottle() { return mThrottle;};
|
||||||
|
|
||||||
/// Interpolates between move ticks @see processTick
|
/// Interpolates between move ticks @see processTick
|
||||||
/// @param dt Change in time between the last call and this call to the function
|
/// @param dt Change in time between the last call and this call to the function
|
||||||
void advanceTime(F32 dt) override;
|
void advanceTime(F32 dt) override;
|
||||||
|
|
|
||||||
|
|
@ -711,7 +711,7 @@ bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() );
|
mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() );
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,10 @@ struct WheeledVehicleTire: public SimDataBlock
|
||||||
void packData(BitStream* stream) override;
|
void packData(BitStream* stream) override;
|
||||||
void unpackData(BitStream* stream) override;
|
void unpackData(BitStream* stream) override;
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,10 @@ protected:
|
||||||
public:
|
public:
|
||||||
enum { MaxLifetimeTicks = 4095 };
|
enum { MaxLifetimeTicks = 4095 };
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// variables set in datablock definition:
|
// variables set in datablock definition:
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ bool afxSpellBook::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,10 @@ public:
|
||||||
|
|
||||||
static void initPersistFields();
|
static void initPersistFields();
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
void onSequenceChanged() {}
|
void onSequenceChanged() {}
|
||||||
|
|
||||||
DECLARE_CONOBJECT(afxModelData);
|
DECLARE_CONOBJECT(afxModelData);
|
||||||
|
|
|
||||||
|
|
@ -1064,7 +1064,7 @@ bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1108,7 +1108,7 @@ bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1177,7 +1177,7 @@ bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1294,7 +1294,7 @@ bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload)
|
||||||
if (mDataBlock->isTempClone())
|
if (mDataBlock->isTempClone())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
scriptOnNewDataBlock();
|
scriptOnNewDataBlock(reload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1563,6 +1563,53 @@ bool AssetManager::restoreAssetTags( void )
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const char* AssetManager::getAssetLooseFiles(const char* pAssetId)
|
||||||
|
{
|
||||||
|
// Debug Profiling.
|
||||||
|
PROFILE_SCOPE(AssetManager_getAssetLooseFIles);
|
||||||
|
|
||||||
|
// Sanity!
|
||||||
|
AssertFatal(pAssetId != NULL, "Cannot look up NULL asset Id.");
|
||||||
|
|
||||||
|
// Find asset.
|
||||||
|
AssetDefinition* pAssetDefinition = findAsset(pAssetId);
|
||||||
|
|
||||||
|
// Did we find the asset?
|
||||||
|
if (pAssetDefinition == NULL)
|
||||||
|
{
|
||||||
|
// No, so warn.
|
||||||
|
Con::warnf("Asset Manager: Failed to find asset Id '%s' as it does not exist.", pAssetId);
|
||||||
|
return String::EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info.
|
||||||
|
if (mEchoInfo)
|
||||||
|
{
|
||||||
|
Con::printSeparator();
|
||||||
|
Con::printf("Asset Manager: Started getting loose files of Asset Id '%s'...", pAssetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
String looseFileList = "";
|
||||||
|
Vector<StringTableEntry>& assetLooseFiles = pAssetDefinition->mAssetLooseFiles;
|
||||||
|
for (Vector<StringTableEntry>::iterator looseFileItr = assetLooseFiles.begin(); looseFileItr != assetLooseFiles.end(); ++looseFileItr)
|
||||||
|
{
|
||||||
|
// Fetch loose file.
|
||||||
|
StringTableEntry looseFile = *looseFileItr;
|
||||||
|
|
||||||
|
looseFileList += looseFile;
|
||||||
|
|
||||||
|
if (looseFileItr != assetLooseFiles.end())
|
||||||
|
looseFileList += "\t";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ret = Con::getReturnBuffer(1024);
|
||||||
|
dSprintf(ret, 1024, "%s", looseFileList.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
S32 QSORT_CALLBACK descendingAssetDefinitionLoadCount(const void* a, const void* b)
|
S32 QSORT_CALLBACK descendingAssetDefinitionLoadCount(const void* a, const void* b)
|
||||||
{
|
{
|
||||||
// Debug Profiling.
|
// Debug Profiling.
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,9 @@ public:
|
||||||
bool restoreAssetTags( void );
|
bool restoreAssetTags( void );
|
||||||
inline AssetTagsManifest* getAssetTags( void ) const { return mAssetTagsManifest; }
|
inline AssetTagsManifest* getAssetTags( void ) const { return mAssetTagsManifest; }
|
||||||
|
|
||||||
|
/// Loose File management
|
||||||
|
const char* getAssetLooseFiles(const char* pAssetId);
|
||||||
|
|
||||||
/// Info.
|
/// Info.
|
||||||
inline U32 getDeclaredAssetCount( void ) const { return (U32)mDeclaredAssets.size(); }
|
inline U32 getDeclaredAssetCount( void ) const { return (U32)mDeclaredAssets.size(); }
|
||||||
inline U32 getReferencedAssetCount( void ) const { return (U32)mReferencedAssets.size(); }
|
inline U32 getReferencedAssetCount( void ) const { return (U32)mReferencedAssets.size(); }
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,20 @@ DefineEngineMethod(AssetManager, getAssetTags, S32, (), ,
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DefineEngineMethod(AssetManager, getAssetLooseFiles, const char*, (const char* assetId), (""),
|
||||||
|
"Finds the specified asset Id and gets a list of its loose files.\n"
|
||||||
|
"@param assetId The selected asset Id.\n"
|
||||||
|
"@return A tab-delinated list of loose files associated to the assetId.\n")
|
||||||
|
{
|
||||||
|
// Fetch asset Id.
|
||||||
|
const char* pAssetId = assetId;
|
||||||
|
|
||||||
|
// Delete asset.
|
||||||
|
return object->getAssetLooseFiles(pAssetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
DefineEngineMethod(AssetManager, findAllAssets, S32, (const char* assetQuery, bool ignoreInternal, bool ignorePrivate), ("", true, true),
|
DefineEngineMethod(AssetManager, findAllAssets, S32, (const char* assetQuery, bool ignoreInternal, bool ignorePrivate), ("", true, true),
|
||||||
"Performs an asset query searching for all assets optionally ignoring internal assets.\n"
|
"Performs an asset query searching for all assets optionally ignoring internal assets.\n"
|
||||||
"@param assetQuery The asset query object that will be populated with the results.\n"
|
"@param assetQuery The asset query object that will be populated with the results.\n"
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,69 @@ namespace PropertyInfo
|
||||||
bool hex_print(String & string, const S8 & hex);
|
bool hex_print(String & string, const S8 & hex);
|
||||||
|
|
||||||
bool default_print(String & result, SimObjectType * const & data);
|
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
|
// Default Scan/print definition
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,15 @@ DefineEngineFunction( isObject, bool, (const char * objectName), ,"isObject(obje
|
||||||
if (!String::compare(objectName, "0") || !String::compare(objectName, ""))
|
if (!String::compare(objectName, "0") || !String::compare(objectName, ""))
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
return (Sim::findObject(objectName) != NULL);
|
{
|
||||||
|
SimObject* obj= Sim::findObject(objectName);
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
if (!obj->isProperlyAdded() || obj->isRemoved())
|
||||||
|
obj = NULL;
|
||||||
|
}
|
||||||
|
return obj != NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleDocFragment _spawnObject1(
|
ConsoleDocFragment _spawnObject1(
|
||||||
|
|
|
||||||
|
|
@ -425,39 +425,42 @@ void SimDataBlock::write(Stream &stream, U32 tabStop, U32 flags)
|
||||||
// MARK: ---- API ----
|
// MARK: ---- API ----
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
void SimDataBlock::reloadOnLocalClient()
|
||||||
DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),,
|
|
||||||
"Reload the datablock. This can only be used with a local client configuration." )
|
|
||||||
{
|
{
|
||||||
// Make sure we're running a local client.
|
// Make sure we're running a local client.
|
||||||
|
|
||||||
GameConnection* localClient = GameConnection::getLocalClientConnection();
|
GameConnection* localClient = GameConnection::getLocalClientConnection();
|
||||||
if( !localClient )
|
if (!localClient)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Do an in-place pack/unpack/preload.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
U8 buffer[ 16384 ];
|
U8 buffer[16384];
|
||||||
BitStream stream( buffer, 16384 );
|
BitStream stream(buffer, 16384);
|
||||||
|
|
||||||
object->packData( &stream );
|
packData(&stream);
|
||||||
stream.setPosition(0);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger a post-apply so that change notifications respond.
|
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,8 @@ public:
|
||||||
/// Used by the console system to automatically tell datablock classes apart
|
/// Used by the console system to automatically tell datablock classes apart
|
||||||
/// from non-datablock classes.
|
/// from non-datablock classes.
|
||||||
static const bool __smIsDatablock = true;
|
static const bool __smIsDatablock = true;
|
||||||
|
|
||||||
|
void reloadOnLocalClient();
|
||||||
protected:
|
protected:
|
||||||
struct SubstitutionStatement
|
struct SubstitutionStatement
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ ImplementBitfieldType(GameTypeMasksType,
|
||||||
{ SceneObjectTypes::PathShapeObjectType, "$TypeMasks::PathShapeObjectType", "Path-following Objects.\n" },
|
{ SceneObjectTypes::PathShapeObjectType, "$TypeMasks::PathShapeObjectType", "Path-following Objects.\n" },
|
||||||
{ SceneObjectTypes::TurretObjectType, "$TypeMasks::TurretObjectType", "Turret Objects.\n" },
|
{ SceneObjectTypes::TurretObjectType, "$TypeMasks::TurretObjectType", "Turret Objects.\n" },
|
||||||
{ SceneObjectTypes::N_A_31, "$TypeMasks::N_A_31", "unused 31st bit.\n" },
|
{ SceneObjectTypes::N_A_31, "$TypeMasks::N_A_31", "unused 31st bit.\n" },
|
||||||
{ SceneObjectTypes::N_A_32, "$TypeMasks::N_A_32", "unused 32nd bit.\n" },
|
{ SceneObjectTypes::AIObjectType, "$TypeMasks::AIObjectType", "AIObjectType.\n" },
|
||||||
|
|
||||||
EndImplementBitfieldType;
|
EndImplementBitfieldType;
|
||||||
|
|
||||||
|
|
@ -3296,6 +3296,9 @@ DefineEngineMethod( SimObject, getGroup, SimGroup*, (),,
|
||||||
DefineEngineMethod( SimObject, delete, void, (),,
|
DefineEngineMethod( SimObject, delete, void, (),,
|
||||||
"Delete and remove the object." )
|
"Delete and remove the object." )
|
||||||
{
|
{
|
||||||
|
if (!object->isProperlyAdded() || object->isRemoved())
|
||||||
|
return;
|
||||||
|
|
||||||
object->deleteObject();
|
object->deleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,10 @@ public:
|
||||||
return theSignal;
|
return theSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onShapeChanged() {}
|
void onShapeChanged()
|
||||||
|
{
|
||||||
|
reloadOnLocalClient();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Vector<ForestItemData*> ForestItemDataVector;
|
typedef Vector<ForestItemData*> ForestItemDataVector;
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ void PopupMenu::enableItem(S32 pos, bool enable)
|
||||||
|
|
||||||
void PopupMenu::checkItem(S32 pos, bool checked)
|
void PopupMenu::checkItem(S32 pos, bool checked)
|
||||||
{
|
{
|
||||||
if (mMenuItems.empty() || mMenuItems.size() < pos || pos < 0)
|
if (mMenuItems.empty() || mMenuItems.size() <= pos || pos < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (checked && mMenuItems[pos].mCheckGroup != -1 && mRadioSelection)
|
if (checked && mMenuItems[pos].mCheckGroup != -1 && mRadioSelection)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "console/consoleTypes.h"
|
#include "console/consoleTypes.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "console/engineAPI.h"
|
#include "console/engineAPI.h"
|
||||||
|
#include "console/propertyParsing.h"
|
||||||
#include "math/mPoint2.h"
|
#include "math/mPoint2.h"
|
||||||
#include "math/mPoint3.h"
|
#include "math/mPoint3.h"
|
||||||
#include "math/mMatrix.h"
|
#include "math/mMatrix.h"
|
||||||
|
|
@ -169,21 +170,27 @@ ImplementConsoleTypeCasters( TypePoint2I, Point2I )
|
||||||
|
|
||||||
ConsoleGetType( TypePoint2I )
|
ConsoleGetType( TypePoint2I )
|
||||||
{
|
{
|
||||||
Point2I *pt = (Point2I *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<S32, 2>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
|
||||||
dSprintf(returnBuffer, bufSize, "%d %d", pt->x, pt->y);
|
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypePoint2I )
|
ConsoleSetType( TypePoint2I )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
if (argc >= 1)
|
||||||
dSscanf(argv[0], "%d %d", &((Point2I *) dptr)->x, &((Point2I *) dptr)->y);
|
{
|
||||||
else if(argc == 2)
|
S32 parsed[2];
|
||||||
*((Point2I *) dptr) = Point2I(dAtoi(argv[0]), dAtoi(argv[1]));
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
else
|
char buffer[256] = { 0 };
|
||||||
Con::printf("Point2I must be set as { x, y } or \"x y\"");
|
|
||||||
|
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 )
|
ConsoleGetType( TypePoint2F )
|
||||||
{
|
{
|
||||||
Point2F *pt = (Point2F *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 2>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
|
||||||
dSprintf(returnBuffer, bufSize, "%g %g", pt->x, pt->y);
|
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypePoint2F )
|
ConsoleSetType( TypePoint2F )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
if (argc >= 1)
|
||||||
dSscanf(argv[0], "%g %g", &((Point2F *) dptr)->x, &((Point2F *) dptr)->y);
|
{
|
||||||
else if(argc == 2)
|
F32 parsed[2];
|
||||||
*((Point2F *) dptr) = Point2F(dAtof(argv[0]), dAtof(argv[1]));
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
else
|
char buffer[256] = { 0 };
|
||||||
Con::printf("Point2F must be set as { x, y } or \"x y\"");
|
|
||||||
|
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 )
|
ConsoleGetType( TypePoint3I )
|
||||||
{
|
{
|
||||||
Point3I *pt = (Point3I *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<S32, 3>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
|
||||||
dSprintf(returnBuffer, bufSize, "%d %d %d", pt->x, pt->y, pt->z);
|
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypePoint3I )
|
ConsoleSetType( TypePoint3I )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
if (argc >= 1)
|
||||||
dSscanf(argv[0], "%d %d %d", &((Point3I *) dptr)->x, &((Point3I *) dptr)->y, &((Point3I *) dptr)->z);
|
{
|
||||||
else if(argc == 3)
|
S32 parsed[3];
|
||||||
*((Point3I *) dptr) = Point3I(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]));
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
else
|
char buffer[256] = { 0 };
|
||||||
Con::printf("Point3I must be set as { x, y, z } or \"x y z\"");
|
|
||||||
|
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 )
|
ConsoleGetType( TypePoint3F )
|
||||||
{
|
{
|
||||||
Point3F *pt = (Point3F *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 3>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
|
||||||
dSprintf(returnBuffer, bufSize, "%g %g %g", pt->x, pt->y, pt->z);
|
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypePoint3F )
|
ConsoleSetType( TypePoint3F )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
if (argc >= 1)
|
||||||
dSscanf(argv[0], "%g %g %g", &((Point3F *) dptr)->x, &((Point3F *) dptr)->y, &((Point3F *) dptr)->z);
|
{
|
||||||
else if(argc == 3)
|
F32 parsed[3];
|
||||||
*((Point3F *) dptr) = Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]));
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
else
|
char buffer[256] = { 0 };
|
||||||
Con::printf("Point3F must be set as { x, y, z } or \"x y z\"");
|
|
||||||
|
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 )
|
ConsoleGetType( TypePoint4F )
|
||||||
{
|
{
|
||||||
Point4F *pt = (Point4F *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 4>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
|
||||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w);
|
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypePoint4F )
|
ConsoleSetType( TypePoint4F )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
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)
|
F32 parsed[4];
|
||||||
*((Point4F *) dptr) = Point4F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3]));
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
else
|
char buffer[256] = { 0 };
|
||||||
Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\"");
|
|
||||||
|
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 )
|
ConsoleGetType( TypeRectI )
|
||||||
{
|
{
|
||||||
RectI *rect = (RectI *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<S32, 4>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeRectI )
|
ConsoleSetType( TypeRectI )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
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);
|
S32 parsed[4];
|
||||||
else if(argc == 4)
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
*((RectI *) dptr) = RectI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3]));
|
char buffer[256] = { 0 };
|
||||||
else
|
|
||||||
Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\"");
|
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 )
|
ConsoleGetType( TypeRectF )
|
||||||
{
|
{
|
||||||
RectF *rect = (RectF *) dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 4>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeRectF )
|
ConsoleSetType( TypeRectF )
|
||||||
{
|
{
|
||||||
if(argc == 1)
|
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);
|
F32 parsed[4];
|
||||||
else if(argc == 4)
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
*((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3]));
|
char buffer[256] = { 0 };
|
||||||
else
|
|
||||||
Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\"");
|
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 )
|
ConsoleGetType( TypeMatrixF )
|
||||||
{
|
{
|
||||||
MatrixF* mat = ( MatrixF* ) dptr;
|
MatrixF* mat = (MatrixF*)dptr;
|
||||||
|
|
||||||
Point3F col0, col1, col2;
|
Point3F col0, col1, col2;
|
||||||
mat->getColumn(0, &col0);
|
mat->getColumn(0, &col0);
|
||||||
mat->getColumn(1, &col1);
|
mat->getColumn(1, &col1);
|
||||||
mat->getColumn(2, &col2);
|
mat->getColumn(2, &col2);
|
||||||
static const U32 bufSize = 256;
|
static const U32 bufSize = 256;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
char* buffer = 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);
|
PropertyInfo::FormatPropertyBuffer<F32, 3>(col0, buffer, bufSize);
|
||||||
return returnBuffer;
|
*buffer++ = ' ';
|
||||||
|
PropertyInfo::FormatPropertyBuffer<F32, 3>(col1, buffer, bufSize);
|
||||||
|
*buffer++ = ' ';
|
||||||
|
PropertyInfo::FormatPropertyBuffer<F32, 3>(col2, buffer, bufSize);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeMatrixF )
|
ConsoleSetType( TypeMatrixF )
|
||||||
{
|
{
|
||||||
if( argc != 1 )
|
if (argc == 1)
|
||||||
{
|
{
|
||||||
Con::errorf( "MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"" );
|
F32 parsed[9];
|
||||||
return;
|
|
||||||
|
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;
|
Con::warnf("MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"");
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -390,32 +437,40 @@ ConsoleMappedType(MatrixPosition, TypeMatrixPosition, Point3F, MatrixF, "")
|
||||||
|
|
||||||
ConsoleGetType( TypeMatrixPosition )
|
ConsoleGetType( TypeMatrixPosition )
|
||||||
{
|
{
|
||||||
F32 *col = (F32 *) dptr + 3;
|
F32* col = (F32*)dptr + 3;
|
||||||
static const U32 bufSize = 256;
|
static const U32 bufSize = 256;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||||
if(col[12] == 1.f)
|
Point4F pos(col[0], col[4], col[8], col[12]);
|
||||||
dSprintf(returnBuffer, bufSize, "%g %g %g", col[0], col[4], col[8]);
|
|
||||||
|
if (col[12] == 1.0f)
|
||||||
|
PropertyInfo::FormatPropertyBuffer<F32, 3>(&pos, returnBuffer, bufSize);
|
||||||
else
|
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;
|
return returnBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeMatrixPosition )
|
ConsoleSetType( TypeMatrixPosition )
|
||||||
{
|
{
|
||||||
F32 *col = ((F32 *) dptr) + 3;
|
if (argc >= 1)
|
||||||
if (argc == 1)
|
|
||||||
{
|
{
|
||||||
col[0] = col[4] = col[8] = 0.f;
|
F32 parsed[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
col[12] = 1.f;
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
dSscanf(argv[0], "%g %g %g %g", &col[0], &col[4], &col[8], &col[12]);
|
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 if (argc <= 4)
|
|
||||||
{
|
Con::warnf("Matrix position must be set as { x, y, z, w } or \"x y z w\"");
|
||||||
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\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -425,42 +480,38 @@ ConsoleMappedType(MatrixRotation, TypeMatrixRotation, AngAxisF, MatrixF, "")
|
||||||
|
|
||||||
ConsoleGetType( TypeMatrixRotation )
|
ConsoleGetType( TypeMatrixRotation )
|
||||||
{
|
{
|
||||||
AngAxisF aa(*(MatrixF *) dptr);
|
AngAxisF aa(*(MatrixF*)dptr);
|
||||||
aa.axis.normalize();
|
aa.axis.normalize();
|
||||||
static const U32 bufSize = 256;
|
aa.angle = mRadToDeg(aa.angle);
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
const char* buff = PropertyInfo::FormatProperty<F32, 4>(&aa);
|
||||||
dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle));
|
return buff;
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeMatrixRotation )
|
ConsoleSetType( TypeMatrixRotation )
|
||||||
{
|
{
|
||||||
// DMM: Note that this will ONLY SET the ULeft 3x3 submatrix.
|
if (argc >= 1)
|
||||||
//
|
|
||||||
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);
|
F32 parsed[4];
|
||||||
aa.angle = mDegToRad(aa.angle);
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
}
|
char buffer[256] = { 0 };
|
||||||
else if (argc == 4)
|
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||||
{
|
|
||||||
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\"");
|
|
||||||
|
|
||||||
//
|
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed))
|
||||||
MatrixF temp;
|
{
|
||||||
aa.setMatrix(&temp);
|
AngAxisF aa(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3]));
|
||||||
|
MatrixF temp;
|
||||||
|
aa.setMatrix(&temp);
|
||||||
|
|
||||||
F32* pDst = *(MatrixF *)dptr;
|
F32* pDst = *(MatrixF*)dptr;
|
||||||
const F32* pSrc = temp;
|
const F32* pSrc = temp;
|
||||||
for (U32 i = 0; i < 3; i++)
|
for (U32 i = 0; i < 3; i++)
|
||||||
for (U32 j = 0; j < 3; j++)
|
for (U32 j = 0; j < 3; j++)
|
||||||
pDst[i*4 + j] = pSrc[i*4 + 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 )
|
ConsoleGetType( TypeAngAxisF )
|
||||||
{
|
{
|
||||||
AngAxisF* aa = ( AngAxisF* ) dptr;
|
AngAxisF* aa = ( AngAxisF* ) dptr;
|
||||||
static const U32 bufSize = 256;
|
aa->angle = mRadToDeg(aa->angle);
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
const char* buff = PropertyInfo::FormatProperty<F32, 4>(aa);
|
||||||
dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle));
|
return buff;
|
||||||
return returnBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeAngAxisF )
|
ConsoleSetType( TypeAngAxisF )
|
||||||
{
|
{
|
||||||
// DMM: Note that this will ONLY SET the ULeft 3x3 submatrix.
|
if (argc >= 1)
|
||||||
//
|
|
||||||
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);
|
F32 parsed[4];
|
||||||
aa->angle = mDegToRad(aa->angle);
|
// 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 if (argc == 4)
|
|
||||||
{
|
Con::warnf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\"");
|
||||||
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\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -510,38 +560,35 @@ ImplementConsoleTypeCasters( TypeTransformF, TransformF )
|
||||||
|
|
||||||
ConsoleGetType( TypeTransformF )
|
ConsoleGetType( TypeTransformF )
|
||||||
{
|
{
|
||||||
TransformF* aa = ( TransformF* ) dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 7>(dptr);
|
||||||
static const U32 bufSize = 256;
|
return buff;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeTransformF )
|
ConsoleSetType( TypeTransformF )
|
||||||
{
|
{
|
||||||
TransformF* aa = ( TransformF* ) dptr;
|
if(argc >= 1)
|
||||||
if( argc == 1 )
|
|
||||||
{
|
{
|
||||||
U32 count = dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g",
|
F32 parsed[7];
|
||||||
&aa->mPosition.x, &aa->mPosition.y, &aa->mPosition.z,
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
&aa->mOrientation.axis.x, &aa->mOrientation.axis.y, &aa->mOrientation.axis.z, &aa->mOrientation.angle );
|
char buffer[256] = { 0 };
|
||||||
|
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||||
|
|
||||||
aa->mHasRotation = ( count == 7 );
|
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 if( argc == 7 )
|
|
||||||
{
|
Con::warnf("TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\"");
|
||||||
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\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -554,32 +601,33 @@ ImplementConsoleTypeCasters( TypeBox3F, Box3F )
|
||||||
|
|
||||||
ConsoleGetType( TypeBox3F )
|
ConsoleGetType( TypeBox3F )
|
||||||
{
|
{
|
||||||
const Box3F* pBox = (const Box3F*)dptr;
|
const char* buff = PropertyInfo::FormatProperty<F32, 6>(dptr);
|
||||||
|
return buff;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSetType( TypeBox3F )
|
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)
|
if (PropertyInfo::ParseProperty<F32, 6>(buffer, parsed))
|
||||||
{
|
{
|
||||||
U32 args = dSscanf(argv[0], "%g %g %g %g %g %g",
|
Box3F* pDst = (Box3F*)dptr;
|
||||||
&pDst->minExtents.x, &pDst->minExtents.y, &pDst->minExtents.z,
|
pDst->minExtents.x = parsed[0];
|
||||||
&pDst->maxExtents.x, &pDst->maxExtents.y, &pDst->maxExtents.z);
|
pDst->minExtents.y = parsed[1];
|
||||||
AssertWarn(args == 6, "Warning, box probably not read properly");
|
pDst->minExtents.z = parsed[2];
|
||||||
}
|
pDst->maxExtents.x = parsed[3];
|
||||||
else
|
pDst->maxExtents.y = parsed[4];
|
||||||
{
|
pDst->maxExtents.z = parsed[5];
|
||||||
Con::printf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\"");
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Con::warnf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -591,31 +639,39 @@ ImplementConsoleTypeCasters( TypeEaseF, EaseF )
|
||||||
|
|
||||||
ConsoleGetType( TypeEaseF )
|
ConsoleGetType( TypeEaseF )
|
||||||
{
|
{
|
||||||
const EaseF* pEase = (const EaseF*)dptr;
|
|
||||||
|
|
||||||
static const U32 bufSize = 256;
|
static const U32 bufSize = 256;
|
||||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
char* buffer = Con::getReturnBuffer(bufSize);
|
||||||
dSprintf(returnBuffer, bufSize, "%d %d %g %g",
|
|
||||||
pEase->mDir, pEase->mType, pEase->mParam[0], pEase->mParam[1]);
|
|
||||||
|
|
||||||
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 )
|
ConsoleSetType( TypeEaseF )
|
||||||
{
|
{
|
||||||
EaseF* pDst = (EaseF*)dptr;
|
if (argc >= 1)
|
||||||
|
{
|
||||||
|
F32 parsed[4];
|
||||||
|
parsed[2] = -1.0f;
|
||||||
|
parsed[3] = -1.0f;
|
||||||
|
|
||||||
// defaults...
|
// Combine argv into a single space-separated string if argc > 1
|
||||||
pDst->mParam[0] = -1.0f;
|
char buffer[256] = { 0 };
|
||||||
pDst->mParam[1] = -1.0f;
|
|
||||||
if (argc == 1) {
|
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||||
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]);
|
// same as matrix do not hard fail based on count!
|
||||||
if( args < 2 )
|
PropertyInfo::ParseProperty<F32, 4>(buffer, parsed);
|
||||||
Con::warnf( "Warning, EaseF probably not read properly" );
|
{
|
||||||
} else {
|
((EaseF*)dptr)->set(mRound(parsed[0]), mRound(parsed[1]), parsed[2], parsed[3]);
|
||||||
Con::printf("EaseF must be set as \"dir type [param0 param1]\"");
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Con::warnf("EaseF must be set as \"dir type [param0 param1]\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -633,12 +689,12 @@ ConsoleGetType(TypeRotationF)
|
||||||
if (pt->mRotationType == RotationF::Euler)
|
if (pt->mRotationType == RotationF::Euler)
|
||||||
{
|
{
|
||||||
EulerF out = pt->asEulerF(RotationF::Degrees);
|
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)
|
else if (pt->mRotationType == RotationF::AxisAngle)
|
||||||
{
|
{
|
||||||
AngAxisF out = pt->asAxisAngle(RotationF::Degrees);
|
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;
|
return returnBuffer;
|
||||||
|
|
@ -646,34 +702,36 @@ ConsoleGetType(TypeRotationF)
|
||||||
|
|
||||||
ConsoleSetType(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)
|
if (elements == 3)
|
||||||
{
|
{
|
||||||
EulerF in;
|
F32 parsed[3];
|
||||||
dSscanf(argv[0], "%g %g %g", &in.x, &in.y, &in.z);
|
if(PropertyInfo::ParseProperty<F32, 3>(buffer, parsed))
|
||||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
{
|
||||||
|
EulerF in(parsed[0], parsed[1], parsed[2]);
|
||||||
|
((RotationF*)dptr)->set(in, RotationF::Degrees);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (elements == 4)
|
||||||
{
|
{
|
||||||
AngAxisF in;
|
F32 parsed[4];
|
||||||
dSscanf(argv[0], "%g %g %g %g", &in.axis.x, &in.axis.y, &in.axis.z, &in.angle);
|
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed))
|
||||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
{
|
||||||
|
AngAxisF in(Point3F(parsed[0], parsed[1], parsed[2]), parsed[3]);
|
||||||
|
((RotationF*)dptr)->set(in, RotationF::Degrees);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (argc == 3)
|
|
||||||
{
|
Con::warnf("RotationF must be set as { x, y, z, w } or \"x y z w\"");
|
||||||
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\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "gui/buttons/guiButtonCtrl.h"
|
#include "gui/buttons/guiButtonCtrl.h"
|
||||||
#include "gui/worldEditor/undoActions.h"
|
#include "gui/worldEditor/undoActions.h"
|
||||||
#include "T3D/gameBase/gameConnection.h"
|
#include "T3D/gameBase/gameConnection.h"
|
||||||
|
#include "T3D/AI/AIController.h"
|
||||||
|
|
||||||
IMPLEMENT_CONOBJECT(GuiNavEditorCtrl);
|
IMPLEMENT_CONOBJECT(GuiNavEditorCtrl);
|
||||||
|
|
||||||
|
|
@ -225,8 +226,29 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos)
|
||||||
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
|
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
|
||||||
missionCleanup->addObject(obj);
|
missionCleanup->addObject(obj);
|
||||||
}
|
}
|
||||||
mPlayer = static_cast<AIPlayer*>(obj);
|
mPlayer = obj;
|
||||||
Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags()));
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(obj);
|
||||||
|
if (asAIPlayer) //try direct
|
||||||
|
{
|
||||||
|
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(obj);
|
||||||
|
if (sbo->getAIController())
|
||||||
|
{
|
||||||
|
if (sbo->getAIController()->mControllerData)
|
||||||
|
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
Con::executef(this, "onPlayerSelected");
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,16 +405,56 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
|
||||||
// Select/move character
|
// Select/move character
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri))
|
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
|
||||||
{
|
{
|
||||||
if(dynamic_cast<AIPlayer*>(ri.object))
|
if(ri.object)
|
||||||
{
|
{
|
||||||
mPlayer = dynamic_cast<AIPlayer*>(ri.object);
|
mPlayer = ri.object;
|
||||||
Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags()));
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
|
||||||
|
if (asAIPlayer) //try direct
|
||||||
|
{
|
||||||
|
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
|
||||||
|
if (sbo->getAIController())
|
||||||
|
{
|
||||||
|
if (sbo->getAIController()->mControllerData)
|
||||||
|
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
Con::executef(this, "onPlayerSelected");
|
||||||
|
}
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
|
||||||
|
{
|
||||||
|
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
|
||||||
|
if (asAIPlayer) //try direct
|
||||||
|
{
|
||||||
|
#ifdef TORQUE_NAVIGATION_ENABLED
|
||||||
|
asAIPlayer->setPathDestination(ri.point);
|
||||||
|
#else
|
||||||
|
asAIPlayer->setMoveDestination(ri.point,false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
|
||||||
|
if (sbo->getAIController())
|
||||||
|
{
|
||||||
|
if (sbo->getAIController()->mControllerData)
|
||||||
|
sbo->getAIController()->getNav()->setPathDestination(ri.point, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
|
|
||||||
mPlayer->setPathDestination(ri.point);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -455,8 +517,8 @@ void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
|
||||||
|
|
||||||
if(mMode == mTestMode)
|
if(mMode == mTestMode)
|
||||||
{
|
{
|
||||||
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri))
|
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
|
||||||
mCurPlayer = dynamic_cast<AIPlayer*>(ri.object);
|
mCurPlayer = ri.object;
|
||||||
else
|
else
|
||||||
mCurPlayer = NULL;
|
mCurPlayer = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,8 @@ protected:
|
||||||
/// @name Test mode
|
/// @name Test mode
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
SimObjectPtr<AIPlayer> mPlayer;
|
SimObjectPtr<SceneObject> mPlayer;
|
||||||
SimObjectPtr<AIPlayer> mCurPlayer;
|
SimObjectPtr<SceneObject> mCurPlayer;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,8 +111,12 @@ function handleConnectionErrorMessage(%msgType, %msgString, %msgError)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Disconnect
|
// Disconnect
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
function disconnect()
|
function disconnect()
|
||||||
|
{
|
||||||
|
callOnModules("disconnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
function Core_ClientServer::disconnect(%this)
|
||||||
{
|
{
|
||||||
// We need to stop the client side simulation
|
// We need to stop the client side simulation
|
||||||
// else physics resources will not cleanup properly.
|
// else physics resources will not cleanup properly.
|
||||||
|
|
@ -158,3 +162,16 @@ function disconnectedCleanup()
|
||||||
|
|
||||||
moduleExec("onDestroyClientConnection", "Game");
|
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;
|
||||||
|
}
|
||||||
|
|
@ -276,6 +276,8 @@ function GameConnection::onPostSpawn( %this )
|
||||||
callOnObjectList("onPostSpawn", %modulesIdList, %this);
|
callOnObjectList("onPostSpawn", %modulesIdList, %this);
|
||||||
else
|
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)
|
function GameConnectionListener::onPostSpawnComplete(%this, %client)
|
||||||
|
|
|
||||||
|
|
@ -169,3 +169,20 @@ datablock LightAnimData( SpinLightAnim )
|
||||||
rotKeys[2] = "az";
|
rotKeys[2] = "az";
|
||||||
rotSmooth[2] = true;
|
rotSmooth[2] = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
datablock AIPlayerControllerData( aiPlayerControl )
|
||||||
|
{
|
||||||
|
moveTolerance = 0.25; followTolerance = 1.0; mAttackRadius = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
datablock AIWheeledVehicleControllerData( aiCarControl )
|
||||||
|
{
|
||||||
|
moveTolerance = 1.0; followTolerance = 2.0; mAttackRadius = 5.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
datablock AIFlyingVehicleControllerData( aiPlaneControl )
|
||||||
|
{
|
||||||
|
moveTolerance = 2.0; followTolerance = 5.0; mAttackRadius = 10.0;
|
||||||
|
FlightFloor = 15; FlightCeiling = 150;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,17 +78,15 @@ function spawnGameObject(%name, %addToScene)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GameBaseData::onNewDataBlock(%this, %obj)
|
function GameBaseData::onNewDataBlock(%this, %obj, %reload)
|
||||||
{
|
{
|
||||||
if (%obj.firstDataCheck)
|
if (%reload)
|
||||||
{
|
{
|
||||||
if(%this.isMethod("onRemove"))
|
if(%this.isMethod("onRemove"))
|
||||||
%this.onRemove(%obj);
|
%this.onRemove(%obj);
|
||||||
if(%this.isMethod("onAdd"))
|
if(%this.isMethod("onAdd"))
|
||||||
%this.onAdd(%obj);
|
%this.onAdd(%obj);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
%obj.firstDataCheck = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveGameObject(%name, %tamlPath, %scriptPath)
|
function saveGameObject(%name, %tamlPath, %scriptPath)
|
||||||
|
|
|
||||||
11
Templates/BaseGame/game/data/DamageModel/DamageModel.module
Normal file
11
Templates/BaseGame/game/data/DamageModel/DamageModel.module
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<ModuleDefinition
|
||||||
|
ModuleId="DamageModel"
|
||||||
|
VersionId="1"
|
||||||
|
Group="Game"
|
||||||
|
scriptFile="DamageModel.tscript"
|
||||||
|
CreateFunction="onCreate"
|
||||||
|
DestroyFunction="onDestroy">
|
||||||
|
<DeclaredAssets
|
||||||
|
Extension="asset.taml"
|
||||||
|
Recurse="true"/>
|
||||||
|
</ModuleDefinition>
|
||||||
48
Templates/BaseGame/game/data/DamageModel/DamageModel.tscript
Normal file
48
Templates/BaseGame/game/data/DamageModel/DamageModel.tscript
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
function DamageModel::onCreate(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
function DamageModel::onDestroy(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when the server is initially set up by the game application
|
||||||
|
function DamageModel::initServer(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when the server is created for an actual game/map to be played
|
||||||
|
function DamageModel::onCreateGameServer(%this)
|
||||||
|
{
|
||||||
|
%this.registerDatablock("./scripts/managedData/managedParticleData");
|
||||||
|
%this.registerDatablock("./scripts/managedData/managedParticleEmitterData");
|
||||||
|
%this.queueExec("./scripts/server/utility");
|
||||||
|
%this.queueExec("./scripts/server/radiusDamage");
|
||||||
|
%this.queueExec("./scripts/server/projectile");
|
||||||
|
%this.queueExec("./scripts/server/weapon");
|
||||||
|
%this.queueExec("./scripts/server/shapeBase");
|
||||||
|
%this.queueExec("./scripts/server/vehicle");
|
||||||
|
%this.queueExec("./scripts/server/player");
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when the server is shut down due to the game/map being exited
|
||||||
|
function DamageModel::onDestroyGameServer(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when the client is initially set up by the game application
|
||||||
|
function DamageModel::initClient(%this)
|
||||||
|
{
|
||||||
|
%this.queueExec("./guis/damageGuiOverlay.gui");
|
||||||
|
%this.queueExec("./scripts/client/playGui");
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when a client connects to a server
|
||||||
|
function DamageModel::onCreateClientConnection(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called when a client disconnects from a server
|
||||||
|
function DamageModel::onDestroyClientConnection(%this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<GUIAsset canSave="true" canSaveDynamicFields="true" AssetName="damageGuiOverlay" scriptFile="@assetFile=damageGuiOverlay.gui" GUIFile="@assetFile=damageGuiOverlay.gui" VersionId="1"/>
|
||||||
|
|
@ -0,0 +1,285 @@
|
||||||
|
//--- OBJECT WRITE BEGIN ---
|
||||||
|
$guiContent = new GuiContainer(DamageGuiOverlay) {
|
||||||
|
isContainer = "1";
|
||||||
|
Profile = "GuiContentProfile";
|
||||||
|
HorizSizing = "relative";
|
||||||
|
VertSizing = "relative";
|
||||||
|
position = "0 0";
|
||||||
|
Extent = "1024 768";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "1";
|
||||||
|
Enabled = "1";
|
||||||
|
helpTag = "0";
|
||||||
|
noCursor = "1";
|
||||||
|
new GuiShapeNameHud() {
|
||||||
|
fillColor = "0 0 0 0.25";
|
||||||
|
frameColor = "0 1 0 1";
|
||||||
|
textColor = "0 1 0 1";
|
||||||
|
showFill = "0";
|
||||||
|
showFrame = "0";
|
||||||
|
verticalOffset = "0.2";
|
||||||
|
distanceFade = "0.1";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "GuiModelessDialogProfile";
|
||||||
|
HorizSizing = "width";
|
||||||
|
VertSizing = "height";
|
||||||
|
position = "0 0";
|
||||||
|
Extent = "1024 768";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiCrossHairHud(Reticle) {
|
||||||
|
damageFillColor = "0 1 0 1";
|
||||||
|
damageFrameColor = "1 0.6 0 1";
|
||||||
|
damageRect = "50 4";
|
||||||
|
damageOffset = "0 10";
|
||||||
|
bitmapAsset = "FPSEquipment:blank_image";
|
||||||
|
wrap = "0";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "GuiModelessDialogProfile";
|
||||||
|
HorizSizing = "center";
|
||||||
|
VertSizing = "center";
|
||||||
|
position = "496 368";
|
||||||
|
Extent = "32 32";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiCrossHairHud(ZoomReticle) {
|
||||||
|
damageFillColor = "0 1 0 1";
|
||||||
|
damageFrameColor = "1 0.6 0 1";
|
||||||
|
damageRect = "50 4";
|
||||||
|
damageOffset = "0 10";
|
||||||
|
bitmapAsset = "DamageModel:bino_image";
|
||||||
|
wrap = "0";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "GuiModelessDialogProfile";
|
||||||
|
HorizSizing = "width";
|
||||||
|
VertSizing = "height";
|
||||||
|
position = "0 0";
|
||||||
|
Extent = "1024 768";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "0";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapBorderCtrl(WeaponHUD) {
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "ChatHudBorderProfile";
|
||||||
|
HorizSizing = "right";
|
||||||
|
VertSizing = "top";
|
||||||
|
position = "78 693";
|
||||||
|
Extent = "124 72";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
|
||||||
|
new GuiBitmapCtrl() {
|
||||||
|
bitmap = "UI:hudfill_image";
|
||||||
|
wrap = "0";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "GuiDefaultProfile";
|
||||||
|
HorizSizing = "width";
|
||||||
|
VertSizing = "height";
|
||||||
|
position = "8 8";
|
||||||
|
Extent = "108 56";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapCtrl(PreviewImage) {
|
||||||
|
bitmapAsset = "UI:hudfill_image";
|
||||||
|
wrap = "0";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "GuiDefaultProfile";
|
||||||
|
HorizSizing = "width";
|
||||||
|
VertSizing = "height";
|
||||||
|
position = "8 8";
|
||||||
|
Extent = "108 56";
|
||||||
|
MinExtent = "8 2";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiTextCtrl(AmmoAmount) {
|
||||||
|
maxLength = "255";
|
||||||
|
Margin = "0 0 0 0";
|
||||||
|
Padding = "0 0 0 0";
|
||||||
|
AnchorTop = "0";
|
||||||
|
AnchorBottom = "0";
|
||||||
|
AnchorLeft = "0";
|
||||||
|
AnchorRight = "0";
|
||||||
|
isContainer = "0";
|
||||||
|
Profile = "HudTextItalicProfile";
|
||||||
|
HorizSizing = "right";
|
||||||
|
VertSizing = "top";
|
||||||
|
position = "40 8";
|
||||||
|
Extent = "120 16";
|
||||||
|
MinExtent = "8 8";
|
||||||
|
canSave = "1";
|
||||||
|
Visible = "1";
|
||||||
|
tooltipprofile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
new GuiHealthTextHud() {
|
||||||
|
fillColor = "0 0 0 0.65";
|
||||||
|
frameColor = "0 0 0 1";
|
||||||
|
textColor = "1 1 1 1";
|
||||||
|
warningColor = "1 0 0 1";
|
||||||
|
showFill = "1";
|
||||||
|
showFrame = "1";
|
||||||
|
showTrueValue = "0";
|
||||||
|
showEnergy = "0";
|
||||||
|
warnThreshold = "25";
|
||||||
|
pulseThreshold = "15";
|
||||||
|
pulseRate = "750";
|
||||||
|
position = "5 693";
|
||||||
|
extent = "72 72";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "top";
|
||||||
|
profile = "GuiBigTextProfile";
|
||||||
|
visible = "1";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiControl(DamageHUD) {
|
||||||
|
position = "384 256";
|
||||||
|
extent = "256 256";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "center";
|
||||||
|
vertSizing = "center";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "1";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
|
||||||
|
new GuiBitmapCtrl(DamageFront) {
|
||||||
|
bitmapAsset = "DamageModel:damageFront_image";
|
||||||
|
wrap = "0";
|
||||||
|
position = "0 0";
|
||||||
|
extent = "256 32";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "bottom";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "0";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
internalName = "Damage[Front]";
|
||||||
|
hidden = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapCtrl(DamageTop) {
|
||||||
|
bitmapAsset = "DamageModel:damageTop_image";
|
||||||
|
wrap = "0";
|
||||||
|
position = "0 0";
|
||||||
|
extent = "256 32";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "bottom";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "0";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
internalName = "Damage[Top]";
|
||||||
|
hidden = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapCtrl(DamageBottom) {
|
||||||
|
bitmapAsset = "DamageModel:damageBottom_image";
|
||||||
|
wrap = "0";
|
||||||
|
position = "0 224";
|
||||||
|
extent = "256 32";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "bottom";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "0";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
internalName = "Damage[Bottom]";
|
||||||
|
hidden = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapCtrl(DamageLeft) {
|
||||||
|
bitmapAsset = "DamageModel:damageLeft_image";
|
||||||
|
wrap = "0";
|
||||||
|
position = "0 0";
|
||||||
|
extent = "32 256";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "bottom";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "0";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
internalName = "Damage[Left]";
|
||||||
|
hidden = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
new GuiBitmapCtrl(DamageRight) {
|
||||||
|
bitmapAsset = "DamageModel:damageRight_image";
|
||||||
|
wrap = "0";
|
||||||
|
position = "224 0";
|
||||||
|
extent = "32 256";
|
||||||
|
minExtent = "8 2";
|
||||||
|
horizSizing = "right";
|
||||||
|
vertSizing = "bottom";
|
||||||
|
profile = "GuiDefaultProfile";
|
||||||
|
visible = "0";
|
||||||
|
active = "1";
|
||||||
|
tooltipProfile = "GuiToolTipProfile";
|
||||||
|
hovertime = "1000";
|
||||||
|
isContainer = "0";
|
||||||
|
internalName = "Damage[Right]";
|
||||||
|
hidden = "1";
|
||||||
|
canSave = "1";
|
||||||
|
canSaveDynamicFields = "0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//--- OBJECT WRITE END ---
|
||||||
BIN
Templates/BaseGame/game/data/DamageModel/images/crosshair.png
Normal file
BIN
Templates/BaseGame/game/data/DamageModel/images/crosshair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 144 B |
Binary file not shown.
|
After Width: | Height: | Size: 134 B |
|
|
@ -0,0 +1,3 @@
|
||||||
|
<ImageAsset
|
||||||
|
AssetName="crosshair_blue_image"
|
||||||
|
imageFile="@assetFile=crosshair_blue.png"/>
|
||||||
BIN
Templates/BaseGame/game/data/DamageModel/images/damageBottom.png
Normal file
BIN
Templates/BaseGame/game/data/DamageModel/images/damageBottom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,3 @@
|
||||||
|
<ImageAsset
|
||||||
|
AssetName="damageBottom_image"
|
||||||
|
imageFile="@assetFile=damageBottom.png"/>
|
||||||
BIN
Templates/BaseGame/game/data/DamageModel/images/damageFront.png
Normal file
BIN
Templates/BaseGame/game/data/DamageModel/images/damageFront.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,3 @@
|
||||||
|
<ImageAsset
|
||||||
|
AssetName="damageFront_image"
|
||||||
|
imageFile="@assetFile=damageFront.png"/>
|
||||||
BIN
Templates/BaseGame/game/data/DamageModel/images/damageLeft.png
Normal file
BIN
Templates/BaseGame/game/data/DamageModel/images/damageLeft.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,3 @@
|
||||||
|
<ImageAsset
|
||||||
|
AssetName="damageLeft_image"
|
||||||
|
imageFile="@assetFile=damageLeft.png"/>
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue