diff --git a/TheForceEngine/TFE_Asset/dfKeywords.cpp b/TheForceEngine/TFE_Asset/dfKeywords.cpp index fb85ff03c..3601394d4 100644 --- a/TheForceEngine/TFE_Asset/dfKeywords.cpp +++ b/TheForceEngine/TFE_Asset/dfKeywords.cpp @@ -227,6 +227,7 @@ static const char* c_keywords[] = "M_TRIGGER", // TFE "SCRIPTCALL:", + "NAME:" }; #define KEYWORD_COUNT TFE_ARRAYSIZE(c_keywords) diff --git a/TheForceEngine/TFE_Asset/dfKeywords.h b/TheForceEngine/TFE_Asset/dfKeywords.h index c8ea12725..373dcef67 100644 --- a/TheForceEngine/TFE_Asset/dfKeywords.h +++ b/TheForceEngine/TFE_Asset/dfKeywords.h @@ -234,6 +234,7 @@ enum KEYWORD KW_M_TRIGGER, // INF // TFE ADDED KW_SCRIPTCALL, + KW_NAME, KW_COUNT }; diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp index 32beda917..68906b1cc 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp @@ -661,6 +661,8 @@ namespace TFE_DarkForces corpse->worldWidth = 0; corpse->entityFlags |= ETFLAG_CORPSE; sector_addObject(obj->sector, corpse); + + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting } } actor_kill(); diff --git a/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp b/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp index c32770826..6922b2ced 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp @@ -626,6 +626,7 @@ namespace TFE_DarkForces corpse->worldHeight = 0; corpse->entityFlags |= (ETFLAG_CORPSE | ETFLAG_KEEP_CORPSE); sector_addObject(local(obj)->sector, corpse); + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting local(physicsActor)->alive = JFALSE; actor_handleBossDeath(local(physicsActor)); diff --git a/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp b/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp index b5a93b9ac..dd454050b 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp @@ -780,6 +780,8 @@ namespace TFE_DarkForces corpse->worldHeight = 0; corpse->entityFlags |= (ETFLAG_CORPSE | ETFLAG_KEEP_CORPSE); sector_addObject(sector, corpse); + + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting } local(physicsActor)->alive = JFALSE; actor_handleBossDeath(local(physicsActor)); diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp index 4a7ee313f..0b4df2b09 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp @@ -253,6 +253,8 @@ namespace TFE_DarkForces newObj->worldWidth = 0; newObj->worldHeight = 0; sector_addObject(local(sector), newObj); + + obj_addToRefList(newObj, ObjRefType_Corpse); // scripting } // Spawn a battery. diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp index e12ff3aa1..04a2553b0 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp @@ -628,6 +628,8 @@ namespace TFE_DarkForces corpse->worldHeight = 0; corpse->entityFlags |= (ETFLAG_CORPSE | ETFLAG_KEEP_CORPSE); sector_addObject(sector, corpse); + + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting } local(physicsActor)->alive = JFALSE; actor_handleBossDeath(local(physicsActor)); diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp index da8d2a452..9f29531ea 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp @@ -724,6 +724,8 @@ namespace TFE_DarkForces corpse->worldHeight = 0; corpse->entityFlags |= (ETFLAG_CORPSE | ETFLAG_KEEP_CORPSE); sector_addObject(sector, corpse); + + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting } local(physicsActor)->alive = JFALSE; actor_handleBossDeath(local(physicsActor)); diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp index a3d4087f8..04d2487b4 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp @@ -716,6 +716,7 @@ namespace TFE_DarkForces corpse->worldHeight = 0; corpse->entityFlags |= (ETFLAG_CORPSE | ETFLAG_KEEP_CORPSE); sector_addObject(sector, corpse); + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting // Create Plasma pickup SecObject* item = item_create(ITEM_PLASMA); diff --git a/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp b/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp index ca76018d6..090b9c219 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp @@ -33,6 +33,7 @@ namespace TFE_DarkForces newObj->worldWidth = obj->worldWidth; sector_addObject(obj->sector, newObj); + obj_addToRefList(newObj, ObjRefType_Corpse); // scripting actor_kill(); return 0; } diff --git a/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp b/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp index db8d3d95d..4ca1384e8 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp @@ -59,6 +59,8 @@ namespace TFE_DarkForces corpse->entityFlags |= ETFLAG_CORPSE; corpse->worldWidth = 0; sector_addObject(sector, corpse); + + obj_addToRefList(corpse, ObjRefType_Corpse); // scripting } actor_kill(); return 0; diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp index 2547eb0fb..28e992704 100644 --- a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp @@ -1,8 +1,10 @@ +#include #include "gs_level.h" #include "scriptTexture.h" #include "scriptElev.h" #include "scriptWall.h" #include "scriptSector.h" +#include "scriptObject.h" #include #include #include @@ -183,16 +185,73 @@ namespace TFE_DarkForces setProjectileGravityAccel(FIXED(pGrav)); } + ScriptObject GS_Level::getObjectById(s32 id) + { + if (id < 0 || id >= s_objectRefList.size()) + { + TFE_System::logWrite(LOG_ERROR, "Level Script", "Runtime error, invalid objectID %d.", id); + id = -1; + } + + ScriptObject object(id); + return object; + } + + ScriptObject GS_Level::getObjectByName(std::string name) + { + const char* cname = name.c_str(); + if (!cname || name.empty() || s_objectRefList.empty()) + { + ScriptObject object(-1); + return object; + } + + s32 objectId = -1; + for (s32 i = 0; i < s_objectRefList.size(); i++) + { + if (strcasecmp(cname, s_objectRefList[i].name) == 0) + { + objectId = i; + break; + } + } + + ScriptObject object(objectId); + return object; + } + + // Objects will be contained in the results array + void GS_Level::getAllObjectsByName(std::string name, CScriptArray& results) + { + const char* cname = name.c_str(); + if (!cname || name.empty() || s_objectRefList.empty()) + { + return; + } + + results.Resize(0); + for (s32 i = 0; i < s_objectRefList.size(); i++) + { + if (strcasecmp(cname, s_objectRefList[i].name) == 0) + { + ScriptObject sObject(i); + results.InsertLast(&sObject); + } + } + } + bool GS_Level::scriptRegister(ScriptAPI api) { ScriptElev scriptElev; ScriptTexture scriptTex; ScriptSector scriptSector; ScriptWall scriptWall; + ScriptObject scriptObject; scriptElev.registerType(); scriptTex.registerType(); scriptWall.registerType(); scriptSector.registerType(); + scriptObject.registerType(); ScriptClassBegin("Level", "level", api); { @@ -257,6 +316,9 @@ namespace TFE_DarkForces ScriptObjMethod("Sector getSector(string)", getSectorByName); ScriptObjMethod("Elevator getElevator(int)", getElevator); ScriptObjMethod("void findConnectedSectors(Sector initSector, uint, array&)", findConnectedSectors); + ScriptObjMethod("Object getObject(int)", getObjectById); + ScriptObjFunc("Object getObject(string)", getObjectByName); + ScriptObjFunc("int getObjectsByName(string, array&)", getAllObjectsByName); ScriptPropertySet("void set_gravity(int)", setGravity); ScriptPropertySet("void set_projectileGravity(int)", setProjectileGravity); diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h index 065bdf70e..09b70113f 100644 --- a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h @@ -17,6 +17,7 @@ namespace TFE_DarkForces { class ScriptSector; class ScriptElev; + class ScriptObject; class GS_Level : public ScriptAPIClass { @@ -27,6 +28,9 @@ namespace TFE_DarkForces ScriptSector getSectorById(s32 id); ScriptSector getSectorByName(std::string name); ScriptElev getElevator(s32 id); + ScriptObject getObjectById(s32 id); + static ScriptObject getObjectByName(std::string name); + static void getAllObjectsByName(std::string name, CScriptArray& result); void findConnectedSectors(ScriptSector initSector, u32 matchProp, CScriptArray& results); void setGravity(s32 grav); void setProjectileGravity(s32 grav); diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.cpp b/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.cpp new file mode 100644 index 000000000..c65e76f54 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.cpp @@ -0,0 +1,305 @@ +#include "gs_level.h" +#include "scriptObject.h" +#include "scriptSector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace TFE_DarkForces +{ + bool isScriptObjectValid(ScriptObject* sObject) + { + return sObject->m_id >= 0 && sObject->m_id < s_objectRefList.size(); + } + + bool doesObjectExist(ScriptObject* sObject) + { + if (!isScriptObjectValid(sObject)) + { + return false; + } + + SecObject* obj = s_objectRefList[sObject->m_id].object; + if (!obj || !obj->self) + { + return false; + } + + return true; + } + + void deleteObject(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = s_objectRefList[sObject->m_id].object; + if (obj) + { + if (obj->entityFlags & ETFLAG_PLAYER || obj->flags & OBJ_FLAG_EYE) + { + TFE_FrontEndUI::logToConsole("Deleting the player or eye object is not allowed."); + return; + } + + freeObject(obj); + } + } + + ScriptSector getSector(ScriptObject* sObject) + { + if (doesObjectExist(sObject)) + { + SecObject* obj = s_objectRefList[sObject->m_id].object; + if (obj && obj->sector) + { + ScriptSector sSector(obj->sector->index); + return sSector; + } + } + + // not found + ScriptSector sSector(-1); + return sSector; + } + + bool isPlayer(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return false; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj && obj->entityFlags & ETFLAG_PLAYER) + { + return true; + } + + return false; + } + + vec3_float getPosition(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return { 0, 0, 0 }; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + return + { + fixed16ToFloat(obj->posWS.x), + -fixed16ToFloat(obj->posWS.y), + fixed16ToFloat(obj->posWS.z), + }; + } + + return { 0, 0, 0 }; + } + + float getYaw(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return 0; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + return angleToFloat(obj->yaw); + } + + return 0; + } + + float getWorldWidth(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return 0; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + return fixed16ToFloat(obj->worldWidth); + } + + return 0; + } + + float getWorldHeight(ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return 0; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + return fixed16ToFloat(obj->worldHeight); + } + + return 0; + } + + void setPosition(vec3_float position, ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (!obj) { return; } + + fixed16_16 newX = floatToFixed16(position.x); + fixed16_16 newY = -floatToFixed16(position.y); + fixed16_16 newZ = floatToFixed16(position.z); + + // Check if object has moved out of its current sector + // Start with a bounds check + bool outOfHorzBounds = newX < obj->sector->boundsMin.x || newX > obj->sector->boundsMax.x || + newZ < obj->sector->boundsMin.z || newZ > obj->sector->boundsMax.z; + bool outOfVertBounds = newY < obj->sector->ceilingHeight || newY > obj->sector->floorHeight; + + // If within bounds, check if it has passed through a wall + RWall* collisionWall = nullptr; + if (!outOfHorzBounds && !outOfVertBounds) + { + collisionWall = collision_wallCollisionFromPath(obj->sector, obj->posWS.x, obj->posWS.z, newX, newZ); + } + + if (collisionWall || outOfHorzBounds || outOfVertBounds) + { + // Try to find a new sector for the object + RSector* sector = sector_which3D(newX, newY, newZ); + if (sector && (sector != obj->sector)) + { + sector_addObject(sector, obj); + } + } + + obj->posWS.x = newX; + obj->posWS.y = newY; + obj->posWS.z = newZ; + } + + void matchPositionToTargetObj(ScriptObject targetSObject, ScriptObject* sObject) + { + if (!doesObjectExist(sObject) || !doesObjectExist(&targetSObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + SecObject* targetObj = TFE_Jedi::s_objectRefList[(targetSObject.m_id)].object; + if (!obj || !targetObj) { return; } + + obj->posWS.x = targetObj->posWS.x; + obj->posWS.y = targetObj->posWS.y; + obj->posWS.z = targetObj->posWS.z; + + if (targetObj->sector && targetObj->sector != obj->sector) + { + sector_addObject(targetObj->sector, obj); + } + } + + void matchPositionToTargetObjByName(std::string targetObjName, ScriptObject* sObject) + { + ScriptObject targetScriptObj = GS_Level::getObjectByName(targetObjName); + matchPositionToTargetObj(targetScriptObj, sObject); + } + + void setYaw(float yaw, ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (!obj) { return; } + + if (yaw > 360.0) { yaw -= 360.0; } + if (yaw < -360.0) { yaw += 360.0; } + obj->yaw = floatToAngle(yaw); + + if (obj->type == OBJ_TYPE_3D) + { + obj3d_computeTransform(obj); + } + } + + void setWorldWidth(float wWidth, ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + wWidth = max(0.0f, wWidth); + obj->worldWidth = floatToFixed16(wWidth); + } + } + + void setWorldHeight(float wHeight, ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + if (obj) + { + obj->worldHeight = floatToFixed16(wHeight); + } + } + + void addLogicToObject(std::string logic, ScriptObject* sObject) + { + if (!doesObjectExist(sObject)) { return; } + + SecObject* obj = TFE_Jedi::s_objectRefList[sObject->m_id].object; + KEYWORD logicId = getKeywordIndex(logic.c_str()); + + if (logicId == KW_UNKNOWN) { return; } + if (logicId == KW_ANIM) + { + obj_setSpriteAnim(obj); + } + else if (logicId >= KW_TROOP && logicId <= KW_SCENERY) + { + obj_setEnemyLogic(obj, logicId); + } + else if (logicId >= KW_BATTERY && logicId <= KW_AUTOGUN) + { + obj_createPickup(obj, getPickupItemId(logic.c_str())); + } + // TODO enable adding custom logic + } + + void ScriptObject::registerType() + { + s32 res = 0; + asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); + + ScriptValueType("Object"); + // Variables + ScriptMemberVariable("int id", m_id); + + // Checks + ScriptObjFunc("bool isValid()", isScriptObjectValid); + ScriptObjFunc("bool exists()", doesObjectExist); + ScriptObjFunc("bool isPlayer()", isPlayer); + + // Getters + ScriptObjFunc("Sector getSector()", getSector); + ScriptPropertyGetFunc("float3 get_position()", getPosition); + ScriptPropertyGetFunc("float get_yaw()", getYaw); + ScriptPropertyGetFunc("float get_radius()", getWorldWidth); + ScriptPropertyGetFunc("float get_height()", getWorldHeight); // Let's allow both sets of terminology here + ScriptPropertyGetFunc("float get_worldWidth()", getWorldWidth); // radius = world width + ScriptPropertyGetFunc("float get_worldHeight()", getWorldHeight); // height = world height + + // Setters + ScriptPropertySetFunc("void set_position(float3)", setPosition); + ScriptPropertySetFunc("void set_yaw(float)", setYaw); + ScriptPropertySetFunc("void set_radius(float)", setWorldWidth); + ScriptPropertySetFunc("void set_height(float)", setWorldHeight); + ScriptPropertySetFunc("void set_worldWidth(float)", setWorldWidth); + ScriptPropertySetFunc("void set_worldHeight(float)", setWorldHeight); + ScriptObjFunc("void matchPosition(Object)", matchPositionToTargetObj); + ScriptObjFunc("void matchPosition(string)", matchPositionToTargetObjByName); + + // Other functions + ScriptObjFunc("void delete()", deleteObject); + ScriptObjFunc("void addLogic(string)", addLogicToObject); + } +} diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.h b/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.h new file mode 100644 index 000000000..7d1dcf3a8 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptObject.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace TFE_DarkForces +{ + class ScriptObject + { + public: + ScriptObject() : m_id(-1) {}; + ScriptObject(s32 id) : m_id(id) {}; + void registerType(); + + public: + s32 m_id; + }; + + extern bool isScriptObjectValid(ScriptObject* object); +} \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/generator.cpp b/TheForceEngine/TFE_DarkForces/generator.cpp index d9df0407f..1672867e7 100644 --- a/TheForceEngine/TFE_DarkForces/generator.cpp +++ b/TheForceEngine/TFE_DarkForces/generator.cpp @@ -105,6 +105,7 @@ namespace TFE_DarkForces spawn->yaw = random_next() & ANGLE_MASK; spawn->worldHeight = FIXED(7); sector_addObject(obj->sector, spawn); + obj_addToRefList(spawn, ObjRefType_GeneratorSpawn); // scripting assert(gen->entities && allocator_validate(gen->entities)); diff --git a/TheForceEngine/TFE_DarkForces/hitEffect.cpp b/TheForceEngine/TFE_DarkForces/hitEffect.cpp index fa41684bd..b8ad6a3c4 100644 --- a/TheForceEngine/TFE_DarkForces/hitEffect.cpp +++ b/TheForceEngine/TFE_DarkForces/hitEffect.cpp @@ -213,6 +213,9 @@ namespace TFE_DarkForces setupAnimationFromLogic(animLogic, 0/*animIndex*/, 0/*firstFrame*/, 0xffffffff/*lastFrame*/, 1/*loopCount*/); obj->worldWidth = 0; + + // Add this if we want scripting access to effects + // obj_addToRefList(obj, ObjRefType_Effect); // scripting } } if (s_curEffectData->soundEffect) @@ -293,6 +296,9 @@ namespace TFE_DarkForces setupAnimationFromLogic(logic, 0/*animIndex*/, 0/*firstFrame*/, 0xffffffff/*lastFrame*/, 1/*loopCount*/); sound_playCued(s_concussionExplodeSnd, newObj->posWS); + // Add this if we want scripting access to effects + // obj_addToRefList(obj, ObjRefType_Effect); // scripting + s_msgArg1 = s_curEffectData->damage; } else diff --git a/TheForceEngine/TFE_DarkForces/item.cpp b/TheForceEngine/TFE_DarkForces/item.cpp index 5c56861a9..ccbb11841 100644 --- a/TheForceEngine/TFE_DarkForces/item.cpp +++ b/TheForceEngine/TFE_DarkForces/item.cpp @@ -122,6 +122,7 @@ namespace TFE_DarkForces obj_setSpriteAnim(newObj); } + obj_addToRefList(newObj, ObjRefType_SpawnItem); // scripting return newObj; } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/logic.cpp b/TheForceEngine/TFE_DarkForces/logic.cpp index 675c86558..a900ecb83 100644 --- a/TheForceEngine/TFE_DarkForces/logic.cpp +++ b/TheForceEngine/TFE_DarkForces/logic.cpp @@ -9,6 +9,7 @@ #include "projectile.h" #include #include +#include #include #include #include @@ -101,6 +102,11 @@ namespace TFE_DarkForces { obj->worldWidth = floatToFixed16(strtof(s_objSeqArg1, &endPtr)); } + else if (key == KW_NAME) + { + // TFE - scripting + TFE_Jedi::obj_addName(s_objSeqArg1, obj); + } else // Invalid key. { retValue = JFALSE; @@ -402,6 +408,7 @@ namespace TFE_DarkForces sprite_setData(spawn, wax); obj_setEnemyLogic(spawn, type); + obj_addToRefList(spawn, ObjRefType_ConsoleSpawn); // scripting return spawn; } diff --git a/TheForceEngine/TFE_DarkForces/projectile.cpp b/TheForceEngine/TFE_DarkForces/projectile.cpp index df62a75ba..1ac0bc76c 100644 --- a/TheForceEngine/TFE_DarkForces/projectile.cpp +++ b/TheForceEngine/TFE_DarkForces/projectile.cpp @@ -450,6 +450,7 @@ namespace TFE_DarkForces sector_addObject(sector, projObj); task_makeActive(s_projectileTask); + obj_addToRefList(projObj, ObjRefType_Projectile); // scripting return (Logic*)projLogic; } diff --git a/TheForceEngine/TFE_Jedi/Level/level.cpp b/TheForceEngine/TFE_Jedi/Level/level.cpp index 2101ef05b..bad8d25dc 100644 --- a/TheForceEngine/TFE_Jedi/Level/level.cpp +++ b/TheForceEngine/TFE_Jedi/Level/level.cpp @@ -1002,6 +1002,11 @@ namespace TFE_Jedi } } + if (obj) + { + obj_addToRefList(obj, ObjRefType_Startup); // Scripting + } + if (obj) { readNextLine = object_parseSeq(obj, &parser, &bufferPos); diff --git a/TheForceEngine/TFE_Jedi/Level/robjData.cpp b/TheForceEngine/TFE_Jedi/Level/robjData.cpp index f670111bd..27f0be871 100644 --- a/TheForceEngine/TFE_Jedi/Level/robjData.cpp +++ b/TheForceEngine/TFE_Jedi/Level/robjData.cpp @@ -24,10 +24,20 @@ namespace TFE_Jedi ChunkedArray* objectList = nullptr; }; static SectorObjectData s_objData = {}; - + + // TFE - immutable reference list of objects for scripting + // Entries are never deleted from this list so objects will keep a unique ID based on their position in the list + std::vector s_objectRefList; + + // Forward declarations + void obj_refListClear(); + void objRefList_serialize(Stream* stream); + + void objData_clear() { s_objData = {}; + obj_refListClear(); } SecObject* objData_allocFromArray() @@ -260,5 +270,138 @@ namespace TFE_Jedi allocator_restoreIter((Allocator*)obj->logic); } } + + objRefList_serialize(stream); + } + + //////////////////////////////////////////// + // Scripting - global object reference list + //////////////////////////////////////////// + void obj_refListClear() + { + s_objectRefList.clear(); + } + + void obj_addToRefList(SecObject* obj, ObjectRefType refType) + { + if (!obj) { return; } // don't bother adding a null object + + ObjectRef objRef; + memset(objRef.name, 0, sizeof(objRef.name)); + objRef.object = obj; + objRef.type = refType; + + s_objectRefList.push_back(objRef); + } + + ObjectRef* obj_getRef(SecObject* obj) + { + if (!obj || s_objectRefList.empty()) + { + return nullptr; + } + + for (s32 i = 0; i < s_objectRefList.size(); i++) + { + if (s_objectRefList[i].object == obj) + { + return &s_objectRefList[i]; + } + } + + return nullptr; // object is not in the list + } + + s32 obj_getRefIndex(SecObject* obj) + { + if (!obj || s_objectRefList.empty()) + { + return -1; + } + + for (s32 i = 0; i < s_objectRefList.size(); i++) + { + if (s_objectRefList[i].object == obj) + { + return i; + } + } + + return -1; // object is not in the list } + + void obj_removeFromRefList(SecObject* obj) + { + // Remove the object pointer, and mark the object as removed. + // This creates an "empty space" in the list so the Ids of other objects don't change + ObjectRef* objRef = obj_getRef(obj); + if (!objRef) { return; } + + objRef->object = nullptr; + objRef->type = ObjRefType_Removed; + } + + void obj_addName(const char* name, SecObject* obj) + { + if (strlen(name) == 0) { return; } + + ObjectRef* objRef = obj_getRef(obj); + if (objRef) + { + memset(objRef->name, 0, 32); + strncpy(objRef->name, name, 31); + } + } + + void objRefList_serialize(Stream* stream) + { + if (serialization_getMode() == SMODE_READ) + { + obj_refListClear(); + } + + s32 refListSize = s_objectRefList.size(); + SERIALIZE(ObjState_RefList, refListSize, 0); + + for (int o = 0; o < refListSize; o++) + { + ObjectRef objRef = {}; + s32 objId; + + if (serialization_getMode() == SMODE_WRITE) + { + objRef = s_objectRefList[o]; + objId = objRef.object ? objRef.object->serializeIndex : -1; + } + SERIALIZE(ObjState_RefList, objId, -1); + if (serialization_getMode() == SMODE_READ) + { + objRef.object = objId >= 0 ? objData_getObjectBySerializationId(objId) : nullptr; + } + + SERIALIZE(ObjState_RefList, objRef.type, ObjRefType_Removed); + + if (serialization_getMode() == SMODE_READ) + { + memset(objRef.name, 0, sizeof(objRef.name)); + } + + s32 nameLength = 0; + if (serialization_getMode() == SMODE_WRITE) + { + nameLength = strlen(objRef.name); + } + SERIALIZE(ObjState_RefList, nameLength, 0); + for (int i = 0; i < nameLength; i++) + { + SERIALIZE(ObjState_RefList, objRef.name[i], 0); + } + + if (serialization_getMode() == SMODE_READ) + { + s_objectRefList.push_back(objRef); + } + } + } + } // namespace TFE_Jedi \ No newline at end of file diff --git a/TheForceEngine/TFE_Jedi/Level/robjData.h b/TheForceEngine/TFE_Jedi/Level/robjData.h index 47fbdaad0..bdf3d886c 100644 --- a/TheForceEngine/TFE_Jedi/Level/robjData.h +++ b/TheForceEngine/TFE_Jedi/Level/robjData.h @@ -65,7 +65,21 @@ enum ObjStateVersion : u32 ObjState_CustomLogics = 6, ObjState_ConstOverrides = 7, ObjState_DisablePlayerMovement = 8, - ObjState_CurVersion = ObjState_DisablePlayerMovement, + ObjState_RefList = 9, + ObjState_CurVersion = ObjState_RefList, +}; + +// TFE Scripting +enum ObjectRefType : s32 +{ + ObjRefType_Startup = 0, // Present at level start + ObjRefType_Projectile = 1, + ObjRefType_Effect = 2, + ObjRefType_SpawnItem = 3, // Item spawned at runtime + ObjRefType_Corpse = 4, + ObjRefType_GeneratorSpawn = 5, + ObjRefType_ConsoleSpawn = 6, + ObjRefType_Removed = -1, // Object no longer exists }; #define SPRITE_SCALE_FIXED FIXED(10) @@ -117,6 +131,15 @@ struct SecObject namespace TFE_Jedi { + // TFE - reference list of objects for scripting + struct ObjectRef + { + SecObject* object; + ObjectRefType type; + char name[32]; + }; + extern std::vector s_objectRefList; + void objData_clear(); SecObject* objData_allocFromArray(); void objData_freeToArray(SecObject* obj); @@ -126,4 +149,9 @@ namespace TFE_Jedi // Used for downstream serialization, to get the object from the serialized object ID. SecObject* objData_getObjectBySerializationId(u32 id); SecObject* objData_getObjectBySerializationId_NoValidation(u32 id); + + // TFE - scripting + void obj_addToRefList(SecObject* obj, ObjectRefType refType); + void obj_removeFromRefList(SecObject* obj); + void obj_addName(const char* name, SecObject* obj); } diff --git a/TheForceEngine/TFE_Jedi/Level/robject.cpp b/TheForceEngine/TFE_Jedi/Level/robject.cpp index 942acd253..fc5f50878 100644 --- a/TheForceEngine/TFE_Jedi/Level/robject.cpp +++ b/TheForceEngine/TFE_Jedi/Level/robject.cpp @@ -52,6 +52,7 @@ namespace TFE_Jedi allocator_free((Allocator*)obj->logic); sector_removeObject(obj); + obj_removeFromRefList(obj); // mark as removed in scripting ref list objData_freeToArray(obj); s_freeObjLock = JFALSE; diff --git a/TheForceEngine/TheForceEngine.vcxproj b/TheForceEngine/TheForceEngine.vcxproj index 70c248047..f511b487d 100644 --- a/TheForceEngine/TheForceEngine.vcxproj +++ b/TheForceEngine/TheForceEngine.vcxproj @@ -445,6 +445,7 @@ echo ^)"; + @@ -861,6 +862,7 @@ echo ^)"; + diff --git a/TheForceEngine/TheForceEngine.vcxproj.filters b/TheForceEngine/TheForceEngine.vcxproj.filters index 6b1a7cc81..c1d640326 100644 --- a/TheForceEngine/TheForceEngine.vcxproj.filters +++ b/TheForceEngine/TheForceEngine.vcxproj.filters @@ -1492,6 +1492,9 @@ Source\TFE_Editor\LevelEditor + + Source\TFE_DarkForces\Scripting + @@ -2616,6 +2619,9 @@ Source\TFE_Editor\LevelEditor + + Source\TFE_DarkForces\Scripting + @@ -2829,4 +2835,4 @@ Source\TFE_ForceScript\Angelscript\angelscript\source - \ No newline at end of file +