mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
Merge remote-tracking branch 'upstream/development' into imageAsset_refactor_rev3
This commit is contained in:
commit
e1c01cd49a
|
|
@ -58,7 +58,7 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async"
|
|||
|
||||
torqueAddSourceDirectories("platform/nativeDialogs")
|
||||
# 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/lighting"
|
||||
"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");
|
||||
|
||||
SubScene::SubScene() :
|
||||
mLevelAssetId(StringTable->EmptyString()),
|
||||
mSubSceneAssetId(StringTable->EmptyString()),
|
||||
mGameModesNames(StringTable->EmptyString()),
|
||||
mScopeDistance(-1),
|
||||
mLoaded(false),
|
||||
|
|
@ -67,7 +67,7 @@ void SubScene::initPersistFields()
|
|||
{
|
||||
addGroup("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("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
|
||||
endGroup("SubScene");
|
||||
|
|
@ -265,13 +265,13 @@ void SubScene::processTick(const Move* move)
|
|||
|
||||
void SubScene::_onFileChanged(const Torque::Path& path)
|
||||
{
|
||||
if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
|
||||
if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path)
|
||||
return;
|
||||
|
||||
if (mSaving)
|
||||
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);
|
||||
_loadFile(false);
|
||||
|
|
@ -307,9 +307,9 @@ void SubScene::_closeFile(bool removeFileNotify)
|
|||
|
||||
_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();
|
||||
|
|
@ -319,18 +319,18 @@ void SubScene::_loadFile(bool addFileNotify)
|
|||
{
|
||||
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;
|
||||
|
||||
String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
|
||||
String evalCmd = String::ToString("exec(\"%s\");", mSubSceneAsset->getLevelPath());
|
||||
|
||||
String instantGroup = Con::getVariable("InstantGroup");
|
||||
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());
|
||||
|
||||
if (addFileNotify)
|
||||
Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||
Torque::FS::AddChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||
}
|
||||
|
||||
void SubScene::load()
|
||||
|
|
@ -432,7 +432,7 @@ bool SubScene::save()
|
|||
if (size() == 0 || !isLoaded())
|
||||
return false;
|
||||
|
||||
if (mLevelAsset.isNull())
|
||||
if (mSubSceneAsset.isNull())
|
||||
return false;
|
||||
|
||||
if (mSaving)
|
||||
|
|
@ -446,7 +446,7 @@ bool SubScene::save()
|
|||
|
||||
PersistenceManager prMger;
|
||||
|
||||
StringTableEntry levelPath = mLevelAsset->getLevelPath();
|
||||
StringTableEntry levelPath = mSubSceneAsset->getLevelPath();
|
||||
|
||||
FileStream fs;
|
||||
fs.open(levelPath, Torque::FS::File::Write);
|
||||
|
|
@ -460,7 +460,7 @@ bool SubScene::save()
|
|||
{
|
||||
if ((*itr)->isMethod("onSaving"))
|
||||
{
|
||||
Con::executef((*itr), "onSaving", mLevelAssetId);
|
||||
Con::executef((*itr), "onSaving", mSubSceneAssetId);
|
||||
}
|
||||
|
||||
if (childObj->getGroup() == this)
|
||||
|
|
@ -481,14 +481,14 @@ bool SubScene::save()
|
|||
bool saveSuccess = false;
|
||||
|
||||
//Get the level asset
|
||||
if (mLevelAsset.isNull())
|
||||
if (mSubSceneAsset.isNull())
|
||||
return saveSuccess;
|
||||
|
||||
//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
|
||||
saveSuccess = mLevelAsset->saveAsset();
|
||||
saveSuccess = mSubSceneAsset->saveAsset();
|
||||
|
||||
mSaving = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
#ifndef SCENE_GROUP_H
|
||||
#include "SceneGroup.h"
|
||||
#endif
|
||||
#ifndef LEVEL_ASSET_H
|
||||
#include "assets/LevelAsset.h"
|
||||
#ifndef SUBSCENE_ASSET_H
|
||||
#include "assets/SubSceneAsset.h"
|
||||
#endif
|
||||
|
||||
class GameMode;
|
||||
|
|
@ -21,13 +21,13 @@ public:
|
|||
NextFreeMask = Parent::NextFreeMask << 0
|
||||
};
|
||||
|
||||
void onLevelChanged() {}
|
||||
void onSubSceneChanged() {}
|
||||
|
||||
protected:
|
||||
static bool smTransformChildren;
|
||||
|
||||
private:
|
||||
DECLARE_LEVELASSET(SubScene, Level, onLevelChanged);
|
||||
DECLARE_SUBSCENEASSET(SubScene, SubScene, onSubSceneChanged);
|
||||
|
||||
StringTableEntry mGameModesNames;
|
||||
Vector<GameMode*> mGameModesList;
|
||||
|
|
@ -61,7 +61,7 @@ public:
|
|||
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); }
|
||||
StringTableEntry getTypeHint() const override { return (getSubSceneAsset()) ? getSubSceneAsset()->getAssetName() : StringTable->EmptyString(); }
|
||||
|
||||
// SimObject
|
||||
bool onAdd() override;
|
||||
|
|
@ -122,6 +122,6 @@ public:
|
|||
|
||||
DECLARE_CALLBACK(void, onLoaded, ());
|
||||
DECLARE_CALLBACK(void, onUnloaded, ());
|
||||
DECLARE_ASSET_SETGET(SubScene, Level);
|
||||
DECLARE_ASSET_SETGET(SubScene, SubScene);
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -178,3 +178,17 @@ void CppAsset::onAssetRefresh(void)
|
|||
|
||||
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);
|
||||
inline StringTableEntry getHeaderFile(void) const { return mHeaderFile; };
|
||||
|
||||
inline StringTableEntry getCppFilePath(void) const { return mCodePath; };
|
||||
inline StringTableEntry getHeaderFilePath(void) const { return mHeaderPath; };
|
||||
|
||||
protected:
|
||||
void initializeAsset(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 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 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();
|
||||
mLevelFile = StringTable->EmptyString();
|
||||
|
|
@ -117,7 +117,6 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
|
|||
mNavmeshPath = StringTable->EmptyString();
|
||||
|
||||
mGameModesNames = StringTable->EmptyString();
|
||||
mMainLevelAsset = StringTable->EmptyString();
|
||||
|
||||
mEditorFile = StringTable->EmptyString();
|
||||
mBakedSceneFile = StringTable->EmptyString();
|
||||
|
|
@ -158,7 +157,6 @@ void LevelAsset::initPersistFields()
|
|||
addProtectedField("BakedSceneFile", TypeAssetLooseFilePath, Offset(mBakedSceneFile, LevelAsset),
|
||||
&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");
|
||||
}
|
||||
|
||||
|
|
@ -481,15 +479,8 @@ GuiControl* GuiInspectorTypeLevelAssetPtr::constructEditControl()
|
|||
// Create "Open in Editor" button
|
||||
mEditButton = new GuiBitmapButtonCtrl();
|
||||
|
||||
String setSubSceneValue = "$createLevelAssetIsSubScene = \"\";";
|
||||
if(dynamic_cast<SubScene*>(mInspector->getInspectObject()) != NULL)
|
||||
{
|
||||
setSubSceneValue = "$createLevelAssetIsSubScene = true;";
|
||||
}
|
||||
|
||||
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; %s AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
|
||||
getIdString(),
|
||||
setSubSceneValue.c_str());
|
||||
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
|
||||
getIdString());
|
||||
mEditButton->setField("Command", szBuffer);
|
||||
|
||||
char bitmapName[512] = "ToolsModule:iconAdd_image";
|
||||
|
|
|
|||
|
|
@ -66,9 +66,6 @@ class LevelAsset : public AssetBase
|
|||
StringTableEntry mEditorFile;
|
||||
StringTableEntry mBakedSceneFile;
|
||||
|
||||
bool mIsSubLevel;
|
||||
StringTableEntry mMainLevelAsset;
|
||||
|
||||
StringTableEntry mGameModesNames;
|
||||
|
||||
Vector<AssetBase*> mAssetDependencies;
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ public: \
|
|||
|
||||
#pragma region Arrayed Asset Macros
|
||||
|
||||
#define DECLARE_SHAPEASSET_ARRAY(className,name,max) public: \
|
||||
#define DECLARE_SHAPEASSET_ARRAY(className,name,max,changeFunc) public: \
|
||||
static const U32 sm##name##Count = max;\
|
||||
Resource<TSShape>m##name[max];\
|
||||
StringTableEntry m##name##Name[max]; \
|
||||
|
|
@ -384,6 +384,10 @@ public: \
|
|||
\
|
||||
bool _set##name(StringTableEntry _in, const U32& index)\
|
||||
{\
|
||||
if (m##name##Asset[index].notNull())\
|
||||
{\
|
||||
m##name##Asset[index]->getChangedSignal().remove(this, &className::changeFunc);\
|
||||
}\
|
||||
if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\
|
||||
{\
|
||||
if(index >= sm##name##Count || index < 0)\
|
||||
|
|
@ -430,6 +434,8 @@ public: \
|
|||
if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\
|
||||
{\
|
||||
m##name[index] = m##name##Asset[index]->getShapeResource();\
|
||||
\
|
||||
m##name##Asset[index]->getChangedSignal().notify(this, &className::changeFunc);\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
|
|
|
|||
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 ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
|
||||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,10 @@ public:
|
|||
void onPerformSubstitutions() override;
|
||||
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)
|
||||
return;
|
||||
ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
||||
|
||||
//cover the case of a connection controling an object in turn controlling another
|
||||
if (control && control->getControlObject())
|
||||
control = control->getControlObject();
|
||||
|
||||
if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType)))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,12 @@ void GuiHealthTextHud::onRender(Point2I offset, const RectI &updateRect)
|
|||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return;
|
||||
ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
||||
ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
||||
|
||||
//cover the case of a connection controling an object in turn controlling another
|
||||
if (control && control->getControlObject())
|
||||
control = control->getControlObject();
|
||||
|
||||
if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType)))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1127,7 +1127,7 @@ bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
|
||||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,10 @@ public:
|
|||
ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
|
||||
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.
|
||||
_freeCells();
|
||||
|
||||
if ( isProperlyAdded() )
|
||||
if (isProperlyAdded())
|
||||
{
|
||||
_initMaterial();
|
||||
_initShapes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ protected:
|
|||
RectF mBillboardRects[MAX_COVERTYPES];
|
||||
|
||||
/// The cover shape filenames.
|
||||
DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES);
|
||||
DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES, onShapeChanged);
|
||||
DECLARE_ASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1);
|
||||
|
||||
/// The cover shape instances.
|
||||
|
|
@ -409,6 +409,12 @@ protected:
|
|||
S32 randSeed );
|
||||
|
||||
void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
||||
|
||||
void onShapeChanged()
|
||||
{
|
||||
_initShapes();
|
||||
setMaskBits(U32(-1));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GROUNDCOVER_H_
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1134,7 +1134,7 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
return true;
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
return false;
|
||||
|
||||
// Todo: Uncomment if this is a "leaf" class
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
initMaterials();
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
return false;
|
||||
|
||||
// Todo: Uncomment if this is a "leaf" class
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ),
|
|||
"}\n\n"
|
||||
"@endtsexample\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj ), ( obj ),
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj, bool reload), ( obj, reload),
|
||||
"@brief Called when the object has a new datablock assigned.\n\n"
|
||||
"@param obj the GameBase object\n\n"
|
||||
"@see onAdd for an example\n" );
|
||||
|
|
@ -512,12 +512,12 @@ void GameBase::scriptOnAdd()
|
|||
mDataBlock->onAdd_callback( this );
|
||||
}
|
||||
|
||||
void GameBase::scriptOnNewDataBlock()
|
||||
void GameBase::scriptOnNewDataBlock(bool reload)
|
||||
{
|
||||
// Script onNewDataBlock() must be called by the leaf class
|
||||
// after everything is loaded.
|
||||
if (mDataBlock && !isGhost())
|
||||
mDataBlock->onNewDataBlock_callback( this );
|
||||
mDataBlock->onNewDataBlock_callback( this, reload);
|
||||
}
|
||||
|
||||
void GameBase::scriptOnRemove()
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ public:
|
|||
/// @{
|
||||
DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onRemove, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj, bool reload) );
|
||||
DECLARE_CALLBACK( void, onMount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) );
|
||||
DECLARE_CALLBACK( void, onUnmount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) );
|
||||
/// @}
|
||||
|
|
@ -299,7 +299,7 @@ public:
|
|||
/// Executes the 'onNewDataBlock' script function for this object.
|
||||
///
|
||||
/// @note This must be called after everything is loaded.
|
||||
void scriptOnNewDataBlock();
|
||||
void scriptOnNewDataBlock(bool reload = false);
|
||||
|
||||
/// Executes the 'onRemove' script function for this object.
|
||||
/// @note This must be called while the object is still valid
|
||||
|
|
|
|||
|
|
@ -669,6 +669,7 @@ static void RegisterGameFunctions()
|
|||
Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType);
|
||||
// PATHSHAPE END
|
||||
Con::setIntVariable("$TypeMasks::TurretObjectType", TurretObjectType);
|
||||
Con::setIntVariable("$TypeMasks::AIObjectType", AIObjectType);
|
||||
|
||||
Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut,
|
||||
"InOut ease for curve movement.\n"
|
||||
|
|
|
|||
|
|
@ -422,7 +422,7 @@ bool Item::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
return false;
|
||||
|
||||
if (!mSubclassItemHandlesScene)
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
|
||||
if ( isProperlyAdded() )
|
||||
_updatePhysics();
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
mDataBlock = dynamic_cast<MissionMarkerData*>( dptr );
|
||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return(false);
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ enum SceneObjectTypes
|
|||
/// @see TurretShape
|
||||
TurretObjectType = BIT(29),
|
||||
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 ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ bool PathShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,10 @@ public:
|
|||
void packData( BitStream *stream ) override;
|
||||
void unpackData( BitStream *stream ) override;
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
|
||||
DECLARE_CONOBJECT( PhysicsDebrisData );
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,10 @@ public:
|
|||
SimObjectRef< ExplosionData > explosion;
|
||||
SimObjectRef< PhysicsShapeData > destroyedShape;
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
};
|
||||
|
||||
typedef PhysicsShapeData::SimType PhysicsSimType;
|
||||
|
|
|
|||
|
|
@ -460,7 +460,7 @@ PlayerData::PlayerData()
|
|||
jumpTowardsNormal = true;
|
||||
|
||||
physicsPlayerType = StringTable->EmptyString();
|
||||
|
||||
mControlMap = StringTable->EmptyString();
|
||||
dMemset( actionList, 0, sizeof(actionList) );
|
||||
}
|
||||
|
||||
|
|
@ -739,7 +739,9 @@ void PlayerData::initPersistFields()
|
|||
endGroup( "Camera" );
|
||||
|
||||
addGroup( "Movement" );
|
||||
|
||||
addField("controlMap", TypeString, Offset(mControlMap, PlayerData),
|
||||
"@brief movemap used by these types of objects.\n\n");
|
||||
|
||||
addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat,
|
||||
"@brief Maximum height the player can step up.\n\n"
|
||||
"The player will automatically step onto changes in ground height less "
|
||||
|
|
@ -1640,7 +1642,6 @@ Player::Player()
|
|||
mLastAbsoluteYaw = 0.0f;
|
||||
mLastAbsolutePitch = 0.0f;
|
||||
mLastAbsoluteRoll = 0.0f;
|
||||
|
||||
afx_init();
|
||||
}
|
||||
|
||||
|
|
@ -1738,7 +1739,6 @@ bool Player::onAdd()
|
|||
world );
|
||||
mPhysicsRep->setTransform( getTransform() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1923,7 +1923,7 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
onScaleChanged();
|
||||
resetWorldBox();
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
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)
|
||||
{
|
||||
if (state != mState) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ class Player;
|
|||
class OpenVRTrackedObject;
|
||||
#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 {
|
||||
|
|
@ -76,7 +82,7 @@ struct PlayerData: public ShapeBaseData {
|
|||
/// that we don't create a TSThread on the player if we don't
|
||||
/// need to.
|
||||
|
||||
DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages); ///< Used to render with mounted images in first person [optional]
|
||||
DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages, onShapeChanged); ///< Used to render with mounted images in first person [optional]
|
||||
DECLARE_ASSET_ARRAY_SETGET(PlayerData, ShapeFP);
|
||||
|
||||
StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify
|
||||
|
|
@ -346,7 +352,7 @@ struct PlayerData: public ShapeBaseData {
|
|||
|
||||
// Jump off surfaces at their normal rather than straight up
|
||||
bool jumpTowardsNormal;
|
||||
|
||||
StringTableEntry mControlMap;
|
||||
// For use if/when mPhysicsPlayer is created
|
||||
StringTableEntry physicsPlayerType;
|
||||
|
||||
|
|
@ -366,6 +372,11 @@ struct PlayerData: public ShapeBaseData {
|
|||
void packData(BitStream* stream) override;
|
||||
void unpackData(BitStream* stream) override;
|
||||
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
|
||||
/// @name Callbacks
|
||||
/// @{
|
||||
DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) );
|
||||
|
|
@ -753,8 +764,6 @@ public:
|
|||
Point3F getMomentum() const override;
|
||||
void setMomentum(const Point3F &momentum) 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?
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include "T3D/decal/decalData.h"
|
||||
#include "T3D/lightDescription.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/rigidShape.h"
|
||||
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(ProjectileData);
|
||||
|
|
@ -163,6 +164,7 @@ ProjectileData::ProjectileData()
|
|||
scale.set( 1.0f, 1.0f, 1.0f );
|
||||
|
||||
isBallistic = false;
|
||||
mExplodeOnTmeout = false;
|
||||
|
||||
velInheritFactor = 1.0f;
|
||||
muzzleVelocity = 50;
|
||||
|
|
@ -203,6 +205,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G
|
|||
muzzleVelocity = other.muzzleVelocity;
|
||||
impactForce = other.impactForce;
|
||||
isBallistic = other.isBallistic;
|
||||
mExplodeOnTmeout = other.mExplodeOnTmeout;
|
||||
bounceElasticity = other.bounceElasticity;
|
||||
bounceFriction = other.bounceFriction;
|
||||
gravityMod = other.gravityMod;
|
||||
|
|
@ -285,6 +288,8 @@ void ProjectileData::initPersistFields()
|
|||
addProtectedFieldV("fadeDelay", TypeRangedS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, &CommonValidators::NaturalNumber,
|
||||
"@brief Amount of time, in milliseconds, before the projectile begins to fade out.\n\n"
|
||||
"This value must be smaller than the projectile's lifetime to have an affect.");
|
||||
addField("explodeOnTmeout", TypeBool, Offset(mExplodeOnTmeout, ProjectileData),
|
||||
"@brief Detetmines if the projectile should explode on timeout");
|
||||
addField("isBallistic", TypeBool, Offset(isBallistic, ProjectileData),
|
||||
"@brief Detetmines if the projectile should be affected by gravity and whether or not "
|
||||
"it bounces before exploding.\n\n");
|
||||
|
|
@ -455,13 +460,14 @@ void ProjectileData::packData(BitStream* stream)
|
|||
stream->write(armingDelay);
|
||||
stream->write(fadeDelay);
|
||||
|
||||
stream->writeFlag(mExplodeOnTmeout);
|
||||
if(stream->writeFlag(isBallistic))
|
||||
{
|
||||
stream->write(gravityMod);
|
||||
stream->write(bounceElasticity);
|
||||
stream->write(bounceFriction);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ProjectileData::unpackData(BitStream* stream)
|
||||
|
|
@ -514,6 +520,7 @@ void ProjectileData::unpackData(BitStream* stream)
|
|||
stream->read(&armingDelay);
|
||||
stream->read(&fadeDelay);
|
||||
|
||||
mExplodeOnTmeout = stream->readFlag();
|
||||
isBallistic = stream->readFlag();
|
||||
if(isBallistic)
|
||||
{
|
||||
|
|
@ -611,6 +618,7 @@ Projectile::Projectile()
|
|||
mProjectileShape( NULL ),
|
||||
mActivateThread( NULL ),
|
||||
mMaintainThread( NULL ),
|
||||
mHasHit(false),
|
||||
mHasExploded( false ),
|
||||
mFadeValue( 1.0f )
|
||||
{
|
||||
|
|
@ -1128,10 +1136,18 @@ void Projectile::processTick( const Move *move )
|
|||
|
||||
void Projectile::simulate( F32 dt )
|
||||
{
|
||||
if ( isServerObject() && mCurrTick >= mDataBlock->lifetime )
|
||||
if ( isServerObject() )
|
||||
{
|
||||
deleteObject();
|
||||
return;
|
||||
if (mCurrTick >= (mDataBlock->lifetime - TickMs))
|
||||
{
|
||||
if (mDataBlock->mExplodeOnTmeout)
|
||||
explode(mCurrPosition, Point3F::UnitZ, VehicleObjectType);
|
||||
}
|
||||
if (mCurrTick >= mDataBlock->lifetime || (mHasHit && mCurrTick < mDataBlock->armingDelay))
|
||||
{
|
||||
deleteObject();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mHasExploded )
|
||||
|
|
@ -1167,9 +1183,16 @@ void Projectile::simulate( F32 dt )
|
|||
|
||||
if ( mPhysicsWorld )
|
||||
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
|
||||
else
|
||||
else
|
||||
{
|
||||
hit = getContainer()->castRay(oldPosition, newPosition, dynamicCollisionMask | staticCollisionMask, &rInfo);
|
||||
|
||||
if (hit && rInfo.object->getTypeMask() & VehicleObjectType)
|
||||
{
|
||||
RigidShape* aRigid = dynamic_cast<RigidShape*>(rInfo.object);
|
||||
if (aRigid)
|
||||
aRigid->applyImpulse(rInfo.point, Point3F(newPosition - oldPosition) * mDataBlock->impactForce);
|
||||
}
|
||||
}
|
||||
if ( hit )
|
||||
{
|
||||
// make sure the client knows to bounce
|
||||
|
|
@ -1237,6 +1260,8 @@ void Projectile::simulate( F32 dt )
|
|||
else
|
||||
{
|
||||
mCurrVelocity = Point3F::Zero;
|
||||
newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f;
|
||||
mHasHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ public:
|
|||
/// Force imparted on a hit object.
|
||||
F32 impactForce;
|
||||
|
||||
bool mExplodeOnTmeout;
|
||||
/// Should it arc?
|
||||
bool isBallistic;
|
||||
|
||||
/// How HIGH should it bounce (parallel to normal), [0,1]
|
||||
F32 bounceElasticity;
|
||||
/// How much momentum should be lost when it bounces (perpendicular to normal), [0,1]
|
||||
|
|
@ -154,7 +154,10 @@ public:
|
|||
ProjectileData(const ProjectileData&, bool = false);
|
||||
bool allowSubstitutions() const override { return true; }
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -274,7 +277,7 @@ protected:
|
|||
|
||||
LightInfo *mLight;
|
||||
LightState mLightState;
|
||||
|
||||
bool mHasHit;
|
||||
bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions.
|
||||
F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime
|
||||
///< in data block
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload )
|
|||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -906,7 +906,7 @@ bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
else
|
||||
mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
#include "core/stream/fileStream.h"
|
||||
#include "T3D/accumulationVolume.h"
|
||||
#include "console/persistenceManager.h"
|
||||
#include "AI/AIController.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
|
||||
|
||||
|
|
@ -172,6 +173,8 @@ ShapeBaseData::ShapeBaseData()
|
|||
density( 1.0f ),
|
||||
maxEnergy( 0.0f ),
|
||||
maxDamage( 1.0f ),
|
||||
mCollisionMul(0.0f),
|
||||
mImpactMul(0.0f),
|
||||
repairRate( 0.0033f ),
|
||||
disabledLevel( 1.0f ),
|
||||
destroyedLevel( 1.0f ),
|
||||
|
|
@ -195,7 +198,8 @@ ShapeBaseData::ShapeBaseData()
|
|||
useEyePoint( false ),
|
||||
isInvincible( false ),
|
||||
renderWhenDestroyed( true ),
|
||||
inheritEnergyFromMount( false )
|
||||
inheritEnergyFromMount( false ),
|
||||
mAIControllData(NULL)
|
||||
{
|
||||
INIT_ASSET(Shape);
|
||||
INIT_ASSET(DebrisShape);
|
||||
|
|
@ -229,6 +233,8 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game
|
|||
density = other.density;
|
||||
maxEnergy = other.maxEnergy;
|
||||
maxDamage = other.maxDamage;
|
||||
mCollisionMul = other.mCollisionMul;
|
||||
mImpactMul = other.mImpactMul;
|
||||
repairRate = other.repairRate;
|
||||
disabledLevel = other.disabledLevel;
|
||||
destroyedLevel = other.destroyedLevel;
|
||||
|
|
@ -345,7 +351,7 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
|
|||
|
||||
S32 i;
|
||||
U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
|
||||
if (assetStatus == AssetBase::Ok|| assetStatus == AssetBase::UsingFallback)
|
||||
if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback)
|
||||
{
|
||||
if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
|
||||
shapeError = true;
|
||||
|
|
@ -544,6 +550,10 @@ void ShapeBaseData::initPersistFields()
|
|||
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.");
|
||||
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");
|
||||
addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData),
|
||||
|
|
@ -585,6 +595,10 @@ void ShapeBaseData::initPersistFields()
|
|||
addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData),
|
||||
"Invincible flag; when invincible, the object cannot be damaged or "
|
||||
"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" );
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
void ShapeBaseData::onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
|
||||
void ShapeBaseData::onDebrisChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -981,7 +1005,8 @@ ShapeBase::ShapeBase()
|
|||
mCameraFov( 90.0f ),
|
||||
mIsControlled( false ),
|
||||
mLastRenderFrame( 0 ),
|
||||
mLastRenderDistance( 0.0f )
|
||||
mLastRenderDistance( 0.0f ),
|
||||
mAIController(NULL)
|
||||
{
|
||||
mTypeMask |= ShapeBaseObjectType | LightObjectType;
|
||||
|
||||
|
|
@ -1032,6 +1057,7 @@ ShapeBase::~ShapeBase()
|
|||
cur->next = sFreeTimeoutList;
|
||||
sFreeTimeoutList = cur;
|
||||
}
|
||||
if (mAIController) mAIController->deleteObject();
|
||||
}
|
||||
|
||||
void ShapeBase::initPersistFields()
|
||||
|
|
@ -5449,3 +5475,44 @@ DefineEngineMethod(ShapeBase, getNodePoint, Point3F, (const char* nodeName), ,
|
|||
|
||||
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 SFXTrack;
|
||||
class SFXProfile;
|
||||
struct AIController;
|
||||
struct AIControllerData;
|
||||
|
||||
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
|
||||
///< when the script prefix has changed.
|
||||
|
||||
DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes); ///< Name of shape to render.
|
||||
DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes, onShapeChanged); ///< Name of shape to render.
|
||||
DECLARE_ASSET_ARRAY_SETGET(ShapeBaseImageData, Shape);
|
||||
|
||||
//DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP); ///< Name of shape to render in first person (optional).
|
||||
|
|
@ -505,6 +507,11 @@ struct ShapeBaseImageData: public GameBaseData {
|
|||
|
||||
void handleStateSoundTrack(const U32& stateId);
|
||||
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Callbacks
|
||||
|
|
@ -555,6 +562,7 @@ public:
|
|||
U32 cubeDescId;
|
||||
ReflectorDesc *reflectorDesc;
|
||||
|
||||
AIControllerData* mAIControllData;
|
||||
/// @name Destruction
|
||||
///
|
||||
/// Everyone likes to blow things up!
|
||||
|
|
@ -579,6 +587,8 @@ public:
|
|||
F32 density;
|
||||
F32 maxEnergy;
|
||||
F32 maxDamage;
|
||||
F32 mCollisionMul;
|
||||
F32 mImpactMul;
|
||||
F32 repairRate; ///< Rate per tick.
|
||||
|
||||
F32 disabledLevel;
|
||||
|
|
@ -681,8 +691,8 @@ public:
|
|||
Vector<TextureTagRemapping> txr_tag_remappings;
|
||||
bool silent_bbox_check;
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onDebrisChanged() {}
|
||||
void onShapeChanged();
|
||||
void onDebrisChanged();
|
||||
public:
|
||||
ShapeBaseData(const ShapeBaseData&, bool = false);
|
||||
};
|
||||
|
|
@ -1754,6 +1764,11 @@ public:
|
|||
/// Returns true if this object is controlling by something
|
||||
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
|
||||
bool isFirstPerson() const;
|
||||
|
||||
|
|
@ -1895,7 +1910,6 @@ public:
|
|||
void registerCollisionCallback(CollisionEventCallback*);
|
||||
void unregisterCollisionCallback(CollisionEventCallback*);
|
||||
|
||||
protected:
|
||||
enum {
|
||||
ANIM_OVERRIDDEN = BIT(0),
|
||||
BLOCK_USER_CONTROL = BIT(1),
|
||||
|
|
@ -1903,6 +1917,8 @@ protected:
|
|||
BAD_ANIM_ID = 999999999,
|
||||
BLENDED_CLIP = 0x80000000,
|
||||
};
|
||||
U8 anim_clip_flags;
|
||||
protected:
|
||||
struct BlendThread
|
||||
{
|
||||
TSThread* thread;
|
||||
|
|
@ -1910,7 +1926,6 @@ protected:
|
|||
};
|
||||
Vector<BlendThread> blend_clips;
|
||||
static U32 unique_anim_tag_counter;
|
||||
U8 anim_clip_flags;
|
||||
S32 last_anim_id;
|
||||
U32 last_anim_tag;
|
||||
U32 last_anim_lock_tag;
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ bool StaticShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload )
|
|||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
mShapeInstance->setTimeScale(mStateAnimThread,0);
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ TurretShapeData::TurretShapeData()
|
|||
recoilSequence[i] = -1;
|
||||
pitchSequence = -1;
|
||||
headingSequence = -1;
|
||||
mControlMap = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
void TurretShapeData::initPersistFields()
|
||||
|
|
@ -134,6 +135,8 @@ void TurretShapeData::initPersistFields()
|
|||
docsURL;
|
||||
Parent::initPersistFields();
|
||||
addGroup("Steering");
|
||||
addField("controlMap", TypeString, Offset(mControlMap, TurretShapeData),
|
||||
"@brief movemap used by these types of objects.\n\n");
|
||||
addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData),
|
||||
"@brief Should the turret allow only z rotations.\n\n"
|
||||
"True indicates that the turret may only be rotated on its z axis, just like the Item class. "
|
||||
|
|
@ -440,7 +443,7 @@ bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
|
||||
if (!mSubclassTurretShapeHandlesScene)
|
||||
{
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public:
|
|||
bool startLoaded; ///< Should the turret's mounted weapon(s) start in a loaded state?
|
||||
|
||||
bool zRotOnly; ///< Should the turret allow only z rotations (like an item)?
|
||||
StringTableEntry mControlMap;
|
||||
|
||||
public:
|
||||
TurretShapeData();
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@ bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
mJetThread[i] = 0;
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -549,7 +549,7 @@ bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
}
|
||||
|
||||
// Todo: Uncomment if this is a "leaf" class
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,12 +141,14 @@ VehicleData::VehicleData()
|
|||
dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) );
|
||||
dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) );
|
||||
dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) );
|
||||
mControlMap = StringTable->EmptyString();
|
||||
|
||||
numDmgEmitterAreas = 0;
|
||||
|
||||
collDamageThresholdVel = 20;
|
||||
collDamageMultiplier = 0.05f;
|
||||
enablePhysicsRep = true;
|
||||
mControlMap = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -320,7 +322,14 @@ void VehicleData::initPersistFields()
|
|||
"velocity).\n\nCurrently unused." );
|
||||
endGroup("Collision");
|
||||
|
||||
addGroup("Movement");
|
||||
addField("controlMap", TypeString, Offset(mControlMap, VehicleData),
|
||||
"@brief movemap used by these types of objects.\n\n");
|
||||
endGroup("Movement");
|
||||
|
||||
addGroup("Steering");
|
||||
addField("controlMap", TypeString, Offset(mControlMap, VehicleData),
|
||||
"@brief movemap used by these types of objects.\n\n");
|
||||
addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat,
|
||||
"@brief Additional force applied to the vehicle when it is jetting.\n\n"
|
||||
"For WheeledVehicles, the force is applied in the forward direction. For "
|
||||
|
|
@ -471,7 +480,6 @@ bool Vehicle::onAdd()
|
|||
void Vehicle::onRemove()
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
U32 i=0;
|
||||
|
||||
for( i=0; i<VehicleData::VC_NUM_DAMAGE_EMITTERS; i++ )
|
||||
|
|
@ -496,10 +504,17 @@ void Vehicle::processTick(const Move* move)
|
|||
{
|
||||
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);
|
||||
if ( isMounted() )
|
||||
return;
|
||||
|
||||
|
||||
// Warp to catch up to server
|
||||
if (mDelta.warpCount < mDelta.warpTicks)
|
||||
{
|
||||
|
|
@ -726,6 +741,9 @@ void Vehicle::updateMove(const Move* move)
|
|||
if (mDamageState == Enabled) {
|
||||
setImageTriggerState(0,move->trigger[0]);
|
||||
setImageTriggerState(1,move->trigger[1]);
|
||||
//legacy code has trigger 2 and 3 reserved
|
||||
setImageTriggerState(2, move->trigger[4]);
|
||||
setImageTriggerState(3, move->trigger[5]);
|
||||
}
|
||||
|
||||
// Throttle
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "T3D/rigidShape.h"
|
||||
#endif
|
||||
|
||||
#include "T3D/AI/AIController.h"
|
||||
|
||||
class ParticleEmitter;
|
||||
class ParticleEmitterData;
|
||||
class ClippedPolyList;
|
||||
|
|
@ -70,6 +72,7 @@ struct VehicleData : public RigidShapeData
|
|||
F32 numDmgEmitterAreas;
|
||||
|
||||
bool enablePhysicsRep;
|
||||
StringTableEntry mControlMap;
|
||||
|
||||
//
|
||||
VehicleData();
|
||||
|
|
@ -98,7 +101,6 @@ class Vehicle : public RigidShape
|
|||
Point2F mSteering;
|
||||
F32 mThrottle;
|
||||
bool mJetting;
|
||||
|
||||
GFXStateBlockRef mSolidSB;
|
||||
|
||||
SimObjectPtr<ParticleEmitter> mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS];
|
||||
|
|
@ -146,6 +148,9 @@ public:
|
|||
bool onAdd() override;
|
||||
void onRemove() override;
|
||||
|
||||
Point2F getSteering() { return mSteering; };
|
||||
F32 getThrottle() { return mThrottle;};
|
||||
|
||||
/// Interpolates between move ticks @see processTick
|
||||
/// @param dt Change in time between the last call and this call to the function
|
||||
void advanceTime(F32 dt) override;
|
||||
|
|
|
|||
|
|
@ -711,7 +711,7 @@ bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() );
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,10 @@ struct WheeledVehicleTire: public SimDataBlock
|
|||
void packData(BitStream* stream) override;
|
||||
void unpackData(BitStream* stream) override;
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@ protected:
|
|||
public:
|
||||
enum { MaxLifetimeTicks = 4095 };
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
|
||||
public:
|
||||
// variables set in datablock definition:
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ bool afxSpellBook::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,10 @@ public:
|
|||
|
||||
static void initPersistFields();
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
void onSequenceChanged() {}
|
||||
|
||||
DECLARE_CONOBJECT(afxModelData);
|
||||
|
|
|
|||
|
|
@ -1064,7 +1064,7 @@ bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1108,7 +1108,7 @@ bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1177,7 +1177,7 @@ bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1294,7 +1294,7 @@ bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
if (mDataBlock->isTempClone())
|
||||
return true;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
scriptOnNewDataBlock(reload);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
// Debug Profiling.
|
||||
|
|
|
|||
|
|
@ -341,6 +341,9 @@ public:
|
|||
bool restoreAssetTags( void );
|
||||
inline AssetTagsManifest* getAssetTags( void ) const { return mAssetTagsManifest; }
|
||||
|
||||
/// Loose File management
|
||||
const char* getAssetLooseFiles(const char* pAssetId);
|
||||
|
||||
/// Info.
|
||||
inline U32 getDeclaredAssetCount( void ) const { return (U32)mDeclaredAssets.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),
|
||||
"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"
|
||||
|
|
|
|||
|
|
@ -162,6 +162,69 @@ namespace PropertyInfo
|
|||
bool hex_print(String & string, const S8 & hex);
|
||||
|
||||
bool default_print(String & result, SimObjectType * const & data);
|
||||
|
||||
template<typename T, size_t count>
|
||||
char* FormatPropertyBuffer(const void* dataPtr, char* buffer, U32 bufSize)
|
||||
{
|
||||
const T* values = reinterpret_cast<const T*>(dataPtr);
|
||||
char* ptr = buffer;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
S32 written = 0;
|
||||
|
||||
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, S32>)
|
||||
written = dSprintf(ptr, bufSize - (ptr - buffer), "%d", values[i]);
|
||||
else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, F32>)
|
||||
written = dSprintf(ptr, bufSize - (ptr - buffer), "%g", values[i]);
|
||||
else
|
||||
AssertFatal(sizeof(T) == 0, "Unsupported type in FormatProperty");
|
||||
|
||||
ptr += written;
|
||||
if (i < count - 1)
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
|
||||
return ptr; // return end of written string for chaining
|
||||
}
|
||||
|
||||
template<typename T, size_t count>
|
||||
const char* FormatProperty(const void* dataPtr)
|
||||
{
|
||||
static const U32 bufSize = 256;
|
||||
char* buffer = Con::getReturnBuffer(bufSize);
|
||||
FormatPropertyBuffer<T,count>(dataPtr, buffer, bufSize);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template<typename T, size_t count>
|
||||
inline bool ParseProperty(char* str, T(&out)[count])
|
||||
{
|
||||
|
||||
size_t index = 0;
|
||||
char* tok = dStrtok(str, " \t");
|
||||
|
||||
while (tok && index < count)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, S32>)
|
||||
{
|
||||
out[index++] = mRound(dAtof(tok));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, F32>)
|
||||
{
|
||||
out[index++] = dAtof(tok);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertFatal(sizeof(T) == 0, "Unsupported type in PropertyInfo::ParseProperty");
|
||||
}
|
||||
|
||||
tok = dStrtok(nullptr, " \t");
|
||||
}
|
||||
|
||||
return index == count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Default Scan/print definition
|
||||
|
|
|
|||
|
|
@ -102,7 +102,15 @@ DefineEngineFunction( isObject, bool, (const char * objectName), ,"isObject(obje
|
|||
if (!String::compare(objectName, "0") || !String::compare(objectName, ""))
|
||||
return false;
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -425,39 +425,42 @@ void SimDataBlock::write(Stream &stream, U32 tabStop, U32 flags)
|
|||
// MARK: ---- API ----
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),,
|
||||
"Reload the datablock. This can only be used with a local client configuration." )
|
||||
void SimDataBlock::reloadOnLocalClient()
|
||||
{
|
||||
// Make sure we're running a local client.
|
||||
|
||||
GameConnection* localClient = GameConnection::getLocalClientConnection();
|
||||
if( !localClient )
|
||||
if (!localClient)
|
||||
return;
|
||||
|
||||
// Do an in-place pack/unpack/preload.
|
||||
|
||||
if( !object->preload( true, NetConnection::getErrorBuffer() ) )
|
||||
if (!preload(true, NetConnection::getErrorBuffer()))
|
||||
{
|
||||
Con::errorf( NetConnection::getErrorBuffer() );
|
||||
Con::errorf(NetConnection::getErrorBuffer());
|
||||
return;
|
||||
}
|
||||
|
||||
U8 buffer[ 16384 ];
|
||||
BitStream stream( buffer, 16384 );
|
||||
U8 buffer[16384];
|
||||
BitStream stream(buffer, 16384);
|
||||
|
||||
object->packData( &stream );
|
||||
packData(&stream);
|
||||
stream.setPosition(0);
|
||||
object->unpackData( &stream );
|
||||
unpackData(&stream);
|
||||
|
||||
if( !object->preload( false, NetConnection::getErrorBuffer() ) )
|
||||
if (!preload(false, NetConnection::getErrorBuffer()))
|
||||
{
|
||||
Con::errorf( NetConnection::getErrorBuffer() );
|
||||
Con::errorf(NetConnection::getErrorBuffer());
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger a post-apply so that change notifications respond.
|
||||
object->inspectPostApply();
|
||||
inspectPostApply();
|
||||
}
|
||||
DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),,
|
||||
"Reload the datablock. This can only be used with a local client configuration." )
|
||||
{
|
||||
object->reloadOnLocalClient();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ public:
|
|||
/// Used by the console system to automatically tell datablock classes apart
|
||||
/// from non-datablock classes.
|
||||
static const bool __smIsDatablock = true;
|
||||
|
||||
void reloadOnLocalClient();
|
||||
protected:
|
||||
struct SubstitutionStatement
|
||||
{
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ ImplementBitfieldType(GameTypeMasksType,
|
|||
{ SceneObjectTypes::PathShapeObjectType, "$TypeMasks::PathShapeObjectType", "Path-following Objects.\n" },
|
||||
{ SceneObjectTypes::TurretObjectType, "$TypeMasks::TurretObjectType", "Turret Objects.\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;
|
||||
|
||||
|
|
@ -3296,6 +3296,9 @@ DefineEngineMethod( SimObject, getGroup, SimGroup*, (),,
|
|||
DefineEngineMethod( SimObject, delete, void, (),,
|
||||
"Delete and remove the object." )
|
||||
{
|
||||
if (!object->isProperlyAdded() || object->isRemoved())
|
||||
return;
|
||||
|
||||
object->deleteObject();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,10 @@ public:
|
|||
return theSignal;
|
||||
}
|
||||
|
||||
void onShapeChanged() {}
|
||||
void onShapeChanged()
|
||||
{
|
||||
reloadOnLocalClient();
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<ForestItemData*> ForestItemDataVector;
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ void PopupMenu::enableItem(S32 pos, bool enable)
|
|||
|
||||
void PopupMenu::checkItem(S32 pos, bool checked)
|
||||
{
|
||||
if (mMenuItems.empty() || mMenuItems.size() < pos || pos < 0)
|
||||
if (mMenuItems.empty() || mMenuItems.size() <= pos || pos < 0)
|
||||
return;
|
||||
|
||||
if (checked && mMenuItems[pos].mCheckGroup != -1 && mRadioSelection)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "console/consoleTypes.h"
|
||||
#include "console/console.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "console/propertyParsing.h"
|
||||
#include "math/mPoint2.h"
|
||||
#include "math/mPoint3.h"
|
||||
#include "math/mMatrix.h"
|
||||
|
|
@ -169,21 +170,27 @@ ImplementConsoleTypeCasters( TypePoint2I, Point2I )
|
|||
|
||||
ConsoleGetType( TypePoint2I )
|
||||
{
|
||||
Point2I *pt = (Point2I *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%d %d", pt->x, pt->y);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<S32, 2>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypePoint2I )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%d %d", &((Point2I *) dptr)->x, &((Point2I *) dptr)->y);
|
||||
else if(argc == 2)
|
||||
*((Point2I *) dptr) = Point2I(dAtoi(argv[0]), dAtoi(argv[1]));
|
||||
else
|
||||
Con::printf("Point2I must be set as { x, y } or \"x y\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
S32 parsed[2];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<S32, 2>(buffer, parsed)) {
|
||||
*((Point2I*)dptr) = Point2I(parsed[0], parsed[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Point2I must be set as { x, y } or \"x y\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -194,21 +201,27 @@ ImplementConsoleTypeCasters( TypePoint2F, Point2F )
|
|||
|
||||
ConsoleGetType( TypePoint2F )
|
||||
{
|
||||
Point2F *pt = (Point2F *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g", pt->x, pt->y);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 2>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypePoint2F )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%g %g", &((Point2F *) dptr)->x, &((Point2F *) dptr)->y);
|
||||
else if(argc == 2)
|
||||
*((Point2F *) dptr) = Point2F(dAtof(argv[0]), dAtof(argv[1]));
|
||||
else
|
||||
Con::printf("Point2F must be set as { x, y } or \"x y\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[2];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<F32, 2>(buffer, parsed)) {
|
||||
*((Point2F*)dptr) = Point2F(parsed[0], parsed[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Point2F must be set as { x, y } or \"x y\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -219,21 +232,27 @@ ImplementConsoleTypeCasters(TypePoint3I, Point3I)
|
|||
|
||||
ConsoleGetType( TypePoint3I )
|
||||
{
|
||||
Point3I *pt = (Point3I *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%d %d %d", pt->x, pt->y, pt->z);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<S32, 3>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypePoint3I )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%d %d %d", &((Point3I *) dptr)->x, &((Point3I *) dptr)->y, &((Point3I *) dptr)->z);
|
||||
else if(argc == 3)
|
||||
*((Point3I *) dptr) = Point3I(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]));
|
||||
else
|
||||
Con::printf("Point3I must be set as { x, y, z } or \"x y z\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
S32 parsed[3];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<S32, 3>(buffer, parsed)) {
|
||||
*((Point3I*)dptr) = Point3I(parsed[0], parsed[1], parsed[2]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Point3I must be set as { x, y, z } or \"x y z\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -244,21 +263,27 @@ ImplementConsoleTypeCasters(TypePoint3F, Point3F)
|
|||
|
||||
ConsoleGetType( TypePoint3F )
|
||||
{
|
||||
Point3F *pt = (Point3F *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g", pt->x, pt->y, pt->z);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 3>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypePoint3F )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%g %g %g", &((Point3F *) dptr)->x, &((Point3F *) dptr)->y, &((Point3F *) dptr)->z);
|
||||
else if(argc == 3)
|
||||
*((Point3F *) dptr) = Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]));
|
||||
else
|
||||
Con::printf("Point3F must be set as { x, y, z } or \"x y z\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[3];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<F32, 3>(buffer, parsed)) {
|
||||
*((Point3F*)dptr) = Point3F(parsed[0], parsed[1], parsed[2]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Point3F must be set as { x, y, z } or \"x y z\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -269,21 +294,27 @@ ImplementConsoleTypeCasters( TypePoint4F, Point4F )
|
|||
|
||||
ConsoleGetType( TypePoint4F )
|
||||
{
|
||||
Point4F *pt = (Point4F *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 4>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypePoint4F )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%g %g %g %g", &((Point4F *) dptr)->x, &((Point4F *) dptr)->y, &((Point4F *) dptr)->z, &((Point4F *) dptr)->w);
|
||||
else if(argc == 4)
|
||||
*((Point4F *) dptr) = Point4F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3]));
|
||||
else
|
||||
Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[4];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed)) {
|
||||
*((Point4F*)dptr) = Point4F(parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Point4F must be set as { x, y, z, w } or \"x y z w\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -294,23 +325,27 @@ ImplementConsoleTypeCasters( TypeRectI, RectI )
|
|||
|
||||
ConsoleGetType( TypeRectI )
|
||||
{
|
||||
RectI *rect = (RectI *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%d %d %d %d", rect->point.x, rect->point.y,
|
||||
rect->extent.x, rect->extent.y);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<S32, 4>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeRectI )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%d %d %d %d", &((RectI *) dptr)->point.x, &((RectI *) dptr)->point.y,
|
||||
&((RectI *) dptr)->extent.x, &((RectI *) dptr)->extent.y);
|
||||
else if(argc == 4)
|
||||
*((RectI *) dptr) = RectI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3]));
|
||||
else
|
||||
Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
S32 parsed[4];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<S32, 4>(buffer, parsed)) {
|
||||
*((RectI*)dptr) = RectI(parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("RectI must be set as { x, y, w, h } or \"x y w h\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -321,23 +356,27 @@ ImplementConsoleTypeCasters( TypeRectF, RectF )
|
|||
|
||||
ConsoleGetType( TypeRectF )
|
||||
{
|
||||
RectF *rect = (RectF *) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g", rect->point.x, rect->point.y,
|
||||
rect->extent.x, rect->extent.y);
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 4>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeRectF )
|
||||
{
|
||||
if(argc == 1)
|
||||
dSscanf(argv[0], "%g %g %g %g", &((RectF *) dptr)->point.x, &((RectF *) dptr)->point.y,
|
||||
&((RectF *) dptr)->extent.x, &((RectF *) dptr)->extent.y);
|
||||
else if(argc == 4)
|
||||
*((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3]));
|
||||
else
|
||||
Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\"");
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[4];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed)) {
|
||||
*((RectF*)dptr) = RectF(parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("RectF must be set as { x, y, w, h } or \"x y w h\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -351,36 +390,44 @@ ImplementConsoleTypeCasters( TypeMatrixF, MatrixF )
|
|||
|
||||
ConsoleGetType( TypeMatrixF )
|
||||
{
|
||||
MatrixF* mat = ( MatrixF* ) dptr;
|
||||
MatrixF* mat = (MatrixF*)dptr;
|
||||
|
||||
Point3F col0, col1, col2;
|
||||
mat->getColumn(0, &col0);
|
||||
mat->getColumn(1, &col1);
|
||||
mat->getColumn(2, &col2);
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer,bufSize,"%g %g %g %g %g %g %g %g %g",
|
||||
col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z);
|
||||
return returnBuffer;
|
||||
char* buffer = Con::getReturnBuffer(bufSize);
|
||||
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 3>(col0, buffer, bufSize);
|
||||
*buffer++ = ' ';
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 3>(col1, buffer, bufSize);
|
||||
*buffer++ = ' ';
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 3>(col2, buffer, bufSize);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeMatrixF )
|
||||
{
|
||||
if( argc != 1 )
|
||||
if (argc == 1)
|
||||
{
|
||||
Con::errorf( "MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"" );
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F col0, col1, col2;
|
||||
dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g %g %g",
|
||||
&col0.x, &col0.y, &col0.z, &col1.x, &col1.y, &col1.z, &col2.x, &col2.y, &col2.z );
|
||||
F32 parsed[9];
|
||||
|
||||
MatrixF* mat = ( MatrixF* ) dptr;
|
||||
|
||||
mat->setColumn( 0, col0 );
|
||||
mat->setColumn( 1, col1 );
|
||||
mat->setColumn( 2, col2 );
|
||||
char* buffer = new char[dStrlen(argv[0])];
|
||||
dStrcpy(buffer, argv[0], sizeof(buffer));
|
||||
|
||||
if (PropertyInfo::ParseProperty<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;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -390,32 +437,40 @@ ConsoleMappedType(MatrixPosition, TypeMatrixPosition, Point3F, MatrixF, "")
|
|||
|
||||
ConsoleGetType( TypeMatrixPosition )
|
||||
{
|
||||
F32 *col = (F32 *) dptr + 3;
|
||||
F32* col = (F32*)dptr + 3;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
if(col[12] == 1.f)
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g", col[0], col[4], col[8]);
|
||||
Point4F pos(col[0], col[4], col[8], col[12]);
|
||||
|
||||
if (col[12] == 1.0f)
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 3>(&pos, returnBuffer, bufSize);
|
||||
else
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g", col[0], col[4], col[8], col[12]);
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 4>(&pos, returnBuffer, bufSize);
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeMatrixPosition )
|
||||
{
|
||||
F32 *col = ((F32 *) dptr) + 3;
|
||||
if (argc == 1)
|
||||
if (argc >= 1)
|
||||
{
|
||||
col[0] = col[4] = col[8] = 0.f;
|
||||
col[12] = 1.f;
|
||||
dSscanf(argv[0], "%g %g %g %g", &col[0], &col[4], &col[8], &col[12]);
|
||||
F32 parsed[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
// we dont want to hard fail based on the count.
|
||||
// this will allow any number of properties to be set.
|
||||
PropertyInfo::ParseProperty<F32, 4>(buffer, parsed);
|
||||
{
|
||||
Point4F temp(parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
MatrixF* mat = (MatrixF*)dptr;
|
||||
mat->setColumn(3, temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (argc <= 4)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
col[i << 2] = dAtof(argv[i]);
|
||||
}
|
||||
else
|
||||
Con::printf("Matrix position must be set as { x, y, z, w } or \"x y z w\"");
|
||||
|
||||
Con::warnf("Matrix position must be set as { x, y, z, w } or \"x y z w\"");
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -425,42 +480,38 @@ ConsoleMappedType(MatrixRotation, TypeMatrixRotation, AngAxisF, MatrixF, "")
|
|||
|
||||
ConsoleGetType( TypeMatrixRotation )
|
||||
{
|
||||
AngAxisF aa(*(MatrixF *) dptr);
|
||||
AngAxisF aa(*(MatrixF*)dptr);
|
||||
aa.axis.normalize();
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle));
|
||||
return returnBuffer;
|
||||
aa.angle = mRadToDeg(aa.angle);
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 4>(&aa);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeMatrixRotation )
|
||||
{
|
||||
// DMM: Note that this will ONLY SET the ULeft 3x3 submatrix.
|
||||
//
|
||||
AngAxisF aa(Point3F(0,0,0),0);
|
||||
if (argc == 1)
|
||||
if (argc >= 1)
|
||||
{
|
||||
dSscanf(argv[0], "%g %g %g %g", &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle);
|
||||
aa.angle = mDegToRad(aa.angle);
|
||||
}
|
||||
else if (argc == 4)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
((F32*)&aa)[i] = dAtof(argv[i]);
|
||||
aa.angle = mDegToRad(aa.angle);
|
||||
}
|
||||
else
|
||||
Con::printf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\"");
|
||||
F32 parsed[4];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
//
|
||||
MatrixF temp;
|
||||
aa.setMatrix(&temp);
|
||||
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed))
|
||||
{
|
||||
AngAxisF aa(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3]));
|
||||
MatrixF temp;
|
||||
aa.setMatrix(&temp);
|
||||
|
||||
F32* pDst = *(MatrixF *)dptr;
|
||||
const F32* pSrc = temp;
|
||||
for (U32 i = 0; i < 3; i++)
|
||||
for (U32 j = 0; j < 3; j++)
|
||||
pDst[i*4 + j] = pSrc[i*4 + j];
|
||||
F32* pDst = *(MatrixF*)dptr;
|
||||
const F32* pSrc = temp;
|
||||
for (U32 i = 0; i < 3; i++)
|
||||
for (U32 j = 0; j < 3; j++)
|
||||
pDst[i * 4 + j] = pSrc[i * 4 + j];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -472,30 +523,29 @@ ImplementConsoleTypeCasters( TypeAngAxisF, AngAxisF )
|
|||
ConsoleGetType( TypeAngAxisF )
|
||||
{
|
||||
AngAxisF* aa = ( AngAxisF* ) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle));
|
||||
return returnBuffer;
|
||||
aa->angle = mRadToDeg(aa->angle);
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 4>(aa);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeAngAxisF )
|
||||
{
|
||||
// DMM: Note that this will ONLY SET the ULeft 3x3 submatrix.
|
||||
//
|
||||
AngAxisF* aa = ( AngAxisF* ) dptr;
|
||||
if (argc == 1)
|
||||
if (argc >= 1)
|
||||
{
|
||||
dSscanf(argv[0], "%g %g %g %g", &aa->axis.x, &aa->axis.y, &aa->axis.z, &aa->angle);
|
||||
aa->angle = mDegToRad(aa->angle);
|
||||
F32 parsed[4];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if(PropertyInfo::ParseProperty<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)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
((F32*)&aa)[i] = dAtof(argv[i]);
|
||||
aa->angle = mDegToRad(aa->angle);
|
||||
}
|
||||
else
|
||||
Con::printf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\"");
|
||||
|
||||
Con::warnf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\"");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -510,38 +560,35 @@ ImplementConsoleTypeCasters( TypeTransformF, TransformF )
|
|||
|
||||
ConsoleGetType( TypeTransformF )
|
||||
{
|
||||
TransformF* aa = ( TransformF* ) dptr;
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf( returnBuffer, bufSize, "%g %g %g %g %g %g %g",
|
||||
aa->mPosition.x, aa->mPosition.y, aa->mPosition.z,
|
||||
aa->mOrientation.axis.x, aa->mOrientation.axis.y, aa->mOrientation.axis.z, aa->mOrientation.angle );
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 7>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeTransformF )
|
||||
{
|
||||
TransformF* aa = ( TransformF* ) dptr;
|
||||
if( argc == 1 )
|
||||
if(argc >= 1)
|
||||
{
|
||||
U32 count = dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g",
|
||||
&aa->mPosition.x, &aa->mPosition.y, &aa->mPosition.z,
|
||||
&aa->mOrientation.axis.x, &aa->mOrientation.axis.y, &aa->mOrientation.axis.z, &aa->mOrientation.angle );
|
||||
F32 parsed[7];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
aa->mHasRotation = ( count == 7 );
|
||||
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 )
|
||||
{
|
||||
aa->mPosition.x = dAtof( argv[ 0 ] );
|
||||
aa->mPosition.y = dAtof( argv[ 1 ] );
|
||||
aa->mPosition.z = dAtof( argv[ 2 ] );
|
||||
aa->mOrientation.axis.x = dAtof( argv[ 3 ] );
|
||||
aa->mOrientation.axis.y = dAtof( argv[ 4 ] );
|
||||
aa->mOrientation.axis.z = dAtof( argv[ 5 ] );
|
||||
aa->mOrientation.angle = dAtof( argv[ 6 ] );
|
||||
}
|
||||
else
|
||||
Con::errorf( "TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\"");
|
||||
|
||||
Con::warnf("TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\"");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -554,32 +601,33 @@ ImplementConsoleTypeCasters( TypeBox3F, Box3F )
|
|||
|
||||
ConsoleGetType( TypeBox3F )
|
||||
{
|
||||
const Box3F* pBox = (const Box3F*)dptr;
|
||||
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g %g %g",
|
||||
pBox->minExtents.x, pBox->minExtents.y, pBox->minExtents.z,
|
||||
pBox->maxExtents.x, pBox->maxExtents.y, pBox->maxExtents.z);
|
||||
|
||||
return returnBuffer;
|
||||
const char* buff = PropertyInfo::FormatProperty<F32, 6>(dptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeBox3F )
|
||||
{
|
||||
Box3F* pDst = (Box3F*)dptr;
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[6];
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
U32 args = dSscanf(argv[0], "%g %g %g %g %g %g",
|
||||
&pDst->minExtents.x, &pDst->minExtents.y, &pDst->minExtents.z,
|
||||
&pDst->maxExtents.x, &pDst->maxExtents.y, &pDst->maxExtents.z);
|
||||
AssertWarn(args == 6, "Warning, box probably not read properly");
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::printf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\"");
|
||||
if (PropertyInfo::ParseProperty<F32, 6>(buffer, parsed))
|
||||
{
|
||||
Box3F* pDst = (Box3F*)dptr;
|
||||
pDst->minExtents.x = parsed[0];
|
||||
pDst->minExtents.y = parsed[1];
|
||||
pDst->minExtents.z = parsed[2];
|
||||
pDst->maxExtents.x = parsed[3];
|
||||
pDst->maxExtents.y = parsed[4];
|
||||
pDst->maxExtents.z = parsed[5];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\"");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -591,31 +639,39 @@ ImplementConsoleTypeCasters( TypeEaseF, EaseF )
|
|||
|
||||
ConsoleGetType( TypeEaseF )
|
||||
{
|
||||
const EaseF* pEase = (const EaseF*)dptr;
|
||||
|
||||
static const U32 bufSize = 256;
|
||||
char* returnBuffer = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(returnBuffer, bufSize, "%d %d %g %g",
|
||||
pEase->mDir, pEase->mType, pEase->mParam[0], pEase->mParam[1]);
|
||||
char* buffer = Con::getReturnBuffer(bufSize);
|
||||
|
||||
return returnBuffer;
|
||||
EaseF* pEase = (EaseF*)dptr;
|
||||
PropertyInfo::FormatPropertyBuffer<S32, 2>(pEase + 0, buffer, bufSize);
|
||||
*buffer++ = ' ';
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 2>(pEase + 2, buffer, bufSize);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeEaseF )
|
||||
{
|
||||
EaseF* pDst = (EaseF*)dptr;
|
||||
if (argc >= 1)
|
||||
{
|
||||
F32 parsed[4];
|
||||
parsed[2] = -1.0f;
|
||||
parsed[3] = -1.0f;
|
||||
|
||||
// defaults...
|
||||
pDst->mParam[0] = -1.0f;
|
||||
pDst->mParam[1] = -1.0f;
|
||||
if (argc == 1) {
|
||||
U32 args = dSscanf(argv[0], "%d %d %f %f", // the two params are optional and assumed -1 if not present...
|
||||
&pDst->mDir, &pDst->mType, &pDst->mParam[0],&pDst->mParam[1]);
|
||||
if( args < 2 )
|
||||
Con::warnf( "Warning, EaseF probably not read properly" );
|
||||
} else {
|
||||
Con::printf("EaseF must be set as \"dir type [param0 param1]\"");
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
// same as matrix do not hard fail based on count!
|
||||
PropertyInfo::ParseProperty<F32, 4>(buffer, parsed);
|
||||
{
|
||||
((EaseF*)dptr)->set(mRound(parsed[0]), mRound(parsed[1]), parsed[2], parsed[3]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Con::warnf("EaseF must be set as \"dir type [param0 param1]\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -633,12 +689,12 @@ ConsoleGetType(TypeRotationF)
|
|||
if (pt->mRotationType == RotationF::Euler)
|
||||
{
|
||||
EulerF out = pt->asEulerF(RotationF::Degrees);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g", out.x, out.y, out.z);
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 3>(out, returnBuffer, bufSize);
|
||||
}
|
||||
else if (pt->mRotationType == RotationF::AxisAngle)
|
||||
{
|
||||
AngAxisF out = pt->asAxisAngle(RotationF::Degrees);
|
||||
dSprintf(returnBuffer, bufSize, "%g %g %g %g", out.axis.x, out.axis.y, out.axis.z, out.angle);
|
||||
PropertyInfo::FormatPropertyBuffer<F32, 4>(&out, returnBuffer, bufSize);
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
|
|
@ -646,34 +702,36 @@ ConsoleGetType(TypeRotationF)
|
|||
|
||||
ConsoleSetType(TypeRotationF)
|
||||
{
|
||||
if (argc == 1)
|
||||
if (argc >= 1)
|
||||
{
|
||||
U32 elements = StringUnit::getUnitCount(argv[0], " \t\n");
|
||||
// Combine argv into a single space-separated string if argc > 1
|
||||
char buffer[256] = { 0 };
|
||||
dStrncpy(buffer, *argv, sizeof(buffer));
|
||||
|
||||
U32 elements = StringUnit::getUnitCount(buffer, " \t\n");
|
||||
if (elements == 3)
|
||||
{
|
||||
EulerF in;
|
||||
dSscanf(argv[0], "%g %g %g", &in.x, &in.y, &in.z);
|
||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
||||
F32 parsed[3];
|
||||
if(PropertyInfo::ParseProperty<F32, 3>(buffer, parsed))
|
||||
{
|
||||
EulerF in(parsed[0], parsed[1], parsed[2]);
|
||||
((RotationF*)dptr)->set(in, RotationF::Degrees);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (elements == 4)
|
||||
{
|
||||
AngAxisF in;
|
||||
dSscanf(argv[0], "%g %g %g %g", &in.axis.x, &in.axis.y, &in.axis.z, &in.angle);
|
||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
||||
F32 parsed[4];
|
||||
if (PropertyInfo::ParseProperty<F32, 4>(buffer, parsed))
|
||||
{
|
||||
AngAxisF in(Point3F(parsed[0], parsed[1], parsed[2]), parsed[3]);
|
||||
((RotationF*)dptr)->set(in, RotationF::Degrees);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
EulerF in(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]));
|
||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
||||
}
|
||||
else if (argc == 4)
|
||||
{
|
||||
AngAxisF in(Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])), dAtof(argv[3]));
|
||||
((RotationF *)dptr)->set(in, RotationF::Degrees);
|
||||
}
|
||||
else
|
||||
Con::printf("RotationF must be set as { x, y, z, w } or \"x y z w\"");
|
||||
|
||||
Con::warnf("RotationF must be set as { x, y, z, w } or \"x y z w\"");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "gui/buttons/guiButtonCtrl.h"
|
||||
#include "gui/worldEditor/undoActions.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/AI/AIController.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiNavEditorCtrl);
|
||||
|
||||
|
|
@ -225,8 +226,29 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos)
|
|||
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
|
||||
missionCleanup->addObject(obj);
|
||||
}
|
||||
mPlayer = static_cast<AIPlayer*>(obj);
|
||||
Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags()));
|
||||
mPlayer = obj;
|
||||
#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
|
||||
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);
|
||||
Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags()));
|
||||
mPlayer = ri.object;
|
||||
#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(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri))
|
||||
mCurPlayer = dynamic_cast<AIPlayer*>(ri.object);
|
||||
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
|
||||
mCurPlayer = ri.object;
|
||||
else
|
||||
mCurPlayer = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ protected:
|
|||
/// @name Test mode
|
||||
/// @{
|
||||
|
||||
SimObjectPtr<AIPlayer> mPlayer;
|
||||
SimObjectPtr<AIPlayer> mCurPlayer;
|
||||
SimObjectPtr<SceneObject> mPlayer;
|
||||
SimObjectPtr<SceneObject> mCurPlayer;
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,8 +111,12 @@ function handleConnectionErrorMessage(%msgType, %msgString, %msgError)
|
|||
//-----------------------------------------------------------------------------
|
||||
// Disconnect
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function disconnect()
|
||||
{
|
||||
callOnModules("disconnect");
|
||||
}
|
||||
|
||||
function Core_ClientServer::disconnect(%this)
|
||||
{
|
||||
// We need to stop the client side simulation
|
||||
// else physics resources will not cleanup properly.
|
||||
|
|
@ -158,3 +162,16 @@ function disconnectedCleanup()
|
|||
|
||||
moduleExec("onDestroyClientConnection", "Game");
|
||||
}
|
||||
|
||||
function clientCmdsetMoveMap(%movemap)
|
||||
{
|
||||
if (!isObject(%movemap)) return;
|
||||
if(isObject(ServerConnection) && isObject(ServerConnection.curMoveMap))
|
||||
ServerConnection.curMoveMap.pop();
|
||||
|
||||
// clear movement
|
||||
$mvForwardAction = 0;
|
||||
$mvBackwardAction = 0;
|
||||
%movemap.push();
|
||||
ServerConnection.curMoveMap = %movemap;
|
||||
}
|
||||
|
|
@ -275,7 +275,9 @@ function GameConnection::onPostSpawn( %this )
|
|||
if (%this.numModsNeedingLoaded)
|
||||
callOnObjectList("onPostSpawn", %modulesIdList, %this);
|
||||
else
|
||||
%this.listener.onPostSpawnComplete(%this);
|
||||
%this.listener.onPostSpawnComplete(%this);
|
||||
if (isObject(%this.player.getDatablock().controlMap))
|
||||
commandToClient(%this, 'setMoveMap', %this.player.getDatablock().controlMap);
|
||||
}
|
||||
|
||||
function GameConnectionListener::onPostSpawnComplete(%this, %client)
|
||||
|
|
|
|||
|
|
@ -169,3 +169,20 @@ datablock LightAnimData( SpinLightAnim )
|
|||
rotKeys[2] = "az";
|
||||
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;
|
||||
}
|
||||
|
||||
function GameBaseData::onNewDataBlock(%this, %obj)
|
||||
function GameBaseData::onNewDataBlock(%this, %obj, %reload)
|
||||
{
|
||||
if (%obj.firstDataCheck)
|
||||
if (%reload)
|
||||
{
|
||||
if(%this.isMethod("onRemove"))
|
||||
%this.onRemove(%obj);
|
||||
if(%this.isMethod("onAdd"))
|
||||
%this.onAdd(%obj);
|
||||
}
|
||||
else
|
||||
%obj.firstDataCheck = true;
|
||||
}
|
||||
|
||||
function saveGameObject(%name, %tamlPath, %scriptPath)
|
||||
|
|
|
|||
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…
Reference in a new issue