//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames // Copyright (C) 2015 Faust Logic, Inc. // // 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 "afx/arcaneFX.h" #include "scene/sceneObject.h" #include "scene/sceneManager.h" #include "T3D/gameBase/gameConnection.h" #include "T3D/gameBase/gameProcess.h" #include "T3D/player.h" #include "math/mathUtils.h" #include "console/compiler.h" #include "console/engineAPI.h" #include "afx/afxChoreographer.h" #include "afx/afxSelectron.h" #include "afx/afxResidueMgr.h" #include "afx/ce/afxZodiacMgr.h" //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #define N_LIGHTING_MODELS 6 // // "SG - Original Advanced (Lighting Pack)" // "SG - Original Stock (Lighting Pack)" // "SG - Inverse Square (Lighting Pack)" // "SG - Inverse Square Fast Falloff (Lighting Pack)" // "SG - Near Linear (Lighting Pack)" // "SG - Near Linear Fast Falloff (Lighting Pack)" static StringTableEntry lm_old_names[N_LIGHTING_MODELS]; // // "Original Advanced" // "Original Stock" // "Inverse Square" // "Inverse Square Fast Falloff" // "Near Linear" // "Near Linear Fast Falloff" static StringTableEntry lm_new_names[N_LIGHTING_MODELS]; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// class ClientZoneInEvent : public NetEvent { typedef NetEvent Parent; public: ClientZoneInEvent() { mGuaranteeType = Guaranteed; } ~ClientZoneInEvent() { } virtual void pack(NetConnection*, BitStream*bstream) { } virtual void write(NetConnection*, BitStream *bstream) { } virtual void unpack(NetConnection* /*ps*/, BitStream *bstream) { } virtual void process(NetConnection* conn) { GameConnection* game_conn = dynamic_cast(conn); if (game_conn && !game_conn->isZonedIn()) { arcaneFX::syncToNewConnection(game_conn); } } DECLARE_CONOBJECT(ClientZoneInEvent); DECLARE_CATEGORY("AFX"); }; IMPLEMENT_CO_SERVEREVENT_V1(ClientZoneInEvent); ConsoleDocClass( ClientZoneInEvent, "@brief Event posted when player is fully loaded into the game and ready for interaction.\n\n" "@internal"); //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// Vector arcaneFX::active_choreographers; Vector arcaneFX::client_choreographers; Vector arcaneFX::selectrons; Vector arcaneFX::scoped_objs; StringTableEntry arcaneFX::NULLSTRING = 0; U32 arcaneFX::sTargetSelectionMask = 0; U32 arcaneFX::sFreeTargetSelectionMask = 0; bool arcaneFX::sIsFreeTargeting = false; Point3F arcaneFX::sFreeTargetPos = Point3F(0.0f, 0.0f, 0.0f); bool arcaneFX::sFreeTargetPosValid = false; F32 arcaneFX::sTargetSelectionRange = 200.0f; U32 arcaneFX::sTargetSelectionTimeoutMS = 500; bool arcaneFX::sClickToTargetSelf = false; U32 arcaneFX::sMissileCollisionMask = 0; StringTableEntry arcaneFX::sParameterFieldPrefix = 0; F32 arcaneFX::sTerrainZodiacZBias = -0.00025f; F32 arcaneFX::sInteriorZodiacZBias = -0.0001f; F32 arcaneFX::sPolysoupZodiacZBias = -0.0001f; U32 arcaneFX::master_choreographer_id = 1; U16 arcaneFX::master_scope_id = 1; bool arcaneFX::is_shutdown = true; bool arcaneFX::sUsePlayerCentricListener = false; void arcaneFX::init() { NULLSTRING = StringTable->insert(""); sParameterFieldPrefix = StringTable->insert("_"); #if defined(TORQUE_OS_MAC) arcaneFX::sTerrainZodiacZBias = -0.00025f; arcaneFX::sInteriorZodiacZBias = -0.00025f; arcaneFX::sPolysoupZodiacZBias = -0.00025f; #endif Con::addVariable( "pref::AFX::targetSelectionMask", TypeS32, &sTargetSelectionMask); Con::addVariable( "pref::AFX::freeTargetSelectionMask", TypeS32, &sFreeTargetSelectionMask); Con::addVariable( "pref::AFX::targetSelectionRange", TypeF32, &sTargetSelectionRange); Con::addVariable( "pref::AFX::targetSelectionTimeoutMS", TypeS32, &sTargetSelectionTimeoutMS); Con::addVariable( "pref::AFX::missileCollisionMask", TypeS32, &sMissileCollisionMask); Con::addVariable( "pref::AFX::clickToTargetSelf", TypeBool, &sClickToTargetSelf); Con::addVariable( "Pref::Server::AFX::parameterFieldPrefix", TypeString, &sParameterFieldPrefix); Con::addVariable( "pref::AFX::terrainZodiacZBias", TypeF32, &sTerrainZodiacZBias); Con::addVariable( "pref::AFX::interiorZodiacZBias", TypeF32, &sInteriorZodiacZBias); Con::addVariable( "pref::AFX::polysoupZodiacZBias", TypeF32, &sPolysoupZodiacZBias); Con::setIntVariable( "$AFX::TARGETING_OFF", TARGETING_OFF); Con::setIntVariable( "$AFX::TARGETING_STANDARD", TARGETING_STANDARD); Con::setIntVariable( "$AFX::TARGETING_FREE", TARGETING_FREE); Con::setIntVariable( "$AFX::TARGET_CHECK_POLL", TARGET_CHECK_POLL); Con::setIntVariable( "$AFX::TARGET_CHECK_ON_MOUSE_MOVE", TARGET_CHECK_ON_MOUSE_MOVE); Con::setIntVariable( "$AFX::IMPACTED_SOMETHING", afxEffectDefs::IMPACTED_SOMETHING); Con::setIntVariable( "$AFX::IMPACTED_TARGET", afxEffectDefs::IMPACTED_TARGET); Con::setIntVariable( "$AFX::IMPACTED_PRIMARY", afxEffectDefs::IMPACTED_PRIMARY); Con::setIntVariable( "$AFX::IMPACT_IN_WATER", afxEffectDefs::IMPACT_IN_WATER); Con::setIntVariable( "$AFX::CASTER_IN_WATER", afxEffectDefs::CASTER_IN_WATER); Con::setIntVariable( "$AFX::SERVER_ONLY", afxEffectDefs::SERVER_ONLY); Con::setIntVariable( "$AFX::SCOPE_ALWAYS", afxEffectDefs::SCOPE_ALWAYS); Con::setIntVariable( "$AFX::GHOSTABLE", afxEffectDefs::GHOSTABLE); Con::setIntVariable( "$AFX::CLIENT_ONLY", afxEffectDefs::CLIENT_ONLY); Con::setIntVariable( "$AFX::SERVER_AND_CLIENT", afxEffectDefs::SERVER_AND_CLIENT); Con::setIntVariable( "$AFX::DELAY", afxEffectDefs::TIMING_DELAY); Con::setIntVariable( "$AFX::LIFETIME", afxEffectDefs::TIMING_LIFETIME); Con::setIntVariable( "$AFX::FADE_IN_TIME", afxEffectDefs::TIMING_FADE_IN); Con::setIntVariable( "$AFX::FADE_OUT_TIME", afxEffectDefs::TIMING_FADE_OUT); Con::setFloatVariable( "$AFX::INFINITE_TIME", -1.0f); Con::setIntVariable( "$AFX::INFINITE_REPEATS", -1); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_0", Player::PLAYER_MOVE_TRIGGER_0); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_1", Player::PLAYER_MOVE_TRIGGER_1); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_2", Player::PLAYER_MOVE_TRIGGER_2); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_3", Player::PLAYER_MOVE_TRIGGER_3); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_4", Player::PLAYER_MOVE_TRIGGER_4); Con::setIntVariable( "$AFX::PLAYER_MOVE_TRIGGER_5", Player::PLAYER_MOVE_TRIGGER_5); Con::setIntVariable( "$AFX::PLAYER_FIRE_S_TRIGGER", Player::PLAYER_FIRE_S_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_FIRE_ALT_S_TRIGGER", Player::PLAYER_FIRE_ALT_S_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_JUMP_S_TRIGGER", Player::PLAYER_JUMP_S_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_LANDING_S_TRIGGER", Player::PLAYER_LANDING_S_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_LF_FOOT_C_TRIGGER", Player::PLAYER_LF_FOOT_C_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_RT_FOOT_C_TRIGGER", Player::PLAYER_RT_FOOT_C_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_LANDING_C_TRIGGER", Player::PLAYER_LANDING_C_TRIGGER); Con::setIntVariable( "$AFX::PLAYER_IDLE_C_TRIGGER", Player::PLAYER_IDLE_C_TRIGGER); Con::setIntVariable( "$AFX::ILLUM_TERRAIN", 0); Con::setIntVariable( "$AFX::ILLUM_ATLAS", 0); Con::setIntVariable( "$AFX::ILLUM_DIF", 0); Con::setIntVariable( "$AFX::ILLUM_DTS", 0); Con::setIntVariable( "$AFX::ILLUM_ALL", 0); Con::setIntVariable("$TypeMasks::TerrainLikeObjectType", TerrainLikeObjectType); Con::setIntVariable("$TypeMasks::InteriorLikeObjectType", InteriorLikeObjectType); Con::setIntVariable("$TypeMasks::PolysoupObjectType", InteriorLikeObjectType); // deprecated Con::addVariable("$pref::Audio::usePlayerCentricListener", TypeBool, &sUsePlayerCentricListener); afxResidueMgr* residue_mgr = new afxResidueMgr; afxResidueMgr::setMaster(residue_mgr); master_scope_id = 1; master_choreographer_id = 1; is_shutdown = false; if (lm_old_names[0] == 0) { lm_old_names[0] = StringTable->insert("SG - Original Advanced (Lighting Pack)"); lm_old_names[1] = StringTable->insert("SG - Original Stock (Lighting Pack)"); lm_old_names[2] = StringTable->insert("SG - Inverse Square (Lighting Pack)"); lm_old_names[3] = StringTable->insert("SG - Inverse Square Fast Falloff (Lighting Pack)"); lm_old_names[4] = StringTable->insert("SG - Near Linear (Lighting Pack)"); lm_old_names[5] = StringTable->insert("SG - Near Linear Fast Falloff (Lighting Pack)"); // lm_new_names[0] = StringTable->insert("Original Advanced"); lm_new_names[1] = StringTable->insert("Original Stock"); lm_new_names[2] = StringTable->insert("Inverse Square"); lm_new_names[3] = StringTable->insert("Inverse Square Fast Falloff"); lm_new_names[4] = StringTable->insert("Near Linear"); lm_new_names[5] = StringTable->insert("Near Linear Fast Falloff"); } } void arcaneFX::shutdown() { is_shutdown = true; for (S32 i = 0; i < scoped_objs.size(); i++) if (scoped_objs[i]) scoped_objs[i]->setScopeRegistered(false); scoped_objs.clear(); for (S32 i = 0; i < client_choreographers.size(); i++) if (client_choreographers[i]) client_choreographers[i]->clearChoreographerId(); client_choreographers.clear(); for (S32 i = 0; i < selectrons.size(); i++) if (selectrons[i]) selectrons[i]->registered = false; selectrons.clear(); afxResidueMgr* residue_mgr = afxResidueMgr::getMaster(); delete residue_mgr; afxResidueMgr::setMaster(NULL); } MODULE_BEGIN( arcaneFX ) MODULE_INIT { arcaneFX::init(); } MODULE_SHUTDOWN { arcaneFX::shutdown(); } MODULE_END; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// void arcaneFX::advanceTime(U32 delta) { GameConnection* conn = GameConnection::getConnectionToServer(); if (conn && !conn->isZonedIn() && conn->getCameraObject() != 0) { conn->setZonedIn(); conn->postNetEvent(new ClientZoneInEvent()); } afxZodiacMgr::frameReset(); afxResidueMgr::getMaster()->residueAdvanceTime(); } // U32 arcaneFX::registerChoreographer(afxChoreographer* ch) { if (!ch) return 0; active_choreographers.push_back(ch); //Con::printf("registerChoreographer() -- size=%d %s", active_choreographers.size(), // (ch->isServerObject()) ? "server" : "client"); return master_choreographer_id++; } void arcaneFX::unregisterChoreographer(afxChoreographer* ch) { if (!ch) return; for (U32 i = 0; i < active_choreographers.size(); i++) { if (ch == active_choreographers[i]) { active_choreographers.erase_fast(i); //Con::printf("unregisterChoreographer() -- size=%d %s", active_choreographers.size(), // (ch->isServerObject()) ? "server" : "client"); return; } } Con::errorf("arcaneFX::unregisterChoreographer() -- failed to find choreographer in list."); } void arcaneFX::registerClientChoreographer(afxChoreographer* ch) { if (!ch || ch->getChoreographerId() == 0) return; client_choreographers.push_back(ch); } void arcaneFX::unregisterClientChoreographer(afxChoreographer* ch) { if (!ch || ch->getChoreographerId() == 0) return; for (U32 i = 0; i < client_choreographers.size(); i++) { if (ch == client_choreographers[i]) { client_choreographers.erase_fast(i); return; } } Con::errorf("arcaneFX::unregisterClientChoreographer() -- failed to find choreographer in list."); } afxChoreographer* arcaneFX::findClientChoreographer(U32 id) { for (U32 i = 0; i < client_choreographers.size(); i++) { if (id == client_choreographers[i]->getChoreographerId()) return client_choreographers[i]; } return 0; } // void arcaneFX::registerSelectronData(afxSelectronData* selectron) { if (!selectron) return; selectrons.push_back(selectron); } void arcaneFX::unregisterSelectronData(afxSelectronData* selectron) { if (!selectron) return; for (U32 i = 0; i < selectrons.size(); i++) { if (selectron == selectrons[i]) { selectrons.erase_fast(i); return; } } Con::errorf("arcaneFX::unregisterSelectronData() -- failed to find selectron in list."); } afxSelectronData* arcaneFX::findSelectronData(U32 mask, U8 style) { for (U32 i = 0; i < selectrons.size(); i++) if (selectrons[i]->matches(mask, style)) return selectrons[i]; return 0; } U16 arcaneFX::generateScopeId() { U16 ret_id = master_scope_id++; if (master_scope_id >= BIT(GameBase::SCOPE_ID_BITS)) master_scope_id = 1; return ret_id; } void arcaneFX::registerScopedObject(SceneObject* object) { scoped_objs.push_back(object); object->setScopeRegistered(true); for (S32 i = 0; i < client_choreographers.size(); i++) if (client_choreographers[i]) client_choreographers[i]->restoreScopedObject(object); } SceneObject* arcaneFX::findScopedObject(U16 scope_id) { if (scoped_objs.size() > 0) { for (S32 i = scoped_objs.size()-1; i >= 0; i--) if (scoped_objs[i] && scoped_objs[i]->getScopeId() == scope_id) return scoped_objs[i]; } return 0; } void arcaneFX::unregisterScopedObject(SceneObject* object) { if (scoped_objs.size() > 0) { for (S32 i = scoped_objs.size()-1; i >= 0; i--) if (scoped_objs[i] == object) { scoped_objs.erase_fast(i); if (object) object->setScopeRegistered(false); return; } } } void arcaneFX::syncToNewConnection(GameConnection* conn) { if (conn) conn->setZonedIn(); for (U32 i = 0; i < active_choreographers.size(); i++) { if (active_choreographers[i]) active_choreographers[i]->sync_with_clients(); } } void arcaneFX::endMissionNotify() { for (S32 i = 0; i < scoped_objs.size(); i++) if (scoped_objs[i]) scoped_objs[i]->setScopeRegistered(false); scoped_objs.clear(); for (S32 i = 0; i < client_choreographers.size(); i++) if (client_choreographers[i]) client_choreographers[i]->clearChoreographerId(); client_choreographers.clear(); for (S32 i = 0; i < selectrons.size(); i++) if (selectrons[i]) selectrons[i]->registered = false; selectrons.clear(); if (afxResidueMgr::getMaster()) afxResidueMgr::getMaster()->cleanup(); afxZodiacMgr::missionCleanup(); } S32 arcaneFX::rolloverRayCast(Point3F start, Point3F end, U32 mask) { sIsFreeTargeting = false; #if !defined(AFX_CAP_ROLLOVER_RAYCASTS) return -1; #else GameConnection* conn = GameConnection::getConnectionToServer(); SceneObject* ctrl_obj = NULL; if (!arcaneFX::sClickToTargetSelf && conn != NULL) ctrl_obj = conn->getControlObject(); if (ctrl_obj) ctrl_obj->disableCollision(); SceneObject* rollover_obj = (conn) ? conn->getRolloverObj() : 0; SceneObject* picked_obj = 0; RayInfo hit_info; if (gClientContainer.castRay(start, end, mask, &hit_info)) picked_obj = dynamic_cast(hit_info.object); if (ctrl_obj) ctrl_obj->enableCollision(); if (picked_obj != rollover_obj) { if (rollover_obj) rollover_obj->setSelectionFlags(rollover_obj->getSelectionFlags() & ~SceneObject::PRE_SELECTED); if (picked_obj) picked_obj->setSelectionFlags(picked_obj->getSelectionFlags() | SceneObject::PRE_SELECTED); rollover_obj = picked_obj; if (conn) conn->setRolloverObj(rollover_obj); } return (picked_obj) ? picked_obj->getId() : -1; #endif } bool arcaneFX::freeTargetingRayCast(Point3F start, Point3F end, U32 mask) { sIsFreeTargeting = true; RayInfo hit_info; if (!gClientContainer.castRay(start, end, mask, &hit_info)) { sFreeTargetPosValid = false; return false; } sFreeTargetPosValid = true; sFreeTargetPos = hit_info.point; return true; } StringTableEntry arcaneFX::convertLightingModelName(StringTableEntry lm_name) { for (U32 i = 0; i < N_LIGHTING_MODELS; i++) { if (lm_name == lm_old_names[i]) return lm_new_names[i]; } return lm_name; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // Console Functions DefineEngineFunction(afxEndMissionNotify, void, (),, "...\n\n" "@ingroup AFX") { arcaneFX::endMissionNotify(); } DefineEngineFunction(afxGetVersion, const char*, (),, "...\n\n" "@ingroup AFX") { return AFX_VERSION_STRING; } DefineEngineFunction(afxGetEngine, const char*, (),, "...\n\n" "@ingroup AFX") { return "T3D"; } #if defined(AFX_CAP_ROLLOVER_RAYCASTS) DefineEngineFunction(rolloverRayCast, S32, (Point3F start, Point3F end, U32 mask),, "Performs a raycast from points start to end and returns the ID of nearest " "intersecting object with a type found in the specified mask. " "Returns -1 if no object is found.\n\n" "@ingroup AFX") { return arcaneFX::rolloverRayCast(start, end, mask); } #endif DefineEngineFunction(getRandomF, F32, (float a, float b), (F32_MAX, F32_MAX), "Get a random float number between a and b.\n\n" "@ingroup AFX") { if (b == F32_MAX) { if (a == F32_MAX) return gRandGen.randF(); return gRandGen.randF(0.0f, a); } return (a + (b-a)*gRandGen.randF()); } DefineEngineFunction(getRandomDir, Point3F, (Point3F axis, float thetaMin, float thetaMax, float phiMin, float phiMax), (Point3F(0.0f,0.0f,0.0f), 0.0f, 180.0f, 0.0f, 360.0f), "Get a random direction vector.\n\n" "@ingroup AFX") { return MathUtils::randomDir(axis, thetaMin, thetaMax, phiMin, phiMax); } DefineEngineFunction(MatrixInverseMulVector, Point3F, (MatrixF xfrm, Point3F vector),, "@brief Multiply the vector by the affine inverse of the transform.\n\n" "@ingroup AFX") { xfrm.affineInverse(); Point3F result; xfrm.mulV(vector, &result); return result; } DefineEngineFunction(moveTransformAbs, MatrixF, (MatrixF xfrm, Point3F pos),, "@brief Move the transform to the new absolute position.\n\n" "@ingroup AFX") { xfrm.setPosition(pos); return xfrm; } DefineEngineFunction(moveTransformRel, MatrixF, (MatrixF xfrm, Point3F pos),, "@brief Move the transform to the new relative position.\n\n" "@ingroup AFX") { pos += xfrm.getPosition(); xfrm.setPosition(pos); return xfrm; } DefineEngineFunction(getFreeTargetPosition, Point3F, (),, "@brief Returns the current location of the free target.\n\n" "@ingroup AFX") { if (!arcaneFX::sFreeTargetPosValid) return Point3F(0.0f, 0.0f, 0.0f); return arcaneFX::sFreeTargetPos; } DefineEngineMethod(SceneObject, getSpeed, F32, (),, "Returns the velocity of a scene-object.\n\n" "@ingroup AFX") { return object->getVelocity().len(); } static S32 mark_modkey = -1; DefineEngineFunction(markDataBlocks, void, (),, "@brief Called before a series of datablocks are reloaded to " "help distinguish reloaded datablocks from already loaded ones.\n\n" "@ingroup AFX") { mark_modkey = SimDataBlock::getNextModifiedKey(); } DefineEngineFunction(touchDataBlocks, void, (),, "@brief Called after a series of datablocks are reloaded to " "trigger some important actions on the reloaded datablocks.\n\n" "@ingroup AFX") { if (mark_modkey < 0) return; SimDataBlockGroup* g = Sim::getDataBlockGroup(); U32 groupCount = g->size(); for (S32 i = groupCount-1; i >= 0; i--) { SimDataBlock* simdb = (SimDataBlock*)(*g)[i]; if (simdb->getModifiedKey() > mark_modkey) { simdb->unregisterObject(); simdb->registerObject(); } } mark_modkey = -1; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// // Syntax Error Checking // (for checking eval() and compile() calls) DefineEngineFunction(wasSyntaxError, bool, (),, "@brief Returns true if script compiler had a syntax error. Useful " "for detecting syntax errors after reloading a script.\n\n" "@ingroup AFX") { return Compiler::gSyntaxError; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// // Network Object Identification // These useful console methods come from the following code resource: // // How to Identify Objects from Client to Server or Server to Client by Nathan Davies // http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4852 // DefineEngineMethod(NetConnection, GetGhostIndex, S32, (NetObject* obj),, "Returns the ghost-index for an object.\n\n" "@ingroup AFX") { if (obj) return object->getGhostIndex(obj); return 0; } DefineEngineMethod(NetConnection, ResolveGhost, S32, (int ghostIndex),, "Resolves a ghost-index into an object ID.\n\n" "@ingroup AFX") { if (ghostIndex != -1) { NetObject* pObject = NULL; if( object->isGhostingTo()) pObject = object->resolveGhost(ghostIndex); else if( object->isGhostingFrom()) pObject = object->resolveObjectFromGhostIndex(ghostIndex); if (pObject) return pObject->getId(); } return 0; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// ////////////////////////////////////////////////////////////////////////// // TypeByteRange ////////////////////////////////////////////////////////////////////////// IMPLEMENT_STRUCT( ByteRange, ByteRange,, "" ) END_IMPLEMENT_STRUCT; ConsoleType( ByteRange, TypeByteRange, ByteRange, "") ConsoleType( ByteRange, TypeByteRange2, ByteRange, "") ConsoleGetType( TypeByteRange ) { ByteRange* pt = (ByteRange *) dptr; char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%u %u", pt->low, pt->high); return returnBuffer; } ConsoleSetType( TypeByteRange ) { if(argc == 1) { ByteRange* range = (ByteRange*) dptr; U32 lo, hi; S32 args = dSscanf(argv[0], "%u %u", &lo, &hi); range->low = (args > 0) ? lo : 0; range->high = (args > 1) ? hi : 255; } else Con::printf("ByteRange must be set as \"low\" or \"low high\""); } ConsoleGetType( TypeByteRange2 ) { ByteRange* pt = (ByteRange *) dptr; char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%u %u", pt->low, pt->high); return returnBuffer; } ConsoleSetType( TypeByteRange2 ) { if(argc == 1) { ByteRange* range = (ByteRange*) dptr; U32 lo, hi; S32 args = dSscanf(argv[0], "%u %u", &lo, &hi); range->low = (args > 0) ? lo : 0; range->high = (args > 1) ? hi : lo; } else Con::printf("ByteRange must be set as \"low\" or \"low high\""); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// static void HSVtoRGB(F32 h, F32 s, F32 v, F32& r, F32& g, F32& b) { h = mFmod(h, 360.0f); if (v == 0.0f) r = g = b = 0.0f; else if (s == 0.0f) r = g = b = v; else { F32 hf = h/60.0f; S32 i = (S32) mFloor(hf); F32 f = hf - i; F32 pv = v*(1.0f - s); F32 qv = v*(1.0f - s*f); F32 tv = v*(1.0f - s*(1.0f - f)); switch (i) { case 0: r = v; g = tv; b = pv; break; case 1: r = qv; g = v; b = pv; break; case 2: r = pv; g = v; b = tv; break; case 3: r = pv; g = qv; b = v; break; case 4: r = tv; g = pv; b = v; break; case 5: r = v; g = pv; b = qv; break; default: r = g = b = 0.0f; break; } } } DefineEngineFunction(getColorFromHSV, const char*, (float hue, float sat, float val, float alpha), (0.0, 0.0, 1.0, 1.0), "Coverts an HSV formatted color into an RBG color.\n\n" "@param hue The hue of the color (0-360).\n" "@param sat The saturation of the color (0-1).\n" "@param val The value of the color (0-1).\n" "@param alpha The alpha of the color (0-1).\n" "@ingroup AFX") { LinearColorF rgb; HSVtoRGB(hue, sat, val, rgb.red, rgb.green, rgb.blue); rgb.alpha = alpha; char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%g %g %g %g", rgb.red, rgb.green, rgb.blue, rgb.alpha); return returnBuffer; } DefineEngineFunction(ColorScale, const char*, ( LinearColorF color, float scalar ),, "Returns color scaled by scalar (color*scalar).\n\n" "@param color The color to be scaled.\n" "@param scalar The amount to scale the color.\n" "@ingroup AFX") { color *= scalar; char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%g %g %g %g", color.red, color.green, color.blue, color.alpha); return returnBuffer; } DefineEngineFunction(getMinF, F32, (float a, float b),, "Returns the lesser of the two arguments.\n\n" "@ingroup AFX") { return getMin(a, b); } DefineEngineFunction(getMaxF, F32, (float a, float b),, "Returns the greater of the two arguments.\n\n" "@ingroup AFX") { return getMax(a, b); } DefineEngineStringlyVariadicFunction(echoThru, const char*, 2, 0, "(string passthru, string text...)" "Like echo(), but first argument is returned.\n" "@ingroup AFX") { U32 len = 0; S32 i; for (i = 2; i < argc; i++) len += dStrlen(argv[i]); char *ret = Con::getReturnBuffer(len + 1); ret[0] = 0; for (i = 2; i < argc; i++) dStrcat(ret, argv[i], len + 1); Con::printf("%s -- [%s]", ret, argv[1].getString()); ret[0] = 0; return argv[1]; } DefineEngineStringlyVariadicFunction(warnThru, const char*, 2, 0, "(string passthru, string text...)" "Like warn(), but first argument is returned.\n" "@ingroup AFX") { U32 len = 0; S32 i; for(i = 2; i < argc; i++) len += dStrlen(argv[i]); char *ret = Con::getReturnBuffer(len + 1); ret[0] = 0; for(i = 2; i < argc; i++) dStrcat(ret, argv[i], len + 1); Con::warnf("%s -- [%s]", ret, argv[1].getString()); ret[0] = 0; return argv[1]; } DefineEngineStringlyVariadicFunction(errorThru, const char*, 2, 0, "(string passthru, string text...)" "Like error(), but first argument is returned.\n" "@ingroup AFX") { U32 len = 0; S32 i; for(i = 2; i < argc; i++) len += dStrlen(argv[i]); char *ret = Con::getReturnBuffer(len + 1); ret[0] = 0; for(i = 2; i < argc; i++) dStrcat(ret, argv[i], len + 1); Con::errorf("%s -- [%s]", ret, argv[1].getString()); ret[0] = 0; return argv[1]; } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//