From 18421a52c0913c056f4bfa488b06000c21ba5b3a Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:26:35 -0500 Subject: [PATCH 01/64] Initial POC commit of replay code --- TheForceEngine/TFE_DarkForces/Actor/actor.cpp | 9 +- .../TFE_DarkForces/Actor/mousebot.cpp | 27 +- .../TFE_DarkForces/darkForcesMain.cpp | 5 +- .../TFE_DarkForces/darkForcesMain.h | 1 + TheForceEngine/TFE_DarkForces/hud.cpp | 49 ++- TheForceEngine/TFE_DarkForces/hud.h | 4 +- TheForceEngine/TFE_DarkForces/mission.cpp | 33 +- TheForceEngine/TFE_DarkForces/mission.h | 2 + TheForceEngine/TFE_DarkForces/random.cpp | 20 +- TheForceEngine/TFE_DarkForces/random.h | 2 + TheForceEngine/TFE_DarkForces/time.cpp | 61 +++- TheForceEngine/TFE_DarkForces/time.h | 4 +- TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp | 16 + TheForceEngine/TFE_Game/igame.h | 1 + TheForceEngine/TFE_Input/input.cpp | 7 + TheForceEngine/TFE_Input/inputMapping.cpp | 237 ++++++++++++ TheForceEngine/TFE_Input/inputMapping.h | 6 + TheForceEngine/TFE_Input/replay.cpp | 343 ++++++++++++++++++ TheForceEngine/TFE_Input/replay.h | 52 +++ TheForceEngine/TFE_Jedi/Task/task.cpp | 44 ++- TheForceEngine/TFE_Settings/settings.h | 2 + TheForceEngine/TFE_System/frameLimiter.cpp | 1 + TheForceEngine/TFE_System/log.cpp | 11 +- TheForceEngine/TFE_System/system.cpp | 41 ++- TheForceEngine/TFE_System/system.h | 6 + TheForceEngine/main.cpp | 79 +++- TheForceEngine/main.h | 1 + 27 files changed, 1022 insertions(+), 42 deletions(-) create mode 100644 TheForceEngine/TFE_Input/replay.cpp create mode 100644 TheForceEngine/TFE_Input/replay.h diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp index a080b411c..883cdfbb0 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include using namespace TFE_Jedi; @@ -425,6 +427,7 @@ namespace TFE_DarkForces { SecObject* obj = moveMod->header.obj; Tick delta = Tick(s_curTick - (*prevColTick)); + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "DELTA DIR CHANGE %d", delta); angle14_32 newAngle; if (delta < 145) { @@ -1318,7 +1321,7 @@ namespace TFE_DarkForces { ActorDispatch* logic = (ActorDispatch*)s_actorState.curLogic; logic->flags = (logic->flags | 1) & 0xfd; - logic->nextTick = s_curTick + logic->delay; + logic->nextTick = s_curTick +logic->delay; SecObject* obj = logic->logic.obj; obj->anim = actor_getAnimationIndex(5); @@ -1626,7 +1629,9 @@ namespace TFE_DarkForces if (approxDist < FIXED(256)) { // Since random() is unsigned, the real visible range is [200, 256) because of the conditional above. + // TFE_System::logWrite(LOG_MSG, "ACTOR", "ACTOR CALL RANDOM"); fixed16_16 rndDist = random(FIXED(256)) + FIXED(200); + //TFE_System::logWrite(LOG_MSG, "ACTOR", "ACTOR rndDist %d update = %d", rndDist, TFE_Input::getCounter()); if (approxDist < rndDist) { return actor_canSeeObject(actorObj, obj); @@ -2058,7 +2063,7 @@ namespace TFE_DarkForces { if (dispatch->nextTick < s_curTick) { - dispatch->nextTick = s_curTick + dispatch->delay; + dispatch->nextTick = s_curTick;// +dispatch->delay; if (actor_isObjectVisible(obj, s_playerObject, dispatch->fov, dispatch->awareRange)) { message_sendToObj(obj, MSG_WAKEUP, actor_hitEffectMsgFunc); diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp index ea2ef709f..bb5e2e88d 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include namespace TFE_DarkForces { @@ -107,12 +110,12 @@ namespace TFE_DarkForces MovementModule* moveMod; CollisionInfo* colInfo; Tick tick; + u32 frame; JBool odd; JBool flip; }; task_begin_ctx; - local(mouseBot) = s_curMouseBot; local(obj) = local(mouseBot)->logic.obj; local(phyActor) = &local(mouseBot)->actor; @@ -142,13 +145,14 @@ namespace TFE_DarkForces if (local(phyActor)->state != MBSTATE_ACTIVE) { break; } + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "CUR TICK = %d", s_curTick); // Go to sleep if the player hasn't been spotted in about 5 seconds. if (actor_isObjectVisible(local(obj), s_playerObject, 0x4000/*360 degrees*/, FIXED(25)/*closeDist*/)) { local(tick) = s_curTick; } else - { + { Tick dt = s_curTick - local(tick); // If enough time has past since the player was last spotted, go back to sleep. if (dt > 728) // ~5 seconds. @@ -163,16 +167,19 @@ namespace TFE_DarkForces if (actorYaw == yaw) { local(odd) = (s_curTick & 1) ? JTRUE : JFALSE; + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM"); angle14_32 deltaYaw = random(16338); local(moveMod)->target.yaw = local(odd) ? (local(obj)->yaw + deltaYaw) : (local(obj)->yaw - deltaYaw); - + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM2"); local(moveMod)->target.speedRotation = random(0x3555) + 0x555; +// TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "delta Yaw %d speedrotate = %d update = %d", deltaYaw, local(moveMod)->target.speedRotation, TFE_Input::getCounter()); local(moveMod)->target.flags |= TARGET_MOVE_ROT; local(flip) = JFALSE; } if (local(colInfo)->wall) { + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM3"); s32 rnd = random(100); if (rnd <= 10) // ~10% chance of playing a sound effect when hitting a wall. { @@ -186,6 +193,7 @@ namespace TFE_DarkForces { local(moveMod)->target.yaw -= 4096; } + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM4"); local(moveMod)->target.speedRotation = random(0x3555); local(flip) = ~local(flip); } @@ -198,6 +206,19 @@ namespace TFE_DarkForces local(moveMod)->target.speed = FIXED(22); local(moveMod)->target.flags |= TARGET_MOVE_XZ; + //char botinfo[80]; + fixed16_16 x = local(moveMod)->target.pos.x; + fixed16_16 z = local(moveMod)->target.pos.z; + angle14_16 myaw = local(moveMod)->target.yaw; + fixed16_16 mspeed = local(moveMod)->target.speed; + fixed16_16 rotspeed = local(moveMod)->target.speedRotation; + Tick curtick = local(tick); + //sprintf(botinfo, "X: %d Z: %d YAW: %d SPEED: %d ROT: %d TICK: %d", x, z, myaw, mspeed, rotspeed, curtick); + + + //TFE_DarkForces::hud_sendTextMessage(botinfo, 1, false); + //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", botinfo); + } task_end; } diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp index 76da19e5a..6d3c50217 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp @@ -538,6 +538,8 @@ namespace TFE_DarkForces ****************************************************/ void DarkForces::loopGame() { + //TFE_System::logWrite(LOG_MSG, "LOOP", "FRAME COUNT"); + updateTime(); switch (s_runGameState.state) @@ -1352,7 +1354,7 @@ namespace TFE_DarkForces } } - void serializeVersion(Stream* stream) + void DarkForces::serializeVersion(Stream* stream) { SERIALIZE_VERSION(SaveVersionInit); } @@ -1360,6 +1362,7 @@ namespace TFE_DarkForces bool DarkForces::serializeGameState(Stream* stream, const char* filename, bool writeState) { if (!stream) { return false; } + SERIALIZE_VERSION(SaveVersionInit); if (writeState && filename) { // Write the save message. diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.h b/TheForceEngine/TFE_DarkForces/darkForcesMain.h index 5315fdf71..896711eb4 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.h +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.h @@ -18,6 +18,7 @@ namespace TFE_DarkForces void exitGame() override; void loopGame() override; bool serializeGameState(Stream* stream, const char* filename, bool writeState) override; + void serializeVersion(Stream* stream); bool canSave() override; bool isPaused() override; void getLevelName(char* name) override; diff --git a/TheForceEngine/TFE_DarkForces/hud.cpp b/TheForceEngine/TFE_DarkForces/hud.cpp index 91228cade..d644054c8 100644 --- a/TheForceEngine/TFE_DarkForces/hud.cpp +++ b/TheForceEngine/TFE_DarkForces/hud.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #define TFE_CONVERT_CAPS 0 #if TFE_CONVERT_CAPS @@ -211,7 +212,7 @@ namespace TFE_DarkForces TFE_Console::addToHistory(msgText); } - void hud_sendTextMessage(const char* msg, s32 priority) + void hud_sendTextMessage(const char* msg, s32 priority, bool skipPriority) { // Only display the message if it is the same or lower priority than the current message. if (!msg || priority > s_hudMsgPriority) @@ -222,7 +223,14 @@ namespace TFE_DarkForces } strCopyAndZero((char*)s_hudMessage, msg, 80); - s_hudMsgExpireTick = s_curTick + ((priority <= HUD_HIGH_PRIORITY) ? HUD_MSG_LONG_DUR : HUD_MSG_SHORT_DUR); + if (skipPriority) + { + s_hudMsgExpireTick = s_curTick; + } + else + { + s_hudMsgExpireTick = s_curTick + ((priority <= HUD_HIGH_PRIORITY) ? HUD_MSG_LONG_DUR : HUD_MSG_SHORT_DUR); + } s_hudCurrentMsgId = 0; s_hudMsgPriority = priority; @@ -519,6 +527,35 @@ namespace TFE_DarkForces } offscreenBuffer_drawTexture(s_cachedHudRight, s_hudLightOff, 19, 0); } + + string hud_getDataStr(bool includeYRP) + { + fixed16_16 x, z; + getCameraXZ(&x, &z); + char* dataStr = new char[64]; + s32 xPos = floor16(x); + s32 yPos = -fixed16ToFloat(s_playerEye->posWS.y); + s32 zPos = floor16(z); + s32 h = fixed16ToFloat(s_playerEye->worldHeight); + s32 s = s_secretsPercent; + angle14_16 y, r, p; + y = s_playerEye->yaw; + r = s_playerEye->roll; + p = s_playerEye->pitch; + + string format = "X:%04d Y:%.1f Z:%04d H:%.1f S:%d"; + if (includeYRP) + { + format += " Y:%04d R:%04d P:%04d"; + sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s, y, r, p); + } + else + { + sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s); + } + std::string result = string(dataStr); + return result; + } void hud_drawMessage(u8* framebuffer) { @@ -536,13 +573,11 @@ namespace TFE_DarkForces if (s_showData && s_playerEye) { - fixed16_16 x, z; - getCameraXZ(&x, &z); - s32 xOffset = floor16(div16(intToFixed16(vfb_getWidescreenOffset()), vfb_getXScale())); - u8 dataStr[64]; - sprintf((char*)dataStr, "X:%04d Y:%.1f Z:%04d H:%.1f S:%d%%", floor16(x), -fixed16ToFloat(s_playerEye->posWS.y), floor16(z), fixed16ToFloat(s_playerEye->worldHeight), s_secretsPercent); + string result = TFE_DarkForces::hud_getDataStr(true); + std::copy(result.begin(), result.end(), dataStr); + dataStr[result.size()] = '\0'; displayHudMessage(s_hudFont, (DrawRect*)vfb_getScreenRect(VFB_RECT_UI), 164 + xOffset, 10, dataStr, framebuffer); // s_screenDirtyRight[s_curFrameBufferIdx] = JTRUE; } diff --git a/TheForceEngine/TFE_DarkForces/hud.h b/TheForceEngine/TFE_DarkForces/hud.h index 90d87c7a0..2d2836721 100644 --- a/TheForceEngine/TFE_DarkForces/hud.h +++ b/TheForceEngine/TFE_DarkForces/hud.h @@ -6,6 +6,7 @@ ////////////////////////////////////////////////////////////////////// #include #include +#include using namespace TFE_Jedi; @@ -15,8 +16,9 @@ struct ScreenRect; namespace TFE_DarkForces { void hud_sendTextMessage(s32 msgId); - void hud_sendTextMessage(const char* msg, s32 priority); + void hud_sendTextMessage(const char* msg, s32 priority, bool skipPriority=true); void hud_clearMessage(); + std::string hud_getDataStr(bool includeYRP); void hud_loadGameMessages(); void hud_loadGraphics(); diff --git a/TheForceEngine/TFE_DarkForces/mission.cpp b/TheForceEngine/TFE_DarkForces/mission.cpp index 25766ad7d..ac21cb935 100644 --- a/TheForceEngine/TFE_DarkForces/mission.cpp +++ b/TheForceEngine/TFE_DarkForces/mission.cpp @@ -37,6 +37,7 @@ #include #include #include +#include using namespace TFE_Jedi; using namespace TFE_Input; @@ -73,6 +74,7 @@ namespace TFE_DarkForces JBool s_lumMaskChanged = JFALSE; JBool s_loadingFromSave = JFALSE; + JBool s_inMission = JFALSE; s32 s_flashFxLevel = 0; s32 s_healthFxLevel = 0; @@ -393,6 +395,17 @@ namespace TFE_DarkForces mission_addCheatCommands(); CCMD("spawnEnemy", console_spawnEnemy, 2, "spawnEnemy(waxName, enemyTypeName) - spawns an enemy 8 units away in the player direction. Example: spawnEnemy offcfin.wax i_officer"); + + if (TFE_Settings::getGameSettings()->df_enableReplay) + { + loadReplay(); + } + + if (TFE_Settings::getGameSettings()->df_enableRecording) + { + startRecording(); + } + // Make sure the loading screen is displayed for at least 1 second. if (!s_loadingFromSave) { @@ -405,7 +418,11 @@ namespace TFE_DarkForces s_playerTick = s_curTick; s_levelComplete = JFALSE; } + + + s_mainTask = createTask("main task", mission_mainTaskFunc); + s_invalidLevelIndex = JFALSE; s_exitLevel = JFALSE; @@ -441,6 +458,7 @@ namespace TFE_DarkForces hud_startup(JFALSE); reticle_enable(true); + s_inMission = JTRUE; } s_flatLighting = JFALSE; // Note: I am not sure why this is there but it overrides all player settings @@ -466,11 +484,18 @@ namespace TFE_DarkForces // Cleanup - shut down all tasks. task_freeAll(); + s_inMission = JFALSE; // End the task. task_end; } + JBool isMissionRunning() + { + return s_inMission; + + } + void mission_setLoadMissionTask(Task* task) { s_missionLoadTask = task; @@ -663,7 +688,10 @@ namespace TFE_DarkForces } } while (msg != MSG_FREE_TASK && msg != MSG_RUN_TASK); } - + if (TFE_Input::isRecording()) + { + endRecording(); + } s_mainTask = nullptr; task_makeActive(s_missionLoadTask); task_end; @@ -1271,6 +1299,9 @@ namespace TFE_DarkForces void handleGeneralInput() { + + //TFE_Input::playbackKeyState(); + // Early out if the player is dying. if (s_playerDying) { diff --git a/TheForceEngine/TFE_DarkForces/mission.h b/TheForceEngine/TFE_DarkForces/mission.h index 27272dc90..fefe24894 100644 --- a/TheForceEngine/TFE_DarkForces/mission.h +++ b/TheForceEngine/TFE_DarkForces/mission.h @@ -50,6 +50,8 @@ namespace TFE_DarkForces void cheat_supercharge(); void cheat_toggleData(); void cheat_toggleFullBright(); + + JBool isMissionRunning(); extern JBool s_gamePaused; extern GameMissionMode s_missionMode; diff --git a/TheForceEngine/TFE_DarkForces/random.cpp b/TheForceEngine/TFE_DarkForces/random.cpp index 72ea3eeb7..5d2ca16c8 100644 --- a/TheForceEngine/TFE_DarkForces/random.cpp +++ b/TheForceEngine/TFE_DarkForces/random.cpp @@ -1,5 +1,6 @@ #include "random.h" #include +#include namespace TFE_DarkForces { @@ -12,8 +13,21 @@ namespace TFE_DarkForces SERIALIZE(SaveVersionInit, s_seed, 0xf444bb3b); } + s32 getSeed() + { + return s_seed; + } + + void setSeed(s32 seed) + { + s_seed = seed; + } + + s32 random_next() { + //TFE_System::logWrite(LOG_MSG, "RANDOM", "SEED START: %d", s_seed); + // Shift the seed by 1 (divide by two), and // xor the top 8 bits with b10100011 (163) if the starting seed is odd. // This has the effect of increasing the size of the seed if it gets too small due to the shifts, and increasing "randomness" @@ -25,6 +39,7 @@ namespace TFE_DarkForces { s_seed = (s_seed >> 1); } + //TFE_System::logWrite(LOG_MSG, "RANDOM", "SEED END: %d", s_seed); return s32(s_seed); } @@ -34,6 +49,7 @@ namespace TFE_DarkForces s32 random(s32 value) { s32 newValue = random_next(); + //TFE_System::logWrite(LOG_MSG, "RANDOM", "NEW VALUE %d", newValue); if (newValue > value || newValue < 0) { // Note the value is cast to fixed16_16 but is not actually converted. @@ -41,10 +57,12 @@ namespace TFE_DarkForces newValue = mul16(fixed16_16(value), fract16(newValue)); } return newValue; - } + } void random_seed(u32 seed) { + TFE_System::logWrite(LOG_MSG, "RANDOM", "Setting SEED to %d", seed); s_seed = seed; + } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/random.h b/TheForceEngine/TFE_DarkForces/random.h index a7563b2b1..e76089863 100644 --- a/TheForceEngine/TFE_DarkForces/random.h +++ b/TheForceEngine/TFE_DarkForces/random.h @@ -18,4 +18,6 @@ namespace TFE_DarkForces void random_serialize(Stream* stream); void random_seed(u32 seed); + s32 getSeed(); + void setSeed(s32 seed); } // namespace TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index abea83af9..fb0c29a0e 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include using namespace TFE_Jedi; @@ -23,6 +25,26 @@ namespace TFE_DarkForces SERIALIZE_BUF(SaveVersionInit, s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(s_frameTicks)); } + void resetTime() + { + s_curTick = 0; + s_prevTick = 0; + f64 s_timeAccum = 0.0; + s_deltaTime; + s_frameTicks[13] = { 0 }; + s_pauseTimeUpdate = JFALSE; + } + + void setTimeAccum(f64 timeAccum) + { + s_timeAccum = timeAccum; + } + + f64 getTimeAccum() + { + return s_timeAccum; + } + Tick time_frameRateToDelay(u32 frameRate) { return Tick(SECONDS_TO_TICKS_ROUNDED / f32(frameRate)); @@ -48,12 +70,47 @@ namespace TFE_DarkForces if (!s_pauseTimeUpdate) { s_timeAccum += TFE_System::getDeltaTime() * TIMER_FREQ; + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME ACCUM %u %d", s_timeAccum, s_timeAccum); + } - Tick prevTick = s_curTick; - s_curTick = Tick(s_timeAccum); + Tick prevTick = s_curTick; + if (TFE_Input::isRecording()) + { + TFE_Input::sTick(s_curTick); + /*if (TFE_Input::getCounter() == 0) + { + resetTime(); + }*/ + // std::tuple < Tick, Tick, f64, fixed16_16> tData = { s_curTick, prevTick, s_timeAccum, s_deltaTime}; + TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME curtick = %d prevtick = %d accum = %d delta = %d", s_curTick, s_prevTick, s_timeAccum, s_deltaTime); + // TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK %u %d", s_curTick, s_curTick); + //TFE_Input::saveTick(tData); + } + + if (TFE_Input::isDemoPlayback()) + { + Tick t = TFE_Input::lTick(); + if (t != 0) + { + s_curTick = t; + } + //std::tuple < Tick, Tick, f64, fixed16_16> tData = TFE_Input::loadTick(); + //s_curTick = std::get<0>(tData); + /*prevTick = std::get<1>(tData); + s_timeAccum = std::get<2>(tData); + s_deltaTime = std::get<3>(tData);*/ + + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "LOADTICK %u %d", s_curTick, s_curTick); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "REPLAYTICK %u %d", replayTick, replayTick); + } + else + { + s_curTick = Tick(s_timeAccum); + } fixed16_16 dt = div16(intToFixed16(s_curTick - prevTick), FIXED(TICKS_PER_SECOND)); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "DT %u %d", dt, dt); for (s32 i = 0; i < 13; i++) { s_frameTicks[i] += mul16(dt, intToFixed16(i)); diff --git a/TheForceEngine/TFE_DarkForces/time.h b/TheForceEngine/TFE_DarkForces/time.h index 9a22a9be1..7accc6d8b 100644 --- a/TheForceEngine/TFE_DarkForces/time.h +++ b/TheForceEngine/TFE_DarkForces/time.h @@ -52,6 +52,8 @@ namespace TFE_DarkForces Tick time_frameRateToDelay(f32 frameRate); void updateTime(); void time_pause(JBool pause); - + void setTimeAccum(f64 timeAccum); + f64 getTimeAccum(); + void resetTime(); void time_serialize(Stream* stream); } // namespace TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp index 855b9c3e8..424db3e51 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1212,6 +1213,20 @@ namespace TFE_FrontEndUI gameSettings->df_jsonAiLogics = jsonAiLogics; } + bool enableRecord = gameSettings->df_enableRecording; + if (ImGui::Checkbox("Enable Replay Recording", &enableRecord)) + { + gameSettings->df_enableRecording = enableRecord; + gameSettings->df_enableReplay = false; + } + + bool enableReplay = gameSettings->df_enableReplay; + if (ImGui::Checkbox("Enable Replay", &enableReplay)) + { + gameSettings->df_enableReplay = enableReplay; + gameSettings->df_enableRecording = false; + } + if (s_drawNoGameDataMsg) { ImGui::Separator(); @@ -2184,6 +2199,7 @@ namespace TFE_FrontEndUI inputMapping("Automap", IADF_AUTOMAP); inputMapping("Screenshot", IADF_SCREENSHOT); inputMapping("GIF Recording", IADF_GIF_RECORD); + inputMapping("Demo Record", IADF_DEMO_RECORD); Tooltip("Display a countdown and then start recording a GIF. Press again to stop recording."); inputMapping("Instant GIF Record",IADF_GIF_RECORD_NO_COUNTDOWN); Tooltip("Start recording immediately without the countdown. Press again to stop recording."); diff --git a/TheForceEngine/TFE_Game/igame.h b/TheForceEngine/TFE_Game/igame.h index 8c2f3ebe5..5e21c4924 100644 --- a/TheForceEngine/TFE_Game/igame.h +++ b/TheForceEngine/TFE_Game/igame.h @@ -27,6 +27,7 @@ struct IGame virtual void restartMusic() = 0; virtual void loopGame() {}; virtual bool serializeGameState(Stream* stream, const char* filename, bool writeState) { return false; }; + virtual void serializeVersion(Stream* stream) {} virtual bool canSave() { return false; } virtual bool isPaused() { return false; } virtual void getLevelName(char* name) {}; diff --git a/TheForceEngine/TFE_Input/input.cpp b/TheForceEngine/TFE_Input/input.cpp index 79514f689..822319a95 100644 --- a/TheForceEngine/TFE_Input/input.cpp +++ b/TheForceEngine/TFE_Input/input.cpp @@ -6,6 +6,7 @@ #include #include + namespace TFE_Input { #define BUFFERED_TEXT_LEN 64 @@ -86,6 +87,8 @@ namespace TFE_Input void setKeyDown(KeyboardCode key, bool repeat) { + + //TFE_System::logWrite(LOG_MSG, "Input", "DOWN '%d'", key); if (!s_keyDown[key] && !repeat) { s_keyPressed[key] = 1; @@ -99,6 +102,8 @@ namespace TFE_Input void setKeyUp(KeyboardCode key) { + + //TFE_System::logWrite(LOG_MSG, "Input", "UP '%d'", key); s_keyDown[key] = 0; } @@ -124,6 +129,7 @@ namespace TFE_Input void setRelativeMousePos(s32 x, s32 y) { + //TFE_System::logWrite(LOG_MSG, "MOUSE RELATIVE Input", "X %d Y %d", x,y); s_mouseMove[0] = x; s_mouseMove[1] = y; s_mouseMoveAccum[0] += x; @@ -132,6 +138,7 @@ namespace TFE_Input void setMousePos(s32 x, s32 y) { + //TFE_System::logWrite(LOG_MSG, "MOUSE Input", "X %d Y %d", x, y); s_mousePos[0] = x; s_mousePos[1] = y; } diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index 49614a352..a96a5373b 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -5,6 +5,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include namespace TFE_Input { @@ -94,6 +100,7 @@ namespace TFE_Input { IADF_SCREENSHOT, ITYPE_KEYBOARD, KEY_PRINTSCREEN }, { IADF_GIF_RECORD, ITYPE_KEYBOARD, KEY_F2, KEYMOD_ALT}, { IADF_GIF_RECORD_NO_COUNTDOWN, ITYPE_KEYBOARD, KEY_F2, KEYMOD_CTRL}, + { IADF_DEMO_RECORD, ITYPE_KEYBOARD, KEY_F6, KEYMOD_ALT}, }; static InputBinding s_defaultControllerBinds[] = @@ -121,6 +128,10 @@ namespace TFE_Input static InputConfig s_inputConfig = { 0 }; static ActionState s_actions[IA_COUNT]; + int inputCounter = 0; + int maxInputCounter = 0; + vector currentKeys; + vector currentMouse; void addDefaultControlBinds(); @@ -137,6 +148,7 @@ namespace TFE_Input // Once the defaults are setup, write out the file to disk for next time. inputMapping_serialize(); + TFE_Settings::getGameSettings()->df_enableRecording; } void inputMapping_shutdown() @@ -271,6 +283,7 @@ namespace TFE_Input inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_SCREENSHOT]); inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_GIF_RECORD]); inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_GIF_RECORD_NO_COUNTDOWN]); + inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_DEMO_RECORD]); } return true; @@ -315,8 +328,77 @@ namespace TFE_Input return action >= IADF_FORWARD && action <= IADF_LOOK_DN; } + void resetCounter() + { + inputCounter = 0; + } + + int getCounter() + { + return inputCounter; + } + + void setMaxInputCounter(int counter) + { + maxInputCounter = counter; + } + void inputMapping_updateInput() { + + std::vector keysDown; + std::vector mouseDown; + int keyIndex = 0; + int mouseIndex = 0; + + ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + + if (TFE_Input::isDemoPlayback()) + { + + if (maxInputCounter > inputCounter) + { + // Replay Keys + keysDown = event.keysDown; + for (int i = 0; i < keysDown.size(); i++) + { + KeyboardCode key = (KeyboardCode)keysDown[i]; + TFE_Input::setKeyDown(key, false); + currentKeys.push_back(key); + } + for (int i = 0; i < currentKeys.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeys[i]; + if (std::find(keysDown.begin(), keysDown.end(), key) == keysDown.end()) + { + TFE_Input::setKeyUp(key); + currentKeys.erase(std::remove(currentKeys.begin(), currentKeys.end(), key), currentKeys.end()); + } + } + + // Replay Mouse buttons + mouseDown = event.mouseDown; + for (int i = 0; i < mouseDown.size(); i++) + { + MouseButton key = (MouseButton)mouseDown[i]; + TFE_Input::setMouseButtonDown(key); + currentMouse.push_back(key); + } + for (int i = 0; i < currentMouse.size(); i++) + { + MouseButton key = (MouseButton)currentMouse[i]; + if (std::find(mouseDown.begin(), mouseDown.end(), key) == mouseDown.end()) + { + TFE_Input::setMouseButtonUp(key); + currentMouse.erase(std::remove(currentMouse.begin(), currentMouse.end(), key), currentMouse.end()); + } + } + } + + // Clear all keys after playback. + //else if (maxInputCounter <= inputCounter) inputMapping_endFrame(); + } + for (u32 i = 0; i < s_inputConfig.bindCount; i++) { InputBinding* bind = &s_inputConfig.binds[i]; @@ -335,6 +417,23 @@ namespace TFE_Input else if (TFE_Input::keyDown(bind->keyCode) && s_actions[bind->action] != STATE_PRESSED) { s_actions[bind->action] = STATE_DOWN; + + // Fore recording keys + //TFE_System::logWrite(LOG_MSG, "INPUT PUSH BACK", "INPUT UPDATE %d", bind->keyCode); + if (TFE_Input::isRecording()) + { + if (keysDown.size() > 0) + { + int x = 2; + x = x + 3; + } + // Do not record Escape + if (bind->keyCode != KEY_ESCAPE) + { + keysDown.push_back(bind->keyCode); + } + } + keyIndex++; } } } break; @@ -350,6 +449,11 @@ namespace TFE_Input { s_actions[bind->action] = STATE_DOWN; } + if (TFE_Input::isRecording() && TFE_Input::mouseDown(bind->mouseBtn)) + { + mouseDown.push_back(bind->mouseBtn); + } + mouseIndex++; } } break; case ITYPE_MOUSEWHEEL: @@ -394,6 +498,18 @@ namespace TFE_Input } break; } } + if (TFE_Input::isRecording()) + { + if (keyIndex > 0) + { + event.keysDown = keysDown; + } + if (mouseIndex > 0) + { + event.mouseDown = mouseDown; + } + inputEvents[inputCounter] = event; + } } void inputMapping_removeState(InputAction action) @@ -500,4 +616,125 @@ namespace TFE_Input { return &s_inputConfig; } + + void handleInputs() + { + // Allow escape during playback + if (keyPressed(KEY_ESCAPE)) + { + if (isDemoPlayback()) + { + + TFE_Input::setDemoPlayback(false); + } + if (isRecording()) + { + setDemoPlayback(false); + string msg = "DEMO Playback Complete!"; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); + TFE_Input::endRecording(); + } + } + + + /* + if (inputCounter == 0) + { + if (isDemoPlayback()) + { + loadTiming(); + } + if (isRecording()) + { + recordTiming(); + } + }*/ + std::vector mousePos; + s32 mouseX, mouseY; + s32 mouseAbsX, mouseAbsY; + u32 state = SDL_GetRelativeMouseState(&mouseX, &mouseY); + SDL_GetMouseState(&mouseAbsX, &mouseAbsY); + + + string hudData = ""; + string keys = ""; + string mouse = ""; + string mouseP = ""; + // Wipe current keys and mouse buttons + if (isDemoPlayback()) + { + if (inputCounter >= maxInputCounter) + { + setDemoPlayback(false); + string msg = "DEMO Playback Complete!"; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); + + } + else + { + string msg = "DEMO Playback [" + std::to_string(inputCounter) + " out of " + std::to_string(maxInputCounter) + "] ..."; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); + + inputMapping_endFrame(); + ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + mousePos = event.mousePos; + if (mousePos.size() == 4) + { + mouseX = mousePos[0]; + mouseY = mousePos[1]; + mouseAbsX = mousePos[2]; + mouseAbsY = mousePos[3]; + } + mouseP = convertToString(event.mousePos); + //event.keysDown; + } + } + + else if (isRecording()) + { + string msg = "DEMO Recording [" + std::to_string(inputCounter) + "] ..."; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); + + ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + mousePos = event.mousePos; + mousePos.clear(); + mousePos.push_back(mouseX); + mousePos.push_back(mouseY); + mousePos.push_back(mouseAbsX); + mousePos.push_back(mouseAbsY); + event.mousePos = mousePos; + mouseP = convertToString(event.mousePos); + TFE_Input::inputEvents[inputCounter] = event; + } + bool rec = isRecording(); + bool play = isDemoPlayback(); + bool recAllow = TFE_Settings::getGameSettings()->df_enableRecording; + bool playAllow = TFE_Settings::getGameSettings()->df_enableReplay; + + TFE_Input::setRelativeMousePos(mouseX, mouseY); + TFE_Input::setMousePos(mouseAbsX, mouseAbsY); + inputMapping_updateInput(); + + if ((isRecording() || isDemoPlayback()) && TFE_DarkForces::s_playerEye) + { + ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + hudData = TFE_DarkForces::hud_getDataStr(true); + keys = convertToString(event.keysDown); + mouse = convertToString(event.mouseDown); + } + + if ((isRecording() || isDemoPlayback())) + { + string hudDataStr = "Rec=%d, Play=%d, RecAllow=%d, PlayAllow=%d, upd=%d "; + if (hudData.size() != 0) hudDataStr += hudData; + if (keys.size() != 0) hudDataStr += " Keys: " + keys; + if (mouse.size() != 0) hudDataStr += " Mouse: " + mouse; + if (mouseP.size() != 0) hudDataStr += " MousePos " + mouseP; + + TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), rec, play, recAllow, playAllow, inputCounter); + } + + inputCounter++; + } + } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/inputMapping.h b/TheForceEngine/TFE_Input/inputMapping.h index f8d35f81d..5df9a1b94 100644 --- a/TheForceEngine/TFE_Input/inputMapping.h +++ b/TheForceEngine/TFE_Input/inputMapping.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace TFE_Input { @@ -84,6 +85,7 @@ namespace TFE_Input IADF_SCREENSHOT, IADF_GIF_RECORD, IADF_GIF_RECORD_NO_COUNTDOWN, + IADF_DEMO_RECORD, IA_COUNT, IAS_COUNT = IAS_SYSTEM_MENU + 1, @@ -194,4 +196,8 @@ namespace TFE_Input f32 inputMapping_getHorzMouseSensitivity(); f32 inputMapping_getVertMouseSensitivity(); + void resetCounter(); + int getCounter(); + void handleInputs(); + void setMaxInputCounter(int counter); } // TFE_Input \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.cpp b/TheForceEngine/TFE_Input/replay.cpp new file mode 100644 index 000000000..ad0909195 --- /dev/null +++ b/TheForceEngine/TFE_Input/replay.cpp @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace TFE_Input +{ + static char s_replayDir[TFE_MAX_PATH]; + static char s_replayPath[TFE_MAX_PATH]; + FileStream s_replayFile; + static bool s_recording = false; + static bool s_playback = false; + static Tick replayCurTick; + static Tick replayPrevTick; + static f64 replayTimeAccum = 0.0; + static fixed16_16 replayDeltaTime; + static fixed16_16 replayFrameTicks[13] = { 0 }; + + std::unordered_map inputEvents; + u64 s_startTime = 0; + s32 replay_seed = 0; + int tickCounter = 0; + + void initReplays() + { + sprintf(s_replayDir, "%sReplays/", TFE_Paths::getPath(PATH_USER_DOCUMENTS)); + TFE_Paths::fixupPathAsDirectory(s_replayDir); + + if (!FileUtil::directoryExits(s_replayDir)) + { + FileUtil::makeDirectory(s_replayDir); + } + sprintf(s_replayPath, "%stest.demo", s_replayDir); + } + + bool isRecording() + { + return s_recording; + } + + void setRecording(bool recording) + { + s_recording = recording; + } + + bool isDemoPlayback() + { + return s_playback; + } + + void setDemoPlayback(bool playback) + { + s_playback = playback; + } + + void recordTiming() + { + TFE_DarkForces::time_pause(JTRUE); + /*replayCurTick = TFE_DarkForces::s_curTick; + replayPrevTick = TFE_DarkForces::s_prevTick; + replayTimeAccum = TFE_DarkForces::getTimeAccum();; + replayDeltaTime = TFE_DarkForces::s_deltaTime; + memcpy(replayFrameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); + */ + recordReplaySeed(); + sTick(TFE_DarkForces::s_curTick); + TFE_DarkForces::time_pause(JFALSE); + } + + void loadTiming() + { + TFE_DarkForces::time_pause(JTRUE); + /* + TFE_DarkForces::s_curTick = replayCurTick; + TFE_DarkForces::s_prevTick = replayPrevTick; + TFE_DarkForces::setTimeAccum(replayTimeAccum); + TFE_DarkForces::s_deltaTime = replayDeltaTime; + memcpy(TFE_DarkForces::s_frameTicks, replayFrameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(replayFrameTicks)); + */ + restoreReplaySeed(); + TFE_DarkForces::s_curTick = lTick(); + TFE_DarkForces::time_pause(JFALSE); + } + + void sTick(Tick curTick) + { + int inputCounter = getCounter(); + inputEvents[inputCounter].curTick = curTick; + TFE_System::logWrite(LOG_MSG, "Progam Flow", "STICK curtick = %d counter = %d ", curTick, inputCounter); + + } + + void saveTick(std::tuple tData) + { + int inputCounter = getCounter(); + TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK curtick = %d prevtick = %d accum = %d delta = %d", get<0>(tData), get<1>(tData), get<2>(tData), get<3>(tData)); + inputEvents[inputCounter].curTick = get<0>(tData); + inputEvents[inputCounter].timeData = tData; + } + + Tick lTick() + { + int inputCounter = getCounter(); + Tick curTick = inputEvents[inputCounter].curTick; + TFE_System::logWrite(LOG_MSG, "Progam Flow", "LTICK curtick = %d counter = %d ", curTick, inputCounter); + return curTick; + } + + std::tuple loadTick() + { + int inputCounter = getCounter(); + TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; + + return inputEvents[inputCounter].timeData; + } + + void recordReplaySeed() + { + replay_seed = TFE_DarkForces::getSeed(); + TFE_System::logWrite(LOG_MSG, "REPLAY", "Recording Seed: %d", replay_seed); + } + + + void restoreReplaySeed() + { + TFE_DarkForces::setSeed(replay_seed); + TFE_System::logWrite(LOG_MSG, "REPLAY", "Loading Seed: %d", replay_seed); + } + + + string convertToString(vector keysDown) + { + std::ostringstream oss; + for (size_t i = 0; i < keysDown.size(); ++i) { + if (i != 0) oss << ","; + oss << keysDown[i]; + } + return oss.str(); + } + + vector convertFromString(string keyStr) + { + std::vector intVector; + std::istringstream iss(keyStr); + std::string token; + while (std::getline(iss, token, ',')) { + intVector.push_back(std::stoi(token)); + }; + return intVector; + } + + vector serializeInputs(Stream* stream, vector inputList, bool writeFlag) + { + int keySize = 0; + string keyString; + + if (writeFlag) + { + keyString = convertToString(inputList); + keySize = keyString.size(); + } + + SERIALIZE(ReplayVersionInit, keySize, 0); + if (!writeFlag) + { + keyString.resize(keySize); + } + SERIALIZE_BUF(ReplayVersionInit, &keyString[0], keySize); + + + if (!writeFlag) + { + return convertFromString(keyString); + } + return inputList; + } + + void serializeDemo(Stream* stream, bool writeFlag) + { + int fileHandler = 0; + int updateCounter = 0; + TFE_DarkForces::time_pause(JTRUE); + if (writeFlag) + { + serialization_setMode(SMODE_WRITE); + fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_WRITE); + } + else + { + serialization_setMode(SMODE_READ); + fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_READ); + inputEvents.clear(); + } + + if (fileHandler > 0) + { + SERIALIZE_VERSION(ReplayVersionInit); + SERIALIZE(ReplayVersionInit, s_startTime, 0); + SERIALIZE(ReplayVersionInit, replay_seed, 0); + + TFE_DarkForces::time_serialize(stream); + + int inputListsSize = getCounter(); + SERIALIZE(ReplayVersionInit, inputListsSize, 0); + + if (writeFlag) + { + // Loop through inputEvents + for (const auto& pair : inputEvents) + { + updateCounter = pair.first; + if (updateCounter > inputListsSize) continue; + SERIALIZE(ReplayVersionInit, updateCounter, 0); + serializeInputs(stream, pair.second.keysDown, writeFlag); + serializeInputs(stream, pair.second.mouseDown, writeFlag); + serializeInputs(stream, pair.second.mousePos, writeFlag); + + u32 curTick = pair.second.curTick; + TFE_System::logWrite(LOG_MSG, "STORING", "STORING CURTIME %d COUNTER %d", curTick, updateCounter); + SERIALIZE(ReplayVersionInit, curTick, 0); + /* + std::tuple tData = pair.second.timeData; + u32 curTick = std::get<0>(tData); + u32 prevTick = std::get<1>(tData); + f64 timeAccum = std::get<2>(tData); + fixed16_16 deltaTime = std::get<3>(tData); + SERIALIZE(ReplayVersionInit, curTick, 0); + SERIALIZE(ReplayVersionInit, prevTick, 0); + SERIALIZE(ReplayVersionInit, timeAccum, 0.0); + SERIALIZE(ReplayVersionInit, deltaTime, 0);*/ + + } + } + else + { + for (int i = 0; i < inputListsSize; i++) + { + SERIALIZE(ReplayVersionInit, updateCounter, 0); + ReplayEvent event = {}; + event.keysDown = serializeInputs(stream, event.keysDown, writeFlag); + event.mouseDown = serializeInputs(stream, event.mouseDown, writeFlag); + event.mousePos = serializeInputs(stream, event.mousePos, writeFlag); + + u32 curTick = 0; + SERIALIZE(ReplayVersionInit, curTick, 0); + TFE_System::logWrite(LOG_MSG, "LOADING", "LOADING CURTIME %d COUNTER %d", curTick, updateCounter); + event.curTick = curTick; + /* + u32 curTick; + u32 prevTick; + f64 timeAccum; + fixed16_16 deltaTime; + + SERIALIZE(ReplayVersionInit, curTick, 0); + SERIALIZE(ReplayVersionInit, prevTick, 0); + SERIALIZE(ReplayVersionInit, timeAccum, 0.0); + SERIALIZE(ReplayVersionInit, deltaTime, 0); + + + event.curTick = curTick; + std::tuple tData = std::make_tuple(curTick, prevTick, timeAccum, deltaTime); + event.timeData = tData; + */ + inputEvents[updateCounter] = event; + } + resetCounter(); + setMaxInputCounter(updateCounter); + } + + s_replayFile.close(); + } + + if (!writeFlag) + { + TFE_System::setStartTime(s_startTime); + TFE_System::logWrite(LOG_MSG, "PLAYBACK START DATE", "INPUT UPDATE TIME %d MAX COUNT %d", s_startTime, updateCounter); + } + else + { + TFE_System::logWrite(LOG_MSG, "RECORD START DATE", "INPUT UPDATE TIME %d", s_startTime); + } + TFE_DarkForces::time_pause(JFALSE); + } + + void startRecording() + { + if (FileUtil::exists(s_replayPath)) + { + FileUtil::deleteFile(s_replayPath); + } + recordReplaySeed(); + inputMapping_endFrame(); + //TFE_DarkForces::resetTime(); + s_recording = true; + setDemoPlayback(false); + resetCounter(); + setMaxInputCounter(0); + } + + void endRecording() + { + s_recording = false; + FileStream* stream = &s_replayFile; + serializeDemo(&s_replayFile, true); + setDemoPlayback(false); + inputMapping_endFrame(); + } + + void recordReplayTime(u64 startTime) + { + s_startTime = startTime; + } + + void loadReplay() + { + if (TFE_Settings::getGameSettings()->df_enableRecording) return; + resetCounter(); + restoreReplaySeed(); + inputMapping_endFrame(); + //TFE_DarkForces::resetTime(); + FileStream* stream = &s_replayFile; + serializeDemo(&s_replayFile, false); + setDemoPlayback(true); + TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK BEFORE %d", TFE_DarkForces::s_curTick); + TFE_DarkForces::s_curTick = inputEvents[1].curTick; + TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK AFTER %d", TFE_DarkForces::s_curTick); + } +} \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.h b/TheForceEngine/TFE_Input/replay.h new file mode 100644 index 000000000..db412b615 --- /dev/null +++ b/TheForceEngine/TFE_Input/replay.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace TFE_Input +{ + enum ReplayVersion : u32 + { + ReplayVersionInit = 1, + ReplayVersionCur = ReplayVersionInit + }; + + + struct ReplayEvent + { + std::vector keysDown; + std::vector mouseDown; + std::vector mousePos; + Tick curTick; + std::tuple timeData; + }; + + extern std::unordered_map inputEvents; + + void initReplays(); + void recordReplayTime(u64 startTime); + void startRecording(); + void endRecording(); + void loadReplay(); + + bool isRecording(); + void setRecording(bool recording); + bool isDemoPlayback(); + void setDemoPlayback(bool playback); + void recordReplaySeed(); + void restoreReplaySeed(); + void recordTiming(); + void loadTiming(); + void saveTick(std::tuple tData); + void sTick(Tick curTick); + Tick lTick(); + std::tuple loadTick(); + + std::string convertToString(std::vector keysDown); + std::vector convertFromString(std::string keyStr); +} \ No newline at end of file diff --git a/TheForceEngine/TFE_Jedi/Task/task.cpp b/TheForceEngine/TFE_Jedi/Task/task.cpp index 99ddc6dba..f95bd5b86 100644 --- a/TheForceEngine/TFE_Jedi/Task/task.cpp +++ b/TheForceEngine/TFE_Jedi/Task/task.cpp @@ -126,7 +126,10 @@ namespace TFE_Jedi s_taskCount++; strcpy(newTask->name, name); - + if (std::string(newTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING SUBTASK %s TASK COUNT = %d", name, s_taskCount); + } // Insert newTask at the head of the subtask list in the current "mainline" task. newTask->next = s_curTask->subtaskNext; newTask->prev = nullptr; @@ -166,6 +169,10 @@ namespace TFE_Jedi s_taskCount++; // Insert the task after 's_taskIter' strcpy(newTask->name, name); + if (std::string(newTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING TASK %s TASK COUNT = %d", name, s_taskCount); + } newTask->next = s_taskIter->next; // This was missing? if (s_taskIter->next) @@ -253,7 +260,10 @@ namespace TFE_Jedi { parent->subtaskNext = task->next; } - + if (std::string(task->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "ENDING TASK %s TASK COUNT = %d", task->name, s_taskCount); + } // Free any memory allocated for the local context. freeToChunkedArray(s_stackBlocks, task->context.stackMem); // Finally free the task itself from the chunked array. @@ -417,6 +427,10 @@ namespace TFE_Jedi task = task->subtaskNext; } // Then execute the task. + if (std::string(task->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Root Task %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); + } if (task->nextTick <= s_curTick || task->framebreak) { s_currentMsg = MSG_RUN_TASK; @@ -428,6 +442,10 @@ namespace TFE_Jedi { // Otherwise, try to execute the parent. task = task->subtaskParent; + if (std::string(task->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "SubTask %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); + } if (task->nextTick <= s_curTick || task->framebreak) { s_currentMsg = MSG_RUN_TASK; @@ -487,7 +505,10 @@ namespace TFE_Jedi assert(s_curContext->level >= 0 && s_curContext->level < TASK_MAX_LEVELS); s_curContext->ip[s_curContext->level] = ip; s_curContext->level--; - + if (std::string(s_curTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Yielding task %s for %u ticks", s_curTask->name, delay); + } TASK_MSG("Task yield: '%s' for %u ticks", s_curTask->name, delay); // If there is a return task, then take it next. @@ -501,14 +522,24 @@ namespace TFE_Jedi s_currentMsg = MSG_RUN_TASK; s_curTask = retTask; s_curContext = &s_curTask->context; - + if (std::string(s_curTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Return task %s", s_curTask->name); + } TASK_MSG("Return Task: '%s'", s_curTask->name); return; } // Update the current tick based on the delay. + if (std::string(s_curTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Tick before %d delay=%d ", s_curTick, delay); + } s_curTask->nextTick = (delay < TASK_SLEEP) ? s_curTick + delay : delay; - + if (std::string(s_curTask->name) == "main task") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Tick after %d ", s_curTick); + } // Find the next task to run. selectNextTask(); assert(s_curTask); @@ -552,10 +583,11 @@ namespace TFE_Jedi // Limit the update rate by the minimum interval. // Dark Forces uses discrete 'ticks' to track time and the game behavior is very odd with 0 tick frames. const f64 time = TFE_System::getTime(); + /* if (time - s_prevTime < s_minIntervalInSec) { return JFALSE; - } + }*/ s_prevTime = time; s_currentMsg = MSG_RUN_TASK; s_frameActiveTaskCount = 0; diff --git a/TheForceEngine/TFE_Settings/settings.h b/TheForceEngine/TFE_Settings/settings.h index d6072225c..1ead781f0 100644 --- a/TheForceEngine/TFE_Settings/settings.h +++ b/TheForceEngine/TFE_Settings/settings.h @@ -217,6 +217,8 @@ struct TFE_Settings_Game bool df_solidWallFlagFix = true; // Solid wall flag is enforced for collision with moving walls. bool df_enableUnusedItem = true; // Enables the unused item in the inventory (delt 10). bool df_jsonAiLogics = true; // AI logics can be loaded from external JSON files + bool df_enableRecording = false; // Enable recording of gameplay. + bool df_enableReplay = false; // Enable replay of gameplay. PitchLimit df_pitchLimit = PITCH_VANILLA_PLUS; }; diff --git a/TheForceEngine/TFE_System/frameLimiter.cpp b/TheForceEngine/TFE_System/frameLimiter.cpp index 58b2c6971..e050601bf 100644 --- a/TheForceEngine/TFE_System/frameLimiter.cpp +++ b/TheForceEngine/TFE_System/frameLimiter.cpp @@ -44,6 +44,7 @@ namespace TFE_System void frameLimiter_end() { + return; if (s_limitDelta == 0.0) { return; } u64 curTick = TFE_System::getCurrentTimeInTicks(); diff --git a/TheForceEngine/TFE_System/log.cpp b/TheForceEngine/TFE_System/log.cpp index f955b6d7c..51b1853b8 100644 --- a/TheForceEngine/TFE_System/log.cpp +++ b/TheForceEngine/TFE_System/log.cpp @@ -77,8 +77,15 @@ namespace TFE_System localtime_r(&now_c, &now_tm); // For thread safety on Linux #endif - char timeStr[32]; - strftime(timeStr, sizeof(timeStr), "%Y-%b-%d %H:%M:%S", &now_tm); + auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch()) % 1000; + + char timeStr[40]; + strftime(timeStr, sizeof(timeStr) - 4, "%Y-%b-%d %H:%M:%S", &now_tm); // Leave space for milliseconds + + // Add milliseconds to the formatted time + snprintf(timeStr + strlen(timeStr), 5, ".%03lld", milliseconds.count()); + //Handle the variable input, "printf" style messages va_list arg; diff --git a/TheForceEngine/TFE_System/system.cpp b/TheForceEngine/TFE_System/system.cpp index 4ce39dce1..83e877344 100644 --- a/TheForceEngine/TFE_System/system.cpp +++ b/TheForceEngine/TFE_System/system.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #ifdef _WIN32 // Following includes for Windows LinkCallback @@ -45,13 +47,27 @@ namespace TFE_System static s32 s_missedFrameCount = 0; static char s_versionString[64]; + static u32 s_frame = 0; + + void setFrame(u32 frame) + { + s_frame = frame; + } + + u32 getFrame() + { + return s_frame; + } + + void init(f32 refreshRate, bool synced, const char* versionString) { TFE_System::logWrite(LOG_MSG, "Startup", "TFE_System::init"); - s_time = SDL_GetPerformanceCounter(); + s_time = 42156207250291;// SDL_GetPerformanceCounter(); s_lastSyncCheck = s_time; s_startTime = s_time; + TFE_Input::recordReplayTime(s_startTime); const u64 timerFreq = SDL_GetPerformanceFrequency(); s_syncCheckDelay = timerFreq * 10; // 10 seconds. @@ -102,12 +118,28 @@ namespace TFE_System return dt; } + u64 getStartTime() + { + return s_startTime; + } + + void setStartTime(u64 startTime) + { + s_startTime = startTime; + } + + u64 getTimePerf() + { + return SDL_GetPerformanceCounter(); + } + void update() { // This assumes that SDL_GetPerformanceCounter() is monotonic. // However if errors do occur, the dt clamp later should limit the side effects. - const u64 curTime = SDL_GetPerformanceCounter(); + const u64 curTime = getTimePerf(); const u64 uDt = (curTime > s_time) ? (curTime - s_time) : 1; // Make sure time is monotonic. + //TFE_System::logWrite(LOG_MSG, "Startup", "CURTIME = %d, UDT = %d", curTime, uDt); s_time = curTime; if (s_resetStartTime) { @@ -184,6 +216,11 @@ namespace TFE_System return f64(ticks) * s_freq; } + f64 convertFromTicksToMillis(u64 ticks) + { + return f64(ticks) * 1000.0 * s_freq; + } + f64 microsecondsToSeconds(f64 mu) { return mu / 1000000.0; diff --git a/TheForceEngine/TFE_System/system.h b/TheForceEngine/TFE_System/system.h index 6d2e558d7..f5ea332de 100644 --- a/TheForceEngine/TFE_System/system.h +++ b/TheForceEngine/TFE_System/system.h @@ -50,7 +50,10 @@ namespace TFE_System u64 getCurrentTimeInTicks(); f64 convertFromTicksToSeconds(u64 ticks); + f64 convertFromTicksToMillis(u64 ticks); f64 microsecondsToSeconds(f64 mu); + u64 getStartTime(); + void setStartTime(u64 startTime); void getDateTimeString(char* output); @@ -76,6 +79,9 @@ namespace TFE_System const char* getVersionString(); extern f64 c_gameTimeScale; + + void setFrame(u32 frame); + u32 getFrame(); } // _strlwr/_strupr exist on Windows; roll our own diff --git a/TheForceEngine/main.cpp b/TheForceEngine/main.cpp index b903932bb..20ac31bab 100644 --- a/TheForceEngine/main.cpp +++ b/TheForceEngine/main.cpp @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include #if ENABLE_EDITOR == 1 #include @@ -570,6 +573,10 @@ int main(int argc, char* argv[]) } generateScreenshotTime(); + // Create Replay Directory + initReplays(); + + // Initialize SDL if (!sdlInit()) { @@ -580,7 +587,7 @@ int main(int argc, char* argv[]) TFE_Settings_Window* windowSettings = TFE_Settings::getWindowSettings(); TFE_Settings_Graphics* graphics = TFE_Settings::getGraphicsSettings(); TFE_System::init(s_refreshRate, graphics->vsync, c_gitVersion); - + // Setup the GPU Device and Window. u32 windowFlags = 0; if (windowSettings->fullscreen || TFE_Settings::getTempSettings()->forceFullscreen) @@ -657,14 +664,52 @@ int main(int argc, char* argv[]) // Game loop u32 frame = 0u; + TFE_System::setFrame(frame); bool showPerf = false; bool relativeMode = false; TFE_System::logWrite(LOG_MSG, "Progam Flow", "The Force Engine Game Loop Started"); + Tick accum = 0; + Tick step = 300000;// hardcode to 3 ticks per frame. + + Tick start = TFE_System::getCurrentTimeInTicks(); + Tick maxdt = 0; + Tick avg = start; + int skipCount = 0; + bool canwork = true; + while (s_loop && !TFE_System::quitMessagePosted()) { - TFE_FRAME_BEGIN(); + + /* + // Loop, called once per frame with the delta time (dt). + if (TFE_DarkForces::isMissionRunning()) + { + canwork = false; + skipCount++; + Tick end = TFE_System::getCurrentTimeInTicks(); + Tick dt = end - start; + accum += dt; + + if (accum > step && skipCount > 3) + { + accum -= step; + canwork = true; + } + + + + start = end; + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "Cycle = %u end = %u DT = %u accum = %u", start, end, dt, accum); + + } + + if (!canwork) + { + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "Skipping due to accum = %u", accum); + continue; + }*/ + TFE_System::frameLimiter_begin(); - bool enableRelative = TFE_Input::relativeModeEnabled(); if (enableRelative != relativeMode) { @@ -685,14 +730,8 @@ int main(int argc, char* argv[]) SDL_Event event; while (SDL_PollEvent(&event)) { handleEvent(event); } - // Handle mouse state. - s32 mouseX, mouseY; - s32 mouseAbsX, mouseAbsY; - u32 state = SDL_GetRelativeMouseState(&mouseX, &mouseY); - SDL_GetMouseState(&mouseAbsX, &mouseAbsY); - TFE_Input::setRelativeMousePos(mouseX, mouseY); - TFE_Input::setMousePos(mouseAbsX, mouseAbsY); - inputMapping_updateInput(); + // Inputs Main Entry + handleInputs(); // Can we save? TFE_FrontEndUI::setCanSave(s_curGame ? s_curGame->canSave() : false); @@ -853,6 +892,17 @@ int main(int argc, char* argv[]) } } + /* + if (inputMapping_getActionState(IADF_DEMO_RECORD) == STATE_PRESSED) + { + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + gameSettings->df_enableRecording = !gameSettings->df_enableRecording; + TFE_Input::setRecordingAllowed(gameSettings->df_enableRecording); + string cur_setting = isRecordingAllowed() ? "true" : "false"; + string message = "Toggling Recording. Currently " + cur_setting; + TFE_DarkForces::hud_sendTextMessage(message.c_str(), 1); + }*/ + #ifdef ENABLE_FORCE_SCRIPT TFE_ForceScript::update(); #endif @@ -886,7 +936,7 @@ int main(int argc, char* argv[]) TFE_RenderBackend::clearWindow(); } - bool drawFps = s_curGame && graphics->showFps; + bool drawFps = s_curGame&& graphics->showFps; if (s_curGame) { drawFps = drawFps && (!s_curGame->isPaused()); } TFE_FrontEndUI::setCurrentGame(s_curGame); @@ -925,7 +975,10 @@ int main(int argc, char* argv[]) { TFE_FRAME_END(); } - } + } + + TFE_System::logWrite(LOG_MSG, "Progam Flow", "END average = %u maxdt = %u", avg, maxdt); + if (s_curGame) { diff --git a/TheForceEngine/main.h b/TheForceEngine/main.h index d00d47e78..1685d3e70 100644 --- a/TheForceEngine/main.h +++ b/TheForceEngine/main.h @@ -1,3 +1,4 @@ #pragma once #include "resource.h" + From 9ba869b3f64e0d266a9e52a1f7e47117840d57e2 Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:21:29 -0500 Subject: [PATCH 02/64] Added more debug logs and update tick recording --- .../TFE_DarkForces/darkForcesMain.cpp | 11 ++ TheForceEngine/TFE_DarkForces/mission.cpp | 4 +- TheForceEngine/TFE_DarkForces/player.cpp | 34 +++- TheForceEngine/TFE_DarkForces/player.h | 1 + TheForceEngine/TFE_DarkForces/time.cpp | 13 +- TheForceEngine/TFE_DarkForces/time.h | 2 + TheForceEngine/TFE_Input/input.cpp | 25 ++- TheForceEngine/TFE_Input/input.h | 4 + TheForceEngine/TFE_Input/inputMapping.cpp | 147 ++++++++++++++++-- TheForceEngine/TFE_Input/replay.cpp | 104 +++++++++++-- TheForceEngine/TFE_Input/replay.h | 28 +++- .../TFE_Jedi/InfSystem/infSystem.cpp | 1 + TheForceEngine/TFE_Jedi/Task/task.cpp | 29 ++-- TheForceEngine/main.cpp | 31 +--- 14 files changed, 340 insertions(+), 94 deletions(-) diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp index 6d3c50217..46a84063b 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp @@ -782,6 +782,17 @@ namespace TFE_DarkForces task_reset(); inf_clearState(); + + if (TFE_Settings::getGameSettings()->df_enableReplay) + { + loadReplay(); + } + + if (TFE_Settings::getGameSettings()->df_enableRecording) + { + startRecording(); + } + s_sharedState.loadMissionTask = createTask("start mission", mission_startTaskFunc, JTRUE); mission_setLoadMissionTask(s_sharedState.loadMissionTask); diff --git a/TheForceEngine/TFE_DarkForces/mission.cpp b/TheForceEngine/TFE_DarkForces/mission.cpp index ac21cb935..50667756c 100644 --- a/TheForceEngine/TFE_DarkForces/mission.cpp +++ b/TheForceEngine/TFE_DarkForces/mission.cpp @@ -395,7 +395,7 @@ namespace TFE_DarkForces mission_addCheatCommands(); CCMD("spawnEnemy", console_spawnEnemy, 2, "spawnEnemy(waxName, enemyTypeName) - spawns an enemy 8 units away in the player direction. Example: spawnEnemy offcfin.wax i_officer"); - + /* if (TFE_Settings::getGameSettings()->df_enableReplay) { loadReplay(); @@ -404,7 +404,7 @@ namespace TFE_DarkForces if (TFE_Settings::getGameSettings()->df_enableRecording) { startRecording(); - } + }*/ // Make sure the loading screen is displayed for at least 1 second. if (!s_loadingFromSave) diff --git a/TheForceEngine/TFE_DarkForces/player.cpp b/TheForceEngine/TFE_DarkForces/player.cpp index 704939ae8..26b51d0d4 100644 --- a/TheForceEngine/TFE_DarkForces/player.cpp +++ b/TheForceEngine/TFE_DarkForces/player.cpp @@ -1570,6 +1570,7 @@ namespace TFE_DarkForces s32 mdx, mdy; TFE_Input::getAccumulatedMouseMove(&mdx, &mdy); + TFE_System::logWrite(LOG_MSG, "PLAYER", "mdx=%d mdy=%d startdelta=%d", mdx, mdy, s_deltaTime); InputConfig* inputConfig = TFE_Input::inputMapping_get(); // Yaw change @@ -1614,6 +1615,8 @@ namespace TFE_DarkForces } } + TFE_System::logWrite(LOG_MSG, "PLAYER", "s_forwardSpd = %d", s_forwardSpd); + if (settings->df_autorun) // TFE: Optional feature. { s_playerRun = 1; @@ -1704,6 +1707,8 @@ namespace TFE_DarkForces s_playerUpVel2 = 0; } + TFE_System::logWrite(LOG_MSG, "PLAYER", "s_playerUpVel = %d s_playerUpVel2 = %d", s_playerUpVel, s_playerUpVel2); + ////////////////////////////////////////// // Pitch and Roll controls. ////////////////////////////////////////// @@ -1814,6 +1819,8 @@ namespace TFE_DarkForces s_forwardSpd >>= airControl; s_strafeSpd >>= airControl; } + + TFE_System::logWrite(LOG_MSG, "PLAYER", "s_forwardSpd = %d", s_forwardSpd); } fixed16_16 adjustForwardSpeed(fixed16_16 spd) @@ -1915,6 +1922,7 @@ namespace TFE_DarkForces s_playerVelX = 0; s_playerVelZ = 0; } + TFE_System::logWrite(LOG_MSG, "PLAYER move", "s_playerVelX = %d s_playerVelZ = %d", s_playerVelX, s_playerVelZ); } // Handle moving surfaces. @@ -1948,7 +1956,7 @@ namespace TFE_DarkForces fixed16_16 angularSpd; vec3_fixed vel; inf_getMovingElevatorVelocity(elev, &vel, &angularSpd); - + TFE_System::logWrite(LOG_MSG, "PLAYER ELEV", "vel = %d %d %d angspd = %d", vel.x, vel.y, vel.z, angularSpd); if (!angularSpd && secHeight > 0) { // liquid behavior - dampens velocity. @@ -1964,6 +1972,7 @@ namespace TFE_DarkForces if (vel.x || vel.z) { + TFE_System::logWrite(LOG_MSG, "PLAYER VEL ", "velx = %d velz = %d", vel.x, vel.z); if (!onMovingSurface) { s_externalVelX = vel.x; @@ -2002,6 +2011,7 @@ namespace TFE_DarkForces speed = adjustStrafeSpeed(s_strafeSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw + 4095, speed); limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerVelX = %d s_playerVelZ = %d s_maxMoveDist = %d", s_playerVelX, s_playerVelZ, s_maxMoveDist); } // Then convert from player velocity to per-frame movement. @@ -2009,6 +2019,7 @@ namespace TFE_DarkForces fixed16_16 moveZ = adjustForwardSpeed(s_playerVelZ); s_playerLogic.move.x = mul16(moveX, s_deltaTime) + mul16(s_externalVelX, s_deltaTime); s_playerLogic.move.z = mul16(moveZ, s_deltaTime) + mul16(s_externalVelZ, s_deltaTime); + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerLogic.move.x = %d s_playerLogic.move.z = %d", s_playerLogic.move.x, s_playerLogic.move.z); s_playerLogic.stepHeight = s_limitStepHeight ? PLAYER_STEP : PLAYER_INF_STEP; fixed16_16 width = (s_smallModeEnabled) ? PLAYER_SIZE_SMALL : PLAYER_WIDTH; player->worldWidth = width; @@ -2084,6 +2095,7 @@ namespace TFE_DarkForces s_externalVelZ = mul16(projVel, slideDirZ); } } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerVelX = %d, s_playerVelZ = %d s_externalVelX = %d s_externalVelZ = %d", s_playerVelX, s_playerVelZ, s_externalVelX, s_externalVelZ); // If this fails, the system will treat it as if the player didn't move (see below). // However, it should succeed since the original collision detection did. @@ -2110,6 +2122,8 @@ namespace TFE_DarkForces s_playerVelX = 0; s_playerVelZ = 0; } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d", s_playerUpVel); + s_playerSector = s_colMinSector; if (s_externalVelX || s_externalVelZ) @@ -2199,11 +2213,13 @@ namespace TFE_DarkForces { s_playerUpVel2 += gravityAccelDt; } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d s_playerUpVel2 = %d", player->posWS.y, s_playerUpVel2); + s_playerUpVel = s_playerUpVel2; s_playerYPos += mul16(s_playerUpVel, s_deltaTime); s_playerLogic.move.y = s_playerYPos - player->posWS.y; - player->posWS.y = s_playerYPos; - + player->posWS.y = s_playerYPos; + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d s_playerLogic.move.y = %d s_playerYPos = %d", player->posWS.y, s_playerLogic.move.y, s_playerYPos); if (s_playerYPos >= s_colCurLowestFloor && (!s_noclip || !s_flyMode)) { if (s_kyleScreamSoundId) @@ -2236,6 +2252,7 @@ namespace TFE_DarkForces s_postLandVel = min((s_playerUpVel >> 2) - ((s_playerUpVel - PLAYER_LAND_VEL_CHANGE) >> 3), PLAYER_LAND_VEL_MAX); } s_landUpVel = s_playerUpVel; + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_landUpVel = %d s_postLandVel = %d", s_landUpVel, s_postLandVel); } else { @@ -2248,6 +2265,7 @@ namespace TFE_DarkForces { player->worldHeight += (s_colCurBot - yPos + yMove); } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "ypos = %d ymove = %d distFromFloor = %d", yPos, yMove, distFromFloor); } s_playerYPos = s_colCurLowestFloor; @@ -2255,6 +2273,8 @@ namespace TFE_DarkForces fixed16_16 newUpVel = min(0, s_playerUpVel2); s_playerUpVel = newUpVel; s_playerUpVel2 = newUpVel; + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d", s_playerUpVel); + } else { @@ -2269,6 +2289,7 @@ namespace TFE_DarkForces s_playerYPos = min(s_colCurLowestFloor, newPlayerBot); player->posWS.y = s_playerYPos; } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d s_playerYPos = %d", s_playerUpVel, s_playerYPos); } // Crouch @@ -2287,13 +2308,14 @@ namespace TFE_DarkForces { s_landUpVel = 0; } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_landUpVel = %d s_postLandVel = %d", s_landUpVel, s_postLandVel); fixed16_16 eyeToCeil = max(0, player->posWS.y - s_colCurHighestCeil); fixed16_16 eyeHeight = eyeToCeil; eyeToCeil = min(ONE_16, eyeToCeil); // the player eye should be clamped to 1 unit below the ceiling if possible. eyeHeight -= eyeToCeil; eyeHeight = min(PLAYER_HEIGHT, eyeHeight); - + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "eyeToCeil = %d eyeHeight = %d", eyeToCeil, eyeHeight); RSector* sector = player->sector; fixed16_16 minEyeDistFromFloor = (s_smallModeEnabled) ? PLAYER_SIZE_SMALL : s_minEyeDistFromFloor; secHeight = sector->secHeight; @@ -2317,6 +2339,8 @@ namespace TFE_DarkForces } player->worldHeight = minDistToFloor; } + + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->worldHeight = %d", player->worldHeight); // Make sure eye height is clamped. if (!s_noclip || !s_flyMode) { @@ -2477,6 +2501,8 @@ namespace TFE_DarkForces } } + TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d, s_playerYPos = %d player->worldHeight = %d", player->posWS.y, s_playerYPos, player->worldHeight); + weapon->rollOffset = -s_playerRoll / 13; weapon->pchOffset = s_playerPitch / 64; diff --git a/TheForceEngine/TFE_DarkForces/player.h b/TheForceEngine/TFE_DarkForces/player.h index 11eb02013..3be4a83b7 100644 --- a/TheForceEngine/TFE_DarkForces/player.h +++ b/TheForceEngine/TFE_DarkForces/player.h @@ -108,6 +108,7 @@ namespace TFE_DarkForces extern angle14_32 s_pitch, s_yaw, s_roll; extern angle14_32 s_playerYaw; extern Tick s_playerTick; + extern Tick s_prevPlayerTick; extern Tick s_reviveTick; extern SecObject* s_playerObject; diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index fb0c29a0e..726c556d0 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -29,7 +29,7 @@ namespace TFE_DarkForces { s_curTick = 0; s_prevTick = 0; - f64 s_timeAccum = 0.0; + s_timeAccum = 0.0; s_deltaTime; s_frameTicks[13] = { 0 }; s_pauseTimeUpdate = JFALSE; @@ -77,24 +77,21 @@ namespace TFE_DarkForces Tick prevTick = s_curTick; if (TFE_Input::isRecording()) { - TFE_Input::sTick(s_curTick); + TFE_Input::saveTick(); /*if (TFE_Input::getCounter() == 0) { resetTime(); }*/ // std::tuple < Tick, Tick, f64, fixed16_16> tData = { s_curTick, prevTick, s_timeAccum, s_deltaTime}; - TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME curtick = %d prevtick = %d accum = %d delta = %d", s_curTick, s_prevTick, s_timeAccum, s_deltaTime); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME curtick = %d prevtick = %d accum = %d delta = %d", s_curTick, s_prevTick, s_timeAccum, s_deltaTime); // TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK %u %d", s_curTick, s_curTick); //TFE_Input::saveTick(tData); } if (TFE_Input::isDemoPlayback()) { - Tick t = TFE_Input::lTick(); - if (t != 0) - { - s_curTick = t; - } + TFE_Input::loadTick(); + //std::tuple < Tick, Tick, f64, fixed16_16> tData = TFE_Input::loadTick(); //s_curTick = std::get<0>(tData); /*prevTick = std::get<1>(tData); diff --git a/TheForceEngine/TFE_DarkForces/time.h b/TheForceEngine/TFE_DarkForces/time.h index 7accc6d8b..fe99b3380 100644 --- a/TheForceEngine/TFE_DarkForces/time.h +++ b/TheForceEngine/TFE_DarkForces/time.h @@ -46,6 +46,8 @@ namespace TFE_DarkForces // Each computes dt*frameRate and the indexed framerate (i.e. s_frameTicks[12] = 12 fps). extern fixed16_16 s_frameTicks[13]; + extern f64 s_timeAccum; + // Convert from frames per second (fps) to Ticks. Tick time_frameRateToDelay(u32 frameRate); Tick time_frameRateToDelay(s32 frameRate); diff --git a/TheForceEngine/TFE_Input/input.cpp b/TheForceEngine/TFE_Input/input.cpp index 822319a95..038b90c73 100644 --- a/TheForceEngine/TFE_Input/input.cpp +++ b/TheForceEngine/TFE_Input/input.cpp @@ -34,6 +34,7 @@ namespace TFE_Input s32 s_mousePos[2] = { 0 }; bool s_relativeMode = false; + bool s_isRepeating = false; static const char* const* s_controllerAxisNames; static const char* const* s_controllerButtonNames; @@ -85,10 +86,30 @@ namespace TFE_Input s_buttonDown[button] = 0; } + void setKeyPress(KeyboardCode key) + { + s_keyPressed[key] = 1; + } + + void setKeyPressRepeat(KeyboardCode key) + { + s_keyPressedRepeat[key] = 1; + } + + void setRepeating(bool repeat) + { + s_isRepeating = repeat; + } + + bool isRepeating() + { + return s_isRepeating; + } + void setKeyDown(KeyboardCode key, bool repeat) { - - //TFE_System::logWrite(LOG_MSG, "Input", "DOWN '%d'", key); + const char* boolStr = repeat ? "true" : "false"; + // TFE_System::logWrite(LOG_MSG, "Input", "DOWN '%d' repeat = %s", key, boolStr); if (!s_keyDown[key] && !repeat) { s_keyPressed[key] = 1; diff --git a/TheForceEngine/TFE_Input/input.h b/TheForceEngine/TFE_Input/input.h index 390ab0d5e..bfb641890 100644 --- a/TheForceEngine/TFE_Input/input.h +++ b/TheForceEngine/TFE_Input/input.h @@ -56,6 +56,10 @@ namespace TFE_Input bool mouseDown(MouseButton button); bool mousePressed(MouseButton button); bool relativeModeEnabled(); + void setRepeating(bool repeat); + bool isRepeating(); + void setKeyPress(KeyboardCode key); + void setKeyPressRepeat(KeyboardCode key); void clearKeyPressed(KeyboardCode key); void clearMouseButtonPressed(MouseButton btn); void clearAccumulatedMouseMove(); diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index a96a5373b..dcc5634bf 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -131,6 +131,7 @@ namespace TFE_Input int inputCounter = 0; int maxInputCounter = 0; vector currentKeys; + vector currentKeyPresses; vector currentMouse; void addDefaultControlBinds(); @@ -347,9 +348,13 @@ namespace TFE_Input { std::vector keysDown; + std::vector keysPressed; std::vector mouseDown; + std::vector mouseWheel; int keyIndex = 0; + int keyPressIndex = 0; int mouseIndex = 0; + int mouseWheelIndex = 0; ReplayEvent event = TFE_Input::inputEvents[inputCounter]; @@ -360,10 +365,11 @@ namespace TFE_Input { // Replay Keys keysDown = event.keysDown; - for (int i = 0; i < keysDown.size(); i++) + for (int i = 0; i < keysDown.size(); i+=2) { KeyboardCode key = (KeyboardCode)keysDown[i]; - TFE_Input::setKeyDown(key, false); + bool repeat = keysDown[i+1]; + TFE_Input::setKeyDown(key, repeat); currentKeys.push_back(key); } for (int i = 0; i < currentKeys.size(); i++) @@ -376,6 +382,32 @@ namespace TFE_Input } } + // Replay Key Presses + keysPressed = event.keysPressed; + for (int i = 0; i < keysPressed.size(); i+=2) + { + KeyboardCode key = (KeyboardCode)keysPressed[i]; + bool repeat = keysPressed[i + 1]; + if (repeat) + { + TFE_Input::setKeyPressRepeat(key); + } + else + { + TFE_Input::setKeyPress(key); + } + currentKeyPresses.push_back(key); + } + for (int i = 0; i < currentKeyPresses.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeyPresses[i]; + if (std::find(keysPressed.begin(), keysPressed.end(), key) == keysPressed.end()) + { + TFE_Input::clearKeyPressed(key); + currentKeyPresses.erase(std::remove(currentKeyPresses.begin(), currentKeyPresses.end(), key), currentKeyPresses.end()); + } + } + // Replay Mouse buttons mouseDown = event.mouseDown; for (int i = 0; i < mouseDown.size(); i++) @@ -393,6 +425,13 @@ namespace TFE_Input currentMouse.erase(std::remove(currentMouse.begin(), currentMouse.end(), key), currentMouse.end()); } } + + //Replay MouseWheel + mouseWheel = event.mouseWheel; + if (mouseWheel.size() > 0) + { + TFE_Input::setMouseWheel(mouseWheel[0], mouseWheel[1]); + } } // Clear all keys after playback. @@ -413,6 +452,16 @@ namespace TFE_Input if (TFE_Input::keyPressed(bind->keyCode)) { s_actions[bind->action] = STATE_PRESSED; + if (TFE_Input::isRecording()) + { + // Do not record Escape + if (bind->keyCode != KEY_ESCAPE) + { + keysPressed.push_back(bind->keyCode); + keysPressed.push_back(isRepeating()); + } + } + keyPressIndex++; } else if (TFE_Input::keyDown(bind->keyCode) && s_actions[bind->action] != STATE_PRESSED) { @@ -422,15 +471,11 @@ namespace TFE_Input //TFE_System::logWrite(LOG_MSG, "INPUT PUSH BACK", "INPUT UPDATE %d", bind->keyCode); if (TFE_Input::isRecording()) { - if (keysDown.size() > 0) - { - int x = 2; - x = x + 3; - } // Do not record Escape if (bind->keyCode != KEY_ESCAPE) { keysDown.push_back(bind->keyCode); + keysDown.push_back(isRepeating()); } } keyIndex++; @@ -460,6 +505,14 @@ namespace TFE_Input { s32 dx, dy; TFE_Input::getMouseWheel(&dx, &dy); + + if (TFE_Input::isRecording()) + { + mouseWheel.push_back(dx); + mouseWheel.push_back(dy); + mouseWheelIndex++; + } + if ((bind->mouseWheel == MOUSEWHEEL_LEFT && dx < 0) || (bind->mouseWheel == MOUSEWHEEL_RIGHT && dx > 0) || (bind->mouseWheel == MOUSEWHEEL_UP && dy > 0) || @@ -504,10 +557,18 @@ namespace TFE_Input { event.keysDown = keysDown; } + if (keyPressIndex > 0) + { + event.keysPressed = keysPressed; + } if (mouseIndex > 0) { event.mouseDown = mouseDown; } + if (mouseWheelIndex > 0) + { + event.mouseWheel = mouseWheel; + } inputEvents[inputCounter] = event; } } @@ -623,16 +684,30 @@ namespace TFE_Input if (keyPressed(KEY_ESCAPE)) { if (isDemoPlayback()) - { - - TFE_Input::setDemoPlayback(false); - } - if (isRecording()) { setDemoPlayback(false); string msg = "DEMO Playback Complete!"; TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); + for (int i = 0; i < currentKeys.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeys[i]; + TFE_Input::setKeyUp(key); + } + currentKeys.clear(); + for (int i = 0; i < currentKeyPresses.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeyPresses[i]; + TFE_Input::clearKeyPressed(key); + } + currentKeyPresses.clear(); + TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYEND ======================================"); + + + } + if (isRecording()) + { TFE_Input::endRecording(); + TFE_System::logWrite(LOG_MSG, "LOG", "====================================== RECORDEND ======================================"); } } @@ -668,7 +743,7 @@ namespace TFE_Input setDemoPlayback(false); string msg = "DEMO Playback Complete!"; TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); - + inputMapping_updateInput(); } else { @@ -717,23 +792,63 @@ namespace TFE_Input if ((isRecording() || isDemoPlayback()) && TFE_DarkForces::s_playerEye) { + if (inputCounter == 62) + { + if (isRecording()) + { + TFE_System::logWrite(LOG_MSG, "LOG", "====================================== RECORDSTART ======================================"); + } + else + { + TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYSTART ======================================"); + } + } + + if (isRecording() && inputCounter == 61) + { + recordEye(); + } + if (isDemoPlayback() && inputCounter == 62) + { + setEye(); + } + ReplayEvent event = TFE_Input::inputEvents[inputCounter]; - hudData = TFE_DarkForces::hud_getDataStr(true); + vec3_fixed ws = TFE_DarkForces::s_playerEye->posWS; + vec3_fixed vs = TFE_DarkForces::s_playerEye->posVS; + hudData += " WS: X: " + std::to_string(ws.x) + " Y:" + std::to_string(ws.y) + " Z:" + std::to_string(ws.z); + //hudData += " VS: " + std::to_string(vs.x) + " " + std::to_string(vs.y) + " " + std::to_string(vs.z); + + angle14_16 yaw = TFE_DarkForces::s_playerEye->yaw; + angle14_16 pitch = TFE_DarkForces::s_playerEye->pitch; + angle14_16 roll = TFE_DarkForces::s_playerEye->roll; + //TFE_DarkForces::s_playerEye->posWS.z + string playerpos = " X: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.x) + " Y: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.y) + " Z: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.z); + hudData += playerpos; + hudData += " Y: " + std::to_string(yaw) + " P: " + std::to_string(pitch); + vec3_fixed vel = {}; + TFE_DarkForces::player_getVelocity(&vel); + hudData += " V: " + std::to_string(vel.x) + " " + std::to_string(vel.z) + " "; + hudData += TFE_DarkForces::hud_getDataStr(true); keys = convertToString(event.keysDown); mouse = convertToString(event.mouseDown); } if ((isRecording() || isDemoPlayback())) { - string hudDataStr = "Rec=%d, Play=%d, RecAllow=%d, PlayAllow=%d, upd=%d "; + string rec = isRecording() ? "REC " : "PLAY"; + string hudDataStr = rec + " upd=%d cur=%d pt=%d ptp=%d delta=%d"; + if (hudData.size() != 0) hudDataStr += hudData; if (keys.size() != 0) hudDataStr += " Keys: " + keys; if (mouse.size() != 0) hudDataStr += " Mouse: " + mouse; if (mouseP.size() != 0) hudDataStr += " MousePos " + mouseP; - TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), rec, play, recAllow, playAllow, inputCounter); + TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), inputCounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); } + + //TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); inputCounter++; } diff --git a/TheForceEngine/TFE_Input/replay.cpp b/TheForceEngine/TFE_Input/replay.cpp index ad0909195..a3ae4b82a 100644 --- a/TheForceEngine/TFE_Input/replay.cpp +++ b/TheForceEngine/TFE_Input/replay.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace TFE_Input { @@ -30,6 +31,10 @@ namespace TFE_Input static f64 replayTimeAccum = 0.0; static fixed16_16 replayDeltaTime; static fixed16_16 replayFrameTicks[13] = { 0 }; + angle14_16 r_yaw = 0; + angle14_16 r_pitch = 0; + angle14_16 r_roll = 0; + std::unordered_map inputEvents; u64 s_startTime = 0; @@ -101,32 +106,44 @@ namespace TFE_Input { int inputCounter = getCounter(); inputEvents[inputCounter].curTick = curTick; - TFE_System::logWrite(LOG_MSG, "Progam Flow", "STICK curtick = %d counter = %d ", curTick, inputCounter); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STICK curtick = %d counter = %d ", curTick, inputCounter); } - void saveTick(std::tuple tData) + void saveTick() { int inputCounter = getCounter(); - TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK curtick = %d prevtick = %d accum = %d delta = %d", get<0>(tData), get<1>(tData), get<2>(tData), get<3>(tData)); - inputEvents[inputCounter].curTick = get<0>(tData); - inputEvents[inputCounter].timeData = tData; + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK curtick = %d prevtick = %d accum = %d delta = %d", get<0>(tData), get<1>(tData), get<2>(tData), get<3>(tData)); + if (inputCounter > 0) + { + inputEvents[inputCounter - 1].curTick = TFE_DarkForces::s_curTick; + } + else + { + inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; + } + //inputEvents[inputCounter].prevTick = TFE_DarkForces::s_prevTick; + //inputEvents[inputCounter].deltaTime = TFE_DarkForces::s_deltaTime; + //inputEvents[inputCounter].timeAccum = TFE_DarkForces::getTimeAccum(); + //memcpy(inputEvents[inputCounter].frameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); } Tick lTick() { int inputCounter = getCounter(); Tick curTick = inputEvents[inputCounter].curTick; - TFE_System::logWrite(LOG_MSG, "Progam Flow", "LTICK curtick = %d counter = %d ", curTick, inputCounter); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "LTICK curtick = %d counter = %d ", curTick, inputCounter); return curTick; } - std::tuple loadTick() + void loadTick() { int inputCounter = getCounter(); TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; - - return inputEvents[inputCounter].timeData; + //TFE_DarkForces::s_prevTick = inputEvents[inputCounter].prevTick; + //TFE_DarkForces::setTimeAccum(inputEvents[inputCounter].timeAccum); + //TFE_DarkForces::s_deltaTime = inputEvents[inputCounter].deltaTime; + //memcpy(TFE_DarkForces::s_frameTicks, inputEvents[inputCounter].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[inputCounter].frameTicks)); } void recordReplaySeed() @@ -172,6 +189,10 @@ namespace TFE_Input if (writeFlag) { keyString = convertToString(inputList); + if (keyString.size() == 0) + { + int x; + } keySize = keyString.size(); } @@ -183,7 +204,7 @@ namespace TFE_Input SERIALIZE_BUF(ReplayVersionInit, &keyString[0], keySize); - if (!writeFlag) + if (!writeFlag && keySize > 0) { return convertFromString(keyString); } @@ -207,31 +228,52 @@ namespace TFE_Input inputEvents.clear(); } + + + + if (fileHandler > 0) { SERIALIZE_VERSION(ReplayVersionInit); SERIALIZE(ReplayVersionInit, s_startTime, 0); SERIALIZE(ReplayVersionInit, replay_seed, 0); + + u32 plTick = TFE_DarkForces::s_playerTick; + u32 plPrevTick = TFE_DarkForces::s_prevPlayerTick; + SERIALIZE(ReplayVersionInit, plTick, 0); + SERIALIZE(ReplayVersionInit, plPrevTick, 0); + TFE_DarkForces::time_serialize(stream); int inputListsSize = getCounter(); SERIALIZE(ReplayVersionInit, inputListsSize, 0); + SERIALIZE(ReplayVersionInit, r_yaw, 0); + SERIALIZE(ReplayVersionInit, r_pitch, 0); + SERIALIZE(ReplayVersionInit, r_roll, 0); + if (writeFlag) { + // Loop through inputEvents for (const auto& pair : inputEvents) { updateCounter = pair.first; + if (updateCounter > inputListsSize) continue; SERIALIZE(ReplayVersionInit, updateCounter, 0); serializeInputs(stream, pair.second.keysDown, writeFlag); + serializeInputs(stream, pair.second.keysPressed, writeFlag); serializeInputs(stream, pair.second.mouseDown, writeFlag); serializeInputs(stream, pair.second.mousePos, writeFlag); + //TFE_System::logWrite(LOG_MSG, "STORING", "upd = %d wheelsize = %d", updateCounter, pair.second.mouseWheel.size()); + + serializeInputs(stream, pair.second.mouseWheel, writeFlag); + u32 curTick = pair.second.curTick; - TFE_System::logWrite(LOG_MSG, "STORING", "STORING CURTIME %d COUNTER %d", curTick, updateCounter); + // TFE_System::logWrite(LOG_MSG, "STORING", "STORING CURTIME %d COUNTER %d", curTick, updateCounter); SERIALIZE(ReplayVersionInit, curTick, 0); /* std::tuple tData = pair.second.timeData; @@ -248,17 +290,24 @@ namespace TFE_Input } else { + + TFE_DarkForces::s_playerTick = plTick; + TFE_DarkForces::s_prevPlayerTick = plPrevTick; + for (int i = 0; i < inputListsSize; i++) { SERIALIZE(ReplayVersionInit, updateCounter, 0); ReplayEvent event = {}; event.keysDown = serializeInputs(stream, event.keysDown, writeFlag); + event.keysPressed = serializeInputs(stream, event.keysPressed, writeFlag); event.mouseDown = serializeInputs(stream, event.mouseDown, writeFlag); event.mousePos = serializeInputs(stream, event.mousePos, writeFlag); + event.mouseWheel = serializeInputs(stream, event.mouseWheel, writeFlag); + u32 curTick = 0; SERIALIZE(ReplayVersionInit, curTick, 0); - TFE_System::logWrite(LOG_MSG, "LOADING", "LOADING CURTIME %d COUNTER %d", curTick, updateCounter); + //TFE_System::logWrite(LOG_MSG, "LOADING", "LOADING CURTIME %d COUNTER %d", curTick, updateCounter); event.curTick = curTick; /* u32 curTick; @@ -297,12 +346,35 @@ namespace TFE_Input TFE_DarkForces::time_pause(JFALSE); } + void recordEye() + { + r_yaw = TFE_DarkForces::s_playerEye->yaw; + r_pitch = TFE_DarkForces::s_playerEye->pitch; + r_roll = TFE_DarkForces::s_playerEye->roll; + TFE_System::logWrite(LOG_MSG, "REPLAY", "Record yaw=%d pitch=%d roll=%d", r_yaw, r_pitch, r_roll); + + } + + void setEye() + { + TFE_DarkForces::s_playerEye->yaw = r_yaw; + TFE_DarkForces::s_playerEye->pitch = r_pitch; + TFE_DarkForces::s_playerEye->roll = r_roll; + TFE_System::logWrite(LOG_MSG, "REPLAY", "Set yaw=%d pitch=%d roll=%d", r_yaw, r_pitch, r_roll); + } + void startRecording() { if (FileUtil::exists(s_replayPath)) { FileUtil::deleteFile(s_replayPath); } + + for (int i = 0; i < inputEvents.size(); i++) + { + inputEvents[i].clear(); + } + recordReplaySeed(); inputMapping_endFrame(); //TFE_DarkForces::resetTime(); @@ -336,8 +408,12 @@ namespace TFE_Input FileStream* stream = &s_replayFile; serializeDemo(&s_replayFile, false); setDemoPlayback(true); - TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK BEFORE %d", TFE_DarkForces::s_curTick); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK BEFORE %d", TFE_DarkForces::s_curTick); TFE_DarkForces::s_curTick = inputEvents[1].curTick; - TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK AFTER %d", TFE_DarkForces::s_curTick); + TFE_DarkForces::s_prevTick = inputEvents[1].prevTick; + //TFE_DarkForces::setTimeAccum(inputEvents[1].timeAccum); + TFE_DarkForces::s_deltaTime = inputEvents[1].deltaTime; + memcpy(TFE_DarkForces::s_frameTicks, inputEvents[1].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK AFTER %d", TFE_DarkForces::s_curTick); } } \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.h b/TheForceEngine/TFE_Input/replay.h index db412b615..659dc9069 100644 --- a/TheForceEngine/TFE_Input/replay.h +++ b/TheForceEngine/TFE_Input/replay.h @@ -20,10 +20,30 @@ namespace TFE_Input struct ReplayEvent { std::vector keysDown; + std::vector keysPressed; std::vector mouseDown; std::vector mousePos; + std::vector mouseWheel; Tick curTick; - std::tuple timeData; + Tick prevTick; + fixed16_16 deltaTime; + f64 timeAccum; + fixed16_16 frameTicks[13]; + //std::tuple timeData; + + void clear() { + keysDown.clear(); + keysPressed.clear(); + mouseDown.clear(); + mousePos.clear(); + mouseWheel.clear(); + curTick = 0; + prevTick = 0; + deltaTime = 0; + timeAccum = 0.0; + std::fill(std::begin(frameTicks), std::end(frameTicks), 0); + //timeData = std::make_tuple(0, 0, 0.0, 0); // Reset tuple values + } }; extern std::unordered_map inputEvents; @@ -42,10 +62,12 @@ namespace TFE_Input void restoreReplaySeed(); void recordTiming(); void loadTiming(); - void saveTick(std::tuple tData); + void saveTick(); void sTick(Tick curTick); Tick lTick(); - std::tuple loadTick(); + void loadTick(); + void recordEye(); + void setEye(); std::string convertToString(std::vector keysDown); std::vector convertFromString(std::string keyStr); diff --git a/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp b/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp index ffea9f5c9..5d4a5e7dc 100644 --- a/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp +++ b/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp @@ -3476,6 +3476,7 @@ namespace TFE_Jedi sector_adjustTextureMirrorOffsets_Floor(sector, floorDelta); } sector_adjustHeights(sector, floorDelta, ceilDelta, secHeightDelta); + TFE_System::logWrite(LOG_MSG, "INF", "sector = %d, floorDelta = %d, ceilDelta = %d, secHeightDelta = %d", sector->id, floorDelta, ceilDelta, secHeightDelta); // Apply adjustments to "slave" sectors. Slave* child = (Slave*)allocator_getHead(elev->slaves); diff --git a/TheForceEngine/TFE_Jedi/Task/task.cpp b/TheForceEngine/TFE_Jedi/Task/task.cpp index f95bd5b86..08fb72f40 100644 --- a/TheForceEngine/TFE_Jedi/Task/task.cpp +++ b/TheForceEngine/TFE_Jedi/Task/task.cpp @@ -126,10 +126,8 @@ namespace TFE_Jedi s_taskCount++; strcpy(newTask->name, name); - if (std::string(newTask->name) == "main task") - { - TFE_System::logWrite(LOG_WARNING, "Task", "STARTING SUBTASK %s TASK COUNT = %d", name, s_taskCount); - } + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING SUBTASK %s TASK COUNT = %d", name, s_taskCount); + // Insert newTask at the head of the subtask list in the current "mainline" task. newTask->next = s_curTask->subtaskNext; newTask->prev = nullptr; @@ -169,10 +167,9 @@ namespace TFE_Jedi s_taskCount++; // Insert the task after 's_taskIter' strcpy(newTask->name, name); - if (std::string(newTask->name) == "main task") - { - TFE_System::logWrite(LOG_WARNING, "Task", "STARTING TASK %s TASK COUNT = %d", name, s_taskCount); - } + + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING TASK %s TASK COUNT = %d", name, s_taskCount); + newTask->next = s_taskIter->next; // This was missing? if (s_taskIter->next) @@ -262,7 +259,7 @@ namespace TFE_Jedi } if (std::string(task->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "ENDING TASK %s TASK COUNT = %d", task->name, s_taskCount); + //TFE_System::logWrite(LOG_WARNING, "Task", "ENDING TASK %s TASK COUNT = %d", task->name, s_taskCount); } // Free any memory allocated for the local context. freeToChunkedArray(s_stackBlocks, task->context.stackMem); @@ -429,7 +426,7 @@ namespace TFE_Jedi // Then execute the task. if (std::string(task->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "Root Task %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); + //TFE_System::logWrite(LOG_WARNING, "Task", "Root Task %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); } if (task->nextTick <= s_curTick || task->framebreak) { @@ -444,7 +441,7 @@ namespace TFE_Jedi task = task->subtaskParent; if (std::string(task->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "SubTask %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); + //TFE_System::logWrite(LOG_WARNING, "Task", "SubTask %s Next Tick %d curtick %d", task->name, task->nextTick, s_curTick); } if (task->nextTick <= s_curTick || task->framebreak) { @@ -485,7 +482,7 @@ namespace TFE_Jedi } if (retTask != s_curTask) { - TFE_System::logWrite(LOG_WARNING, "Task", "Correction required upon returning for task_runAndReturn."); + //TFE_System::logWrite(LOG_WARNING, "Task", "Correction required upon returning for task_runAndReturn."); s_curTask = retTask; s_curContext = &s_curTask->context; } @@ -507,7 +504,7 @@ namespace TFE_Jedi s_curContext->level--; if (std::string(s_curTask->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "Yielding task %s for %u ticks", s_curTask->name, delay); + //TFE_System::logWrite(LOG_WARNING, "Task", "Yielding task %s for %u ticks", s_curTask->name, delay); } TASK_MSG("Task yield: '%s' for %u ticks", s_curTask->name, delay); @@ -524,7 +521,7 @@ namespace TFE_Jedi s_curContext = &s_curTask->context; if (std::string(s_curTask->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "Return task %s", s_curTask->name); + //TFE_System::logWrite(LOG_WARNING, "Task", "Return task %s", s_curTask->name); } TASK_MSG("Return Task: '%s'", s_curTask->name); return; @@ -533,12 +530,12 @@ namespace TFE_Jedi // Update the current tick based on the delay. if (std::string(s_curTask->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "Tick before %d delay=%d ", s_curTick, delay); + //TFE_System::logWrite(LOG_WARNING, "Task", "Tick before %d delay=%d ", s_curTick, delay); } s_curTask->nextTick = (delay < TASK_SLEEP) ? s_curTick + delay : delay; if (std::string(s_curTask->name) == "main task") { - TFE_System::logWrite(LOG_WARNING, "Task", "Tick after %d ", s_curTick); + //TFE_System::logWrite(LOG_WARNING, "Task", "Tick after %d ", s_curTick); } // Find the next task to run. selectNextTask(); diff --git a/TheForceEngine/main.cpp b/TheForceEngine/main.cpp index 20ac31bab..a688b2286 100644 --- a/TheForceEngine/main.cpp +++ b/TheForceEngine/main.cpp @@ -141,6 +141,8 @@ void handleEvent(SDL_Event& Event) { TFE_Input::setBufferedKey(KeyboardCode(Event.key.keysym.scancode)); } + + TFE_Input::setRepeating(Event.key.repeat > 0); } break; case SDL_KEYUP: { @@ -680,35 +682,6 @@ int main(int argc, char* argv[]) while (s_loop && !TFE_System::quitMessagePosted()) { - /* - // Loop, called once per frame with the delta time (dt). - if (TFE_DarkForces::isMissionRunning()) - { - canwork = false; - skipCount++; - Tick end = TFE_System::getCurrentTimeInTicks(); - Tick dt = end - start; - accum += dt; - - if (accum > step && skipCount > 3) - { - accum -= step; - canwork = true; - } - - - - start = end; - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "Cycle = %u end = %u DT = %u accum = %u", start, end, dt, accum); - - } - - if (!canwork) - { - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "Skipping due to accum = %u", accum); - continue; - }*/ - TFE_System::frameLimiter_begin(); bool enableRelative = TFE_Input::relativeModeEnabled(); if (enableRelative != relativeMode) From 5d4f089d994314ba4d4ced553c01a4febf03dbc6 Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:16:51 -0500 Subject: [PATCH 03/64] Working version of replays. --- TheForceEngine/TFE_DarkForces/player.cpp | 4 ++ TheForceEngine/TFE_DarkForces/time.cpp | 3 +- TheForceEngine/TFE_Input/inputMapping.cpp | 59 ++++++++++++------ TheForceEngine/TFE_Input/inputMapping.h | 1 + TheForceEngine/TFE_Input/replay.cpp | 75 +++++++---------------- TheForceEngine/TFE_Input/replay.h | 4 -- TheForceEngine/TFE_Jedi/Task/task.cpp | 6 +- 7 files changed, 73 insertions(+), 79 deletions(-) diff --git a/TheForceEngine/TFE_DarkForces/player.cpp b/TheForceEngine/TFE_DarkForces/player.cpp index 26b51d0d4..db19ebc63 100644 --- a/TheForceEngine/TFE_DarkForces/player.cpp +++ b/TheForceEngine/TFE_DarkForces/player.cpp @@ -795,6 +795,8 @@ namespace TFE_DarkForces const char* levelName = agent_getLevelName(); TFE_System::logWrite(LOG_MSG, "Player", "Setting up level '%s'", levelName); + //resetCounter(); + // Handle custom level player overrides ModSettingLevelOverride modLevelOverride = TFE_Settings::getLevelOverrides(levelName); if (!modLevelOverride.levName.empty()) @@ -913,6 +915,8 @@ namespace TFE_DarkForces setCameraOffset(0, 0, 0); setCameraAngleOffset(0, 0, 0); + TFE_System::logWrite(LOG_MSG, "PLAYER", "EYE Created at s_curTick = %d", s_curTick); + } void player_revive() diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index 726c556d0..414040d9e 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -67,6 +67,7 @@ namespace TFE_DarkForces void updateTime() { + if (!s_pauseTimeUpdate) { s_timeAccum += TFE_System::getDeltaTime() * TIMER_FREQ; @@ -105,7 +106,7 @@ namespace TFE_DarkForces { s_curTick = Tick(s_timeAccum); } - + TFE_System::logWrite(LOG_MSG, "TIME", "Update Tick s_curTick = %d prevTick = %d s_=revTick = %d updateCounter = %d", s_curTick, prevTick, s_prevTick, TFE_Input::getCounter()); fixed16_16 dt = div16(intToFixed16(s_curTick - prevTick), FIXED(TICKS_PER_SECOND)); //TFE_System::logWrite(LOG_MSG, "Progam Flow", "DT %u %d", dt, dt); for (s32 i = 0; i < 13; i++) diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index dcc5634bf..36389b280 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -329,6 +329,11 @@ namespace TFE_Input return action >= IADF_FORWARD && action <= IADF_LOOK_DN; } + void setCounter(int counter) + { + inputCounter = counter; + } + void resetCounter() { inputCounter = 0; @@ -356,11 +361,14 @@ namespace TFE_Input int mouseIndex = 0; int mouseWheelIndex = 0; - ReplayEvent event = TFE_Input::inputEvents[inputCounter]; - + ReplayEvent event; + if (TFE_Input::isRecording()) + { + event = TFE_Input::inputEvents[inputCounter]; + } if (TFE_Input::isDemoPlayback()) { - + event = TFE_Input::inputEvents[inputCounter-1]; if (maxInputCounter > inputCounter) { // Replay Keys @@ -630,7 +638,7 @@ namespace TFE_Input return axisValue; } - + f32 inputMapping_getHorzMouseSensitivity() { return s_inputConfig.mouseSensitivity[0] * ((s_inputConfig.mouseFlags & MFLAG_INVERT_HORZ) ? -1.0f : 1.0f); @@ -691,7 +699,7 @@ namespace TFE_Input for (int i = 0; i < currentKeys.size(); i++) { KeyboardCode key = (KeyboardCode)currentKeys[i]; - TFE_Input::setKeyUp(key); + TFE_Input::setKeyUp(key); } currentKeys.clear(); for (int i = 0; i < currentKeyPresses.size(); i++) @@ -712,18 +720,19 @@ namespace TFE_Input } - /* - if (inputCounter == 0) + + if (inputCounter == 0 && TFE_DarkForces::s_playerEye) { - if (isDemoPlayback()) + + if (isRecording()) { - loadTiming(); + recordEye(); } - if (isRecording()) + if (isDemoPlayback()) { - recordTiming(); - } - }*/ + setEye(); + } + } std::vector mousePos; s32 mouseX, mouseY; s32 mouseAbsX, mouseAbsY; @@ -751,7 +760,7 @@ namespace TFE_Input TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); inputMapping_endFrame(); - ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + ReplayEvent event = TFE_Input::inputEvents[inputCounter-1]; mousePos = event.mousePos; if (mousePos.size() == 4) { @@ -803,7 +812,7 @@ namespace TFE_Input TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYSTART ======================================"); } } - + /* if (isRecording() && inputCounter == 61) { recordEye(); @@ -811,9 +820,18 @@ namespace TFE_Input if (isDemoPlayback() && inputCounter == 62) { setEye(); - } + }*/ - ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + + ReplayEvent event; + if (isDemoPlayback()) + { + event = TFE_Input::inputEvents[inputCounter-1]; + } + else + { + event = TFE_Input::inputEvents[inputCounter]; + } vec3_fixed ws = TFE_DarkForces::s_playerEye->posWS; vec3_fixed vs = TFE_DarkForces::s_playerEye->posVS; hudData += " WS: X: " + std::to_string(ws.x) + " Y:" + std::to_string(ws.y) + " Z:" + std::to_string(ws.z); @@ -848,8 +866,13 @@ namespace TFE_Input } - //TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); + TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); inputCounter++; + /* + if (TFE_DarkForces::s_playerEye) + { + inputCounter++; + }*/ } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/inputMapping.h b/TheForceEngine/TFE_Input/inputMapping.h index 5df9a1b94..102953b51 100644 --- a/TheForceEngine/TFE_Input/inputMapping.h +++ b/TheForceEngine/TFE_Input/inputMapping.h @@ -198,6 +198,7 @@ namespace TFE_Input f32 inputMapping_getVertMouseSensitivity(); void resetCounter(); int getCounter(); + void setCounter(int counter); void handleInputs(); void setMaxInputCounter(int counter); } // TFE_Input \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.cpp b/TheForceEngine/TFE_Input/replay.cpp index a3ae4b82a..ee69d94d1 100644 --- a/TheForceEngine/TFE_Input/replay.cpp +++ b/TheForceEngine/TFE_Input/replay.cpp @@ -73,47 +73,18 @@ namespace TFE_Input s_playback = playback; } - void recordTiming() - { - TFE_DarkForces::time_pause(JTRUE); - /*replayCurTick = TFE_DarkForces::s_curTick; - replayPrevTick = TFE_DarkForces::s_prevTick; - replayTimeAccum = TFE_DarkForces::getTimeAccum();; - replayDeltaTime = TFE_DarkForces::s_deltaTime; - memcpy(replayFrameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); - */ - recordReplaySeed(); - sTick(TFE_DarkForces::s_curTick); - TFE_DarkForces::time_pause(JFALSE); - } - - void loadTiming() - { - TFE_DarkForces::time_pause(JTRUE); - /* - TFE_DarkForces::s_curTick = replayCurTick; - TFE_DarkForces::s_prevTick = replayPrevTick; - TFE_DarkForces::setTimeAccum(replayTimeAccum); - TFE_DarkForces::s_deltaTime = replayDeltaTime; - memcpy(TFE_DarkForces::s_frameTicks, replayFrameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(replayFrameTicks)); - */ - restoreReplaySeed(); - TFE_DarkForces::s_curTick = lTick(); - TFE_DarkForces::time_pause(JFALSE); - } - - void sTick(Tick curTick) - { - int inputCounter = getCounter(); - inputEvents[inputCounter].curTick = curTick; - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STICK curtick = %d counter = %d ", curTick, inputCounter); - - } - void saveTick() { int inputCounter = getCounter(); + + if (TFE_DarkForces::s_curTick == 0) + { + int x = 0; + } + //TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK curtick = %d prevtick = %d accum = %d delta = %d", get<0>(tData), get<1>(tData), get<2>(tData), get<3>(tData)); + inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; + /* if (inputCounter > 0) { inputEvents[inputCounter - 1].curTick = TFE_DarkForces::s_curTick; @@ -121,20 +92,14 @@ namespace TFE_Input else { inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; - } + }*/ //inputEvents[inputCounter].prevTick = TFE_DarkForces::s_prevTick; //inputEvents[inputCounter].deltaTime = TFE_DarkForces::s_deltaTime; //inputEvents[inputCounter].timeAccum = TFE_DarkForces::getTimeAccum(); //memcpy(inputEvents[inputCounter].frameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); } - Tick lTick() - { - int inputCounter = getCounter(); - Tick curTick = inputEvents[inputCounter].curTick; - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "LTICK curtick = %d counter = %d ", curTick, inputCounter); - return curTick; - } + void loadTick() { @@ -268,7 +233,7 @@ namespace TFE_Input serializeInputs(stream, pair.second.mouseDown, writeFlag); serializeInputs(stream, pair.second.mousePos, writeFlag); - //TFE_System::logWrite(LOG_MSG, "STORING", "upd = %d wheelsize = %d", updateCounter, pair.second.mouseWheel.size()); + TFE_System::logWrite(LOG_MSG, "STORING", "upd = %d curtick = %d", updateCounter, inputEvents[updateCounter].curTick); serializeInputs(stream, pair.second.mouseWheel, writeFlag); @@ -369,18 +334,20 @@ namespace TFE_Input { FileUtil::deleteFile(s_replayPath); } - - for (int i = 0; i < inputEvents.size(); i++) + int iSize = inputEvents.size(); + for (int i = 0; i < iSize; i++) { inputEvents[i].clear(); + } - + inputEvents.clear(); recordReplaySeed(); inputMapping_endFrame(); //TFE_DarkForces::resetTime(); s_recording = true; setDemoPlayback(false); resetCounter(); + saveTick(); setMaxInputCounter(0); } @@ -401,7 +368,9 @@ namespace TFE_Input void loadReplay() { if (TFE_Settings::getGameSettings()->df_enableRecording) return; + resetCounter(); + setCounter(1); restoreReplaySeed(); inputMapping_endFrame(); //TFE_DarkForces::resetTime(); @@ -409,11 +378,11 @@ namespace TFE_Input serializeDemo(&s_replayFile, false); setDemoPlayback(true); //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK BEFORE %d", TFE_DarkForces::s_curTick); - TFE_DarkForces::s_curTick = inputEvents[1].curTick; - TFE_DarkForces::s_prevTick = inputEvents[1].prevTick; + TFE_DarkForces::s_curTick = inputEvents[0].curTick; + TFE_DarkForces::s_prevTick = inputEvents[0].prevTick; //TFE_DarkForces::setTimeAccum(inputEvents[1].timeAccum); - TFE_DarkForces::s_deltaTime = inputEvents[1].deltaTime; - memcpy(TFE_DarkForces::s_frameTicks, inputEvents[1].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); + TFE_DarkForces::s_deltaTime = inputEvents[0].deltaTime; + memcpy(TFE_DarkForces::s_frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK AFTER %d", TFE_DarkForces::s_curTick); } } \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.h b/TheForceEngine/TFE_Input/replay.h index 659dc9069..c054e619d 100644 --- a/TheForceEngine/TFE_Input/replay.h +++ b/TheForceEngine/TFE_Input/replay.h @@ -60,11 +60,7 @@ namespace TFE_Input void setDemoPlayback(bool playback); void recordReplaySeed(); void restoreReplaySeed(); - void recordTiming(); - void loadTiming(); void saveTick(); - void sTick(Tick curTick); - Tick lTick(); void loadTick(); void recordEye(); void setEye(); diff --git a/TheForceEngine/TFE_Jedi/Task/task.cpp b/TheForceEngine/TFE_Jedi/Task/task.cpp index 08fb72f40..3976dcefd 100644 --- a/TheForceEngine/TFE_Jedi/Task/task.cpp +++ b/TheForceEngine/TFE_Jedi/Task/task.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -126,8 +127,7 @@ namespace TFE_Jedi s_taskCount++; strcpy(newTask->name, name); - TFE_System::logWrite(LOG_WARNING, "Task", "STARTING SUBTASK %s TASK COUNT = %d", name, s_taskCount); - + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING SUBTASK %s TASK COUNT = %d s_curTick = %d counter = %d", name, s_taskCount, s_curTick, TFE_Input::getCounter()); // Insert newTask at the head of the subtask list in the current "mainline" task. newTask->next = s_curTask->subtaskNext; newTask->prev = nullptr; @@ -168,7 +168,7 @@ namespace TFE_Jedi // Insert the task after 's_taskIter' strcpy(newTask->name, name); - TFE_System::logWrite(LOG_WARNING, "Task", "STARTING TASK %s TASK COUNT = %d", name, s_taskCount); + TFE_System::logWrite(LOG_WARNING, "Task", "STARTING TASK %s TASK COUNT = %d s_curTick = %d counter = %d", name, s_taskCount, s_curTick, TFE_Input::getCounter()); newTask->next = s_taskIter->next; // This was missing? From f9b9a3388face6345ab66d000b761829d38888ed Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Sat, 7 Dec 2024 12:18:20 -0500 Subject: [PATCH 04/64] Working version of demo replays. Still rare issues with long term replays. --- TheForceEngine/TFE_DarkForces/Actor/actor.cpp | 9 +- .../TFE_DarkForces/darkForcesMain.cpp | 4 +- .../TFE_DarkForces/darkForcesMain.h | 1 + TheForceEngine/TFE_DarkForces/mission.cpp | 11 - TheForceEngine/TFE_DarkForces/player.cpp | 28 +- TheForceEngine/TFE_DarkForces/time.cpp | 46 +--- TheForceEngine/TFE_DarkForces/time.h | 3 - TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp | 252 ++++++++++++++++++ TheForceEngine/TFE_FrontEndUI/frontEndUi.h | 1 + TheForceEngine/TFE_Game/saveSystem.h | 3 + TheForceEngine/TFE_Input/input.cpp | 3 +- TheForceEngine/TFE_Input/inputMapping.cpp | 28 +- TheForceEngine/TFE_Input/replay.cpp | 214 +++++++-------- TheForceEngine/TFE_Input/replay.h | 9 +- .../TFE_Jedi/InfSystem/infSystem.cpp | 3 +- TheForceEngine/TFE_Jedi/Task/task.cpp | 4 + 16 files changed, 391 insertions(+), 228 deletions(-) diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp index 883cdfbb0..a7a1862ce 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp @@ -875,7 +875,7 @@ namespace TFE_DarkForces } attackMod->target.flags &= ~TARGET_ALL_MOVE; // Next AI update. - return s_curTick + random(attackMod->timing.delay); + return s_curTick + random(attackMod->timing.delay); } } break; case STATE_ANIMATEATTACK: @@ -907,6 +907,7 @@ namespace TFE_DarkForces { actor_updatePlayerVisiblity(JTRUE, s_eyePos.x, s_eyePos.z); attackMod->timing.nextTick = s_curTick + attackMod->timing.losDelay; + fixed16_16 dist = distApprox(s_playerObject->posWS.x, s_playerObject->posWS.z, obj->posWS.x, obj->posWS.z); fixed16_16 yDiff = TFE_Jedi::abs(obj->posWS.y - obj->worldHeight - s_eyePos.y); angle14_32 vertAngle = vec2ToAngle(yDiff, dist); @@ -1719,6 +1720,7 @@ namespace TFE_DarkForces JBool actor_advanceAnimation(LogicAnimation* anim, SecObject* obj) { + if (!anim->prevTick) { anim->prevTick = s_frameTicks[anim->frameRate]; @@ -2063,7 +2065,7 @@ namespace TFE_DarkForces { if (dispatch->nextTick < s_curTick) { - dispatch->nextTick = s_curTick;// +dispatch->delay; + dispatch->nextTick = s_curTick +dispatch->delay; if (actor_isObjectVisible(obj, s_playerObject, dispatch->fov, dispatch->awareRange)) { message_sendToObj(obj, MSG_WAKEUP, actor_hitEffectMsgFunc); @@ -2078,11 +2080,12 @@ namespace TFE_DarkForces s_actorState.curAnimation = nullptr; for (s32 i = 0; i < ACTOR_MAX_MODULES; i++) { - ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; + ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; if (module && module->func && module->nextTick < s_curTick) { module->nextTick = module->func(module, dispatch->moveMod); } + } if (s_actorState.curLogic && !(dispatch->flags & 1)) diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp index 46a84063b..beccba8b9 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp @@ -783,11 +783,13 @@ namespace TFE_DarkForces task_reset(); inf_clearState(); + // Entry point to recording a demo if (TFE_Settings::getGameSettings()->df_enableReplay) { loadReplay(); } + // Entry point to load a demo if (TFE_Settings::getGameSettings()->df_enableRecording) { startRecording(); @@ -834,7 +836,7 @@ namespace TFE_DarkForces { c = arg[1]; if (c == 'c' || c == 'C') - { + { enableCutscenes(arg[2] == '1' ? JTRUE : JFALSE); } else if ((c == 'l' || c == 'L') && arg[2]) diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.h b/TheForceEngine/TFE_DarkForces/darkForcesMain.h index 896711eb4..e7542b319 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.h +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.h @@ -26,4 +26,5 @@ namespace TFE_DarkForces }; extern void saveLevelStatus(); + void enableCutscenes(JBool enable); } diff --git a/TheForceEngine/TFE_DarkForces/mission.cpp b/TheForceEngine/TFE_DarkForces/mission.cpp index 50667756c..5197c7165 100644 --- a/TheForceEngine/TFE_DarkForces/mission.cpp +++ b/TheForceEngine/TFE_DarkForces/mission.cpp @@ -395,17 +395,6 @@ namespace TFE_DarkForces mission_addCheatCommands(); CCMD("spawnEnemy", console_spawnEnemy, 2, "spawnEnemy(waxName, enemyTypeName) - spawns an enemy 8 units away in the player direction. Example: spawnEnemy offcfin.wax i_officer"); - /* - if (TFE_Settings::getGameSettings()->df_enableReplay) - { - loadReplay(); - } - - if (TFE_Settings::getGameSettings()->df_enableRecording) - { - startRecording(); - }*/ - // Make sure the loading screen is displayed for at least 1 second. if (!s_loadingFromSave) { diff --git a/TheForceEngine/TFE_DarkForces/player.cpp b/TheForceEngine/TFE_DarkForces/player.cpp index db19ebc63..0d7f0b61c 100644 --- a/TheForceEngine/TFE_DarkForces/player.cpp +++ b/TheForceEngine/TFE_DarkForces/player.cpp @@ -914,9 +914,7 @@ namespace TFE_DarkForces s_roll = s_playerEye->roll; setCameraOffset(0, 0, 0); - setCameraAngleOffset(0, 0, 0); - TFE_System::logWrite(LOG_MSG, "PLAYER", "EYE Created at s_curTick = %d", s_curTick); - + setCameraAngleOffset(0, 0, 0); } void player_revive() @@ -1574,7 +1572,6 @@ namespace TFE_DarkForces s32 mdx, mdy; TFE_Input::getAccumulatedMouseMove(&mdx, &mdy); - TFE_System::logWrite(LOG_MSG, "PLAYER", "mdx=%d mdy=%d startdelta=%d", mdx, mdy, s_deltaTime); InputConfig* inputConfig = TFE_Input::inputMapping_get(); // Yaw change @@ -1619,7 +1616,6 @@ namespace TFE_DarkForces } } - TFE_System::logWrite(LOG_MSG, "PLAYER", "s_forwardSpd = %d", s_forwardSpd); if (settings->df_autorun) // TFE: Optional feature. { @@ -1711,8 +1707,7 @@ namespace TFE_DarkForces s_playerUpVel2 = 0; } - TFE_System::logWrite(LOG_MSG, "PLAYER", "s_playerUpVel = %d s_playerUpVel2 = %d", s_playerUpVel, s_playerUpVel2); - + ////////////////////////////////////////// // Pitch and Roll controls. ////////////////////////////////////////// @@ -1824,7 +1819,6 @@ namespace TFE_DarkForces s_strafeSpd >>= airControl; } - TFE_System::logWrite(LOG_MSG, "PLAYER", "s_forwardSpd = %d", s_forwardSpd); } fixed16_16 adjustForwardSpeed(fixed16_16 spd) @@ -1926,7 +1920,6 @@ namespace TFE_DarkForces s_playerVelX = 0; s_playerVelZ = 0; } - TFE_System::logWrite(LOG_MSG, "PLAYER move", "s_playerVelX = %d s_playerVelZ = %d", s_playerVelX, s_playerVelZ); } // Handle moving surfaces. @@ -1960,7 +1953,6 @@ namespace TFE_DarkForces fixed16_16 angularSpd; vec3_fixed vel; inf_getMovingElevatorVelocity(elev, &vel, &angularSpd); - TFE_System::logWrite(LOG_MSG, "PLAYER ELEV", "vel = %d %d %d angspd = %d", vel.x, vel.y, vel.z, angularSpd); if (!angularSpd && secHeight > 0) { // liquid behavior - dampens velocity. @@ -1976,7 +1968,6 @@ namespace TFE_DarkForces if (vel.x || vel.z) { - TFE_System::logWrite(LOG_MSG, "PLAYER VEL ", "velx = %d velz = %d", vel.x, vel.z); if (!onMovingSurface) { s_externalVelX = vel.x; @@ -2015,7 +2006,6 @@ namespace TFE_DarkForces speed = adjustStrafeSpeed(s_strafeSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw + 4095, speed); limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerVelX = %d s_playerVelZ = %d s_maxMoveDist = %d", s_playerVelX, s_playerVelZ, s_maxMoveDist); } // Then convert from player velocity to per-frame movement. @@ -2023,7 +2013,6 @@ namespace TFE_DarkForces fixed16_16 moveZ = adjustForwardSpeed(s_playerVelZ); s_playerLogic.move.x = mul16(moveX, s_deltaTime) + mul16(s_externalVelX, s_deltaTime); s_playerLogic.move.z = mul16(moveZ, s_deltaTime) + mul16(s_externalVelZ, s_deltaTime); - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerLogic.move.x = %d s_playerLogic.move.z = %d", s_playerLogic.move.x, s_playerLogic.move.z); s_playerLogic.stepHeight = s_limitStepHeight ? PLAYER_STEP : PLAYER_INF_STEP; fixed16_16 width = (s_smallModeEnabled) ? PLAYER_SIZE_SMALL : PLAYER_WIDTH; player->worldWidth = width; @@ -2099,7 +2088,6 @@ namespace TFE_DarkForces s_externalVelZ = mul16(projVel, slideDirZ); } } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerVelX = %d, s_playerVelZ = %d s_externalVelX = %d s_externalVelZ = %d", s_playerVelX, s_playerVelZ, s_externalVelX, s_externalVelZ); // If this fails, the system will treat it as if the player didn't move (see below). // However, it should succeed since the original collision detection did. @@ -2126,7 +2114,6 @@ namespace TFE_DarkForces s_playerVelX = 0; s_playerVelZ = 0; } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d", s_playerUpVel); s_playerSector = s_colMinSector; @@ -2217,13 +2204,11 @@ namespace TFE_DarkForces { s_playerUpVel2 += gravityAccelDt; } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d s_playerUpVel2 = %d", player->posWS.y, s_playerUpVel2); s_playerUpVel = s_playerUpVel2; s_playerYPos += mul16(s_playerUpVel, s_deltaTime); s_playerLogic.move.y = s_playerYPos - player->posWS.y; player->posWS.y = s_playerYPos; - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d s_playerLogic.move.y = %d s_playerYPos = %d", player->posWS.y, s_playerLogic.move.y, s_playerYPos); if (s_playerYPos >= s_colCurLowestFloor && (!s_noclip || !s_flyMode)) { if (s_kyleScreamSoundId) @@ -2256,7 +2241,6 @@ namespace TFE_DarkForces s_postLandVel = min((s_playerUpVel >> 2) - ((s_playerUpVel - PLAYER_LAND_VEL_CHANGE) >> 3), PLAYER_LAND_VEL_MAX); } s_landUpVel = s_playerUpVel; - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_landUpVel = %d s_postLandVel = %d", s_landUpVel, s_postLandVel); } else { @@ -2269,7 +2253,6 @@ namespace TFE_DarkForces { player->worldHeight += (s_colCurBot - yPos + yMove); } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "ypos = %d ymove = %d distFromFloor = %d", yPos, yMove, distFromFloor); } s_playerYPos = s_colCurLowestFloor; @@ -2277,7 +2260,6 @@ namespace TFE_DarkForces fixed16_16 newUpVel = min(0, s_playerUpVel2); s_playerUpVel = newUpVel; s_playerUpVel2 = newUpVel; - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d", s_playerUpVel); } else @@ -2293,7 +2275,6 @@ namespace TFE_DarkForces s_playerYPos = min(s_colCurLowestFloor, newPlayerBot); player->posWS.y = s_playerYPos; } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_playerUpVel = %d s_playerYPos = %d", s_playerUpVel, s_playerYPos); } // Crouch @@ -2312,14 +2293,12 @@ namespace TFE_DarkForces { s_landUpVel = 0; } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "s_landUpVel = %d s_postLandVel = %d", s_landUpVel, s_postLandVel); fixed16_16 eyeToCeil = max(0, player->posWS.y - s_colCurHighestCeil); fixed16_16 eyeHeight = eyeToCeil; eyeToCeil = min(ONE_16, eyeToCeil); // the player eye should be clamped to 1 unit below the ceiling if possible. eyeHeight -= eyeToCeil; eyeHeight = min(PLAYER_HEIGHT, eyeHeight); - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "eyeToCeil = %d eyeHeight = %d", eyeToCeil, eyeHeight); RSector* sector = player->sector; fixed16_16 minEyeDistFromFloor = (s_smallModeEnabled) ? PLAYER_SIZE_SMALL : s_minEyeDistFromFloor; secHeight = sector->secHeight; @@ -2344,7 +2323,6 @@ namespace TFE_DarkForces player->worldHeight = minDistToFloor; } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->worldHeight = %d", player->worldHeight); // Make sure eye height is clamped. if (!s_noclip || !s_flyMode) { @@ -2505,7 +2483,7 @@ namespace TFE_DarkForces } } - TFE_System::logWrite(LOG_MSG, "PLAYER move ", "player->posWS.y = %d, s_playerYPos = %d player->worldHeight = %d", player->posWS.y, s_playerYPos, player->worldHeight); + weapon->rollOffset = -s_playerRoll / 13; weapon->pchOffset = s_playerPitch / 64; diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index 414040d9e..be72aafeb 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -25,26 +25,6 @@ namespace TFE_DarkForces SERIALIZE_BUF(SaveVersionInit, s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(s_frameTicks)); } - void resetTime() - { - s_curTick = 0; - s_prevTick = 0; - s_timeAccum = 0.0; - s_deltaTime; - s_frameTicks[13] = { 0 }; - s_pauseTimeUpdate = JFALSE; - } - - void setTimeAccum(f64 timeAccum) - { - s_timeAccum = timeAccum; - } - - f64 getTimeAccum() - { - return s_timeAccum; - } - Tick time_frameRateToDelay(u32 frameRate) { return Tick(SECONDS_TO_TICKS_ROUNDED / f32(frameRate)); @@ -71,44 +51,26 @@ namespace TFE_DarkForces if (!s_pauseTimeUpdate) { s_timeAccum += TFE_System::getDeltaTime() * TIMER_FREQ; - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME ACCUM %u %d", s_timeAccum, s_timeAccum); - } - Tick prevTick = s_curTick; + Tick prevTick = s_curTick; + + // Record Replay Ticks if (TFE_Input::isRecording()) { TFE_Input::saveTick(); - /*if (TFE_Input::getCounter() == 0) - { - resetTime(); - }*/ - // std::tuple < Tick, Tick, f64, fixed16_16> tData = { s_curTick, prevTick, s_timeAccum, s_deltaTime}; - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "TIME curtick = %d prevtick = %d accum = %d delta = %d", s_curTick, s_prevTick, s_timeAccum, s_deltaTime); - // TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK %u %d", s_curTick, s_curTick); - //TFE_Input::saveTick(tData); } + // Either Playback Ticks or use the current time. if (TFE_Input::isDemoPlayback()) { TFE_Input::loadTick(); - - //std::tuple < Tick, Tick, f64, fixed16_16> tData = TFE_Input::loadTick(); - //s_curTick = std::get<0>(tData); - /*prevTick = std::get<1>(tData); - s_timeAccum = std::get<2>(tData); - s_deltaTime = std::get<3>(tData);*/ - - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "LOADTICK %u %d", s_curTick, s_curTick); - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "REPLAYTICK %u %d", replayTick, replayTick); } else { s_curTick = Tick(s_timeAccum); } - TFE_System::logWrite(LOG_MSG, "TIME", "Update Tick s_curTick = %d prevTick = %d s_=revTick = %d updateCounter = %d", s_curTick, prevTick, s_prevTick, TFE_Input::getCounter()); fixed16_16 dt = div16(intToFixed16(s_curTick - prevTick), FIXED(TICKS_PER_SECOND)); - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "DT %u %d", dt, dt); for (s32 i = 0; i < 13; i++) { s_frameTicks[i] += mul16(dt, intToFixed16(i)); diff --git a/TheForceEngine/TFE_DarkForces/time.h b/TheForceEngine/TFE_DarkForces/time.h index fe99b3380..9045c6f1a 100644 --- a/TheForceEngine/TFE_DarkForces/time.h +++ b/TheForceEngine/TFE_DarkForces/time.h @@ -54,8 +54,5 @@ namespace TFE_DarkForces Tick time_frameRateToDelay(f32 frameRate); void updateTime(); void time_pause(JBool pause); - void setTimeAccum(f64 timeAccum); - f64 getTimeAccum(); - void resetTime(); void time_serialize(Stream* stream); } // namespace TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp index 424db3e51..8286ef94d 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp @@ -68,6 +68,7 @@ namespace TFE_FrontEndUI CONFIG_GAME, CONFIG_SAVE, CONFIG_LOAD, + CONFIG_REPLAY, CONFIG_INPUT, CONFIG_GRAPHICS, CONFIG_HUD, @@ -96,6 +97,7 @@ namespace TFE_FrontEndUI "Game Settings", "Save", "Load", + "Replay", "Input", "Graphics", "Hud", @@ -197,6 +199,7 @@ namespace TFE_FrontEndUI static ConfigTab s_configTab; static bool s_modLoaded = false; static bool s_saveLoadSetupRequired = true; + static bool s_replaySetupRequired = true; static bool s_bindingPopupOpen = false; static bool s_consoleActive = false; @@ -253,6 +256,7 @@ namespace TFE_FrontEndUI bool configEnhancements(); void configSave(); void configLoad(); + void configReplay(); void configInput(); void configGraphics(); void configHud(); @@ -265,6 +269,7 @@ namespace TFE_FrontEndUI void manual(); void credits(); + void setupReplay(); void configSaveLoadBegin(bool save); void renderBackground(bool forceTextureUpdate = false); void Tooltip(const char* text); @@ -555,6 +560,11 @@ namespace TFE_FrontEndUI s_game = game; } + IGame* getCurrentGame() + { + return s_game; + } + void draw(bool drawFrontEnd, bool noGameData, bool setDefaults, bool showFps) { const u32 windowInvisFlags = ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings; @@ -832,6 +842,13 @@ namespace TFE_FrontEndUI TFE_Settings::writeToDisk(); inputMapping_serialize(); } + if (ImGui::Button("Replay", sideBarButtonSize)) + { + s_configTab = CONFIG_REPLAY; + setupReplay(); + TFE_Settings::writeToDisk(); + inputMapping_serialize(); + } if (ImGui::Button("Input", sideBarButtonSize)) { s_configTab = CONFIG_INPUT; @@ -933,6 +950,9 @@ namespace TFE_FrontEndUI case CONFIG_LOAD: configLoad(); break; + case CONFIG_REPLAY: + configReplay(); + break; case CONFIG_INPUT: configInput(); break; @@ -1861,6 +1881,238 @@ namespace TFE_FrontEndUI { saveLoadDialog(false); } + + + + /////////////////////////////////////////////////////////////////////////////// + // Replay Demo UI + /////////////////////////////////////////////////////////////////////////////// + + static std::vector s_replayDir; + static TextureGpu* s_replayImageView = nullptr; + static s32 s_selectedReplay = -1; + static s32 s_selectedReplaySlot = -1; + static char s_replayFileName[TFE_MAX_PATH]; + static char s_replayGameConfirmMsg[TFE_MAX_PATH]; + static char s_newReplayName[256]; + static char s_modReplayName[256]; + static char s_levelReplayName[256]; + + void clearReplayImage() + { + u32 zero[TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT]; + memset(zero, 0, sizeof(u32) * TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT); + s_replayImageView->update(zero, TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT * 4); + } + + void updateReplayImage(s32 index) + { + s_replayImageView->update(s_replayDir[index].imageData, TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT * 4); + } + + void openReplayConfirmPopup() + { + sprintf(s_replayGameConfirmMsg, "Load '%s'?###SaveConfirm", s_newReplayName); + ImGui::OpenPopup(s_replayGameConfirmMsg); + s_popupOpen = true; + s_popupSetFocus = true; + } + + void closeReplayEditPopup() + { + s_popupOpen = false; + s_popupSetFocus = false; + ImGui::CloseCurrentPopup(); + } + + + void replayDialog() + { + if (s_replaySetupRequired) + { + setupReplay(); + } + + // Create the current display info to adjust menu sizes. + DisplayInfo displayInfo; + TFE_RenderBackend::getDisplayInfo(&displayInfo); + + f32 leftColumn = displayInfo.width < 1200 ? 196.0f * s_uiScale : 256.0f * s_uiScale; + f32 rightColumn = leftColumn + ((f32)TFE_SaveSystem::SAVE_IMAGE_WIDTH + 32.0f) * s_uiScale; + const s32 listOffset = 0; + + // Left Column + ImGui::SetNextWindowPos(ImVec2(leftColumn, floorf(displayInfo.height * 0.25f))); + ImGui::BeginChild("##ImageAndInfo"); + { + // Image + ImVec2 size((f32)TFE_SaveSystem::SAVE_IMAGE_WIDTH, (f32)TFE_SaveSystem::SAVE_IMAGE_HEIGHT); + size.x *= s_uiScale; + size.y *= s_uiScale; + + ImGui::Image(TFE_RenderBackend::getGpuPtr(s_replayImageView), size, + ImVec2(0, 0), ImVec2(1, 1), ImVec4(1, 1, 1, 1), ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + + // Info + size.y = 140 * s_uiScale; + ImGui::BeginChild("##InfoWithBorder", size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + { + if (!s_replayDir.empty()) + { + ImGui::PushFont(s_dialogFont); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + + char textBuffer[TFE_MAX_PATH]; + sprintf(textBuffer, "Game: Dark Forces\nTime: %s\nLevel: %s\nMods: %s\n", s_replayDir[s_selectedReplay - listOffset].dateTime, + s_replayDir[s_selectedReplay - listOffset].levelName, s_replayDir[s_selectedReplay - listOffset].modNames); + ImGui::InputTextMultiline("##Info", textBuffer, strlen(textBuffer) + 1, size, ImGuiInputTextFlags_ReadOnly); + + ImGui::PopFont(); + ImGui::PopStyleColor(); + } + else + { + ImGui::PushFont(s_dialogFont); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + + char textBuffer[TFE_MAX_PATH]; + sprintf(textBuffer, "Game: Dark Forces\nTime:\n\nLevel:\nMods:\n"); + ImGui::InputTextMultiline("##Info", textBuffer, strlen(textBuffer) + 1, size, ImGuiInputTextFlags_ReadOnly); + + ImGui::PopFont(); + ImGui::PopStyleColor(); + } + } + ImGui::EndChild(); + } + ImGui::EndChild(); + + // Right Column + f32 rwidth = 1024.0f * s_uiScale; + if (rightColumn + rwidth > displayInfo.width - 64.0f) + { + rwidth = displayInfo.width - 64.0f - rightColumn; + } + + f32 rheight = displayInfo.height - 80.0f; + ImGui::SetNextWindowPos(ImVec2(rightColumn, 64.0f * s_uiScale)); + ImGui::BeginChild("##FileList", ImVec2(rwidth, rheight), true); + { + const size_t count = s_replayDir.size() + size_t(listOffset); + const TFE_SaveSystem::SaveHeader* header = s_replayDir.data(); + ImVec2 buttonSize(rwidth - 2.0f, floorf(20 * s_uiScale + 0.5f)); + ImGui::PushFont(s_dialogFont); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + s32 prevSelected = s_selectedReplay; + for (size_t i = 0; i < count; i++) + { + bool selected = i == s_selectedReplay; + const char* replayName; + replayName = header[i - listOffset].saveName; + + if (ImGui::Selectable(replayName, selected, ImGuiSelectableFlags_None, buttonSize)) + { + if (!s_popupOpen) + { + s_selectedReplay = s32(i); + s_selectedReplaySlot = s_selectedReplay - listOffset; + bool shouldExit = false; + strcpy(s_replayFileName, s_replayDir[s_selectedReplaySlot].fileName); + strcpy(s_newReplayName, s_replayDir[s_selectedReplaySlot].saveName); + strcpy(s_modReplayName, s_replayDir[s_selectedReplaySlot].modNames); + strcpy(s_levelReplayName, s_replayDir[s_selectedReplaySlot].levelName); + openReplayConfirmPopup(); + if (shouldExit) + { + exitLoadSaveMenu(); + } + } + } + else if ((ImGui::IsItemHovered() || ImGui::IsItemClicked()) && !s_popupOpen) + { + s_selectedReplay = s32(i); + } + } + if (prevSelected != s_selectedReplay) + { + s32 index = s_selectedReplay - listOffset; + if (index >= 0) + { + updateReplayImage(s_selectedReplay - listOffset); + } + else + { + clearReplayImage(); + } + } + prevSelected = s_selectedReplay; + if (ImGui::BeginPopupModal(s_replayGameConfirmMsg, NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + bool shouldExit = false; + if (s_popupSetFocus) + { + ImGui::SetKeyboardFocusHere(); + s_popupSetFocus = false; + } + ImGui::SetNextItemWidth(768 * s_uiScale); + if (TFE_Input::keyPressed(KEY_RETURN)) + { + shouldExit = true; + loadReplayWrapper(s_replayFileName, s_modReplayName, s_levelReplayName); + } + if (ImGui::Button("OK", ImVec2(120, 0))) + { + shouldExit = true; + loadReplayWrapper(s_replayFileName, s_modReplayName, s_levelReplayName); + } + ImGui::SameLine(0.0f, 32.0f); + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + shouldExit = false; + closeSaveNameEditPopup(); + } + ImGui::EndPopup(); + + if (shouldExit) + { + exitLoadSaveMenu(); + } + } + ImGui::PopFont(); + ImGui::PopStyleColor(); + } + ImGui::EndChild(); + s_selectedReplaySlot = s_selectedReplay - listOffset; + } + + void configReplay() + { + replayDialog(); + } + + void setupReplay() + { + s_selectedReplay = 0; + + if (!s_replayImageView) + { + s_replayImageView = TFE_RenderBackend::createTexture(TFE_SaveSystem::SAVE_IMAGE_WIDTH, TFE_SaveSystem::SAVE_IMAGE_HEIGHT, TexFormat::TEX_RGBA8); + } + TFE_Input::populateReplayDirectory(s_replayDir); + + if (!s_replayDir.empty()) + { + updateReplayImage(s_selectedReplay); + } + else + { + clearReplayImage(); + } + + s_popupOpen = false; + s_replaySetupRequired = false; + s_popupSetFocus = false; + } void getBindingString(InputBinding* binding, char* inputName) { diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.h b/TheForceEngine/TFE_FrontEndUI/frontEndUi.h index ded814702..90511a4ee 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.h +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.h @@ -36,6 +36,7 @@ namespace TFE_FrontEndUI AppState update(); void draw(bool drawFrontEnd = true, bool noGameData = false, bool setDefaults = false, bool showFps = false); void setCurrentGame(IGame* game); + IGame* getCurrentGame(); void setAppState(AppState state); void enableConfigMenu(); diff --git a/TheForceEngine/TFE_Game/saveSystem.h b/TheForceEngine/TFE_Game/saveSystem.h index c04aef272..77176b285 100644 --- a/TheForceEngine/TFE_Game/saveSystem.h +++ b/TheForceEngine/TFE_Game/saveSystem.h @@ -35,6 +35,9 @@ namespace TFE_SaveSystem // Load only the header for UI. bool loadGameHeader(const char* filename, SaveHeader* header); + void saveHeader(Stream* stream, const char* saveName); + void loadHeader(Stream* stream, SaveHeader* header, const char* fileName); + void postLoadRequest(const char* filename); void postSaveRequest(const char* filename, const char* saveName, s32 delay = 0); const char* loadRequestFilename(); diff --git a/TheForceEngine/TFE_Input/input.cpp b/TheForceEngine/TFE_Input/input.cpp index 038b90c73..34dac5116 100644 --- a/TheForceEngine/TFE_Input/input.cpp +++ b/TheForceEngine/TFE_Input/input.cpp @@ -108,8 +108,7 @@ namespace TFE_Input void setKeyDown(KeyboardCode key, bool repeat) { - const char* boolStr = repeat ? "true" : "false"; - // TFE_System::logWrite(LOG_MSG, "Input", "DOWN '%d' repeat = %s", key, boolStr); + if (!s_keyDown[key] && !repeat) { s_keyPressed[key] = 1; diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index 36389b280..2b15ee1d8 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -689,6 +689,7 @@ namespace TFE_Input void handleInputs() { // Allow escape during playback + if (keyPressed(KEY_ESCAPE)) { if (isDemoPlayback()) @@ -721,18 +722,7 @@ namespace TFE_Input - if (inputCounter == 0 && TFE_DarkForces::s_playerEye) - { - - if (isRecording()) - { - recordEye(); - } - if (isDemoPlayback()) - { - setEye(); - } - } + std::vector mousePos; s32 mouseX, mouseY; s32 mouseAbsX, mouseAbsY; @@ -812,16 +802,8 @@ namespace TFE_Input TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYSTART ======================================"); } } - /* - if (isRecording() && inputCounter == 61) - { - recordEye(); - } - if (isDemoPlayback() && inputCounter == 62) - { - setEye(); - }*/ + handleEye(); ReplayEvent event; if (isDemoPlayback()) @@ -862,11 +844,11 @@ namespace TFE_Input if (mouse.size() != 0) hudDataStr += " Mouse: " + mouse; if (mouseP.size() != 0) hudDataStr += " MousePos " + mouseP; - TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), inputCounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); + //TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), inputCounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); } - TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); + //TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); inputCounter++; /* if (TFE_DarkForces::s_playerEye) diff --git a/TheForceEngine/TFE_Input/replay.cpp b/TheForceEngine/TFE_Input/replay.cpp index ee69d94d1..fb67af6a3 100644 --- a/TheForceEngine/TFE_Input/replay.cpp +++ b/TheForceEngine/TFE_Input/replay.cpp @@ -18,28 +18,30 @@ #include #include #include +#include +#include +#include namespace TFE_Input { + // Handle replay File Paths + FileStream s_replayFile; static char s_replayDir[TFE_MAX_PATH]; static char s_replayPath[TFE_MAX_PATH]; - FileStream s_replayFile; + + // Recording States static bool s_recording = false; static bool s_playback = false; - static Tick replayCurTick; - static Tick replayPrevTick; - static f64 replayTimeAccum = 0.0; - static fixed16_16 replayDeltaTime; - static fixed16_16 replayFrameTicks[13] = { 0 }; + static bool eyeSet = false; + angle14_16 r_yaw = 0; angle14_16 r_pitch = 0; angle14_16 r_roll = 0; - + // Main storage of event structures. std::unordered_map inputEvents; - u64 s_startTime = 0; + u64 replayStartTime = 0; s32 replay_seed = 0; - int tickCounter = 0; void initReplays() { @@ -76,27 +78,7 @@ namespace TFE_Input void saveTick() { int inputCounter = getCounter(); - - if (TFE_DarkForces::s_curTick == 0) - { - int x = 0; - } - - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "SAVETICK curtick = %d prevtick = %d accum = %d delta = %d", get<0>(tData), get<1>(tData), get<2>(tData), get<3>(tData)); inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; - /* - if (inputCounter > 0) - { - inputEvents[inputCounter - 1].curTick = TFE_DarkForces::s_curTick; - } - else - { - inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; - }*/ - //inputEvents[inputCounter].prevTick = TFE_DarkForces::s_prevTick; - //inputEvents[inputCounter].deltaTime = TFE_DarkForces::s_deltaTime; - //inputEvents[inputCounter].timeAccum = TFE_DarkForces::getTimeAccum(); - //memcpy(inputEvents[inputCounter].frameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); } @@ -104,11 +86,7 @@ namespace TFE_Input void loadTick() { int inputCounter = getCounter(); - TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; - //TFE_DarkForces::s_prevTick = inputEvents[inputCounter].prevTick; - //TFE_DarkForces::setTimeAccum(inputEvents[inputCounter].timeAccum); - //TFE_DarkForces::s_deltaTime = inputEvents[inputCounter].deltaTime; - //memcpy(TFE_DarkForces::s_frameTicks, inputEvents[inputCounter].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[inputCounter].frameTicks)); + TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; } void recordReplaySeed() @@ -124,6 +102,15 @@ namespace TFE_Input TFE_System::logWrite(LOG_MSG, "REPLAY", "Loading Seed: %d", replay_seed); } + void clearEvents() + { + int iSize = inputEvents.size(); + for (int i = 0; i < iSize; i++) + { + inputEvents[i].clear(); + } + inputEvents.clear(); + } string convertToString(vector keysDown) { @@ -176,6 +163,39 @@ namespace TFE_Input return inputList; } + bool loadReplayHeader(const char* filename, TFE_SaveSystem::SaveHeader* header) + { + char filePath[TFE_MAX_PATH]; + sprintf(filePath, "%s%s", s_replayDir, filename); + + bool ret = false; + FileStream stream; + if (stream.open(filePath, Stream::MODE_READ)) + { + loadHeader(&stream, header, filename); + strcpy(header->fileName, filename); + stream.close(); + ret = true; + } + return ret; + } + + void populateReplayDirectory(std::vector& dir) + { + dir.clear(); + FileList fileList; + FileUtil::readDirectory(s_replayDir, "demo", fileList); + size_t saveCount = fileList.size(); + dir.resize(saveCount); + + const std::string* filenames = fileList.data(); + TFE_SaveSystem::SaveHeader* headers = dir.data(); + for (size_t i = 0; i < saveCount; i++) + { + loadReplayHeader(filenames[i].c_str(), &headers[i]); + } + } + void serializeDemo(Stream* stream, bool writeFlag) { int fileHandler = 0; @@ -185,25 +205,27 @@ namespace TFE_Input { serialization_setMode(SMODE_WRITE); fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_WRITE); + replayStartTime = TFE_System::getStartTime(); + TFE_SaveSystem::saveHeader(stream, "TEST"); + + //void saveHeader(Stream * stream, const char* saveName); + //void loadHeader(Stream * stream, SaveHeader * header, const char* fileName); } else { serialization_setMode(SMODE_READ); fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_READ); - inputEvents.clear(); + clearEvents(); + TFE_SaveSystem::SaveHeader * header = new TFE_SaveSystem::SaveHeader(); + TFE_SaveSystem::loadHeader(stream, header, "TEST"); } - - - - if (fileHandler > 0) { SERIALIZE_VERSION(ReplayVersionInit); - SERIALIZE(ReplayVersionInit, s_startTime, 0); + SERIALIZE(ReplayVersionInit, replayStartTime, 0); SERIALIZE(ReplayVersionInit, replay_seed, 0); - u32 plTick = TFE_DarkForces::s_playerTick; u32 plPrevTick = TFE_DarkForces::s_prevPlayerTick; SERIALIZE(ReplayVersionInit, plTick, 0); @@ -232,25 +254,10 @@ namespace TFE_Input serializeInputs(stream, pair.second.keysPressed, writeFlag); serializeInputs(stream, pair.second.mouseDown, writeFlag); serializeInputs(stream, pair.second.mousePos, writeFlag); - - TFE_System::logWrite(LOG_MSG, "STORING", "upd = %d curtick = %d", updateCounter, inputEvents[updateCounter].curTick); - serializeInputs(stream, pair.second.mouseWheel, writeFlag); u32 curTick = pair.second.curTick; - // TFE_System::logWrite(LOG_MSG, "STORING", "STORING CURTIME %d COUNTER %d", curTick, updateCounter); - SERIALIZE(ReplayVersionInit, curTick, 0); - /* - std::tuple tData = pair.second.timeData; - u32 curTick = std::get<0>(tData); - u32 prevTick = std::get<1>(tData); - f64 timeAccum = std::get<2>(tData); - fixed16_16 deltaTime = std::get<3>(tData); - SERIALIZE(ReplayVersionInit, curTick, 0); - SERIALIZE(ReplayVersionInit, prevTick, 0); - SERIALIZE(ReplayVersionInit, timeAccum, 0.0); - SERIALIZE(ReplayVersionInit, deltaTime, 0);*/ - + SERIALIZE(ReplayVersionInit, curTick, 0); } } else @@ -272,24 +279,7 @@ namespace TFE_Input u32 curTick = 0; SERIALIZE(ReplayVersionInit, curTick, 0); - //TFE_System::logWrite(LOG_MSG, "LOADING", "LOADING CURTIME %d COUNTER %d", curTick, updateCounter); - event.curTick = curTick; - /* - u32 curTick; - u32 prevTick; - f64 timeAccum; - fixed16_16 deltaTime; - - SERIALIZE(ReplayVersionInit, curTick, 0); - SERIALIZE(ReplayVersionInit, prevTick, 0); - SERIALIZE(ReplayVersionInit, timeAccum, 0.0); - SERIALIZE(ReplayVersionInit, deltaTime, 0); - - - event.curTick = curTick; - std::tuple tData = std::make_tuple(curTick, prevTick, timeAccum, deltaTime); - event.timeData = tData; - */ + event.curTick = curTick; inputEvents[updateCounter] = event; } resetCounter(); @@ -301,31 +291,29 @@ namespace TFE_Input if (!writeFlag) { - TFE_System::setStartTime(s_startTime); - TFE_System::logWrite(LOG_MSG, "PLAYBACK START DATE", "INPUT UPDATE TIME %d MAX COUNT %d", s_startTime, updateCounter); - } - else - { - TFE_System::logWrite(LOG_MSG, "RECORD START DATE", "INPUT UPDATE TIME %d", s_startTime); + TFE_System::setStartTime(replayStartTime); } TFE_DarkForces::time_pause(JFALSE); } - void recordEye() + void handleEye() { - r_yaw = TFE_DarkForces::s_playerEye->yaw; - r_pitch = TFE_DarkForces::s_playerEye->pitch; - r_roll = TFE_DarkForces::s_playerEye->roll; - TFE_System::logWrite(LOG_MSG, "REPLAY", "Record yaw=%d pitch=%d roll=%d", r_yaw, r_pitch, r_roll); - - } - - void setEye() - { - TFE_DarkForces::s_playerEye->yaw = r_yaw; - TFE_DarkForces::s_playerEye->pitch = r_pitch; - TFE_DarkForces::s_playerEye->roll = r_roll; - TFE_System::logWrite(LOG_MSG, "REPLAY", "Set yaw=%d pitch=%d roll=%d", r_yaw, r_pitch, r_roll); + if (!eyeSet) + { + if (isRecording()) + { + r_yaw = TFE_DarkForces::s_playerEye->yaw; + r_pitch = TFE_DarkForces::s_playerEye->pitch; + r_roll = TFE_DarkForces::s_playerEye->roll; + } + if (isDemoPlayback()) + { + TFE_DarkForces::s_playerEye->yaw = r_yaw; + TFE_DarkForces::s_playerEye->pitch = r_pitch; + TFE_DarkForces::s_playerEye->roll = r_roll; + } + eyeSet = true; + } } void startRecording() @@ -334,16 +322,10 @@ namespace TFE_Input { FileUtil::deleteFile(s_replayPath); } - int iSize = inputEvents.size(); - for (int i = 0; i < iSize; i++) - { - inputEvents[i].clear(); - - } - inputEvents.clear(); + eyeSet = false; + clearEvents(); recordReplaySeed(); inputMapping_endFrame(); - //TFE_DarkForces::resetTime(); s_recording = true; setDemoPlayback(false); resetCounter(); @@ -356,33 +338,41 @@ namespace TFE_Input s_recording = false; FileStream* stream = &s_replayFile; serializeDemo(&s_replayFile, true); - setDemoPlayback(false); inputMapping_endFrame(); } void recordReplayTime(u64 startTime) { - s_startTime = startTime; + replayStartTime = startTime; + } + + void loadReplayWrapper(string replayFile, string modName, string levelName) + { + string x = replayFile; + string y = modName; + string z = levelName; + TFE_Settings::getGameSettings()->df_enableRecording = true; + //IGame* curGame = TFE_FrontEndUI::getCurrentGame(); + //curGame->enable Cutscenes(JFALSE); + TFE_DarkForces::enableCutscenes(JFALSE); + } void loadReplay() { if (TFE_Settings::getGameSettings()->df_enableRecording) return; - + eyeSet = false; + clearEvents(); resetCounter(); setCounter(1); - restoreReplaySeed(); inputMapping_endFrame(); - //TFE_DarkForces::resetTime(); FileStream* stream = &s_replayFile; serializeDemo(&s_replayFile, false); + restoreReplaySeed(); setDemoPlayback(true); - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK BEFORE %d", TFE_DarkForces::s_curTick); TFE_DarkForces::s_curTick = inputEvents[0].curTick; TFE_DarkForces::s_prevTick = inputEvents[0].prevTick; - //TFE_DarkForces::setTimeAccum(inputEvents[1].timeAccum); TFE_DarkForces::s_deltaTime = inputEvents[0].deltaTime; - memcpy(TFE_DarkForces::s_frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); - //TFE_System::logWrite(LOG_MSG, "Progam Flow", "STARTTICK AFTER %d", TFE_DarkForces::s_curTick); + //memcpy(TFE_DarkForces::s_frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); } } \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.h b/TheForceEngine/TFE_Input/replay.h index c054e619d..425fdd8f9 100644 --- a/TheForceEngine/TFE_Input/replay.h +++ b/TheForceEngine/TFE_Input/replay.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace TFE_Input { @@ -42,7 +43,6 @@ namespace TFE_Input deltaTime = 0; timeAccum = 0.0; std::fill(std::begin(frameTicks), std::end(frameTicks), 0); - //timeData = std::make_tuple(0, 0, 0.0, 0); // Reset tuple values } }; @@ -53,7 +53,9 @@ namespace TFE_Input void startRecording(); void endRecording(); void loadReplay(); - + void populateReplayDirectory(std::vector& dir); + bool loadReplayHeader(const char* filename, TFE_SaveSystem::SaveHeader* header); + void loadReplayWrapper(string replayFile, string modName, string levelName); bool isRecording(); void setRecording(bool recording); bool isDemoPlayback(); @@ -62,8 +64,7 @@ namespace TFE_Input void restoreReplaySeed(); void saveTick(); void loadTick(); - void recordEye(); - void setEye(); + void handleEye(); std::string convertToString(std::vector keysDown); std::vector convertFromString(std::string keyStr); diff --git a/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp b/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp index 5d4a5e7dc..250160b07 100644 --- a/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp +++ b/TheForceEngine/TFE_Jedi/InfSystem/infSystem.cpp @@ -3476,8 +3476,7 @@ namespace TFE_Jedi sector_adjustTextureMirrorOffsets_Floor(sector, floorDelta); } sector_adjustHeights(sector, floorDelta, ceilDelta, secHeightDelta); - TFE_System::logWrite(LOG_MSG, "INF", "sector = %d, floorDelta = %d, ceilDelta = %d, secHeightDelta = %d", sector->id, floorDelta, ceilDelta, secHeightDelta); - + // Apply adjustments to "slave" sectors. Slave* child = (Slave*)allocator_getHead(elev->slaves); while (child) diff --git a/TheForceEngine/TFE_Jedi/Task/task.cpp b/TheForceEngine/TFE_Jedi/Task/task.cpp index 3976dcefd..7605485b9 100644 --- a/TheForceEngine/TFE_Jedi/Task/task.cpp +++ b/TheForceEngine/TFE_Jedi/Task/task.cpp @@ -621,6 +621,10 @@ namespace TFE_Jedi { TASK_MSG("Current Task: '%s'.", s_curTask->name); JBool framebreak = s_curTask->framebreak; + if (s_curTask->name == "actor") + { + TFE_System::logWrite(LOG_WARNING, "Task", "Curtask nextTick = %d curtick = %d", s_curTask->nextTick, s_curTick); + } // This should only be false when hitting the "framebreak" task which is sleeping. if (s_curTask->nextTick <= s_curTick) From c6471789310b6c769106ee65ba3d01c7c890ff3a Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Wed, 19 Feb 2025 19:49:09 -0500 Subject: [PATCH 05/64] Add replay POC --- TheForceEngine/TFE_DarkForces/Actor/actor.cpp | 25 +- .../TFE_DarkForces/Actor/mousebot.cpp | 5 + .../TFE_DarkForces/Actor/mousebot.h | 1 + .../TFE_DarkForces/Actor/turret.cpp | 5 + TheForceEngine/TFE_DarkForces/Actor/turret.h | 1 + .../TFE_DarkForces/GameUI/agentMenu.cpp | 29 + .../TFE_DarkForces/GameUI/agentMenu.h | 7 + TheForceEngine/TFE_DarkForces/GameUI/menu.cpp | 28 +- TheForceEngine/TFE_DarkForces/GameUI/pda.cpp | 45 +- TheForceEngine/TFE_DarkForces/GameUI/pda.h | 1 + TheForceEngine/TFE_DarkForces/agent.h | 5 +- .../TFE_DarkForces/darkForcesMain.cpp | 33 +- .../TFE_DarkForces/darkForcesMain.h | 4 + TheForceEngine/TFE_DarkForces/hud.cpp | 18 +- TheForceEngine/TFE_DarkForces/hud.h | 2 +- TheForceEngine/TFE_DarkForces/mission.cpp | 17 +- TheForceEngine/TFE_DarkForces/mission.h | 1 + TheForceEngine/TFE_DarkForces/player.cpp | 22 +- .../TFE_DarkForces/playerCollision.cpp | 11 +- TheForceEngine/TFE_DarkForces/time.cpp | 2 +- .../TFE_DarkForces/weaponFireFunc.cpp | 7 + .../TFE_DarkForces/weaponFireFunc.h | 1 + TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp | 341 +++++-- TheForceEngine/TFE_FrontEndUI/frontEndUi.h | 6 + TheForceEngine/TFE_FrontEndUI/modLoader.cpp | 14 + TheForceEngine/TFE_FrontEndUI/modLoader.h | 1 + TheForceEngine/TFE_Game/igame.h | 1 + TheForceEngine/TFE_Game/saveSystem.cpp | 40 +- TheForceEngine/TFE_Game/saveSystem.h | 6 + TheForceEngine/TFE_Input/input.cpp | 4 +- TheForceEngine/TFE_Input/inputMapping.cpp | 522 +++++----- TheForceEngine/TFE_Input/inputMapping.h | 17 +- TheForceEngine/TFE_Input/replay.cpp | 904 +++++++++++++++--- TheForceEngine/TFE_Input/replay.h | 65 +- TheForceEngine/TFE_Settings/settings.cpp | 60 +- TheForceEngine/TFE_Settings/settings.h | 8 +- TheForceEngine/TFE_System/frameLimiter.cpp | 7 +- TheForceEngine/TFE_System/frameLimiter.h | 1 + TheForceEngine/main.cpp | 67 +- 39 files changed, 1716 insertions(+), 618 deletions(-) diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp index a7a1862ce..70d84a9e3 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp @@ -284,11 +284,10 @@ namespace TFE_DarkForces attackMod->fireSpread = FIXED(30); attackMod->accuracyNextTick = 0; attackMod->fireOffset.x = 0; - attackMod->fireOffset.z = 0; - + attackMod->fireOffset.z = 0; attackMod->target.flags &= ~TARGET_ALL; attackMod->anim.flags |= 3; - attackMod->timing.nextTick = s_curTick + 0x4446; // ~120 seconds + attackMod->timing.nextTick = s_curTick + 0x4446; // ~120 seconds SecObject* obj = attackMod->header.obj; // world width and height was set from the sprite data. @@ -427,7 +426,6 @@ namespace TFE_DarkForces { SecObject* obj = moveMod->header.obj; Tick delta = Tick(s_curTick - (*prevColTick)); - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "DELTA DIR CHANGE %d", delta); angle14_32 newAngle; if (delta < 145) { @@ -587,7 +585,6 @@ namespace TFE_DarkForces AttackModule* attackMod = &damageMod->attackMod; SecObject* obj = attackMod->header.obj; RSector* sector = obj->sector; - if (!(attackMod->anim.flags & 2)) { if (obj->type == OBJ_TYPE_SPRITE) @@ -671,7 +668,6 @@ namespace TFE_DarkForces AttackModule* attackMod = &damageMod->attackMod; SecObject* obj = attackMod->header.obj; RSector* sector = obj->sector; - if (msg == MSG_DAMAGE) { if (damageMod->hp > 0) @@ -809,7 +805,6 @@ namespace TFE_DarkForces } } } - return JFALSE; } @@ -845,7 +840,7 @@ namespace TFE_DarkForces SecObject* obj = attackMod->header.obj; LogicAnimation* anim = &attackMod->anim; s32 state = attackMod->anim.state; - + switch (state) { case STATE_DELAY: @@ -875,7 +870,7 @@ namespace TFE_DarkForces } attackMod->target.flags &= ~TARGET_ALL_MOVE; // Next AI update. - return s_curTick + random(attackMod->timing.delay); + return s_curTick + random(attackMod->timing.delay); } } break; case STATE_ANIMATEATTACK: @@ -1630,9 +1625,7 @@ namespace TFE_DarkForces if (approxDist < FIXED(256)) { // Since random() is unsigned, the real visible range is [200, 256) because of the conditional above. - // TFE_System::logWrite(LOG_MSG, "ACTOR", "ACTOR CALL RANDOM"); fixed16_16 rndDist = random(FIXED(256)) + FIXED(200); - //TFE_System::logWrite(LOG_MSG, "ACTOR", "ACTOR rndDist %d update = %d", rndDist, TFE_Input::getCounter()); if (approxDist < rndDist) { return actor_canSeeObject(actorObj, obj); @@ -1670,7 +1663,6 @@ namespace TFE_DarkForces actor_initModule((ActorModule*)moveMod, (Logic*)dispatch); actor_setupSmartObj(moveMod); - moveMod->header.func = defaultActorFunc; moveMod->header.freeFunc = nullptr; moveMod->header.type = ACTMOD_MOVE; @@ -1720,7 +1712,7 @@ namespace TFE_DarkForces JBool actor_advanceAnimation(LogicAnimation* anim, SecObject* obj) { - + if (!anim->prevTick) { anim->prevTick = s_frameTicks[anim->frameRate]; @@ -2080,10 +2072,11 @@ namespace TFE_DarkForces s_actorState.curAnimation = nullptr; for (s32 i = 0; i < ACTOR_MAX_MODULES; i++) { - ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; + ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; if (module && module->func && module->nextTick < s_curTick) { - module->nextTick = module->func(module, dispatch->moveMod); + Tick newtick = module->func(module, dispatch->moveMod); + module->nextTick = newtick; } } @@ -2096,7 +2089,7 @@ namespace TFE_DarkForces actor_handlePhysics(moveMod, &dispatch->vel); moveMod->header.func((ActorModule*)moveMod, moveMod); } - + if ((obj->type & OBJ_TYPE_SPRITE) && s_actorState.curAnimation) { obj->anim = s_actorState.curAnimation->animId; diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp index bb5e2e88d..d683521b3 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp @@ -48,6 +48,11 @@ namespace TFE_DarkForces static MouseBot* s_curMouseBot; static s32 s_mouseNum = 0; + void resetMouseNum() + { + s_mouseNum = 0; + } + MessageType mousebot_handleDamage(MessageType msg, MouseBot* mouseBot) { SecObject* obj = mouseBot->logic.obj; diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.h b/TheForceEngine/TFE_DarkForces/Actor/mousebot.h index 4b8a02b3a..65571d9fd 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.h +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.h @@ -11,6 +11,7 @@ namespace TFE_DarkForces { Logic* mousebot_setup(SecObject* obj, LogicSetupFunc* setupFunc); + void resetMouseNum(); void mousebot_clear(); void mousebot_exit(); void mousebot_precache(); diff --git a/TheForceEngine/TFE_DarkForces/Actor/turret.cpp b/TheForceEngine/TFE_DarkForces/Actor/turret.cpp index 3a43919c5..ddf28b0b0 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/turret.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/turret.cpp @@ -83,6 +83,11 @@ namespace TFE_DarkForces s_turretRes.sound1 = sound_load("TURRET-1.VOC", SOUND_PRIORITY_MED2); } + void resetTurretNum() + { + s_turretNum = 0; + } + MessageType turret_handleDamage(MessageType msg, Turret* turret) { PhysicsActor* physicsActor = &turret->actor; diff --git a/TheForceEngine/TFE_DarkForces/Actor/turret.h b/TheForceEngine/TFE_DarkForces/Actor/turret.h index 39b5a251b..baa1e7fdb 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/turret.h +++ b/TheForceEngine/TFE_DarkForces/Actor/turret.h @@ -9,6 +9,7 @@ namespace TFE_DarkForces { + void resetTurretNum(); void turret_exit(); void turret_precache(); Logic* turret_setup(SecObject* obj, LogicSetupFunc* setupFunc); diff --git a/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.cpp b/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.cpp index 82808c720..7a73b6ad0 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.cpp +++ b/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.cpp @@ -501,6 +501,35 @@ namespace TFE_DarkForces menu_blitToScreen(s_framebuffer); } + void setAgentName(const char* name) + { + if (s_agentId < 0 || s_agentId >= MAX_AGENT_COUNT) + { + return; + } + strncpy(s_agentData[s_agentId].name, name, 32); + } + + s32 getAgentID() + { + return s_agentId; + } + + void setAgentId(s32 id) + { + s_agentId = id; + } + + void setAgentCount(s32 count) + { + s_agentCount = count; + } + + s32 getAgentCount() + { + return s_agentCount; + } + void agentMenu_createNewAgent() { if (s_agentCount < MAX_AGENT_COUNT) diff --git a/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.h b/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.h index cb03e387c..93fd9a026 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.h +++ b/TheForceEngine/TFE_DarkForces/GameUI/agentMenu.h @@ -16,5 +16,12 @@ namespace TFE_DarkForces void agentMenu_load(LangHotkeys* hotkeys); // Reset Presistent State. + + s32 getAgentID(); + void setAgentId(s32 id); + void setAgentCount(s32 count); + s32 getAgentCount(); + void agentMenu_createNewAgent(); + void setAgentName(const char* name); void agentMenu_resetState(); } diff --git a/TheForceEngine/TFE_DarkForces/GameUI/menu.cpp b/TheForceEngine/TFE_DarkForces/GameUI/menu.cpp index 4f9ad4833..31868b128 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/menu.cpp +++ b/TheForceEngine/TFE_DarkForces/GameUI/menu.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace TFE_Jedi; @@ -62,16 +63,31 @@ namespace TFE_DarkForces s32 mx, my; TFE_Input::getMousePos(&mx, &my); s_cursorPosAccum = { 12*mx/10, my }; // Account for 320x200 in 4:3 scaling. - - if (displayInfo.width >= displayInfo.height) + + // Load the replay PDA positions. + if (TFE_Input::isDemoPlayback()) { - s_cursorPos.x = clamp(s_cursorPosAccum.x * (s32)height / (s32)displayInfo.height, 0, (s32)width - 3); - s_cursorPos.z = clamp(s_cursorPosAccum.z * (s32)height / (s32)displayInfo.height, 0, (s32)height - 3); + Vec2i pdap = TFE_Input::getPDAPosition(); + s_cursorPos = pdap; } else { - s_cursorPos.x = clamp(s_cursorPosAccum.x * (s32)width / (s32)displayInfo.width, 0, (s32)width - 3); - s_cursorPos.z = clamp(s_cursorPosAccum.z * (s32)width / (s32)displayInfo.width, 0, (s32)height - 3); + if (displayInfo.width >= displayInfo.height) + { + s_cursorPos.x = clamp(s_cursorPosAccum.x * (s32)height / (s32)displayInfo.height, 0, (s32)width - 3); + s_cursorPos.z = clamp(s_cursorPosAccum.z * (s32)height / (s32)displayInfo.height, 0, (s32)height - 3); + } + else + { + s_cursorPos.x = clamp(s_cursorPosAccum.x * (s32)width / (s32)displayInfo.width, 0, (s32)width - 3); + s_cursorPos.z = clamp(s_cursorPosAccum.z * (s32)width / (s32)displayInfo.width, 0, (s32)height - 3); + } + } + + // Store the PDA positions for replay. + if (TFE_Input::isRecording()) + { + TFE_Input::storePDAPosition(s_cursorPos); } } diff --git a/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp b/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp index 42a5a00f6..7a44ddb81 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp +++ b/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include using namespace TFE_Jedi; using namespace TFE_Input; @@ -126,7 +127,7 @@ namespace TFE_DarkForces // TFE reticle_enable(false); s_mouseAccum = { 0 }; - + mouseOldPosX = 0, mouseOldPosZ = 0; pauseLevelSound(); if (!s_pdaLoaded) { @@ -160,6 +161,7 @@ namespace TFE_DarkForces s_briefingMaxY = -BRIEF_VERT_MARGIN; } s_briefY = -BRIEF_VERT_MARGIN; + } s_overlayRect = s_pdaRect; @@ -169,7 +171,7 @@ namespace TFE_DarkForces } s_framebuffer = ldraw_getBitmap(); lcanvas_getBounds(&s_viewBounds); - + s_pdaArt = lactorAnim_load("pda", &s_viewBounds, 0, 0, 0); s_palette = lpalette_load("menu"); lactor_setTime(s_pdaArt, -1, -1); @@ -210,6 +212,12 @@ namespace TFE_DarkForces pda_resetState(); } + // Reset the tab to the map to support replay consistency. + void pda_resetTab() + { + s_pdaMode = PDA_MODE_MAP; + } + void pda_resetState() { s_pdaOpen = JFALSE; @@ -255,7 +263,10 @@ namespace TFE_DarkForces return; } - if (inputMapping_getActionState(IADF_PDA_TOGGLE) == STATE_PRESSED || TFE_Input::keyPressed(KEY_ESCAPE)) + // Special case to support Escape during demo playback + if (inputMapping_getActionState(IADF_PDA_TOGGLE) == STATE_PRESSED + || TFE_Input::keyPressed(KEY_ESCAPE) + || inputMapping_getAction(IADF_MENU_TOGGLE) == STATE_PRESSED) { pda_close(); return; @@ -277,10 +288,10 @@ namespace TFE_DarkForces u32 outWidth, outHeight; vfb_getResolution(&outWidth, &outHeight); memset(vfb_getCpuBuffer(), 0, outWidth * outHeight); - + // Draw the overlay *behind* the main view - which means we must blit it to the view. pda_drawOverlay(); - + // Finally draw the PDA background and UI controls. lcanvas_eraseRect(&s_viewBounds); lactor_setState(s_pdaArt, 0, 0); @@ -351,12 +362,16 @@ namespace TFE_DarkForces // Ensure that mouse movements are not lost between updates. pda_handleMouseAccum(&wdx, &wdy); + // Ensure PDA key presses handle replay data + bool mouseDown = TFE_Input::isDemoPlayback() && inputMapping_getAction(IADF_PRIMARY_FIRE) == STATE_DOWN || TFE_Input::mouseDown(MBUTTON_LEFT); + bool mousePressed = TFE_Input::isDemoPlayback() && inputMapping_getAction(IADF_PRIMARY_FIRE) == STATE_PRESSED || TFE_Input::mousePressed(MBUTTON_LEFT); + // Handle Mouse Panning - if (TFE_Input::mouseDown(MBUTTON_LEFT)) + if (mouseDown) { s32 mouseX, mouseZ; TFE_Input::getMousePos(&mouseX, &mouseZ); - + // Calculate deltas fixed16_16 deltaX = (mouseX - mouseOldPosX) << 16; fixed16_16 deltaZ = (mouseZ - mouseOldPosZ) << 16; @@ -377,7 +392,7 @@ namespace TFE_DarkForces mouseMoveReset = true; } - if (TFE_Input::mousePressed(MBUTTON_LEFT) || s_simulatePressed >= 0 || wdy) + if (s_simulatePressed >= 0 || wdy || mousePressed) { s_buttonPressed = -1; s32 count = (s_pdaMode == PDA_MODE_MAP || s_pdaMode == PDA_MODE_BRIEF) ? PDA_BTN_COUNT : PDA_BTN_EXIT + 1; @@ -386,8 +401,8 @@ namespace TFE_DarkForces lactor_setState(s_pdaArt, 2 * (1 + i), 0); LRect buttonRect; lactorAnim_getFrame(s_pdaArt, &buttonRect); - - if (!TFE_Input::mousePressed(MBUTTON_LEFT)) + + if (!TFE_Input::mousePressed(MBUTTON_LEFT) && !mousePressed) { if (i == s_simulatePressed) { @@ -408,7 +423,7 @@ namespace TFE_DarkForces } } - if (s_pdaMode == PDA_MODE_MAP && (s_buttonPressed || wdy) && s_frameReady) + if (s_pdaMode == PDA_MODE_MAP && (s_buttonPressed || wdy || mousePressed) && s_frameReady) { // Add support for mouse wheel scrolling. if (wdy < 0) @@ -464,7 +479,7 @@ namespace TFE_DarkForces } break; } } - else if (s_pdaMode == PDA_MODE_BRIEF && (s_buttonPressed || wdy) && s_frameReady) + else if (s_pdaMode == PDA_MODE_BRIEF && (s_buttonPressed || wdy || mousePressed) && s_frameReady) { // Add support for mouse wheel scrolling. if (wdy > 0) // up @@ -506,7 +521,7 @@ namespace TFE_DarkForces } } } - else if (TFE_Input::mouseDown(MBUTTON_LEFT) && s_buttonPressed >= 0) + else if (mouseDown && s_buttonPressed >= 0) { lactor_setState(s_pdaArt, 2 * (1 + s_buttonPressed), 0); LRect buttonRect; @@ -850,7 +865,7 @@ namespace TFE_DarkForces u32 outWidth, outHeight; vfb_getResolution(&outWidth, &outHeight); memset(vfb_getCpuBuffer(), 0, outWidth * outHeight); - + u32 palette[256] = { 0 }; vfb_setPalette(palette); } diff --git a/TheForceEngine/TFE_DarkForces/GameUI/pda.h b/TheForceEngine/TFE_DarkForces/GameUI/pda.h index e75502aae..c5ce47d54 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/pda.h +++ b/TheForceEngine/TFE_DarkForces/GameUI/pda.h @@ -11,6 +11,7 @@ namespace TFE_DarkForces void pda_start(const char* levelName); void pda_cleanup(); void pda_resetState(); + void pda_resetTab(); JBool pda_isOpen(); void pda_update(); diff --git a/TheForceEngine/TFE_DarkForces/agent.h b/TheForceEngine/TFE_DarkForces/agent.h index e1ef49ac0..a4acf4285 100644 --- a/TheForceEngine/TFE_DarkForces/agent.h +++ b/TheForceEngine/TFE_DarkForces/agent.h @@ -52,9 +52,10 @@ namespace TFE_DarkForces JBool agent_loadLevelList(const char* fileName); void agent_updateAgentSavedData(); void agent_levelEndTask(); - JBool agent_readConfigData(FileStream* file, s32 agentId, LevelSaveData* saveData); JBool agent_writeAgentConfigData(FileStream* file, s32 agentId, const LevelSaveData* saveData); + void agent_writeSavedData(s32 agentId, LevelSaveData* saveData); + void agent_readSavedData(s32 agentId, LevelSaveData* levelData); void agent_readSavedDataForLevel(s32 agentId, s32 levelIndex); void agent_saveLevelCompletion(u8 diff, s32 levelIndex); s32 agent_saveInventory(s32 agentId, s32 nextLevel); @@ -72,6 +73,8 @@ namespace TFE_DarkForces void agent_levelComplete(); void agent_createLevelEndTask(); + + JBool createDarkPilotConfig(const char* path); extern AgentData s_agentData[MAX_AGENT_COUNT]; extern s32 s_maxLevelIndex; diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp index beccba8b9..69feb5af2 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp @@ -454,6 +454,19 @@ namespace TFE_DarkForces } } + void DarkForces::getLevelId(char* name) + { + const char* levelName = agent_getLevelName(); + if (levelName) + { + strcpy(name, levelName); + } + else + { + name[0] = 0; + } + } + void skipToLevelNextScene(s32 index) { if (!index) { return; } @@ -548,6 +561,12 @@ namespace TFE_DarkForces { s_runGameState.state = GSTATE_CUTSCENE; s_invalidLevelIndex = JTRUE; + + if (isDemoPlayback()) + { + s_runGameState.cutscenesEnabled = JFALSE; + } + if (s_runGameState.cutscenesEnabled && !s_runGameState.startLevel) { cutscene_play(10); @@ -782,15 +801,16 @@ namespace TFE_DarkForces task_reset(); inf_clearState(); + TFE_Settings_Game * gameSettings = TFE_Settings::getGameSettings(); - // Entry point to recording a demo - if (TFE_Settings::getGameSettings()->df_enableReplay) + // Entry point to replay a demo + if (gameSettings->df_enableReplay && !isDemoPlayback()) { loadReplay(); } - // Entry point to load a demo - if (TFE_Settings::getGameSettings()->df_enableRecording) + // Entry point to recording a demo + if (gameSettings->df_enableRecording && !isRecording()) { startRecording(); } @@ -890,6 +910,11 @@ namespace TFE_DarkForces s_runGameState.cutscenesEnabled = enable; } + bool getCutscenesEnabled() + { + return s_runGameState.cutscenesEnabled; + } + void setInitialLevel(const char* levelName) { s_runGameState.startLevel = 0; diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.h b/TheForceEngine/TFE_DarkForces/darkForcesMain.h index e7542b319..935ead3f5 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.h +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.h @@ -22,9 +22,13 @@ namespace TFE_DarkForces bool canSave() override; bool isPaused() override; void getLevelName(char* name) override; + void getLevelId(char* name) override; void getModList(char* modList) override; }; + void loadAgentAndLevelData(); extern void saveLevelStatus(); void enableCutscenes(JBool enable); + bool getCutscenesEnabled(); + void startMissionFromSave(s32 levelIndex); } diff --git a/TheForceEngine/TFE_DarkForces/hud.cpp b/TheForceEngine/TFE_DarkForces/hud.cpp index d644054c8..dbbc7a22b 100644 --- a/TheForceEngine/TFE_DarkForces/hud.cpp +++ b/TheForceEngine/TFE_DarkForces/hud.cpp @@ -528,7 +528,7 @@ namespace TFE_DarkForces offscreenBuffer_drawTexture(s_cachedHudRight, s_hudLightOff, 19, 0); } - string hud_getDataStr(bool includeYRP) + string hud_getDataStr() { fixed16_16 x, z; getCameraXZ(&x, &z); @@ -543,16 +543,8 @@ namespace TFE_DarkForces r = s_playerEye->roll; p = s_playerEye->pitch; - string format = "X:%04d Y:%.1f Z:%04d H:%.1f S:%d"; - if (includeYRP) - { - format += " Y:%04d R:%04d P:%04d"; - sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s, y, r, p); - } - else - { - sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s); - } + string format = "X:%04d Y:%.1f Z:%04d H:%.1f S:%d"; + sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s); std::string result = string(dataStr); return result; } @@ -568,18 +560,16 @@ namespace TFE_DarkForces s_hudCurrentMsgId = 0; s_hudMsgPriority = HUD_LOWEST_PRIORITY; } - // s_screenDirtyLeft[s_curFrameBufferIdx] = JTRUE; } if (s_showData && s_playerEye) { s32 xOffset = floor16(div16(intToFixed16(vfb_getWidescreenOffset()), vfb_getXScale())); u8 dataStr[64]; - string result = TFE_DarkForces::hud_getDataStr(true); + string result = TFE_DarkForces::hud_getDataStr(); std::copy(result.begin(), result.end(), dataStr); dataStr[result.size()] = '\0'; displayHudMessage(s_hudFont, (DrawRect*)vfb_getScreenRect(VFB_RECT_UI), 164 + xOffset, 10, dataStr, framebuffer); - // s_screenDirtyRight[s_curFrameBufferIdx] = JTRUE; } } diff --git a/TheForceEngine/TFE_DarkForces/hud.h b/TheForceEngine/TFE_DarkForces/hud.h index 2d2836721..269df5f01 100644 --- a/TheForceEngine/TFE_DarkForces/hud.h +++ b/TheForceEngine/TFE_DarkForces/hud.h @@ -18,7 +18,7 @@ namespace TFE_DarkForces void hud_sendTextMessage(s32 msgId); void hud_sendTextMessage(const char* msg, s32 priority, bool skipPriority=true); void hud_clearMessage(); - std::string hud_getDataStr(bool includeYRP); + std::string hud_getDataStr(); void hud_loadGameMessages(); void hud_loadGraphics(); diff --git a/TheForceEngine/TFE_DarkForces/mission.cpp b/TheForceEngine/TFE_DarkForces/mission.cpp index 5197c7165..b411f359e 100644 --- a/TheForceEngine/TFE_DarkForces/mission.cpp +++ b/TheForceEngine/TFE_DarkForces/mission.cpp @@ -54,6 +54,8 @@ namespace TFE_DarkForces JBool s_canTeleport = JTRUE; GameMissionMode s_missionMode = MISSION_MODE_MAIN; + JBool stopRecordEscOnly = JTRUE; + TextureData* s_loadScreen = nullptr; u8 s_loadingScreenPal[768]; u8 s_levelPalette[768]; @@ -517,6 +519,16 @@ namespace TFE_DarkForces void mission_exitLevel() { + if (isDemoPlayback()) + { + endReplay(); + } + + if (isRecording()) + { + endRecording(); + } + s_exitLevel = JTRUE; } @@ -677,7 +689,7 @@ namespace TFE_DarkForces } } while (msg != MSG_FREE_TASK && msg != MSG_RUN_TASK); } - if (TFE_Input::isRecording()) + if (TFE_Input::isRecording() && !stopRecordEscOnly) { endRecording(); } @@ -1080,7 +1092,8 @@ namespace TFE_DarkForces void executeCheat(CheatID cheatID) { - if (cheatID == CHEAT_NONE) + // Do not allow cheats while recording + if (cheatID == CHEAT_NONE || isRecording()) { return; } diff --git a/TheForceEngine/TFE_DarkForces/mission.h b/TheForceEngine/TFE_DarkForces/mission.h index fefe24894..020c0a9c4 100644 --- a/TheForceEngine/TFE_DarkForces/mission.h +++ b/TheForceEngine/TFE_DarkForces/mission.h @@ -50,6 +50,7 @@ namespace TFE_DarkForces void cheat_supercharge(); void cheat_toggleData(); void cheat_toggleFullBright(); + void cheat_levelSkip(); JBool isMissionRunning(); diff --git a/TheForceEngine/TFE_DarkForces/player.cpp b/TheForceEngine/TFE_DarkForces/player.cpp index 0d7f0b61c..d1dfade3e 100644 --- a/TheForceEngine/TFE_DarkForces/player.cpp +++ b/TheForceEngine/TFE_DarkForces/player.cpp @@ -311,6 +311,8 @@ namespace TFE_DarkForces s_playerInfo.health = pickup_addToValue(0, 100, 100); s_playerInfo.healthFract = 0; s_batteryPower = FIXED(2); + s_playerTick = 0; + s_prevPlayerTick = 0; s_reviveTick = 0; s_automapLocked = JTRUE; @@ -869,6 +871,10 @@ namespace TFE_DarkForces { s_playerObject = obj; obj_addLogic(obj, (Logic*)&s_playerLogic, LOGIC_PLAYER, s_playerTask, playerLogicCleanupFunc); + + // Wipe out the player logic. + s_playerLogic.move = {}; + s_playerLogic.dir = {}; s_playerObject->entityFlags|= ETFLAG_PLAYER; s_playerObject->flags |= OBJ_FLAG_MOVABLE; @@ -1322,7 +1328,7 @@ namespace TFE_DarkForces s_playerVelX += pushVel.x; s_playerUpVel2 += pushVel.y; s_playerVelZ += pushVel.z; - + if (s_invincibility || s_config.superShield) { // TODO @@ -1346,7 +1352,7 @@ namespace TFE_DarkForces s_playerVelX += mul16(force, pushDir.x); s_playerUpVel2 += mul16(force, pushDir.y); s_playerVelZ += mul16(force, pushDir.z); - + if (s_invincibility || s_config.superShield) { // Return because no damage is applied. @@ -1906,7 +1912,7 @@ namespace TFE_DarkForces // Apply friction to existing velocity. if (s_playerVelX || s_playerVelZ) { - Tick dt = s_playerTick - s_prevPlayerTick; + Tick dt = s_playerTick - s_prevPlayerTick; // Exponential friction, this is the same as vel * friction^dt for (Tick i = 0; i < dt; i++) { @@ -2001,11 +2007,11 @@ namespace TFE_DarkForces } fixed16_16 speed = adjustForwardSpeed(s_forwardSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw, speed); - + // Add 90 degrees to get the strafe direction. speed = adjustStrafeSpeed(s_strafeSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw + 4095, speed); - limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); + limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); } // Then convert from player velocity to per-frame movement. @@ -2026,7 +2032,7 @@ namespace TFE_DarkForces s_playerSlideWall = nullptr; // Up to 4 iterations, to handle sliding on walls. if (s_noclip) - { + { if (s_playerLogic.move.x || s_playerLogic.move.y) { moved = JTRUE; @@ -2150,7 +2156,7 @@ namespace TFE_DarkForces } s_playerSecMoved = JFALSE; } - + if (origSector != player->sector) { RSector* newSector = player->sector; @@ -2208,7 +2214,7 @@ namespace TFE_DarkForces s_playerUpVel = s_playerUpVel2; s_playerYPos += mul16(s_playerUpVel, s_deltaTime); s_playerLogic.move.y = s_playerYPos - player->posWS.y; - player->posWS.y = s_playerYPos; + player->posWS.y = s_playerYPos; if (s_playerYPos >= s_colCurLowestFloor && (!s_noclip || !s_flyMode)) { if (s_kyleScreamSoundId) diff --git a/TheForceEngine/TFE_DarkForces/playerCollision.cpp b/TheForceEngine/TFE_DarkForces/playerCollision.cpp index c22cf54bb..e50656fc4 100644 --- a/TheForceEngine/TFE_DarkForces/playerCollision.cpp +++ b/TheForceEngine/TFE_DarkForces/playerCollision.cpp @@ -278,7 +278,7 @@ namespace TFE_DarkForces s_colCurBot = s_colCurLowestFloor; s_colMinSector = sector; } - + if (ignore || (s_colCurBot >= s_colBottom && s_colCurTop <= s_colTop && TFE_Jedi::abs(s_colCurBot - s_colCurTop) >= s_colHeight && curFloor <= s_colY1)) { s_collidedObj = col_findObjectCollision(sector); @@ -346,14 +346,14 @@ namespace TFE_DarkForces fixed16_16 dz = z1 - z0; fixed16_16 dx = x1 - x0; // Check to see if the movement direction is not moving *away* from the wall. - side = mul16(dx, wall->wallDir.z) - mul16(dz, wall->wallDir.x); + side = mul16(dx, wall->wallDir.z) - mul16(dz, wall->wallDir.x); if (side > 0) { // The collision path is moving away from the wall. continue; } } - + // This wall might be passable. if (canPass) { @@ -494,7 +494,7 @@ namespace TFE_DarkForces s_colDstPosX = obj->posWS.x + playerLogic->move.x; s_colDstPosZ = obj->posWS.z + playerLogic->move.z; - + // If there was no collision, return false. if (col_computeCollisionResponse(sector, yVel)) { @@ -712,6 +712,9 @@ namespace TFE_DarkForces void initPlayerCollision() { + // Reset to 0 on all collisions to ensure replay consistency. + s_collisionFrameSector = 0; + s_collisionFrameWall = 0; CCMD("exportTexture", console_exportTexture, 0, "Export the texture at the center of the screen."); } diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index be72aafeb..8c460eb9c 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -11,7 +11,7 @@ namespace TFE_DarkForces { Tick s_curTick = 0; Tick s_prevTick = 0; - static f64 s_timeAccum = 0.0; + f64 s_timeAccum = 0.0; fixed16_16 s_deltaTime; fixed16_16 s_frameTicks[13] = { 0 }; JBool s_pauseTimeUpdate = JFALSE; diff --git a/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp b/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp index 7783cb657..399eec742 100644 --- a/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp +++ b/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp @@ -141,6 +141,13 @@ namespace TFE_DarkForces extern void weapon_animateOnOrOffscreen(MessageType msg); JBool computeAutoaim(fixed16_16 xPos, fixed16_16 yPos, fixed16_16 zPos, angle14_32 pitch, angle14_32 yaw, s32 variation); + // Reset for replay consistency. + void resetWeaponFunc() + { + s_weaponFirePitch = 0; + s_weaponFireYaw = 0; + } + // Adjust the speed - in TFE the framerate may be higher than expected by the original code, which means that this // step (proj->delta) is too large for the 'speed'. void tfe_adjustWeaponCollisionSpeed(ProjectileLogic* proj) diff --git a/TheForceEngine/TFE_DarkForces/weaponFireFunc.h b/TheForceEngine/TFE_DarkForces/weaponFireFunc.h index ec633345d..dd412cf1e 100644 --- a/TheForceEngine/TFE_DarkForces/weaponFireFunc.h +++ b/TheForceEngine/TFE_DarkForces/weaponFireFunc.h @@ -20,4 +20,5 @@ namespace TFE_DarkForces void weaponFire_mine(MessageType msg); void weaponFire_concussion(MessageType msg); void weaponFire_cannon(MessageType msg); + void resetWeaponFunc(); } // namespace TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp index 8286ef94d..65510d491 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp @@ -37,7 +37,7 @@ #include #include #include - +#include #include using namespace TFE_Input; @@ -249,6 +249,7 @@ namespace TFE_FrontEndUI // Mod Support static char s_selectedModCmd[TFE_MAX_PATH] = ""; + std::vector s_selectedModOverride; void configAbout(); void configGame(); @@ -471,7 +472,13 @@ namespace TFE_FrontEndUI void logToConsole(const char* str) { - TFE_Console::addToHistory(str); + try { + TFE_Console::addToHistory(str); + } + catch (...) + { + + } } void toggleProfilerView() @@ -1233,19 +1240,13 @@ namespace TFE_FrontEndUI gameSettings->df_jsonAiLogics = jsonAiLogics; } - bool enableRecord = gameSettings->df_enableRecording; - if (ImGui::Checkbox("Enable Replay Recording", &enableRecord)) - { - gameSettings->df_enableRecording = enableRecord; - gameSettings->df_enableReplay = false; - } - + /* bool enableReplay = gameSettings->df_enableReplay; if (ImGui::Checkbox("Enable Replay", &enableReplay)) { gameSettings->df_enableReplay = enableReplay; gameSettings->df_enableRecording = false; - } + }*/ if (s_drawNoGameDataMsg) { @@ -1888,15 +1889,37 @@ namespace TFE_FrontEndUI // Replay Demo UI /////////////////////////////////////////////////////////////////////////////// - static std::vector s_replayDir; + static std::vector s_replayDirContents; static TextureGpu* s_replayImageView = nullptr; static s32 s_selectedReplay = -1; static s32 s_selectedReplaySlot = -1; static char s_replayFileName[TFE_MAX_PATH]; static char s_replayGameConfirmMsg[TFE_MAX_PATH]; static char s_newReplayName[256]; + static char s_replayLevelId[256]; static char s_modReplayName[256]; static char s_levelReplayName[256]; + static s32 s_replayVersion = 0; + + static const char * c_frameRecording[] = + { + "30", + "60", + "90", + "120", + "240", + }; + + static const char* c_framePlayback[] = + { + "Original", + "30", + "60", + "90", + "120", + "240", + "Unlimited" + }; void clearReplayImage() { @@ -1907,12 +1930,12 @@ namespace TFE_FrontEndUI void updateReplayImage(s32 index) { - s_replayImageView->update(s_replayDir[index].imageData, TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT * 4); + s_replayImageView->update(s_replayDirContents[index].imageData, TFE_SaveSystem::SAVE_IMAGE_WIDTH * TFE_SaveSystem::SAVE_IMAGE_HEIGHT * 4); } void openReplayConfirmPopup() { - sprintf(s_replayGameConfirmMsg, "Load '%s'?###SaveConfirm", s_newReplayName); + sprintf(s_replayGameConfirmMsg, " Load Replay?"); ImGui::OpenPopup(s_replayGameConfirmMsg); s_popupOpen = true; s_popupSetFocus = true; @@ -1925,6 +1948,40 @@ namespace TFE_FrontEndUI ImGui::CloseCurrentPopup(); } + void exitReplayMenu() + { + s_subUI = FEUI_NONE; + if (s_appState != APP_STATE_GAME) + { + s_appState = s_menuRetState; + } + s_drawNoGameDataMsg = false; + TFE_Input::enableRelativeMode(s_relativeMode); + } + + void setupReplay() + { + s_selectedReplay = 0; + + if (!s_replayImageView) + { + s_replayImageView = TFE_RenderBackend::createTexture(TFE_SaveSystem::SAVE_IMAGE_WIDTH, TFE_SaveSystem::SAVE_IMAGE_HEIGHT, TexFormat::TEX_RGBA8); + } + TFE_Input::populateReplayDirectory(s_replayDirContents); + + if (!s_replayDirContents.empty()) + { + updateReplayImage(s_selectedReplay); + } + else + { + clearReplayImage(); + } + + s_popupOpen = false; + s_replaySetupRequired = false; + s_popupSetFocus = false; + } void replayDialog() { @@ -1942,7 +1999,7 @@ namespace TFE_FrontEndUI const s32 listOffset = 0; // Left Column - ImGui::SetNextWindowPos(ImVec2(leftColumn, floorf(displayInfo.height * 0.25f))); + ImGui::SetNextWindowPos(ImVec2(leftColumn, floorf(displayInfo.height * 0.07f))); ImGui::BeginChild("##ImageAndInfo"); { // Image @@ -1951,20 +2008,29 @@ namespace TFE_FrontEndUI size.y *= s_uiScale; ImGui::Image(TFE_RenderBackend::getGpuPtr(s_replayImageView), size, - ImVec2(0, 0), ImVec2(1, 1), ImVec4(1, 1, 1, 1), ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImVec2(0, 0), ImVec2(1, 1), ImVec4(1, 1, 1, 1), ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); - // Info - size.y = 140 * s_uiScale; + // Replay Info + size.y = 200 * s_uiScale; ImGui::BeginChild("##InfoWithBorder", size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); { - if (!s_replayDir.empty()) + if (!s_replayDirContents.empty()) { ImGui::PushFont(s_dialogFont); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); char textBuffer[TFE_MAX_PATH]; - sprintf(textBuffer, "Game: Dark Forces\nTime: %s\nLevel: %s\nMods: %s\n", s_replayDir[s_selectedReplay - listOffset].dateTime, - s_replayDir[s_selectedReplay - listOffset].levelName, s_replayDir[s_selectedReplay - listOffset].modNames); + bool modExist = modLoader_exist(s_replayDirContents[s_selectedReplay - listOffset].modNames); + char modExistStr[256]; + + string versionStr = std::to_string(s_replayDirContents[s_selectedReplay - listOffset].saveVersion); + if (!TFE_SaveSystem::versionValid(s_replayDirContents[s_selectedReplay - listOffset].saveVersion)) + { + versionStr = versionStr + " ( INCOMPATIBLE! )"; + } + sprintf(modExistStr, modExist ? "" : "Missing %s", s_replayDirContents[s_selectedReplay - listOffset].modNames); + sprintf(textBuffer, "Game: Dark Forces\nTime: %s\nLevel: %s\nLevel Id: %s\nMods: %s\n%s\nDuration: %d\nVersion: %s", s_replayDirContents[s_selectedReplay - listOffset].dateTime, + s_replayDirContents[s_selectedReplay - listOffset].levelName, s_replayDirContents[s_selectedReplay - listOffset].levelId, s_replayDirContents[s_selectedReplay - listOffset].modNames, modExistStr, s_replayDirContents[s_selectedReplay - listOffset].replayCounter, versionStr.c_str()); ImGui::InputTextMultiline("##Info", textBuffer, strlen(textBuffer) + 1, size, ImGuiInputTextFlags_ReadOnly); ImGui::PopFont(); @@ -1976,7 +2042,7 @@ namespace TFE_FrontEndUI ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); char textBuffer[TFE_MAX_PATH]; - sprintf(textBuffer, "Game: Dark Forces\nTime:\n\nLevel:\nMods:\n"); + sprintf(textBuffer, "Game: Dark Forces\nTime:\n\nLevel:\nLevel Id:\nMods:\n"); ImGui::InputTextMultiline("##Info", textBuffer, strlen(textBuffer) + 1, size, ImGuiInputTextFlags_ReadOnly); ImGui::PopFont(); @@ -1984,6 +2050,121 @@ namespace TFE_FrontEndUI } } ImGui::EndChild(); + + ImGui::Spacing(); + ImGui::LabelText("##InfoReplay", "Note: Press the Escape key to cancel recording or demo play back"); + ImGui::Spacing(); + + ImGui::PushFont(s_dialogFont); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 0.8f)); + ImGui::LabelText("##ConfigLabel", "Recording"); + ImGui::PopStyleColor(); + ImGui::PopFont(); + ImGui::Spacing(); + + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + bool enableRecordAll = gameSettings->df_enableRecordingAll; + bool enableRecord = gameSettings->df_enableRecording || enableRecordAll; + + if (enableRecordAll) + { + enableRecord = true; + } + + size.y = 120 * s_uiScale; + size.x = 420 * s_uiScale; + ImGui::BeginChild("##InfoWithBorderRecord", size, true); + { + ImGui::TextWrapped("When this checkbox is pressed simply start any mission and the game will record the mission for replay."); + ImGui::TextWrapped("Your recordings will be saved in the 'Replays' folder"); + ImGui::Spacing(); + + // Change the colors if recording all. + if (enableRecordAll) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.5f, 0.5f, 0.5f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.5f, 0.5f, 0.5f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.5f, 0.5f, 0.5f, 0.8f)); + } + + if (ImGui::Checkbox("Record next mission", &enableRecord)) + { + gameSettings->df_enableRecording = enableRecord; + gameSettings->df_enableReplay = false; + TFE_DarkForces::enableCutscenes(true); + } + + if (enableRecordAll) + { + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + gameSettings->df_enableRecording = true; + } + + if (ImGui::Checkbox("Always Record missions", &enableRecordAll)) + { + gameSettings->df_enableRecordingAll = enableRecordAll; + gameSettings->df_enableRecording = true; + gameSettings->df_enableReplay = false; + TFE_DarkForces::enableCutscenes(true); + } + + ImGui::LabelText("##FrameRecord", "Recording FrameRate"); + ImGui::SameLine(140 * s_uiScale); + ImGui::SetNextItemWidth(64.0f); + ImGui::Combo("##frameRecordingText", &gameSettings->df_recordFrameRate, c_frameRecording, IM_ARRAYSIZE(c_frameRecording)); + ImGui::Spacing(); + + } + ImGui::EndChild(); + + ImGui::PushFont(s_dialogFont); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 0.8f)); + + ImGui::LabelText("##InfoReplay5", "Playback"); + ImGui::PopStyleColor(); + ImGui::PopFont(); + + size.y = 80 * s_uiScale; + ImGui::BeginChild("##InfoWithBorderPlayBack", size, true); + { + ImGui::Spacing(); + ImGui::LabelText("##InfoReplay6", "Playback FrameRate"); + ImGui::SameLine(140 * s_uiScale); + ImGui::SetNextItemWidth(140.0f); + ImGui::Combo("##framePlayback", &gameSettings->df_playbackFrameRate, c_framePlayback, IM_ARRAYSIZE(c_framePlayback)); + ImGui::Spacing(); + ImGui::TextWrapped("You can speed-up or slow-down playback with Numpad + and -\nYou can rebind these in the Inputs section"); + + ImGui::Checkbox("Show Record/Playback progress", &gameSettings->df_showReplayCounter); + } + ImGui::EndChild(); + + + ImGui::Spacing(); + if (ImGui::Button("Open Replay Folder")) + { + if (!TFE_System::osShellExecute(s_replayDir, NULL, NULL, false)) + { + TFE_System::logWrite(LOG_ERROR, "frontEndUi", "Failed to open the directory: '%s'", s_replayDir); + } + } + ImGui::Spacing(); + if (ImGui::Button("Refresh Demo Folder")) + { + TFE_Input::populateReplayDirectory(s_replayDirContents); + if (!s_replayDirContents.empty()) + { + updateReplayImage(s_selectedReplay - listOffset); + } + else + { + clearReplayImage(); + } + } } ImGui::EndChild(); @@ -1994,21 +2175,38 @@ namespace TFE_FrontEndUI rwidth = displayInfo.width - 64.0f - rightColumn; } - f32 rheight = displayInfo.height - 80.0f; + f32 rheight = displayInfo.height - 100.0f; ImGui::SetNextWindowPos(ImVec2(rightColumn, 64.0f * s_uiScale)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(1.0f, 0.0f, 0.0f, 0.0f)); ImGui::BeginChild("##FileList", ImVec2(rwidth, rheight), true); { - const size_t count = s_replayDir.size() + size_t(listOffset); - const TFE_SaveSystem::SaveHeader* header = s_replayDir.data(); + const size_t count = s_replayDirContents.size() + size_t(listOffset); + const TFE_SaveSystem::SaveHeader* header = s_replayDirContents.data(); ImVec2 buttonSize(rwidth - 2.0f, floorf(20 * s_uiScale + 0.5f)); ImGui::PushFont(s_dialogFont); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); s32 prevSelected = s_selectedReplay; for (size_t i = 0; i < count; i++) { bool selected = i == s_selectedReplay; const char* replayName; - replayName = header[i - listOffset].saveName; + replayName = header[i - listOffset].fileName; + + // Color things differently depending if the mod exists or if the version is valid. + if (!TFE_SaveSystem::versionValid(s_replayDirContents[s32(i) - listOffset].saveVersion)) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 0.75f)); + } + else + { + if (!modLoader_exist(s_replayDirContents[s32(i) - listOffset].modNames)) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.0f, 0.75f)); + } + else + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 0.75f)); + } + } if (ImGui::Selectable(replayName, selected, ImGuiSelectableFlags_None, buttonSize)) { @@ -2017,10 +2215,12 @@ namespace TFE_FrontEndUI s_selectedReplay = s32(i); s_selectedReplaySlot = s_selectedReplay - listOffset; bool shouldExit = false; - strcpy(s_replayFileName, s_replayDir[s_selectedReplaySlot].fileName); - strcpy(s_newReplayName, s_replayDir[s_selectedReplaySlot].saveName); - strcpy(s_modReplayName, s_replayDir[s_selectedReplaySlot].modNames); - strcpy(s_levelReplayName, s_replayDir[s_selectedReplaySlot].levelName); + strcpy(s_replayFileName, s_replayDirContents[s_selectedReplaySlot].fileName); + strcpy(s_newReplayName, s_replayDirContents[s_selectedReplaySlot].saveName); + strcpy(s_modReplayName, s_replayDirContents[s_selectedReplaySlot].modNames); + strcpy(s_levelReplayName, s_replayDirContents[s_selectedReplaySlot].levelName); + strcpy(s_replayLevelId, s_replayDirContents[s_selectedReplaySlot].levelId); + s_replayVersion = s_replayDirContents[s_selectedReplaySlot].saveVersion; openReplayConfirmPopup(); if (shouldExit) { @@ -2032,6 +2232,8 @@ namespace TFE_FrontEndUI { s_selectedReplay = s32(i); } + + ImGui::PopStyleColor(); } if (prevSelected != s_selectedReplay) { @@ -2054,28 +2256,40 @@ namespace TFE_FrontEndUI ImGui::SetKeyboardFocusHere(); s_popupSetFocus = false; } - ImGui::SetNextItemWidth(768 * s_uiScale); - if (TFE_Input::keyPressed(KEY_RETURN)) + + if (!TFE_SaveSystem::versionValid(s_replayVersion)) { - shouldExit = true; - loadReplayWrapper(s_replayFileName, s_modReplayName, s_levelReplayName); + ImGui::Text("Incompatible Replay Version", s_modReplayName); } - if (ImGui::Button("OK", ImVec2(120, 0))) + else if (modLoader_exist(s_modReplayName)) { - shouldExit = true; - loadReplayWrapper(s_replayFileName, s_modReplayName, s_levelReplayName); + ImGui::SetNextItemWidth(768 * s_uiScale); + if (TFE_Input::keyPressed(KEY_RETURN)) + { + shouldExit = true; + loadReplayWrapper(s_replayFileName, s_modReplayName, s_replayLevelId); + } + if (ImGui::Button("OK", ImVec2(120, 0))) + { + shouldExit = true; + loadReplayWrapper(s_replayFileName, s_modReplayName, s_replayLevelId); + } + } + else + { + ImGui::Text("Mod %s not found", s_modReplayName); } ImGui::SameLine(0.0f, 32.0f); if (ImGui::Button("Cancel", ImVec2(120, 0))) { shouldExit = false; - closeSaveNameEditPopup(); + closeReplayEditPopup(); } ImGui::EndPopup(); if (shouldExit) { - exitLoadSaveMenu(); + exitReplayMenu(); } } ImGui::PopFont(); @@ -2085,34 +2299,24 @@ namespace TFE_FrontEndUI s_selectedReplaySlot = s_selectedReplay - listOffset; } - void configReplay() + int getRecordFramerate() { - replayDialog(); + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + return std::atoi(c_frameRecording[gameSettings->df_recordFrameRate]); } - void setupReplay() + std::string getPlaybackFramerate() { - s_selectedReplay = 0; - - if (!s_replayImageView) - { - s_replayImageView = TFE_RenderBackend::createTexture(TFE_SaveSystem::SAVE_IMAGE_WIDTH, TFE_SaveSystem::SAVE_IMAGE_HEIGHT, TexFormat::TEX_RGBA8); - } - TFE_Input::populateReplayDirectory(s_replayDir); - - if (!s_replayDir.empty()) - { - updateReplayImage(s_selectedReplay); - } - else - { - clearReplayImage(); - } + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + return std::string(c_framePlayback[gameSettings->df_playbackFrameRate]); + } - s_popupOpen = false; - s_replaySetupRequired = false; - s_popupSetFocus = false; + void configReplay() + { + replayDialog(); } + + // INPUT HANDLING void getBindingString(InputBinding* binding, char* inputName) { @@ -2450,11 +2654,12 @@ namespace TFE_FrontEndUI inputMapping("Pause", IADF_PAUSE); inputMapping("Automap", IADF_AUTOMAP); inputMapping("Screenshot", IADF_SCREENSHOT); - inputMapping("GIF Recording", IADF_GIF_RECORD); - inputMapping("Demo Record", IADF_DEMO_RECORD); + inputMapping("GIF Recording", IADF_GIF_RECORD); Tooltip("Display a countdown and then start recording a GIF. Press again to stop recording."); inputMapping("Instant GIF Record",IADF_GIF_RECORD_NO_COUNTDOWN); Tooltip("Start recording immediately without the countdown. Press again to stop recording."); + inputMapping("Playback Speedup", IADF_DEMO_SPEEDUP); + inputMapping("Playback Slowdown", IADF_DEMO_SLOWDOWN); ImGui::Separator(); @@ -3499,6 +3704,16 @@ namespace TFE_FrontEndUI strcpy(s_selectedModCmd, mod); } + void setModOverrides(std::vector & argv) + { + s_selectedModOverride = argv; + } + + std::vector getModOverrides() + { + return s_selectedModOverride; + } + void* getGradientTexture() { return s_gradientImage.image; diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.h b/TheForceEngine/TFE_FrontEndUI/frontEndUi.h index 90511a4ee..2956b0118 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.h +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.h @@ -8,6 +8,8 @@ ////////////////////////////////////////////////////////////////////// #include +#include +#include enum AppState { @@ -56,6 +58,10 @@ namespace TFE_FrontEndUI char* getSelectedMod(); void clearSelectedMod(); + int getRecordFramerate(); + std::string getPlaybackFramerate(); + void setModOverrides(std::vector&); + std::vector getModOverrides(); void setSelectedMod(const char* mod); void* getGradientTexture(); void setState(AppState state); diff --git a/TheForceEngine/TFE_FrontEndUI/modLoader.cpp b/TheForceEngine/TFE_FrontEndUI/modLoader.cpp index 7ca52c528..10b4575fb 100644 --- a/TheForceEngine/TFE_FrontEndUI/modLoader.cpp +++ b/TheForceEngine/TFE_FrontEndUI/modLoader.cpp @@ -384,6 +384,20 @@ namespace TFE_FrontEndUI ImGui::PopFont(); } + bool modLoader_exist(const char* modName) + { + char programDirModDir[TFE_MAX_PATH]; + sprintf(programDirModDir, "%sMods/%s", TFE_Paths::getPath(PATH_PROGRAM), modName); + TFE_Paths::fixupPathAsDirectory(programDirModDir); + + if (FileUtil::exists(programDirModDir)) + { + return true; + } + + return false; + } + void modLoader_preLoad() { readFromQueue(c_itemsPerFrame); diff --git a/TheForceEngine/TFE_FrontEndUI/modLoader.h b/TheForceEngine/TFE_FrontEndUI/modLoader.h index ca7a6221e..560ddefe6 100644 --- a/TheForceEngine/TFE_FrontEndUI/modLoader.h +++ b/TheForceEngine/TFE_FrontEndUI/modLoader.h @@ -19,6 +19,7 @@ namespace TFE_FrontEndUI VIEW_COUNT }; + bool modLoader_exist(const char* modName); void modLoader_read(); void modLoader_cleanupResources(); bool modLoader_selectionUI(); diff --git a/TheForceEngine/TFE_Game/igame.h b/TheForceEngine/TFE_Game/igame.h index 5e21c4924..cc9a00d86 100644 --- a/TheForceEngine/TFE_Game/igame.h +++ b/TheForceEngine/TFE_Game/igame.h @@ -31,6 +31,7 @@ struct IGame virtual bool canSave() { return false; } virtual bool isPaused() { return false; } virtual void getLevelName(char* name) {}; + virtual void getLevelId(char* name) {}; virtual void getModList(char* modList) {}; GameID id; diff --git a/TheForceEngine/TFE_Game/saveSystem.cpp b/TheForceEngine/TFE_Game/saveSystem.cpp index fc434d99d..5871534b1 100644 --- a/TheForceEngine/TFE_Game/saveSystem.cpp +++ b/TheForceEngine/TFE_Game/saveSystem.cpp @@ -23,7 +23,8 @@ namespace TFE_SaveSystem enum SaveMasterVersion { SVER_INIT = 1, - SVER_CUR = SVER_INIT + SVER_REPLAY = 6, + SVER_CUR = SVER_REPLAY }; static SaveRequest s_req = SF_REQ_NONE; @@ -36,6 +37,11 @@ namespace TFE_SaveSystem static u32* s_imageBuffer[2] = { nullptr, nullptr }; static size_t s_imageBufferSize[2] = { 0 }; + bool versionValid(s32 version) + { + return version == SVER_CUR; + } + void saveHeader(Stream* stream, const char* saveName) { // Generate a screenshot. @@ -88,6 +94,20 @@ namespace TFE_SaveSystem stream->write(&len); stream->writeBuffer(levelName, len); + //Level ID + char levelId[256]; + s_game->getLevelId(levelId); + len = (u8)strlen(levelId); + stream->write(&len); + stream->writeBuffer(levelId, len); + + // For Replays - Counter ID + int counter = getCounter(); + len = sizeof(counter); + stream->write(&len); + stream->writeBuffer(&counter, len); + + // Mod List char modList[256]; s_game->getModList(modList); @@ -106,6 +126,7 @@ namespace TFE_SaveSystem // Master version. u32 version; stream->read(&version); + header->saveVersion = version; // Save Name. u8 len; @@ -131,6 +152,18 @@ namespace TFE_SaveSystem stream->readBuffer(header->levelName, len); header->levelName[len] = 0; + if (version >= SVER_REPLAY) + { + // Level ID + stream->read(&len); + stream->readBuffer(header->levelId, len); + header->levelId[len] = 0; + + // Counter + stream->read(&len); + stream->readBuffer(&header->replayCounter, len); + } + // Mod List stream->read(&len); stream->readBuffer(header->modNames, len); @@ -308,6 +341,11 @@ namespace TFE_SaveSystem setCurrentGame(game->id); } + IGame* getCurrentGame() + { + return s_game; + } + void update() { if (!s_game) { return; } diff --git a/TheForceEngine/TFE_Game/saveSystem.h b/TheForceEngine/TFE_Game/saveSystem.h index 77176b285..4a647fdcf 100644 --- a/TheForceEngine/TFE_Game/saveSystem.h +++ b/TheForceEngine/TFE_Game/saveSystem.h @@ -20,8 +20,11 @@ namespace TFE_SaveSystem char saveName[SAVE_MAX_NAME_LEN]; char dateTime[256]; char levelName[256]; + char levelId[256]; char modNames[256]; u32 imageData[SAVE_IMAGE_WIDTH * SAVE_IMAGE_HEIGHT]; + s32 saveVersion; + int replayCounter; }; void init(); @@ -29,12 +32,15 @@ namespace TFE_SaveSystem void setCurrentGame(IGame* game); void setCurrentGame(GameID id); + + IGame * getCurrentGame(); void update(); bool saveGame(const char* filename, const char* saveName); bool loadGame(const char* filename); // Load only the header for UI. bool loadGameHeader(const char* filename, SaveHeader* header); + bool versionValid(s32 version); void saveHeader(Stream* stream, const char* saveName); void loadHeader(Stream* stream, SaveHeader* header, const char* fileName); diff --git a/TheForceEngine/TFE_Input/input.cpp b/TheForceEngine/TFE_Input/input.cpp index 34dac5116..4c77b6064 100644 --- a/TheForceEngine/TFE_Input/input.cpp +++ b/TheForceEngine/TFE_Input/input.cpp @@ -121,9 +121,7 @@ namespace TFE_Input } void setKeyUp(KeyboardCode key) - { - - //TFE_System::logWrite(LOG_MSG, "Input", "UP '%d'", key); + { s_keyDown[key] = 0; } diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index 2b15ee1d8..75d41956a 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include namespace TFE_Input { @@ -19,8 +21,9 @@ namespace TFE_Input INPUT_INIT_VER = 0x00010000, INPUT_ADD_QUICKSAVE = 0x00020001, INPUT_ADD_DEADZONE = 0x00020002, - INPUT_ADD_HIGH_DEF = 0x00020003, - INPUT_CUR_VERSION = INPUT_ADD_HIGH_DEF + INPUT_ADD_HIGH_DEF = 0x00020003, + INPUT_DEMO_CONFIG = 0x00020004, + INPUT_CUR_VERSION = INPUT_DEMO_CONFIG }; static const char* c_inputRemappingName = "tfe_input_remapping.bin"; @@ -98,9 +101,12 @@ namespace TFE_Input // HD Asset { IADF_HD_ASSET_TOGGLE, ITYPE_KEYBOARD, KEY_F3, KEYMOD_ALT }, { IADF_SCREENSHOT, ITYPE_KEYBOARD, KEY_PRINTSCREEN }, - { IADF_GIF_RECORD, ITYPE_KEYBOARD, KEY_F2, KEYMOD_ALT}, - { IADF_GIF_RECORD_NO_COUNTDOWN, ITYPE_KEYBOARD, KEY_F2, KEYMOD_CTRL}, - { IADF_DEMO_RECORD, ITYPE_KEYBOARD, KEY_F6, KEYMOD_ALT}, + { IADF_GIF_RECORD, ITYPE_KEYBOARD, KEY_F2, KEYMOD_ALT }, + { IADF_GIF_RECORD_NO_COUNTDOWN, ITYPE_KEYBOARD, KEY_F2, KEYMOD_CTRL }, + + // DEMO handling + { IADF_DEMO_SPEEDUP, ITYPE_KEYBOARD, KEY_KP_PLUS }, + { IADF_DEMO_SLOWDOWN, ITYPE_KEYBOARD, KEY_KP_MINUS }, }; static InputBinding s_defaultControllerBinds[] = @@ -128,8 +134,8 @@ namespace TFE_Input static InputConfig s_inputConfig = { 0 }; static ActionState s_actions[IA_COUNT]; - int inputCounter = 0; - int maxInputCounter = 0; + int replayCounter = 0; + int maxReplayCounter = 0; vector currentKeys; vector currentKeyPresses; vector currentMouse; @@ -284,7 +290,13 @@ namespace TFE_Input inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_SCREENSHOT]); inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_GIF_RECORD]); inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_GIF_RECORD_NO_COUNTDOWN]); - inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_DEMO_RECORD]); + } + + // Demo handling + if (version < INPUT_DEMO_CONFIG) + { + inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_DEMO_SPEEDUP]); + inputMapping_addBinding(&s_defaultKeyboardBinds[IADF_DEMO_SLOWDOWN]); } return true; @@ -324,260 +336,135 @@ namespace TFE_Input } } + ActionState inputMapping_getAction(InputAction act) + { + return s_actions[act]; + } + bool inputMapping_isMovementAction(InputAction action) { return action >= IADF_FORWARD && action <= IADF_LOOK_DN; } - void setCounter(int counter) + void setReplayCounter(int counter) { - inputCounter = counter; + replayCounter = counter; } void resetCounter() { - inputCounter = 0; + replayCounter = 0; } int getCounter() { - return inputCounter; + return replayCounter; } - void setMaxInputCounter(int counter) + void setMaxCounter(int counter) { - maxInputCounter = counter; + maxReplayCounter = counter; } void inputMapping_updateInput() { + - std::vector keysDown; - std::vector keysPressed; - std::vector mouseDown; - std::vector mouseWheel; - int keyIndex = 0; - int keyPressIndex = 0; - int mouseIndex = 0; - int mouseWheelIndex = 0; - - ReplayEvent event; - if (TFE_Input::isRecording()) + // If you are playing back a demo just replay the events for that time frame. + if (isDemoPlayback()) { - event = TFE_Input::inputEvents[inputCounter]; + replayEvent(); } - if (TFE_Input::isDemoPlayback()) + else { - event = TFE_Input::inputEvents[inputCounter-1]; - if (maxInputCounter > inputCounter) - { - // Replay Keys - keysDown = event.keysDown; - for (int i = 0; i < keysDown.size(); i+=2) - { - KeyboardCode key = (KeyboardCode)keysDown[i]; - bool repeat = keysDown[i+1]; - TFE_Input::setKeyDown(key, repeat); - currentKeys.push_back(key); - } - for (int i = 0; i < currentKeys.size(); i++) - { - KeyboardCode key = (KeyboardCode)currentKeys[i]; - if (std::find(keysDown.begin(), keysDown.end(), key) == keysDown.end()) - { - TFE_Input::setKeyUp(key); - currentKeys.erase(std::remove(currentKeys.begin(), currentKeys.end(), key), currentKeys.end()); - } - } - - // Replay Key Presses - keysPressed = event.keysPressed; - for (int i = 0; i < keysPressed.size(); i+=2) + // For each bind record it if it is pressed or down. + for (u32 i = 0; i < s_inputConfig.bindCount; i++) + { + InputBinding* bind = &s_inputConfig.binds[i]; + switch (bind->type) { - KeyboardCode key = (KeyboardCode)keysPressed[i]; - bool repeat = keysPressed[i + 1]; - if (repeat) - { - TFE_Input::setKeyPressRepeat(key); - } - else + case ITYPE_KEYBOARD: { - TFE_Input::setKeyPress(key); - } - currentKeyPresses.push_back(key); - } - for (int i = 0; i < currentKeyPresses.size(); i++) - { - KeyboardCode key = (KeyboardCode)currentKeyPresses[i]; - if (std::find(keysPressed.begin(), keysPressed.end(), key) == keysPressed.end()) - { - TFE_Input::clearKeyPressed(key); - currentKeyPresses.erase(std::remove(currentKeyPresses.begin(), currentKeyPresses.end(), key), currentKeyPresses.end()); - } - } - - // Replay Mouse buttons - mouseDown = event.mouseDown; - for (int i = 0; i < mouseDown.size(); i++) - { - MouseButton key = (MouseButton)mouseDown[i]; - TFE_Input::setMouseButtonDown(key); - currentMouse.push_back(key); - } - for (int i = 0; i < currentMouse.size(); i++) - { - MouseButton key = (MouseButton)currentMouse[i]; - if (std::find(mouseDown.begin(), mouseDown.end(), key) == mouseDown.end()) - { - TFE_Input::setMouseButtonUp(key); - currentMouse.erase(std::remove(currentMouse.begin(), currentMouse.end(), key), currentMouse.end()); - } - } - - //Replay MouseWheel - mouseWheel = event.mouseWheel; - if (mouseWheel.size() > 0) - { - TFE_Input::setMouseWheel(mouseWheel[0], mouseWheel[1]); - } - } - - // Clear all keys after playback. - //else if (maxInputCounter <= inputCounter) inputMapping_endFrame(); - } - - for (u32 i = 0; i < s_inputConfig.bindCount; i++) - { - InputBinding* bind = &s_inputConfig.binds[i]; - switch (bind->type) - { - case ITYPE_KEYBOARD: - { - const bool keyIsMod = (s32)bind->keyMod == (s32)bind->keyCode || bind->keyMod == KEYMOD_NONE; - const bool keyIsAlt = bind->keyCode == KEY_LALT || bind->keyCode == KEY_RALT; - if (TFE_Input::keyModDown(bind->keyMod, inputMapping_isMovementAction(bind->action)) || (keyIsMod && keyIsAlt)) - { - if (TFE_Input::keyPressed(bind->keyCode)) + const bool keyIsMod = (s32)bind->keyMod == (s32)bind->keyCode || bind->keyMod == KEYMOD_NONE; + const bool keyIsAlt = bind->keyCode == KEY_LALT || bind->keyCode == KEY_RALT; + if (TFE_Input::keyModDown(bind->keyMod, inputMapping_isMovementAction(bind->action)) || (keyIsMod && keyIsAlt)) { - s_actions[bind->action] = STATE_PRESSED; - if (TFE_Input::isRecording()) + if (TFE_Input::keyPressed(bind->keyCode)) { - // Do not record Escape - if (bind->keyCode != KEY_ESCAPE) - { - keysPressed.push_back(bind->keyCode); - keysPressed.push_back(isRepeating()); - } + s_actions[bind->action] = STATE_PRESSED; + recordEvent(bind->action, bind->keyCode, true); + } + else if (TFE_Input::keyDown(bind->keyCode) && s_actions[bind->action] != STATE_PRESSED) + { + s_actions[bind->action] = STATE_DOWN; + recordEvent(bind->action, bind->keyCode, false); } - keyPressIndex++; } - else if (TFE_Input::keyDown(bind->keyCode) && s_actions[bind->action] != STATE_PRESSED) + } break; + case ITYPE_MOUSE: + { + if (TFE_Input::keyModDown(bind->keyMod, true)) { - s_actions[bind->action] = STATE_DOWN; - - // Fore recording keys - //TFE_System::logWrite(LOG_MSG, "INPUT PUSH BACK", "INPUT UPDATE %d", bind->keyCode); - if (TFE_Input::isRecording()) + if (TFE_Input::mousePressed(bind->mouseBtn)) { - // Do not record Escape - if (bind->keyCode != KEY_ESCAPE) - { - keysDown.push_back(bind->keyCode); - keysDown.push_back(isRepeating()); - } + s_actions[bind->action] = STATE_PRESSED; + recordEvent(bind->action, bind->keyCode, true); } - keyIndex++; + else if (TFE_Input::mouseDown(bind->mouseBtn) && s_actions[bind->action] != STATE_PRESSED) + { + s_actions[bind->action] = STATE_DOWN; + recordEvent(bind->action, bind->keyCode, false); + } + } - } - } break; - case ITYPE_MOUSE: - { - if (TFE_Input::keyModDown(bind->keyMod, true)) + } break; + case ITYPE_MOUSEWHEEL: { - if (TFE_Input::mousePressed(bind->mouseBtn)) + s32 dx, dy; + TFE_Input::getMouseWheel(&dx, &dy); + + if ((bind->mouseWheel == MOUSEWHEEL_LEFT && dx < 0) || + (bind->mouseWheel == MOUSEWHEEL_RIGHT && dx > 0) || + (bind->mouseWheel == MOUSEWHEEL_UP && dy > 0) || + (bind->mouseWheel == MOUSEWHEEL_DOWN && dy < 0)) { s_actions[bind->action] = STATE_PRESSED; + recordEvent(bind->action, bind->keyCode, true); } - else if (TFE_Input::mouseDown(bind->mouseBtn) && s_actions[bind->action] != STATE_PRESSED) + } break; + case ITYPE_CONTROLLER: + { + if (!(s_inputConfig.controllerFlags & CFLAG_ENABLE)) { - s_actions[bind->action] = STATE_DOWN; + break; } - if (TFE_Input::isRecording() && TFE_Input::mouseDown(bind->mouseBtn)) + + if (TFE_Input::buttonPressed(bind->ctrlBtn)) { - mouseDown.push_back(bind->mouseBtn); + s_actions[bind->action] = STATE_PRESSED; + recordEvent(bind->action, bind->keyCode, true); } - mouseIndex++; - } - } break; - case ITYPE_MOUSEWHEEL: - { - s32 dx, dy; - TFE_Input::getMouseWheel(&dx, &dy); - - if (TFE_Input::isRecording()) - { - mouseWheel.push_back(dx); - mouseWheel.push_back(dy); - mouseWheelIndex++; - } - - if ((bind->mouseWheel == MOUSEWHEEL_LEFT && dx < 0) || - (bind->mouseWheel == MOUSEWHEEL_RIGHT && dx > 0) || - (bind->mouseWheel == MOUSEWHEEL_UP && dy > 0) || - (bind->mouseWheel == MOUSEWHEEL_DOWN && dy < 0)) - { - s_actions[bind->action] = STATE_PRESSED; - } - } break; - case ITYPE_CONTROLLER: - { - if (!(s_inputConfig.controllerFlags & CFLAG_ENABLE)) - { - break; - } - - if (TFE_Input::buttonPressed(bind->ctrlBtn)) - { - s_actions[bind->action] = STATE_PRESSED; - } - else if (TFE_Input::buttonDown(bind->ctrlBtn) && s_actions[bind->action] != STATE_PRESSED) - { - s_actions[bind->action] = STATE_DOWN; - } - } break; - case ITYPE_CONTROLLER_AXIS: - { - if (!(s_inputConfig.controllerFlags & CFLAG_ENABLE)) + else if (TFE_Input::buttonDown(bind->ctrlBtn) && s_actions[bind->action] != STATE_PRESSED) + { + s_actions[bind->action] = STATE_DOWN; + recordEvent(bind->action, bind->keyCode, false); + } + } break; + case ITYPE_CONTROLLER_AXIS: { - break; - } + if (!(s_inputConfig.controllerFlags & CFLAG_ENABLE)) + { + break; + } - if (TFE_Input::getAxis(bind->axis) > 0.5f) - { - s_actions[bind->action] = STATE_DOWN; - } - } break; - } - } - if (TFE_Input::isRecording()) - { - if (keyIndex > 0) - { - event.keysDown = keysDown; - } - if (keyPressIndex > 0) - { - event.keysPressed = keysPressed; - } - if (mouseIndex > 0) - { - event.mouseDown = mouseDown; - } - if (mouseWheelIndex > 0) - { - event.mouseWheel = mouseWheel; + if (TFE_Input::getAxis(bind->axis) > 0.5f) + { + s_actions[bind->action] = STATE_DOWN; + recordEvent(bind->action, bind->keyCode, false); + } + } break; + } } - inputEvents[inputCounter] = event; } } @@ -585,6 +472,16 @@ namespace TFE_Input { s_actions[action] = STATE_UP; } + + void inputMapping_setStateDown(InputAction action) + { + s_actions[action] = STATE_DOWN; + } + + void inputMapping_setStatePress(InputAction action) + { + s_actions[action] = STATE_PRESSED; + } ActionState inputMapping_getActionState(InputAction action) { @@ -686,90 +583,134 @@ namespace TFE_Input return &s_inputConfig; } - void handleInputs() + void clearKeys() { - // Allow escape during playback + for (int i = 0; i < currentKeys.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeys[i]; + TFE_Input::setKeyUp(key); + } + currentKeys.clear(); + for (int i = 0; i < currentKeyPresses.size(); i++) + { + KeyboardCode key = (KeyboardCode)currentKeyPresses[i]; + TFE_Input::clearKeyPressed(key); + } + currentKeyPresses.clear(); + } - if (keyPressed(KEY_ESCAPE)) + bool isBindingPressed(InputAction action) + { + u32 indices[2]; + u32 count = inputMapping_getBindingsForAction(action, indices, 2); + if (count > 0) { - if (isDemoPlayback()) + InputBinding* binding = inputMapping_getBindingByIndex(indices[0]); + if (TFE_Input::keyPressed(binding->keyCode)) { - setDemoPlayback(false); - string msg = "DEMO Playback Complete!"; - TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); - for (int i = 0; i < currentKeys.size(); i++) - { - KeyboardCode key = (KeyboardCode)currentKeys[i]; - TFE_Input::setKeyUp(key); - } - currentKeys.clear(); - for (int i = 0; i < currentKeyPresses.size(); i++) - { - KeyboardCode key = (KeyboardCode)currentKeyPresses[i]; - TFE_Input::clearKeyPressed(key); - } - currentKeyPresses.clear(); - TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYEND ======================================"); - + return true; + } + } + return false; + } + bool handleInputs() + { + // Allow escape during playback except when in PDA mode + if (keyPressed(KEY_ESCAPE) && !TFE_DarkForces::pda_isOpen()) + { + if (isDemoPlayback()) + { + sendEndPlaybackMsg(); + clearKeys(); + endReplay(); } if (isRecording()) { + sendEndRecordingMsg(); TFE_Input::endRecording(); - TFE_System::logWrite(LOG_MSG, "LOG", "====================================== RECORDEND ======================================"); } } - - - - + + // Load the mouse positional data std::vector mousePos; s32 mouseX, mouseY; s32 mouseAbsX, mouseAbsY; u32 state = SDL_GetRelativeMouseState(&mouseX, &mouseY); SDL_GetMouseState(&mouseAbsX, &mouseAbsY); - + string hudData = ""; string keys = ""; string mouse = ""; string mouseP = ""; - // Wipe current keys and mouse buttons + if (isDemoPlayback()) { - if (inputCounter >= maxInputCounter) + + handleReplayPause(); + + if (isBindingPressed(IADF_DEMO_SPEEDUP)) + { + increaseReplayFrameRate(); + } + + if (isBindingPressed(IADF_DEMO_SLOWDOWN)) + { + decreaseReplayFrameRate(); + } + + // If we are at the end of the replay, stop playback. + if (replayCounter >= maxReplayCounter) { - setDemoPlayback(false); - string msg = "DEMO Playback Complete!"; - TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); - inputMapping_updateInput(); + endReplay(); } else { - string msg = "DEMO Playback [" + std::to_string(inputCounter) + " out of " + std::to_string(maxInputCounter) + "] ..."; - TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); + // Show the complete message just before it exits for 60 frames otherwise you won't see it. + if (replayCounter + 60 >= maxReplayCounter) + { + sendEndPlaybackMsg(); + } + // Show the % progress + else if (TFE_Settings::getGameSettings()->df_showReplayCounter) + { + int percentage = (replayCounter * 100) / maxReplayCounter; + string msg = "DEMO Playback [" + to_string(replayCounter) + " out of " + to_string(maxReplayCounter) + "] ( " + to_string(percentage) + " % )..."; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); + } + + // Wipe the binds during playback and populate with new ones. inputMapping_endFrame(); - ReplayEvent event = TFE_Input::inputEvents[inputCounter-1]; + + ReplayEvent event = TFE_Input::inputEvents[replayCounter - 1]; + + // Load Mouse positional information mousePos = event.mousePos; + + // Only load if it has mouse movement if (mousePos.size() == 4) { mouseX = mousePos[0]; mouseY = mousePos[1]; mouseAbsX = mousePos[2]; mouseAbsY = mousePos[3]; + mouseP = convertToString(event.mousePos); } - mouseP = convertToString(event.mousePos); - //event.keysDown; } } else if (isRecording()) { - string msg = "DEMO Recording [" + std::to_string(inputCounter) + "] ..."; - TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); - ReplayEvent event = TFE_Input::inputEvents[inputCounter]; + if (TFE_Settings::getGameSettings()->df_showReplayCounter) + { + string msg = "DEMO Recording [" + std::to_string(replayCounter) + "] ..."; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, true); + } + + ReplayEvent event = TFE_Input::inputEvents[replayCounter]; mousePos = event.mousePos; mousePos.clear(); mousePos.push_back(mouseX); @@ -778,83 +719,80 @@ namespace TFE_Input mousePos.push_back(mouseAbsY); event.mousePos = mousePos; mouseP = convertToString(event.mousePos); - TFE_Input::inputEvents[inputCounter] = event; + TFE_Input::inputEvents[replayCounter] = event; } - bool rec = isRecording(); - bool play = isDemoPlayback(); - bool recAllow = TFE_Settings::getGameSettings()->df_enableRecording; - bool playAllow = TFE_Settings::getGameSettings()->df_enableReplay; + // Apply the mouse position data we either got from SDL or from the demo TFE_Input::setRelativeMousePos(mouseX, mouseY); TFE_Input::setMousePos(mouseAbsX, mouseAbsY); inputMapping_updateInput(); if ((isRecording() || isDemoPlayback()) && TFE_DarkForces::s_playerEye) { - if (inputCounter == 62) - { - if (isRecording()) - { - TFE_System::logWrite(LOG_MSG, "LOG", "====================================== RECORDSTART ======================================"); - } - else - { - TFE_System::logWrite(LOG_MSG, "LOG", "====================================== PLAYSTART ======================================"); - } - } - handleEye(); ReplayEvent event; if (isDemoPlayback()) { - event = TFE_Input::inputEvents[inputCounter-1]; + event = TFE_Input::inputEvents[replayCounter - 1]; } else { - event = TFE_Input::inputEvents[inputCounter]; + event = TFE_Input::inputEvents[replayCounter]; } vec3_fixed ws = TFE_DarkForces::s_playerEye->posWS; vec3_fixed vs = TFE_DarkForces::s_playerEye->posVS; hudData += " WS: X: " + std::to_string(ws.x) + " Y:" + std::to_string(ws.y) + " Z:" + std::to_string(ws.z); - //hudData += " VS: " + std::to_string(vs.x) + " " + std::to_string(vs.y) + " " + std::to_string(vs.z); - + angle14_16 yaw = TFE_DarkForces::s_playerEye->yaw; angle14_16 pitch = TFE_DarkForces::s_playerEye->pitch; angle14_16 roll = TFE_DarkForces::s_playerEye->roll; - //TFE_DarkForces::s_playerEye->posWS.z string playerpos = " X: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.x) + " Y: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.y) + " Z: " + std::to_string(TFE_DarkForces::s_playerObject->posWS.z); hudData += playerpos; hudData += " Y: " + std::to_string(yaw) + " P: " + std::to_string(pitch); + + if (isRecording()) + { + TFE_Input::inputEvents[replayCounter] = event; + } vec3_fixed vel = {}; TFE_DarkForces::player_getVelocity(&vel); hudData += " V: " + std::to_string(vel.x) + " " + std::to_string(vel.z) + " "; - hudData += TFE_DarkForces::hud_getDataStr(true); + hudData += TFE_DarkForces::hud_getDataStr(); keys = convertToString(event.keysDown); - mouse = convertToString(event.mouseDown); } if ((isRecording() || isDemoPlayback())) - { - string rec = isRecording() ? "REC " : "PLAY"; + { + string rec = "UPD"; string hudDataStr = rec + " upd=%d cur=%d pt=%d ptp=%d delta=%d"; if (hudData.size() != 0) hudDataStr += hudData; if (keys.size() != 0) hudDataStr += " Keys: " + keys; - if (mouse.size() != 0) hudDataStr += " Mouse: " + mouse; if (mouseP.size() != 0) hudDataStr += " MousePos " + mouseP; + int printcounter = isRecording() ? replayCounter : replayCounter - 1; - //TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), inputCounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); + if (printcounter != -1) + { + TFE_System::logWrite(LOG_MSG, "LOG", "----------------------------------------------------------------- %d ---------------------------------------------------------------------", printcounter); + TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), printcounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); + } } - - //TFE_System::logWrite(LOG_MSG, "LOG", "LOG input = %d", inputCounter); - inputCounter++; - /* - if (TFE_DarkForces::s_playerEye) + // If you are replaying the demo and the game is paused, we should also halt all logic + bool skipUpdateCounter = isDemoPlayback() + && TFE_FrontEndUI::getCurrentGame()->isPaused() + && TFE_DarkForces::s_playerEye; + + if (skipUpdateCounter) { - inputCounter++; - }*/ + return false; + } + else + { + replayCounter++; + return true; + } } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/inputMapping.h b/TheForceEngine/TFE_Input/inputMapping.h index 102953b51..c2fdbee18 100644 --- a/TheForceEngine/TFE_Input/inputMapping.h +++ b/TheForceEngine/TFE_Input/inputMapping.h @@ -85,7 +85,11 @@ namespace TFE_Input IADF_SCREENSHOT, IADF_GIF_RECORD, IADF_GIF_RECORD_NO_COUNTDOWN, - IADF_DEMO_RECORD, + + // Demo handling + IADF_DEMO_PAUSE, + IADF_DEMO_SPEEDUP, + IADF_DEMO_SLOWDOWN, IA_COUNT, IAS_COUNT = IAS_SYSTEM_MENU + 1, @@ -187,6 +191,11 @@ namespace TFE_Input f32 inputMapping_getAnalogAxis(AnalogAxis axis); void inputMapping_updateInput(); void inputMapping_removeState(InputAction action); + + ActionState inputMapping_getAction(InputAction act); + + void inputMapping_setStateDown(InputAction action); + void inputMapping_setStatePress(InputAction action); void inputMapping_clearKeyBinding(KeyboardCode key); void inputMapping_endFrame(); @@ -198,7 +207,7 @@ namespace TFE_Input f32 inputMapping_getVertMouseSensitivity(); void resetCounter(); int getCounter(); - void setCounter(int counter); - void handleInputs(); - void setMaxInputCounter(int counter); + void setReplayCounter(int counter); + bool handleInputs(); + void setMaxCounter(int counter); } // TFE_Input \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.cpp b/TheForceEngine/TFE_Input/replay.cpp index fb67af6a3..2f6cdc85b 100644 --- a/TheForceEngine/TFE_Input/replay.cpp +++ b/TheForceEngine/TFE_Input/replay.cpp @@ -1,39 +1,76 @@ #include -#include -#include -#include #include -#include -#include -#include #include #include #include #include -#include #include #include -#include #include +#include +#include +#include +#include #include #include #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include namespace TFE_Input { // Handle replay File Paths FileStream s_replayFile; - static char s_replayDir[TFE_MAX_PATH]; + char s_replayDir[TFE_MAX_PATH]; + char s_replayAgentPath[TFE_MAX_PATH]; static char s_replayPath[TFE_MAX_PATH]; + static char s_headerName[256]; + string replayFileName = ""; + // Recording States static bool s_recording = false; static bool s_playback = false; - static bool eyeSet = false; + // Agent and Header values + LevelSaveData replayAgent; + TFE_Settings_Game replayGameSettings = {}; + + // Mouse and Keyboard States + static int s_mouseMode = 0; + static int s_mouseFlags = 0; + static f32 s_mouseSensitivity[2] = { 0, 0 }; + + // Settings preserved when replaying + int replayGraphicsType = 0; + bool playerHeadwave = false; + bool playerAutoAim = false; + bool vsyncEnabled = false; + bool replayInitialized = false; + bool cutscenesEnabled = true; + bool pauseReplay = false; + + extern f64 gameFrameLimit = 0; + extern f64 replayFrameLimit = 0; + + // Player values + bool eyeSet = false; angle14_16 r_yaw = 0; angle14_16 r_pitch = 0; angle14_16 r_roll = 0; @@ -43,16 +80,44 @@ namespace TFE_Input u64 replayStartTime = 0; s32 replay_seed = 0; + int replayFilehandler = -1; + + std::vector settingBuffer; + void initReplays() { - sprintf(s_replayDir, "%sReplays/", TFE_Paths::getPath(PATH_USER_DOCUMENTS)); + + // TO DO - combine replays from both sources. + + sprintf(s_replayDir, "%sReplays/", TFE_Paths::getPath(PATH_PROGRAM)); TFE_Paths::fixupPathAsDirectory(s_replayDir); + // Check TFE/Replays first if (!FileUtil::directoryExits(s_replayDir)) { - FileUtil::makeDirectory(s_replayDir); + sprintf(s_replayDir, "%sReplays/", TFE_Paths::getPath(PATH_USER_DOCUMENTS)); + + // Otherwise check /TheForceEngine/Replays + if (!FileUtil::directoryExits(s_replayDir)) + { + FileUtil::makeDirectory(s_replayDir); + } } - sprintf(s_replayPath, "%stest.demo", s_replayDir); + else + { + sprintf(s_replayAgentPath, "%sreplay.agent", TFE_Paths::getPath(PATH_USER_DOCUMENTS)); + } + TFE_System::logWrite(LOG_MSG, "Replay", "Loading Replays from %s ...", s_replayDir); + + if (TFE_Settings::getGameSettings()->df_enableRecordingAll) + { + TFE_Settings::getGameSettings()->df_enableRecording = true; + } + } + + void getAgentPath(char * agentPath) + { + agentPath = s_replayAgentPath; } bool isRecording() @@ -78,33 +143,53 @@ namespace TFE_Input void saveTick() { int inputCounter = getCounter(); - inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; - } - - + inputEvents[inputCounter].curTick = TFE_DarkForces::s_curTick; + } void loadTick() { int inputCounter = getCounter(); - TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; + TFE_DarkForces::s_curTick = inputEvents[inputCounter].curTick; + } + + void saveInitTime() + { + inputEvents[0].curTick = TFE_DarkForces::s_curTick; + inputEvents[0].prevTick = TFE_DarkForces::s_prevTick; + inputEvents[0].deltaTime = TFE_DarkForces::s_deltaTime; + inputEvents[0].timeAccum = TFE_DarkForces::s_timeAccum; + inputEvents[0].plTick = TFE_DarkForces::s_playerTick; + inputEvents[0].plPrevTick = TFE_DarkForces::s_prevPlayerTick; + memcpy(inputEvents[0].frameTicks, TFE_DarkForces::s_frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(TFE_DarkForces::s_frameTicks)); + } + + void loadInitTime() + { + TFE_DarkForces::s_curTick = inputEvents[0].curTick; + TFE_DarkForces::s_prevTick = inputEvents[0].prevTick; + TFE_DarkForces::s_deltaTime = inputEvents[0].deltaTime; + TFE_DarkForces::s_timeAccum = inputEvents[0].timeAccum; + TFE_DarkForces::s_playerTick = inputEvents[0].plTick; + TFE_DarkForces::s_prevPlayerTick = inputEvents[0].plPrevTick; + memcpy(TFE_DarkForces::s_frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[0].frameTicks)); } void recordReplaySeed() { replay_seed = TFE_DarkForces::getSeed(); - TFE_System::logWrite(LOG_MSG, "REPLAY", "Recording Seed: %d", replay_seed); + TFE_System::logWrite(LOG_MSG, "Replay", "Recording Seed: %d", replay_seed); } - - + void restoreReplaySeed() { TFE_DarkForces::setSeed(replay_seed); - TFE_System::logWrite(LOG_MSG, "REPLAY", "Loading Seed: %d", replay_seed); + TFE_System::logWrite(LOG_MSG, "Replay", "Loading Seed: %d", replay_seed); } + // Wipe all event history void clearEvents() { - int iSize = inputEvents.size(); + size_t iSize = inputEvents.size(); for (int i = 0; i < iSize; i++) { inputEvents[i].clear(); @@ -112,16 +197,56 @@ namespace TFE_Input inputEvents.clear(); } + void recordEvent(int action, KeyboardCode keyCode, bool isPress) + { + // When recording, normally ignore the escape key unless the PDA is open + if (isRecording() && (keyCode != KEY_ESCAPE || TFE_DarkForces::pda_isOpen())) + { + int updateCounter = getCounter(); + if (isPress) + { + inputEvents[updateCounter].keysPressed.push_back((InputAction)action); + } + else + { + inputEvents[updateCounter].keysDown.push_back((InputAction)action); + } + } + } + + void replayEvent() + { + // Plays back the event from the inputEvents map + int updateCounter = getCounter(); + ReplayEvent event = inputEvents[updateCounter-1]; + + // Handle key presses + for (int i = 0; i < event.keysPressed.size(); i++) + { + s32 act = event.keysPressed[i]; + inputMapping_setStatePress((InputAction)act); + } + + // Handle key downs + for (int i = 0; i < event.keysDown.size(); i++) + { + s32 act = event.keysDown[i]; + inputMapping_setStateDown((InputAction)act); + } + } + + // Stores the vectors as strings for writings string convertToString(vector keysDown) { std::ostringstream oss; - for (size_t i = 0; i < keysDown.size(); ++i) { + for (int i = 0; i < keysDown.size(); ++i) { if (i != 0) oss << ","; oss << keysDown[i]; } return oss.str(); } + // Loads the strings int values as vectors for replays vector convertFromString(string keyStr) { std::vector intVector; @@ -133,18 +258,18 @@ namespace TFE_Input return intVector; } + // Serializes the input events into the demo file from vectors to strings vector serializeInputs(Stream* stream, vector inputList, bool writeFlag) { int keySize = 0; string keyString; + // The Vector would consist of the key codes for the input events + // Or it will contain the mouse positions along with yaw/pitch/roll + if (writeFlag) { keyString = convertToString(inputList); - if (keyString.size() == 0) - { - int x; - } keySize = keyString.size(); } @@ -153,8 +278,8 @@ namespace TFE_Input { keyString.resize(keySize); } - SERIALIZE_BUF(ReplayVersionInit, &keyString[0], keySize); + SERIALIZE_BUF(ReplayVersionInit, &keyString[0], keySize); if (!writeFlag && keySize > 0) { @@ -163,23 +288,35 @@ namespace TFE_Input return inputList; } - bool loadReplayHeader(const char* filename, TFE_SaveSystem::SaveHeader* header) + void storePDAPosition(Vec2i pos) { - char filePath[TFE_MAX_PATH]; - sprintf(filePath, "%s%s", s_replayDir, filename); + int updateCounter = getCounter(); + inputEvents[updateCounter].pdaPosition = pos; + } - bool ret = false; - FileStream stream; - if (stream.open(filePath, Stream::MODE_READ)) - { - loadHeader(&stream, header, filename); - strcpy(header->fileName, filename); - stream.close(); - ret = true; - } - return ret; + Vec2i getPDAPosition() + { + int updateCounter = getCounter(); + return inputEvents[updateCounter - 1].pdaPosition; } + void copyGameSettings(TFE_Settings_Game* source, TFE_Settings_Game* dest) + { + dest->df_airControl = source->df_airControl; + dest->df_enableAutoaim = source->df_enableAutoaim; + dest->df_smoothVUEs = source->df_smoothVUEs; + dest->df_autorun = source->df_autorun; + dest->df_crouchToggle = source->df_crouchToggle; + dest->df_ignoreInfLimit = source->df_ignoreInfLimit; + dest->df_stepSecondAlt = source->df_stepSecondAlt; + dest->df_enableUnusedItem = source->df_enableUnusedItem; + dest->df_solidWallFlagFix = source->df_solidWallFlagFix; + dest->df_jsonAiLogics = source->df_jsonAiLogics; + dest->df_pitchLimit = source->df_pitchLimit; + dest->df_recordFrameRate = source->df_recordFrameRate; + } + + // Loads the demo files from the replay folder. void populateReplayDirectory(std::vector& dir) { dir.clear(); @@ -196,108 +333,341 @@ namespace TFE_Input } } - void serializeDemo(Stream* stream, bool writeFlag) + bool setupPath() { - int fileHandler = 0; - int updateCounter = 0; - TFE_DarkForces::time_pause(JTRUE); + + // Replay Initialization - it will set the current game and level name + IGame* s_game = TFE_SaveSystem::getCurrentGame(); + + char timeDate[256]; + TFE_System::getDateTimeString(timeDate); + + char levelName[256]; + s_game->getLevelName(levelName); + + char levelId[256]; + s_game->getLevelId(levelId); + + char modPath[256]; + s_game->getModList(modPath); + + int offset = 0; + if (strlen(modPath) == 0 ) + { + // For now assume all replays are Dark Forces replays + strcpy(modPath, "dark_forces"); + } + + char modName[256]; + FileUtil::getFileNameFromPath(modPath, modName, true); + + // Create the dmeo file path + sprintf(s_replayPath, "%s%s_%s.demo", s_replayDir, modName, levelId); + + int offsetMax = 10000; + while (FileUtil::exists(s_replayPath) || offset > offsetMax) + { + offset++; + sprintf(s_replayPath, "%s%s_%s_%d.demo", s_replayDir, modName, levelId, offset); + if (offset > offsetMax) + { + TFE_System::logWrite(LOG_MSG, "REPLAY", "Failed to create replay file. Too many files with the same level name."); + return false; + } + } + + // Ensure it is all lowercase + std::strcpy(s_replayPath, TFE_A11Y::toLower(string(s_replayPath)).c_str()); + + sprintf(s_headerName, "%s.demo", levelName); + return true; + } + + s32 getFileHandler(Stream* stream, bool writeFlag) + { + s32 fileHandler = 0; if (writeFlag) { serialization_setMode(SMODE_WRITE); fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_WRITE); - replayStartTime = TFE_System::getStartTime(); - TFE_SaveSystem::saveHeader(stream, "TEST"); - - //void saveHeader(Stream * stream, const char* saveName); - //void loadHeader(Stream * stream, SaveHeader * header, const char* fileName); } else { serialization_setMode(SMODE_READ); fileHandler = s_replayFile.open(s_replayPath, Stream::MODE_READ); - clearEvents(); - TFE_SaveSystem::SaveHeader * header = new TFE_SaveSystem::SaveHeader(); - TFE_SaveSystem::loadHeader(stream, header, "TEST"); } + return fileHandler; + } + bool loadReplayHeader(const char* filename, TFE_SaveSystem::SaveHeader* header) + { + char filePath[TFE_MAX_PATH]; + sprintf(filePath, "%s%s", s_replayDir, filename); + + bool ret = false; + FileStream stream; + if (stream.open(filePath, Stream::MODE_READ)) + { + loadHeader(&stream, header, filename); + strcpy(header->fileName, filename); + stream.close(); + ret = true; + } + return ret; + } + + // Handles the header and agent data for the replay + int serializeHeaderAgentInfo(Stream* stream, bool writeFlag) + { + + // Reuse an existing file handler if it already exists + if (replayFilehandler > 0) + { + return replayFilehandler; + } + + // Load the Header and agent information + int fileHandler = getFileHandler(stream, writeFlag); if (fileHandler > 0) { + + // HEADER INFORMATION + + // Store the replay header information including the mod name and level name + // As well as the final image of the demo + if (writeFlag) + { + TFE_SaveSystem::saveHeader(stream, s_headerName); + } + else + { + TFE_SaveSystem::SaveHeader* header = new TFE_SaveSystem::SaveHeader(); + TFE_SaveSystem::loadHeader(stream, header, s_headerName); + } SERIALIZE_VERSION(ReplayVersionInit); + + // AGENT INFORMATION + + // This is where we save / load the agent inventory and difficulty + LevelSaveData data; + if (writeFlag) + { + // Store the agent data from the current agent ID + agent_readSavedData(s_agentId, &data); + data.agentData.difficulty = s_agentData[s_agentId].difficulty; + } + + SERIALIZE(SaveVersionInit, data, { 0 }); + if (!writeFlag) + { + // Create new agent if you don't have any + s32 curCount = getAgentCount(); + if (curCount == 0) + { + agentMenu_createNewAgent(); + setAgentName("replay"); + } + + // Back up current player from the first slot + agent_readSavedData(0, &replayAgent); + + // Set the replay agent to the first slot and load the data to it + s_agentData[0] = data.agentData; + agent_writeSavedData(0, &data); + } + } + return fileHandler; + } + + // Main replay serialization function + // The demo file contains all the information needed to replay the game + // ----------------------------------------------------------- + // + // 1. It will contain the metadata of the replay such as then name and modname + // 2. It will contain the agent data for the replay + // 3. It will contain the input events for the replay + // 4. It will contain the game and graphical settings + // 5. It will contain the seed and tick timing data + // + // ----------------------------------------------------------- + void serializeDemo(FileStream* stream, bool writeFlag) + { + // Initalize the events and frametick obj + int eventCounter = 0; + fixed16_16 frameTicks[13]; + + // Pause everything while we serialize + TFE_DarkForces::time_pause(JTRUE); + + // Handle the header and agent data + int fileHandler = serializeHeaderAgentInfo(stream, writeFlag); + + if (fileHandler > 0) + { + + // Handle Tick timing + SERIALIZE(ReplayVersionInit, inputEvents[0].prevTick, 0); + SERIALIZE(ReplayVersionInit, inputEvents[0].deltaTime, 0); + SERIALIZE(ReplayVersionInit, inputEvents[0].timeAccum, 0); + SERIALIZE(ReplayVersionInit, inputEvents[0].plTick, 0); + SERIALIZE(ReplayVersionInit, inputEvents[0].plPrevTick, 0); + + // Handle the frame ticks + if (writeFlag) + { + replayStartTime = TFE_System::getStartTime(); + memcpy(frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[0].frameTicks)); + } + + // Store the replay seed and start time SERIALIZE(ReplayVersionInit, replayStartTime, 0); SERIALIZE(ReplayVersionInit, replay_seed, 0); - u32 plTick = TFE_DarkForces::s_playerTick; - u32 plPrevTick = TFE_DarkForces::s_prevPlayerTick; - SERIALIZE(ReplayVersionInit, plTick, 0); - SERIALIZE(ReplayVersionInit, plPrevTick, 0); - - TFE_DarkForces::time_serialize(stream); + SERIALIZE_BUF(SaveVersionInit, frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(frameTicks)); - int inputListsSize = getCounter(); - SERIALIZE(ReplayVersionInit, inputListsSize, 0); + // Handle events list size + int eventListsSize = getCounter(); + SERIALIZE(ReplayVersionInit, eventListsSize, 0); + // handle the initial player eye position SERIALIZE(ReplayVersionInit, r_yaw, 0); SERIALIZE(ReplayVersionInit, r_pitch, 0); - SERIALIZE(ReplayVersionInit, r_roll, 0); + SERIALIZE(ReplayVersionInit, r_roll, 0); + + // Settings and Input Handling + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + TFE_Settings_A11y* allySettings = TFE_Settings::getA11ySettings(); + InputConfig* inputConfig = inputMapping_get(); + int mouseMode = inputConfig->mouseMode; + + // If you are not recording first store all the original values + if (!writeFlag) + { + copyGameSettings(gameSettings , &replayGameSettings); + playerHeadwave = allySettings->enableHeadwave; + s_mouseMode = inputConfig->mouseMode; + s_mouseFlags = inputConfig->mouseFlags; + s_mouseSensitivity[0] = inputConfig->mouseSensitivity[0]; + s_mouseSensitivity[1] = inputConfig->mouseSensitivity[1]; + } + + // Serialize game and input settings + SERIALIZE(ReplayVersionInit, gameSettings->df_airControl, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_enableAutoaim, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_smoothVUEs, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_autorun, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_crouchToggle, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_ignoreInfLimit, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_stepSecondAlt, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_enableUnusedItem, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_solidWallFlagFix, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_jsonAiLogics, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_bobaFettFacePlayer, 0); + SERIALIZE(ReplayVersionInit, gameSettings->df_recordFrameRate, 0); + int pitchLimit = gameSettings->df_pitchLimit; + SERIALIZE(ReplayVersionInit, pitchLimit, 0); + + SERIALIZE(ReplayVersionInit, allySettings->enableHeadwave, 0); + + SERIALIZE(ReplayVersionInit, inputConfig->mouseFlags, 0); + SERIALIZE(ReplayVersionInit, mouseMode, 0); + SERIALIZE(ReplayVersionInit, inputConfig->mouseSensitivity[0], 0); + SERIALIZE(ReplayVersionInit, inputConfig->mouseSensitivity[1], 0); + + // Need to cast to the correct type for replays + if (!writeFlag) + { + gameSettings->df_pitchLimit = (PitchLimit)pitchLimit; + inputConfig->mouseMode = (MouseMode)mouseMode; + } + // Handle writing the events if (writeFlag) { - // Loop through inputEvents + // Loop through inputEvents Map for (const auto& pair : inputEvents) { - updateCounter = pair.first; - - if (updateCounter > inputListsSize) continue; - SERIALIZE(ReplayVersionInit, updateCounter, 0); + eventCounter = pair.first; + + // Since the data structure is a Map it may have more values than allocated data + // Skip it if it is the case + if (eventCounter > eventListsSize) continue; + + // Serialize the event + SERIALIZE(ReplayVersionInit, eventCounter, 0); + + // Serialize all the key and mouse inputs serializeInputs(stream, pair.second.keysDown, writeFlag); serializeInputs(stream, pair.second.keysPressed, writeFlag); - serializeInputs(stream, pair.second.mouseDown, writeFlag); serializeInputs(stream, pair.second.mousePos, writeFlag); - serializeInputs(stream, pair.second.mouseWheel, writeFlag); + // Critical tick timing data per event u32 curTick = pair.second.curTick; - SERIALIZE(ReplayVersionInit, curTick, 0); + SERIALIZE(ReplayVersionInit, curTick, 0); + + // Store the PDA positioning data + s32 pdaXpos = pair.second.pdaPosition.x; + s32 pdaZpos = pair.second.pdaPosition.z; + SERIALIZE(ReplayVersionInit, pdaXpos, 0); + SERIALIZE(ReplayVersionInit, pdaZpos, 0); } + } else { + // Wipe the events and load them from the demo + clearEvents(); - TFE_DarkForces::s_playerTick = plTick; - TFE_DarkForces::s_prevPlayerTick = plPrevTick; - - for (int i = 0; i < inputListsSize; i++) + for (int i = 0; i < eventListsSize; i++) { - SERIALIZE(ReplayVersionInit, updateCounter, 0); + SERIALIZE(ReplayVersionInit, eventCounter, 0); + + // Create a new ReplayEvent object ReplayEvent event = {}; + + // Load the key and mouse inputs event.keysDown = serializeInputs(stream, event.keysDown, writeFlag); event.keysPressed = serializeInputs(stream, event.keysPressed, writeFlag); - event.mouseDown = serializeInputs(stream, event.mouseDown, writeFlag); event.mousePos = serializeInputs(stream, event.mousePos, writeFlag); - event.mouseWheel = serializeInputs(stream, event.mouseWheel, writeFlag); - + // Critical tick timing data per event u32 curTick = 0; SERIALIZE(ReplayVersionInit, curTick, 0); - event.curTick = curTick; - inputEvents[updateCounter] = event; + event.curTick = curTick; + + // Load the PDA positioning data + Vec2i pdaPos; + SERIALIZE(ReplayVersionInit, pdaPos.x, 0); + SERIALIZE(ReplayVersionInit, pdaPos.z, 0); + event.pdaPosition = pdaPos; + + // Load the event into the map for playback + inputEvents[eventCounter] = event; } + + // Load the frame ticks + memcpy(inputEvents[0].frameTicks, frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(frameTicks)); + + // Wipe the event counter and set the max input counter resetCounter(); - setMaxInputCounter(updateCounter); + setMaxCounter(eventCounter); + + // Set the new start time + TFE_System::setStartTime(replayStartTime); } s_replayFile.close(); } - if (!writeFlag) - { - TFE_System::setStartTime(replayStartTime); - } + // Resume the game TFE_DarkForces::time_pause(JFALSE); } void handleEye() { + // If the eye is set, then store or restore the eye position + // This only happens after the first 60 frames typically as the player obj is loaded if (!eyeSet) { if (isRecording()) @@ -316,29 +686,205 @@ namespace TFE_Input } } - void startRecording() - { - if (FileUtil::exists(s_replayPath)) - { - FileUtil::deleteFile(s_replayPath); - } + void disableReplayCheats() + { + TFE_DarkForces::s_invincibility = JFALSE; + TFE_DarkForces::s_aiActive = JTRUE; + TFE_DarkForces::s_smallModeEnabled = JFALSE; + TFE_DarkForces::s_flyMode = JFALSE; + TFE_DarkForces::s_noclip = JFALSE; + TFE_DarkForces::s_oneHitKillEnabled = JFALSE; + TFE_DarkForces::s_instaDeathEnabled = JFALSE; + TFE_DarkForces::s_limitStepHeight = JTRUE; + } + + // This handles a lot of the common logic to ensure the replay is in a consistent state + void startCommonReplayStates() + { + // Wipe all the objects and player data. + objData_clear(); + player_clearEyeObject(); eyeSet = false; + + // Wipe all the previous events clearEvents(); - recordReplaySeed(); + + // Wipe the weapon settings + TFE_DarkForces::resetWeaponFunc(); + + // This is not really needed but for replay comparison reset the turret/mouse task IDs + TFE_DarkForces::resetMouseNum(); + TFE_DarkForces::resetTurretNum(); + + // Player frame collisions affect logic and must be reset + initPlayerCollision(); + + // Wipes the frame counter so it is consistent + resetCounter(); + + // Wipe all inputs inputMapping_endFrame(); - s_recording = true; + + // Ensure that the PDA previous location is identical + TFE_DarkForces::pda_resetTab(); + + // Disable autoaim as window size will affect it + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + playerAutoAim = gameSettings->df_enableAutoaim; + gameSettings->df_enableAutoaim = false; + + // Enable VSYNC for replay playback - can be overriden during replays + vsyncEnabled = TFE_System::getVSync(); + TFE_System::setVsync(true); + + // Ensure we are always in GPU mode for consistency + TFE_Settings_Graphics* graphicSetting = TFE_Settings::getGraphicsSettings(); + replayGraphicsType = graphicSetting->rendererIndex; + graphicSetting->rendererIndex = 1; + + // Preserve the original frame rate + gameFrameLimit = TFE_System::frameLimiter_get(); + + // Disable cheats that could affect the replay + disableReplayCheats(); + } + + void endCommonReplayStates() + { + // Restore settings to their original state + TFE_Settings::getGameSettings()->df_enableAutoaim = playerAutoAim; + TFE_System::setVsync(vsyncEnabled); + TFE_System::frameLimiter_set(gameFrameLimit); + TFE_Settings::getGraphicsSettings()->rendererIndex = replayGraphicsType; + + if (TFE_Settings::getGameSettings()->df_enableRecordingAll) + { + TFE_Settings::getGameSettings()->df_enableRecording = true; + } + } + + void handleFrameRate() + { + // Set the frame rate for replay playback. + string framePlaybackStr = TFE_FrontEndUI::getPlaybackFramerate(); + + if (strcmp(framePlaybackStr.c_str(), "Original") == 0) + { + s32 frameRate = TFE_FrontEndUI::getRecordFramerate(); + TFE_System::frameLimiter_set(frameRate); + TFE_System::setVsync(true); + } + else if (strcmp(framePlaybackStr.c_str(), "Unlimited") == 0) + { + TFE_System::setVsync(false); + } + else + { + s32 playbackFrameRate = std::atoi(framePlaybackStr.c_str()); + TFE_System::frameLimiter_set(playbackFrameRate); + TFE_System::setVsync(true); + } + } + + void sendHudPauseMessage() + { + string msg = "Replay Paused"; + if (!pauseReplay) + { + msg = "Replay Resumed"; + } + TFE_System::logWrite(LOG_MSG, "Replay", msg.c_str()); + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 0, false); + } + + void toggleReplayPause() + { + pauseReplay = !pauseReplay; + sendHudPauseMessage(); + } + + void handleReplayPause() + { + mission_pause(pauseReplay); + } + + void increaseReplayFrameRate() + { + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + if (gameSettings->df_playbackFrameRate == 0) + { + pauseReplay = false; + sendHudPauseMessage(); + } + + if (gameSettings->df_playbackFrameRate < 6) + { + gameSettings->df_playbackFrameRate++; + string msg = "Setting Replay Speed to " + TFE_FrontEndUI::getPlaybackFramerate(); + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 0, false); + handleFrameRate(); + } + } + + void decreaseReplayFrameRate() + { + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + if (gameSettings->df_playbackFrameRate > 0) + { + gameSettings->df_playbackFrameRate--; + } + + if (gameSettings->df_playbackFrameRate == 0) + { + pauseReplay = true; + sendHudPauseMessage(); + } + else + { + handleFrameRate(); + string msg = "Setting Replay Speed to " + TFE_FrontEndUI::getPlaybackFramerate(); + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 0, false); + } + } + + void startRecording() + { + // Handle recording initialization + startCommonReplayStates(); + recordReplaySeed(); + setRecording(true); setDemoPlayback(false); - resetCounter(); saveTick(); - setMaxInputCounter(0); + saveInitTime(); } void endRecording() { - s_recording = false; - FileStream* stream = &s_replayFile; - serializeDemo(&s_replayFile, true); + // Disable recording if it is not enabled for all when finished + if (!TFE_Settings::getGameSettings()->df_enableRecordingAll) + { + TFE_Settings::getGameSettings()->df_enableRecording = false; + } + + setRecording(false); + + // Seriaize the demo events + if (setupPath()) + { + FileStream* stream = &s_replayFile; + serializeDemo(&s_replayFile, true); + } + + // Re-enable cutscenes while recording + enableCutscenes(JTRUE); + + // Wipe any latent input inputMapping_endFrame(); + + endCommonReplayStates(); + + TFE_System::logWrite(LOG_MSG, "Replay", "Finished recording demo..."); + } void recordReplayTime(u64 startTime) @@ -346,33 +892,153 @@ namespace TFE_Input replayStartTime = startTime; } - void loadReplayWrapper(string replayFile, string modName, string levelName) + bool startReplayStatus() { - string x = replayFile; - string y = modName; - string z = levelName; - TFE_Settings::getGameSettings()->df_enableRecording = true; - //IGame* curGame = TFE_FrontEndUI::getCurrentGame(); - //curGame->enable Cutscenes(JFALSE); - TFE_DarkForces::enableCutscenes(JFALSE); + return replayInitialized; + } + void sendEndPlaybackMsg() + { + string msg = "Playback Complete!"; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); } - void loadReplay() + void sendEndRecordingMsg() { - if (TFE_Settings::getGameSettings()->df_enableRecording) return; - eyeSet = false; - clearEvents(); - resetCounter(); - setCounter(1); - inputMapping_endFrame(); + string msg = "Recording Ended..."; + TFE_DarkForces::hud_sendTextMessage(msg.c_str(), 1, false); + } + + // This is a replay wrapper that handles agents and replay configuration + void loadReplayWrapper(string replayFile, string modName, string levelId) + { + // If you are replaying a demo, you should not be recording. + TFE_Settings::getGameSettings()->df_enableRecording = false; + TFE_Settings::getGameSettings()->df_enableReplay = true; + + // Ensure you exit the current game in case you are running + task_freeAll(); + TFE_FrontEndUI::exitToMenu(); + + // Handle Agent + setAgentCount(agent_loadData()); + + // Handle starting parameters + char selectedModCmd[TFE_MAX_PATH]; + sprintf(selectedModCmd, "-u%s", modName.c_str()); + + std::vector modOverrides; + modOverrides.push_back("-c0"); // No cutscenes + + // Override level + if (strlen(levelId.c_str()) > 0) + { + string levelOverride = "-l" + levelId; + modOverrides.push_back(levelOverride); + } + + // Set the replay parameters + TFE_FrontEndUI::setModOverrides(modOverrides); + + // Set the replay path + sprintf(s_replayPath, "%s%s", s_replayDir, replayFile.c_str()); FileStream* stream = &s_replayFile; + + // Load the replay header so we can get the agent data + replayFilehandler = serializeHeaderAgentInfo(&s_replayFile, false); + + // Set the selected mod and GAME state + TFE_FrontEndUI::setSelectedMod(selectedModCmd); + TFE_FrontEndUI::setState(APP_STATE_GAME); + + // Preserve cutscene setting and disable them for the replay + cutscenesEnabled = getCutscenesEnabled(); + enableCutscenes(JFALSE); + + // Wipe any current game that may be running and wipe the menu + IGame* curGame = TFE_FrontEndUI::getCurrentGame(); + curGame = nullptr; + TFE_FrontEndUI::clearMenuState(); + + // This is needed to check if the replay is initialized in Dark Forces Main + replayInitialized = true; + + TFE_System::logWrite(LOG_MSG, "Loading replay %s", s_replayPath); + } + + // This is called from the DarkForcesMain + void loadReplay() + { + startCommonReplayStates(); + + // Start replaying with the first event + setReplayCounter(1); + serializeDemo(&s_replayFile, false); + + // Setup frame rate for replay playback + handleFrameRate(); + + // Load the timing and seeds + loadInitTime(); restoreReplaySeed(); + + // Initialize Demo Playback setDemoPlayback(true); - TFE_DarkForces::s_curTick = inputEvents[0].curTick; - TFE_DarkForces::s_prevTick = inputEvents[0].prevTick; - TFE_DarkForces::s_deltaTime = inputEvents[0].deltaTime; - //memcpy(TFE_DarkForces::s_frameTicks, inputEvents[0].frameTicks, sizeof(fixed16_16) * TFE_ARRAYSIZE(inputEvents[1].frameTicks)); + + // Reset the eye + TFE_DarkForces::player_clearEyeObject(); + + // Ensure you always load the first agent. + s_agentId = 0; + + TFE_System::logWrite(LOG_MSG, "Replay", "Started playing back demo..."); + } + + void restoreAgent() + { + // We always restore the first agent in the list + s_agentId = 0; + s_agentData[s_agentId] = replayAgent.agentData; + setAgentName(replayAgent.agentData.name); + agent_writeSavedData(s_agentId, &replayAgent); + } + + void restoreGameSettings() + { + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); + copyGameSettings(&replayGameSettings, gameSettings); + TFE_Settings::getA11ySettings()->enableHeadwave = playerHeadwave; + TFE_Settings::getGameSettings()->df_enableReplay = false; + } + + void restoreInputs() + { + InputConfig* inputConfig = inputMapping_get(); + inputConfig->mouseMode = (MouseMode)s_mouseMode; + inputConfig->mouseFlags = s_mouseFlags; + inputConfig->mouseSensitivity[0] = s_mouseSensitivity[0]; + inputConfig->mouseSensitivity[1] = s_mouseSensitivity[1]; + } + + void endReplay() + { + replayInitialized = false; + replayFilehandler = -1; + setDemoPlayback(false); + + restoreAgent(); + restoreGameSettings(); + restoreInputs(); + + enableCutscenes(cutscenesEnabled); + endCommonReplayStates(); + + // End missiong and go back to Menu after replay. + TFE_DarkForces::mission_exitLevel(); + task_freeAll(); + TFE_FrontEndUI::exitToMenu(); + + TFE_System::logWrite(LOG_MSG, "Replay", "Finished playing back demo..."); } } \ No newline at end of file diff --git a/TheForceEngine/TFE_Input/replay.h b/TheForceEngine/TFE_Input/replay.h index 425fdd8f9..fc00439a1 100644 --- a/TheForceEngine/TFE_Input/replay.h +++ b/TheForceEngine/TFE_Input/replay.h @@ -2,12 +2,14 @@ #include #include +#include #include #include #include #include #include #include +#include namespace TFE_Input { @@ -17,55 +19,88 @@ namespace TFE_Input ReplayVersionCur = ReplayVersionInit }; - + // Struct for storing replay events struct ReplayEvent { + // Store keyboard and mouse input std::vector keysDown; std::vector keysPressed; - std::vector mouseDown; std::vector mousePos; - std::vector mouseWheel; + + // Store the tick information Tick curTick; Tick prevTick; + Tick plTick; + Tick plPrevTick; + fixed16_16 frameTicks[13]; + + // Store time information fixed16_16 deltaTime; f64 timeAccum; - fixed16_16 frameTicks[13]; - //std::tuple timeData; + // Store PDA vector position + Vec2i pdaPosition = {}; + + // Wipe the replay event void clear() { keysDown.clear(); keysPressed.clear(); - mouseDown.clear(); mousePos.clear(); - mouseWheel.clear(); curTick = 0; prevTick = 0; deltaTime = 0; timeAccum = 0.0; + plTick = 0; + plPrevTick = 0; std::fill(std::begin(frameTicks), std::end(frameTicks), 0); + pdaPosition = {}; } }; - + extern std::unordered_map inputEvents; + extern char s_replayDir[TFE_MAX_PATH]; + + std::string convertToString(std::vector keysDown); + std::vector convertFromString(std::string keyStr); void initReplays(); - void recordReplayTime(u64 startTime); - void startRecording(); - void endRecording(); - void loadReplay(); + + void storePDAPosition(Vec2i pos); + Vec2i getPDAPosition(); + void populateReplayDirectory(std::vector& dir); bool loadReplayHeader(const char* filename, TFE_SaveSystem::SaveHeader* header); void loadReplayWrapper(string replayFile, string modName, string levelName); + void getAgentPath(char* agentPath); + bool isRecording(); void setRecording(bool recording); bool isDemoPlayback(); void setDemoPlayback(bool playback); + + void toggleReplayPause(); + void handleReplayPause(); + void increaseReplayFrameRate(); + void decreaseReplayFrameRate(); + + bool startReplayStatus(); void recordReplaySeed(); void restoreReplaySeed(); - void saveTick(); + void recordReplayTime(u64 startTime); + + void saveTick(); void loadTick(); void handleEye(); - std::string convertToString(std::vector keysDown); - std::vector convertFromString(std::string keyStr); + void sendEndPlaybackMsg(); + void sendEndRecordingMsg(); + + void recordEvent(int action, KeyboardCode keyCode, bool isPress); + void replayEvent(); + + void startRecording(); + void endRecording(); + void loadReplay(); + void endReplay(); + } \ No newline at end of file diff --git a/TheForceEngine/TFE_Settings/settings.cpp b/TheForceEngine/TFE_Settings/settings.cpp index e884d852f..1b36e429b 100644 --- a/TheForceEngine/TFE_Settings/settings.cpp +++ b/TheForceEngine/TFE_Settings/settings.cpp @@ -113,7 +113,7 @@ namespace TFE_Settings void parseDark_ForcesSettings(const char* key, const char* value); void parseOutlawsSettings(const char* key, const char* value); void parseCVars(const char* key, const char* value); - void checkGameData(); + void checkGameData(); // Is this necessary? It is not used. ////////////////////////////////////////////////////////////////////////////////// // Implementation @@ -521,6 +521,29 @@ namespace TFE_Settings writeKeyValue_String(settings, "game", s_game.game); } + void writeDarkForcesGameSettings(FileStream& settings) + { + writeKeyValue_Int(settings, "airControl", s_gameSettings.df_airControl); + writeKeyValue_Bool(settings, "bobaFettFacePlayer", s_gameSettings.df_bobaFettFacePlayer); + writeKeyValue_Bool(settings, "smoothVUEs", s_gameSettings.df_smoothVUEs); + writeKeyValue_Bool(settings, "disableFightMusic", s_gameSettings.df_disableFightMusic); + writeKeyValue_Bool(settings, "enableAutoaim", s_gameSettings.df_enableAutoaim); + writeKeyValue_Bool(settings, "showSecretFoundMsg", s_gameSettings.df_showSecretFoundMsg); + writeKeyValue_Bool(settings, "autorun", s_gameSettings.df_autorun); + writeKeyValue_Bool(settings, "crouchToggle", s_gameSettings.df_crouchToggle); + writeKeyValue_Bool(settings, "ignoreInfLimit", s_gameSettings.df_ignoreInfLimit); + writeKeyValue_Bool(settings, "stepSecondAlt", s_gameSettings.df_stepSecondAlt); + writeKeyValue_Int(settings, "pitchLimit", s_gameSettings.df_pitchLimit); + writeKeyValue_Bool(settings, "solidWallFlagFix", s_gameSettings.df_solidWallFlagFix); + writeKeyValue_Bool(settings, "enableUnusedItem", s_gameSettings.df_enableUnusedItem); + writeKeyValue_Bool(settings, "jsonAiLogics", s_gameSettings.df_jsonAiLogics); + writeKeyValue_Bool(settings, "df_showReplayCounter", s_gameSettings.df_showReplayCounter); + writeKeyValue_Int(settings, "df_recordFrameRate", s_gameSettings.df_recordFrameRate); + writeKeyValue_Int(settings, "df_playbackFrameRate", s_gameSettings.df_playbackFrameRate); + writeKeyValue_Bool(settings, "df_enableRecording", s_gameSettings.df_enableRecording); + writeKeyValue_Bool(settings, "df_enableRecordingAll", s_gameSettings.df_enableRecordingAll); + } + void writePerGameSettings(FileStream& settings) { for (u32 i = 0; i < Game_Count; i++) @@ -531,20 +554,7 @@ namespace TFE_Settings if (i == Game_Dark_Forces) { - writeKeyValue_Int(settings, "airControl", s_gameSettings.df_airControl); - writeKeyValue_Bool(settings, "bobaFettFacePlayer", s_gameSettings.df_bobaFettFacePlayer); - writeKeyValue_Bool(settings, "smoothVUEs", s_gameSettings.df_smoothVUEs); - writeKeyValue_Bool(settings, "disableFightMusic", s_gameSettings.df_disableFightMusic); - writeKeyValue_Bool(settings, "enableAutoaim", s_gameSettings.df_enableAutoaim); - writeKeyValue_Bool(settings, "showSecretFoundMsg", s_gameSettings.df_showSecretFoundMsg); - writeKeyValue_Bool(settings, "autorun", s_gameSettings.df_autorun); - writeKeyValue_Bool(settings, "crouchToggle", s_gameSettings.df_crouchToggle); - writeKeyValue_Bool(settings, "ignoreInfLimit", s_gameSettings.df_ignoreInfLimit); - writeKeyValue_Bool(settings, "stepSecondAlt", s_gameSettings.df_stepSecondAlt); - writeKeyValue_Int(settings, "pitchLimit", s_gameSettings.df_pitchLimit); - writeKeyValue_Bool(settings, "solidWallFlagFix", s_gameSettings.df_solidWallFlagFix); - writeKeyValue_Bool(settings, "enableUnusedItem", s_gameSettings.df_enableUnusedItem); - writeKeyValue_Bool(settings, "jsonAiLogics", s_gameSettings.df_jsonAiLogics); + writeDarkForcesGameSettings(settings); } } } @@ -1157,6 +1167,26 @@ namespace TFE_Settings { s_gameSettings.df_jsonAiLogics = parseBool(value); } + else if (strcasecmp("df_showReplayCounter", key) == 0) + { + s_gameSettings.df_showReplayCounter = parseBool(value); + } + else if (strcasecmp("df_recordFrameRate", key) == 0) + { + s_gameSettings.df_recordFrameRate = parseInt(value); + } + else if (strcasecmp("df_playbackFrameRate", key) == 0) + { + s_gameSettings.df_playbackFrameRate = parseInt(value); + } + else if (strcasecmp("df_enableRecording", key) == 0) + { + s_gameSettings.df_enableRecording = parseBool(value); + } + else if (strcasecmp("df_enableRecordingAll", key) == 0) + { + s_gameSettings.df_enableRecordingAll = parseBool(value); + } } void parseOutlawsSettings(const char* key, const char* value) diff --git a/TheForceEngine/TFE_Settings/settings.h b/TheForceEngine/TFE_Settings/settings.h index 1ead781f0..3201efbe5 100644 --- a/TheForceEngine/TFE_Settings/settings.h +++ b/TheForceEngine/TFE_Settings/settings.h @@ -217,8 +217,12 @@ struct TFE_Settings_Game bool df_solidWallFlagFix = true; // Solid wall flag is enforced for collision with moving walls. bool df_enableUnusedItem = true; // Enables the unused item in the inventory (delt 10). bool df_jsonAiLogics = true; // AI logics can be loaded from external JSON files - bool df_enableRecording = false; // Enable recording of gameplay. + bool df_enableRecording = false; // Enable recording of gameplay + bool df_enableRecordingAll = false; // Always record gameplay. bool df_enableReplay = false; // Enable replay of gameplay. + bool df_showReplayCounter = false; // Show the replay counter on the HUD. + s32 df_recordFrameRate = 4; // Recording Framerate value + s32 df_playbackFrameRate = 2; // Playback Framerate value PitchLimit df_pitchLimit = PITCH_VANILLA_PLUS; }; @@ -414,4 +418,6 @@ namespace TFE_Settings void autodetectGamePaths(); void clearModSettings(); void loadCustomModSettings(); + void parseIniFile(const char* buffer, size_t len); + void writeDarkForcesGameSettings(FileStream& settings); } diff --git a/TheForceEngine/TFE_System/frameLimiter.cpp b/TheForceEngine/TFE_System/frameLimiter.cpp index e050601bf..d38d8ec2f 100644 --- a/TheForceEngine/TFE_System/frameLimiter.cpp +++ b/TheForceEngine/TFE_System/frameLimiter.cpp @@ -37,6 +37,12 @@ namespace TFE_System } } + f64 frameLimiter_get() + { + return s_limitFPS; + } + + void frameLimiter_begin() { s_beginTicks = getCurrentTimeInTicks(); @@ -44,7 +50,6 @@ namespace TFE_System void frameLimiter_end() { - return; if (s_limitDelta == 0.0) { return; } u64 curTick = TFE_System::getCurrentTimeInTicks(); diff --git a/TheForceEngine/TFE_System/frameLimiter.h b/TheForceEngine/TFE_System/frameLimiter.h index 505b3096c..d8b2a26ce 100644 --- a/TheForceEngine/TFE_System/frameLimiter.h +++ b/TheForceEngine/TFE_System/frameLimiter.h @@ -11,6 +11,7 @@ namespace TFE_System // Set the frame limit in Frames Per Second (FPS). // A value of 0 sets no limit. void frameLimiter_set(f64 limitFPS = 0.0); + f64 frameLimiter_get(); f64 frameLimiter_getAccuracy(); void frameLimiter_begin(); diff --git a/TheForceEngine/main.cpp b/TheForceEngine/main.cpp index a688b2286..cbaee54bf 100644 --- a/TheForceEngine/main.cpp +++ b/TheForceEngine/main.cpp @@ -354,7 +354,7 @@ void setAppState(AppState newState, int argc, char* argv[]) if (validatePath()) { TFE_Game* gameInfo = TFE_Settings::getGame(); - if (!s_curGame || gameInfo->id != s_curGame->id) + if (!s_curGame || gameInfo->id != s_curGame->id || startReplayStatus()) { s_soundPaused = false; if (s_curGame) @@ -666,19 +666,9 @@ int main(int argc, char* argv[]) // Game loop u32 frame = 0u; - TFE_System::setFrame(frame); bool showPerf = false; bool relativeMode = false; TFE_System::logWrite(LOG_MSG, "Progam Flow", "The Force Engine Game Loop Started"); - Tick accum = 0; - Tick step = 300000;// hardcode to 3 ticks per frame. - - Tick start = TFE_System::getCurrentTimeInTicks(); - Tick maxdt = 0; - Tick avg = start; - int skipCount = 0; - bool canwork = true; - while (s_loop && !TFE_System::quitMessagePosted()) { @@ -703,8 +693,13 @@ int main(int argc, char* argv[]) SDL_Event event; while (SDL_PollEvent(&event)) { handleEvent(event); } - // Inputs Main Entry - handleInputs(); + // Inputs Main Entry - skip frame any further processing during replay pause + if (!handleInputs()) + { + TFE_Input::endFrame(); + inputMapping_endFrame(); + continue; + } // Can we save? TFE_FrontEndUI::setCanSave(s_curGame ? s_curGame->canSave() : false); @@ -736,14 +731,36 @@ int main(int argc, char* argv[]) char* selectedMod = TFE_FrontEndUI::getSelectedMod(); if (selectedMod && selectedMod[0] && appState == APP_STATE_GAME) - { + { + + // Handle mod overrides and setings including calls from replay module char* newArgs[16]; - for (s32 i = 0; i < argc && i < 15; i++) + newArgs[0] = argv[0]; + + std::vector modOverrides; + modOverrides = TFE_FrontEndUI::getModOverrides(); + + size_t newArgc = 0; + newArgs[newArgc] = argv[newArgc]; + size_t modOverrideSize = modOverrides.size(); + newArgc += modOverrideSize + 1; + if (modOverrideSize > 0) + { + for (s32 i = 0; i < modOverrides.size(); i++) + { + newArgs[i+1] = new char[modOverrides[i].size() + 1]; + std::strcpy(newArgs[i+1], modOverrides[i].c_str()); + } + } + else { - newArgs[i] = argv[i]; + for (s32 i = 1; i < argc && i < 15; i++) + { + newArgs[i] = argv[i]; + } } - newArgs[argc] = selectedMod; - setAppState(appState, argc + 1, newArgs); + newArgs[newArgc] = selectedMod; + setAppState(appState, newArgc + 1, newArgs); } else { @@ -865,17 +882,6 @@ int main(int argc, char* argv[]) } } - /* - if (inputMapping_getActionState(IADF_DEMO_RECORD) == STATE_PRESSED) - { - TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); - gameSettings->df_enableRecording = !gameSettings->df_enableRecording; - TFE_Input::setRecordingAllowed(gameSettings->df_enableRecording); - string cur_setting = isRecordingAllowed() ? "true" : "false"; - string message = "Toggling Recording. Currently " + cur_setting; - TFE_DarkForces::hud_sendTextMessage(message.c_str(), 1); - }*/ - #ifdef ENABLE_FORCE_SCRIPT TFE_ForceScript::update(); #endif @@ -950,9 +956,6 @@ int main(int argc, char* argv[]) } } - TFE_System::logWrite(LOG_MSG, "Progam Flow", "END average = %u maxdt = %u", avg, maxdt); - - if (s_curGame) { freeGame(s_curGame); From f705b90e749966fa6ea9d435139544be0e0f34ea Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:01:10 -0500 Subject: [PATCH 06/64] Temp muting logging for events --- TheForceEngine/TFE_Input/inputMapping.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TheForceEngine/TFE_Input/inputMapping.cpp b/TheForceEngine/TFE_Input/inputMapping.cpp index 75d41956a..1a8ad4b5c 100644 --- a/TheForceEngine/TFE_Input/inputMapping.cpp +++ b/TheForceEngine/TFE_Input/inputMapping.cpp @@ -774,8 +774,9 @@ namespace TFE_Input if (printcounter != -1) { - TFE_System::logWrite(LOG_MSG, "LOG", "----------------------------------------------------------------- %d ---------------------------------------------------------------------", printcounter); - TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), printcounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); + // temp muting + //TFE_System::logWrite(LOG_MSG, "LOG", "----------------------------------------------------------------- %d ---------------------------------------------------------------------", printcounter); + //TFE_System::logWrite(LOG_MSG, "LOG", hudDataStr.c_str(), printcounter, TFE_DarkForces::s_curTick, TFE_DarkForces::s_playerTick, TFE_DarkForces::s_prevPlayerTick, TFE_DarkForces::s_deltaTime); } } From e425b757f5d5821230ad77b847675ea29d654f0b Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:54:57 -0500 Subject: [PATCH 07/64] updating projet files --- TheForceEngine/TheForceEngine.vcxproj | 20 ++++++++++--------- TheForceEngine/TheForceEngine.vcxproj.filters | 6 ++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/TheForceEngine/TheForceEngine.vcxproj b/TheForceEngine/TheForceEngine.vcxproj index a5cecff2d..593ebd00e 100644 --- a/TheForceEngine/TheForceEngine.vcxproj +++ b/TheForceEngine/TheForceEngine.vcxproj @@ -39,60 +39,60 @@ {31CD2991-9EF0-4AA9-AEEB-BA1194D2DAB4} Win32Proj TheForceEngine - 10.0.17763.0 + 10.0 Application true - v141 + v143 Unicode Application false - v141 + v143 true Unicode Application false - v141 + v143 true Unicode Application false - v141 + v143 true Unicode Application true - v141 + v143 NotSet Application false - v141 + v143 true NotSet Application false - v141 + v143 true NotSet Application false - v141 + v143 true NotSet @@ -568,6 +568,7 @@ echo ^)"; + @@ -946,6 +947,7 @@ echo ^)"; + diff --git a/TheForceEngine/TheForceEngine.vcxproj.filters b/TheForceEngine/TheForceEngine.vcxproj.filters index ff56aa06d..273206a91 100644 --- a/TheForceEngine/TheForceEngine.vcxproj.filters +++ b/TheForceEngine/TheForceEngine.vcxproj.filters @@ -217,6 +217,9 @@ Source\TFE_Input + + Source\TFE_Input + Source\TFE_Ui\imGUI @@ -1829,6 +1832,9 @@ Source\TFE_Input + + + Source\TFE_Input Source\TFE_Jedi\Renderer\RClassic_Fixed From e47d5d1f653cdd7b0a53c0edbaad72a66fe56012 Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Sun, 23 Feb 2025 02:39:41 -0500 Subject: [PATCH 08/64] Updating Cmakelist --- CMakeLists.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index effc05192..919dd4c5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,20 +60,25 @@ if(ENABLE_TFE) set_target_properties(tfe PROPERTIES OUTPUT_NAME "theforceengine") if(UNIX) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) - find_package(SDL2 2.0.20 REQUIRED) + find_package(SDL2 REQUIRED) pkg_check_modules(SDL2_IMAGE REQUIRED SDL2_image) target_include_directories(tfe PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(tfe PRIVATE ${SDL2_INCLUDE_DIRS}) target_include_directories(tfe PRIVATE ${SDL2_IMAGE_INCLUDE_DIRS}) - target_link_libraries(tfe PRIVATE SDL2::SDL2main SDL2::SDL2 + target_link_libraries(tfe PRIVATE ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} + ${CMAKE_DL_LIBS} + Threads::Threads ) # set up build directory to be able to run TFE immediately: symlink # the necessary support file directories into the build env. execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/Captions) execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/Documentation) + execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/ExternalData) execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/Fonts) execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/Mods) execute_process(COMMAND ln -sf ${CMAKE_SOURCE_DIR}/TheForceEngine/Shaders) @@ -130,6 +135,7 @@ if(ENABLE_TFE) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/Captions" "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/Documentation" + "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/ExternalData" "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/UI_Text" "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/UI_Images" "${CMAKE_CURRENT_SOURCE_DIR}/TheForceEngine/EditorDef" From c6cd639b97e2967d0d2ea9c128ee9c81d0748741 Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Sun, 23 Feb 2025 04:24:07 -0500 Subject: [PATCH 09/64] updating project and merging some files --- .gitignore | 3 + TheForceEngine/EditorDef/Scripts/LevelInfo.fs | 18 +++--- TheForceEngine/EditorDef/Scripts/Loop.fs | 3 +- TheForceEngine/EditorDef/Scripts/Test.fs | 18 ++++++ TheForceEngine/TFE_Asset/dfKeywords.cpp | 2 + TheForceEngine/TFE_Asset/dfKeywords.h | 2 + TheForceEngine/TFE_Asset/modelAsset_jedi.cpp | 40 +++++++++++-- TheForceEngine/TheForceEngine.svg | 2 + TheForceEngine/TheForceEngine.vcxproj | 60 +++++++++++++++---- TheForceEngine/main.cpp | 2 +- TheForceEngine/main.h | 1 - 11 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 TheForceEngine/EditorDef/Scripts/Test.fs create mode 100644 TheForceEngine/TheForceEngine.svg diff --git a/.gitignore b/.gitignore index 5eeb49879..d4e300ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,6 @@ CMakeFiles/ # Linux TheForceEngine/tfelnx +/TheForceEngine/ModCache +/TheForceEngine/Replays - Copy +/TheForceEngine - Copy diff --git a/TheForceEngine/EditorDef/Scripts/LevelInfo.fs b/TheForceEngine/EditorDef/Scripts/LevelInfo.fs index 8d7482957..c332326ba 100644 --- a/TheForceEngine/EditorDef/Scripts/LevelInfo.fs +++ b/TheForceEngine/EditorDef/Scripts/LevelInfo.fs @@ -4,15 +4,15 @@ void main() { - string name = level.getName(); - string slot = level.getSlot(); - int sectorCount = level.getSectorCount(); - int entityCount = level.getEntityCount(); - int levelNoteCount = level.getLevelNoteCount(); - int guidelineCount = level.getGuidelineCount(); - int minLayer = level.getMinLayer(); - int maxLayer = level.getMaxLayer(); - float2 parallax = level.getParallax(); + string name = level.name; + string slot = level.slot; + int sectorCount = level.sectorCount; + int entityCount = level.entityCount; + int levelNoteCount = level.levelNoteCount; + int guidelineCount = level.guidelineCount; + int minLayer = level.minLayer; + int maxLayer = level.maxLayer; + float2 parallax = level.parallax; system.print("==== Level Info ====\n" "Name: {}\n" diff --git a/TheForceEngine/EditorDef/Scripts/Loop.fs b/TheForceEngine/EditorDef/Scripts/Loop.fs index de58e26eb..fd62ec652 100644 --- a/TheForceEngine/EditorDef/Scripts/Loop.fs +++ b/TheForceEngine/EditorDef/Scripts/Loop.fs @@ -13,10 +13,11 @@ void main() { + int[] id = { 1, 3, 4, 5, 6, 780, 103 }; // Loop 20 times, once per second. for (int i = 0; i < 20; i++) { - system.print("Loop# " + (i + 1)); + system.print("Loop# {}, id {}", (i + 1), id[i % id.length]); yield(1.0); } } diff --git a/TheForceEngine/EditorDef/Scripts/Test.fs b/TheForceEngine/EditorDef/Scripts/Test.fs new file mode 100644 index 000000000..550124c68 --- /dev/null +++ b/TheForceEngine/EditorDef/Scripts/Test.fs @@ -0,0 +1,18 @@ +///////////////////////////////////////////////////////////// +// A basic script to test Vector Math. +///////////////////////////////////////////////////////////// + +void add(float a, float b) +{ + system.print("{} + {} = {}", a, b, a + b); +} + +void stringTest(string str) +{ + system.print("string = {}", str); +} + +void float2Add(float2 a, float2 b) +{ + system.print("{} + {} = {}", a, b, a + b); +} \ No newline at end of file diff --git a/TheForceEngine/TFE_Asset/dfKeywords.cpp b/TheForceEngine/TFE_Asset/dfKeywords.cpp index c4d65c92e..fb85ff03c 100644 --- a/TheForceEngine/TFE_Asset/dfKeywords.cpp +++ b/TheForceEngine/TFE_Asset/dfKeywords.cpp @@ -225,6 +225,8 @@ static const char* c_keywords[] = "PERSONALITY", "SEQEND", "M_TRIGGER", + // TFE + "SCRIPTCALL:", }; #define KEYWORD_COUNT TFE_ARRAYSIZE(c_keywords) diff --git a/TheForceEngine/TFE_Asset/dfKeywords.h b/TheForceEngine/TFE_Asset/dfKeywords.h index 204859041..c8ea12725 100644 --- a/TheForceEngine/TFE_Asset/dfKeywords.h +++ b/TheForceEngine/TFE_Asset/dfKeywords.h @@ -232,6 +232,8 @@ enum KEYWORD KW_PERSONALITY, // not implemented in DF KW_SEQEND, KW_M_TRIGGER, // INF + // TFE ADDED + KW_SCRIPTCALL, KW_COUNT }; diff --git a/TheForceEngine/TFE_Asset/modelAsset_jedi.cpp b/TheForceEngine/TFE_Asset/modelAsset_jedi.cpp index b870bfbaa..68a1b8dc3 100644 --- a/TheForceEngine/TFE_Asset/modelAsset_jedi.cpp +++ b/TheForceEngine/TFE_Asset/modelAsset_jedi.cpp @@ -644,7 +644,15 @@ namespace TFE_Model_Jedi if (strcasecmp(shading, "FLAT") == 0) { - polygon->shading = PSHADE_FLAT; + if (TFE_Settings::getGraphicsSettings()->forceGouraudShading) + { + polygon->shading = PSHADE_GOURAUD; + model->flags |= MFLAG_VERTEX_LIT; + } + else + { + polygon->shading = PSHADE_FLAT; + } } else if (strcasecmp(shading, "VERTEX") == 0) { @@ -653,7 +661,15 @@ namespace TFE_Model_Jedi } else if (strcasecmp(shading, "TEXTURE") == 0) { - polygon->shading = PSHADE_TEXTURE; + if (TFE_Settings::getGraphicsSettings()->forceGouraudShading) + { + polygon->shading = PSHADE_GOURAUD_TEXTURE; + model->flags |= MFLAG_VERTEX_LIT; + } + else + { + polygon->shading = PSHADE_TEXTURE; + } } else if (strcasecmp(shading, "GOURAUD") == 0) { @@ -716,7 +732,15 @@ namespace TFE_Model_Jedi if (strcasecmp(shading, "FLAT") == 0) { - polygon->shading = PSHADE_FLAT; + if (TFE_Settings::getGraphicsSettings()->forceGouraudShading) + { + polygon->shading = PSHADE_GOURAUD; + model->flags |= MFLAG_VERTEX_LIT; + } + else + { + polygon->shading = PSHADE_FLAT; + } } else if (strcasecmp(shading, "VERTEX") == 0) { @@ -725,7 +749,15 @@ namespace TFE_Model_Jedi } else if (strcasecmp(shading, "TEXTURE") == 0) { - polygon->shading = PSHADE_TEXTURE; + if (TFE_Settings::getGraphicsSettings()->forceGouraudShading) + { + polygon->shading = PSHADE_GOURAUD_TEXTURE; + model->flags |= MFLAG_VERTEX_LIT; + } + else + { + polygon->shading = PSHADE_TEXTURE; + } } else if (strcasecmp(shading, "GOURAUD") == 0) { diff --git a/TheForceEngine/TheForceEngine.svg b/TheForceEngine/TheForceEngine.svg new file mode 100644 index 000000000..bab2f7ecd --- /dev/null +++ b/TheForceEngine/TheForceEngine.svg @@ -0,0 +1,2 @@ + + diff --git a/TheForceEngine/TheForceEngine.vcxproj b/TheForceEngine/TheForceEngine.vcxproj index 593ebd00e..abc711e52 100644 --- a/TheForceEngine/TheForceEngine.vcxproj +++ b/TheForceEngine/TheForceEngine.vcxproj @@ -39,60 +39,60 @@ {31CD2991-9EF0-4AA9-AEEB-BA1194D2DAB4} Win32Proj TheForceEngine - 10.0 + 10.0.17763.0 Application true - v143 + v141 Unicode Application false - v143 + v141 true Unicode Application false - v143 + v141 true Unicode Application false - v143 + v141 true Unicode Application true - v143 + v141 NotSet Application false - v143 + v141 true NotSet Application false - v143 + v141 true NotSet Application false - v143 + v141 true NotSet @@ -439,6 +439,14 @@ echo ^)"; + + + + + + + + @@ -498,7 +506,6 @@ echo ^)"; - @@ -509,6 +516,7 @@ echo ^)"; + @@ -556,7 +564,17 @@ echo ^)"; + + + + + + + + + + @@ -568,7 +586,7 @@ echo ^)"; - + @@ -699,6 +717,7 @@ echo ^)"; + @@ -830,6 +849,14 @@ echo ^)"; + + + + + + + + @@ -897,6 +924,7 @@ echo ^)"; + @@ -936,7 +964,16 @@ echo ^)"; + + + + + + + + + @@ -1056,6 +1093,7 @@ echo ^)"; + diff --git a/TheForceEngine/main.cpp b/TheForceEngine/main.cpp index cbaee54bf..04128d562 100644 --- a/TheForceEngine/main.cpp +++ b/TheForceEngine/main.cpp @@ -671,7 +671,7 @@ int main(int argc, char* argv[]) TFE_System::logWrite(LOG_MSG, "Progam Flow", "The Force Engine Game Loop Started"); while (s_loop && !TFE_System::quitMessagePosted()) { - + TFE_FRAME_BEGIN(); TFE_System::frameLimiter_begin(); bool enableRelative = TFE_Input::relativeModeEnabled(); if (enableRelative != relativeMode) diff --git a/TheForceEngine/main.h b/TheForceEngine/main.h index 1685d3e70..d00d47e78 100644 --- a/TheForceEngine/main.h +++ b/TheForceEngine/main.h @@ -1,4 +1,3 @@ #pragma once #include "resource.h" - From 83728b33a8c3d987d0ba03944f3d98144092d38a Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Sun, 23 Feb 2025 10:54:58 -0500 Subject: [PATCH 10/64] Ignoring modcache --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d4e300ac6..f38f1453c 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ .vs/ Internal/ TheForceEngine/Games/ +TheForceEngine/ModCache/ TheForceEngine/Mods/ TheForceEngine/screenshots/ TheForceEngine/Reference/ @@ -79,6 +80,3 @@ CMakeFiles/ # Linux TheForceEngine/tfelnx -/TheForceEngine/ModCache -/TheForceEngine/Replays - Copy -/TheForceEngine - Copy From fef63e95308cb73b004947bb8ddf56d2cc473b17 Mon Sep 17 00:00:00 2001 From: Karjala22 <78927981+Karjala22@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:03:47 -0500 Subject: [PATCH 11/64] Merge of 1.15 code --- .../ExternalData/DarkForces/effects.json | 220 ++++ .../ExternalData/DarkForces/pickups.json | 493 +++++++++ .../ExternalData/DarkForces/projectiles.json | 442 ++++++++ .../ExternalData/DarkForces/weapons.json | 388 +++++++ TheForceEngine/ScriptTests/Readme.txt | 2 + TheForceEngine/ScriptTests/Test_float2.fs | 85 ++ TheForceEngine/ScriptTests/Test_float2x2.fs | 55 + TheForceEngine/ScriptTests/Test_float3.fs | 118 +++ TheForceEngine/ScriptTests/Test_float4.fs | 420 ++++++++ TheForceEngine/TFE_DarkForces/Actor/actor.cpp | 194 ++-- TheForceEngine/TFE_DarkForces/Actor/actor.h | 11 +- .../TFE_DarkForces/Actor/actorInternal.h | 4 +- .../TFE_DarkForces/Actor/actorModule.h | 34 +- .../TFE_DarkForces/Actor/animTables.cpp | 1 + .../TFE_DarkForces/Actor/animTables.h | 15 + .../TFE_DarkForces/Actor/bobaFett.cpp | 16 +- .../TFE_DarkForces/Actor/dragon.cpp | 30 +- .../TFE_DarkForces/Actor/enemies.cpp | 22 +- .../TFE_DarkForces/Actor/exploders.cpp | 20 +- .../TFE_DarkForces/Actor/flyers.cpp | 12 +- .../TFE_DarkForces/Actor/mousebot.cpp | 24 +- .../TFE_DarkForces/Actor/mousebot.h | 2 +- .../TFE_DarkForces/Actor/phaseOne.cpp | 24 +- .../TFE_DarkForces/Actor/phaseThree.cpp | 42 +- .../TFE_DarkForces/Actor/phaseTwo.cpp | 44 +- .../TFE_DarkForces/Actor/scenery.cpp | 10 +- TheForceEngine/TFE_DarkForces/Actor/sewer.cpp | 40 +- .../TFE_DarkForces/Actor/troopers.cpp | 18 +- .../TFE_DarkForces/Actor/turret.cpp | 8 +- TheForceEngine/TFE_DarkForces/Actor/turret.h | 2 +- .../TFE_DarkForces/Actor/welder.cpp | 4 +- TheForceEngine/TFE_DarkForces/GameUI/pda.cpp | 4 + .../TFE_DarkForces/Scripting/CMakeLists.txt | 2 + .../TFE_DarkForces/Scripting/gameScripts.cpp | 58 ++ .../TFE_DarkForces/Scripting/gameScripts.h | 15 + .../TFE_DarkForces/Scripting/gs_game.cpp | 29 + .../TFE_DarkForces/Scripting/gs_game.h | 25 + .../TFE_DarkForces/Scripting/gs_level.cpp | 238 +++++ .../TFE_DarkForces/Scripting/gs_level.h | 33 + .../TFE_DarkForces/Scripting/gs_system.cpp | 361 +++++++ .../TFE_DarkForces/Scripting/gs_system.h | 30 + .../TFE_DarkForces/Scripting/scriptElev.cpp | 42 + .../TFE_DarkForces/Scripting/scriptElev.h | 28 + .../TFE_DarkForces/Scripting/scriptSector.cpp | 204 ++++ .../TFE_DarkForces/Scripting/scriptSector.h | 29 + .../Scripting/scriptTexture.cpp | 40 + .../TFE_DarkForces/Scripting/scriptTexture.h | 30 + .../TFE_DarkForces/Scripting/scriptWall.cpp | 259 +++++ .../TFE_DarkForces/Scripting/scriptWall.h | 36 + TheForceEngine/TFE_DarkForces/agent.cpp | 20 +- .../TFE_DarkForces/darkForcesMain.cpp | 272 +++-- .../TFE_DarkForces/darkForcesMain.h | 3 +- TheForceEngine/TFE_DarkForces/generator.cpp | 19 +- TheForceEngine/TFE_DarkForces/hitEffect.cpp | 220 +--- TheForceEngine/TFE_DarkForces/hud.cpp | 34 +- TheForceEngine/TFE_DarkForces/hud.h | 1 - TheForceEngine/TFE_DarkForces/item.cpp | 25 +- TheForceEngine/TFE_DarkForces/item.h | 8 +- TheForceEngine/TFE_DarkForces/logic.cpp | 9 +- TheForceEngine/TFE_DarkForces/mission.cpp | 25 +- TheForceEngine/TFE_DarkForces/mission.h | 2 - TheForceEngine/TFE_DarkForces/pickup.cpp | 623 +++-------- TheForceEngine/TFE_DarkForces/pickup.h | 6 +- TheForceEngine/TFE_DarkForces/player.cpp | 223 ++-- TheForceEngine/TFE_DarkForces/player.h | 24 +- TheForceEngine/TFE_DarkForces/projectile.cpp | 689 ++++--------- TheForceEngine/TFE_DarkForces/projectile.h | 6 +- TheForceEngine/TFE_DarkForces/random.cpp | 6 - TheForceEngine/TFE_DarkForces/sound.cpp | 2 +- TheForceEngine/TFE_DarkForces/time.cpp | 4 +- TheForceEngine/TFE_DarkForces/vueLogic.cpp | 15 +- TheForceEngine/TFE_DarkForces/weapon.cpp | 347 ++----- TheForceEngine/TFE_DarkForces/weapon.h | 17 +- .../TFE_DarkForces/weaponFireFunc.cpp | 516 +++++++--- .../TFE_DarkForces/weaponFireFunc.h | 5 +- .../TFE_Editor/AssetBrowser/assetBrowser.cpp | 2 +- .../TFE_Editor/EditorAsset/editorFrame.cpp | 4 +- .../EditorAsset/editorLevelPreview.cpp | 4 +- .../TFE_Editor/EditorAsset/editorObj3D.cpp | 4 +- .../TFE_Editor/EditorAsset/editorSound.cpp | 2 +- .../TFE_Editor/EditorAsset/editorSprite.cpp | 4 +- .../TFE_Editor/EditorAsset/editorTexture.cpp | 4 +- .../Scripting/levelEditorScripts.cpp | 47 +- .../Scripting/levelEditorScripts.h | 7 +- .../LevelEditor/Scripting/ls_draw.cpp | 28 +- .../LevelEditor/Scripting/ls_draw.h | 8 +- .../LevelEditor/Scripting/ls_level.cpp | 218 ++-- .../LevelEditor/Scripting/ls_level.h | 26 +- .../LevelEditor/Scripting/ls_selection.cpp | 22 +- .../LevelEditor/Scripting/ls_selection.h | 8 +- .../LevelEditor/Scripting/ls_system.cpp | 229 ++++- .../LevelEditor/Scripting/ls_system.h | 7 +- .../TFE_Editor/LevelEditor/infoPanel.cpp | 4 +- .../TFE_Editor/LevelEditor/levelEditor.cpp | 14 +- .../TFE_Editor/LevelEditor/levelEditorData.h | 3 +- .../TFE_Editor/LevelEditor/levelEditorInf.cpp | 125 ++- .../TFE_Editor/LevelEditor/levelEditorInf.h | 13 + TheForceEngine/TFE_Editor/editor.cpp | 2 +- TheForceEngine/TFE_Editor/editorConfig.cpp | 2 +- TheForceEngine/TFE_Editor/editorLevel.cpp | 2 +- TheForceEngine/TFE_Editor/editorProject.cpp | 8 +- .../TFE_ExternalData/logicTables.cpp | 15 + TheForceEngine/TFE_ExternalData/logicTables.h | 1 + .../TFE_ExternalData/pickupExternal.cpp | 581 +++++++++++ .../TFE_ExternalData/pickupExternal.h | 47 + .../TFE_ExternalData/weaponExternal.cpp | 955 +++++++++++++++++ .../TFE_ExternalData/weaponExternal.h | 117 +++ .../add_on/scriptarray/scriptarray.cpp | 9 +- .../add_on/scriptarray/scriptarray.h | 3 +- .../add_on/scriptbuilder/scriptbuilder.cpp | 66 +- .../add_on/scriptbuilder/scriptbuilder.h | 3 + .../ScriptAPI-Shared/CMakeLists.txt | 2 + .../ScriptAPI-Shared/scriptMath.cpp | 413 ++++++++ .../ScriptAPI-Shared/scriptMath.h | 27 + .../ScriptAPI-Shared/scriptTest.cpp | 654 ++++++++++++ .../ScriptAPI-Shared/scriptTest.h | 49 + .../ScriptAPI-Shared/sharedScriptAPI.cpp | 47 + .../ScriptAPI-Shared/sharedScriptAPI.h | 15 + TheForceEngine/TFE_ForceScript/float2.h | 2 + TheForceEngine/TFE_ForceScript/float2x2.cpp | 161 +++ TheForceEngine/TFE_ForceScript/float2x2.h | 52 + TheForceEngine/TFE_ForceScript/float3.cpp | 301 ++++++ TheForceEngine/TFE_ForceScript/float3.h | 115 +++ TheForceEngine/TFE_ForceScript/float3x3.cpp | 174 ++++ TheForceEngine/TFE_ForceScript/float3x3.h | 52 + TheForceEngine/TFE_ForceScript/float4.cpp | 963 ++++++++++++++++++ TheForceEngine/TFE_ForceScript/float4.h | 445 ++++++++ TheForceEngine/TFE_ForceScript/float4x4.cpp | 186 ++++ TheForceEngine/TFE_ForceScript/float4x4.h | 52 + .../TFE_ForceScript/forceScript.cpp | 736 ++++++++++++- TheForceEngine/TFE_ForceScript/forceScript.h | 95 +- TheForceEngine/TFE_ForceScript/scriptAPI.h | 84 ++ .../TFE_ForceScript/scriptInterface.cpp | 211 ++++ .../TFE_ForceScript/scriptInterface.h | 43 + TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp | 132 ++- TheForceEngine/TFE_Game/igame.h | 1 - TheForceEngine/TFE_Input/input.cpp | 8 +- TheForceEngine/TFE_Input/input.h | 1 + TheForceEngine/TFE_Input/inputMapping.cpp | 250 +++-- TheForceEngine/TFE_Input/inputMapping.h | 1 - TheForceEngine/TFE_Input/replay.cpp | 84 +- TheForceEngine/TFE_Input/replay.h | 7 +- .../TFE_Jedi/InfSystem/infSystem.cpp | 159 ++- TheForceEngine/TFE_Jedi/InfSystem/infSystem.h | 3 + .../TFE_Jedi/InfSystem/infTypesInternal.h | 11 + TheForceEngine/TFE_Jedi/Level/level.cpp | 68 ++ TheForceEngine/TFE_Jedi/Level/level.h | 9 + TheForceEngine/TFE_Jedi/Level/levelData.cpp | 3 +- TheForceEngine/TFE_Jedi/Level/levelData.h | 4 + TheForceEngine/TFE_Jedi/Level/robjData.h | 5 +- TheForceEngine/TFE_Jedi/Level/rsector.cpp | 6 +- TheForceEngine/TFE_Jedi/Level/rsector.h | 1 + TheForceEngine/TFE_Jedi/Level/rtexture.cpp | 14 +- TheForceEngine/TFE_Jedi/Level/rtexture.h | 1 + .../robj3d_float/robj3dFloat_PolygonDraw.cpp | 43 +- .../robj3dFloat_TransformAndLighting.cpp | 18 +- .../TFE_Jedi/Renderer/jediRenderer.cpp | 8 +- .../TFE_Jedi/Serialization/serialization.h | 34 +- TheForceEngine/TFE_Jedi/Task/task.cpp | 58 +- TheForceEngine/TFE_Jedi/Task/task.h | 2 +- TheForceEngine/TFE_Settings/settings.cpp | 9 +- TheForceEngine/TFE_Settings/settings.h | 6 +- TheForceEngine/TFE_System/frameLimiter.cpp | 1 - TheForceEngine/TFE_System/log.cpp | 13 + TheForceEngine/TFE_System/system.cpp | 11 +- TheForceEngine/TFE_System/types.h | 1 + TheForceEngine/TheForceEngine.vcxproj | 16 +- TheForceEngine/TheForceEngine.vcxproj.filters | 6 + TheForceEngine/gitVersion.h | 2 +- 169 files changed, 13156 insertions(+), 2697 deletions(-) create mode 100644 TheForceEngine/ExternalData/DarkForces/effects.json create mode 100644 TheForceEngine/ExternalData/DarkForces/pickups.json create mode 100644 TheForceEngine/ExternalData/DarkForces/projectiles.json create mode 100644 TheForceEngine/ExternalData/DarkForces/weapons.json create mode 100644 TheForceEngine/ScriptTests/Readme.txt create mode 100644 TheForceEngine/ScriptTests/Test_float2.fs create mode 100644 TheForceEngine/ScriptTests/Test_float2x2.fs create mode 100644 TheForceEngine/ScriptTests/Test_float3.fs create mode 100644 TheForceEngine/ScriptTests/Test_float4.fs create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/CMakeLists.txt create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gameScripts.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gameScripts.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_game.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_game.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_level.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_system.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/gs_system.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptElev.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptElev.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptSector.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptSector.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.h create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptWall.cpp create mode 100644 TheForceEngine/TFE_DarkForces/Scripting/scriptWall.h create mode 100644 TheForceEngine/TFE_ExternalData/pickupExternal.cpp create mode 100644 TheForceEngine/TFE_ExternalData/pickupExternal.h create mode 100644 TheForceEngine/TFE_ExternalData/weaponExternal.cpp create mode 100644 TheForceEngine/TFE_ExternalData/weaponExternal.h create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/CMakeLists.txt create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.cpp create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.h create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.cpp create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.h create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.cpp create mode 100644 TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.h create mode 100644 TheForceEngine/TFE_ForceScript/float2x2.cpp create mode 100644 TheForceEngine/TFE_ForceScript/float2x2.h create mode 100644 TheForceEngine/TFE_ForceScript/float3.cpp create mode 100644 TheForceEngine/TFE_ForceScript/float3.h create mode 100644 TheForceEngine/TFE_ForceScript/float3x3.cpp create mode 100644 TheForceEngine/TFE_ForceScript/float3x3.h create mode 100644 TheForceEngine/TFE_ForceScript/float4.cpp create mode 100644 TheForceEngine/TFE_ForceScript/float4.h create mode 100644 TheForceEngine/TFE_ForceScript/float4x4.cpp create mode 100644 TheForceEngine/TFE_ForceScript/float4x4.h create mode 100644 TheForceEngine/TFE_ForceScript/scriptAPI.h create mode 100644 TheForceEngine/TFE_ForceScript/scriptInterface.cpp create mode 100644 TheForceEngine/TFE_ForceScript/scriptInterface.h diff --git a/TheForceEngine/ExternalData/DarkForces/effects.json b/TheForceEngine/ExternalData/DarkForces/effects.json new file mode 100644 index 000000000..1a60dae05 --- /dev/null +++ b/TheForceEngine/ExternalData/DarkForces/effects.json @@ -0,0 +1,220 @@ +{ + "effects": [ + { + "type": "SMALL_EXP", + "data": { + "wax": "exptiny.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 40, + "soundEffect": "ex-tiny1.voc", + "soundPriority": 1 + } + }, + { + "type": "THERMDET_EXP", + "data": { + "wax": "detexp.wax", + "force": 50, + "damage": 60, + "explosiveRange": 30, + "wakeupRange": 50, + "soundEffect": "ex-small.voc", + "soundPriority": 1 + } + }, + { + "type": "PLASMA_EXP", + "data": { + "wax": "emisexp.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 40, + "soundEffect": "ex-tiny1.voc", + "soundPriority": 1 + } + }, + { + "type": "MORTAR_EXP", + "data": { + "wax": "mortexp.wax", + "force": 35, + "damage": 50, + "explosiveRange": 40, + "wakeupRange": 60, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + + } + }, + { + "type": "CONCUSSION", + "data": { + "wax": "concexp.wax", + "force": 30, + "damage": 30, + "explosiveRange": 25, + "wakeupRange": 60, + "soundEffect": "ex-lrg1.voc", + "soundPriority": 115 + } + }, + { + "type": "CONCUSSION2", + "data": { + "wax": "", + "force": 30, + "damage": 30, + "explosiveRange": 25, + "wakeupRange": 60, + "soundEffect": "ex-lrg1.voc", + "soundPriority": 115 + } + }, + { + "type": "MISSILE_EXP", + "data": { + "wax": "missexp.wax", + "force": 70, + "damage": 70, + "explosiveRange": 40, + "wakeupRange": 70, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + } + }, + { + "type": "MISSILE_WEAK", + "data": { + "wax": "missexp.wax", + "force": 50, + "damage": 25, + "explosiveRange": 40, + "wakeupRange": 70, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + } + }, + { + "type": "PUNCH", + "data": { + "wax": "", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 10, + "soundEffect": "punch.voc", + "soundPriority": 4 + } + }, + { + "type": "CANNON_EXP", + "data": { + "wax": "plasexp.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 50, + "soundEffect": "ex-med1.voc", + "soundPriority": 1 + } + }, + { + "type": "REPEATER_EXP", + "data": { + "wax": "bullexp.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 50, + "soundEffect": "ex-tiny1.voc", + "soundPriority": 1 + } + }, + { + "type": "LARGE_EXP", + "data": { + "wax": "mineexp.wax", + "force": 80, + "damage": 90, + "explosiveRange": 45, + "wakeupRange": 90, + "soundEffect": "ex-lrg1.voc", + "soundPriority": 115 + } + }, + { + "type": "EXP_BARREL", + "data": { + "wax": "", + "force": 120, + "damage": 60, + "explosiveRange": 40, + "wakeupRange": 60, + "soundEffect": "ex-med1.voc", + "soundPriority": 76 + } + }, + { + "type": "EXP_INVIS", + "data": { + "wax": "", + "force": 60, + "damage": 40, + "explosiveRange": 20, + "wakeupRange": 60, + "soundEffect": "" + } + }, + { + "type": "SPLASH", + "data": { + "wax": "splash.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 40, + "soundEffect": "swim-in.voc", + "soundPriority": 12 + } + }, + { + "type": "EXP_35", + "data": { + "wax": "genexp.wax", + "force": 30, + "damage": 35, + "explosiveRange": 30, + "wakeupRange": 50, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + } + }, + { + "type": "EXP_NO_DMG", + "data": { + "wax": "genexp.wax", + "force": 0, + "damage": 0, + "explosiveRange": 0, + "wakeupRange": 50, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + } + }, + { + "type": "EXP_25", + "data": { + "wax": "genexp.wax", + "force": 20, + "damage": 25, + "explosiveRange": 20, + "wakeupRange": 50, + "soundEffect": "ex-med1.voc", + "soundPriority": 115 + } + } + ] +} diff --git a/TheForceEngine/ExternalData/DarkForces/pickups.json b/TheForceEngine/ExternalData/DarkForces/pickups.json new file mode 100644 index 000000000..81732e666 --- /dev/null +++ b/TheForceEngine/ExternalData/DarkForces/pickups.json @@ -0,0 +1,493 @@ +{ + "maxAmounts": { + "ammoEnergy": 500, + "ammoPower": 500, + "ammoShell": 50, + "ammoPlasma": 400, + "ammoDetonator": 50, + "ammoMine": 30, + "ammoMissile": 20, + "shields": 200, + "batteryPower": 100, + "health": 100 + }, + "pickups": [ + { + "name": "PLANS", + "data": { + "type": "objective", + "playerItem": "itemPlans", + "message1": 400, + "fullbright": true, + "noRemove": true, + "asset": "IDPLANS.WAX" + } + }, + { + "name": "PHRIK", + "data": { + "type": "objective", + "playerItem": "itemPhrik", + "message1": 401, + "fullbright": true, + "noRemove": true, + "asset": "IPHRIK.WAX" + } + }, + { + "name": "NAVA", + "data": { + "type": "objective", + "playerItem": "itemNava", + "message1": 402, + "fullbright": true, + "noRemove": true, + "asset": "INAVA.WAX" + } + }, + { + "name": "DT_WEAPON", + "data": { + "type": "objective", + "playerItem": "itemDtWeapon", + "message1": 405, + "noRemove": true, + "asset": "IDTGUN.WAX" + } + }, + { + "name": "DATATAPE", + "data": { + "type": "objective", + "playerItem": "itemDatatape", + "message1": 406, + "noRemove": true, + "asset": "IDATA.FME" + } + }, + { + "name": "ITEM10", + "data": { + "type": "objective", + "playerItem": "itemUnused", + "message1": 403, + "noRemove": true, + "asset": "ITEM10.WAX" + } + }, + { + "name": "RIFLE", + "data": { + "weaponIndex": 2, + "type": "weapon", + "playerItem": "itemRifle", + "playerAmmo": "ammoEnergy", + "amount": 15, + "message1": 100, + "message2": 101, + "asset": "IST-GUNI.FME" + } + }, + { + "name": "AUTOGUN", + "data": { + "weaponIndex": 4, + "type": "weapon", + "playerItem": "itemAutogun", + "playerAmmo": "ammoPower", + "amount": 30, + "message1": 103, + "message2": 104, + "asset": "IAUTOGUN.FME" + } + }, + { + "name": "MORTAR", + "data": { + "weaponIndex": 6, + "type": "weapon", + "playerItem": "itemMortar", + "playerAmmo": "ammoShell", + "amount": 3, + "message1": 105, + "message2": 106, + "asset": "IMORTAR.FME" + } + }, + { + "name": "FUSION", + "data": { + "weaponIndex": 5, + "type": "weapon", + "playerItem": "itemFusion", + "playerAmmo": "ammoPower", + "amount": 50, + "message1": 107, + "message2": 108, + "asset": "IFUSION.FME" + } + }, + { + "name": "CONCUSSION", + "data": { + "weaponIndex": 8, + "type": "weapon", + "playerItem": "itemConcussion", + "playerAmmo": "ammoPower", + "amount": 100, + "message1": 110, + "message2": 111, + "asset": "ICONCUS.FME" + } + }, + { + "name": "CANNON", + "data": { + "weaponIndex": 9, + "type": "weapon", + "playerItem": "itemCannon", + "playerAmmo": "ammoPlasma", + "amount": 30, + "message1": 112, + "message2": 113, + "asset": "ICANNON.FME" + } + }, + { + "name": "ENERGY", + "data": { + "type": "ammo", + "playerAmmo": "ammoEnergy", + "amount": 15, + "message1": 200, + "asset": "IENERGY.FME" + } + }, + { + "name": "POWER", + "data": { + "type": "ammo", + "playerAmmo": "ammoPower", + "amount": 10, + "message1": 201, + "asset": "IPOWER.FME" + } + }, + { + "name": "PLASMA", + "data": { + "type": "ammo", + "playerAmmo": "ammoPlasma", + "amount": 20, + "message1": 202, + "asset": "IPLAZMA.FME" + } + }, + { + "name": "DETONATOR", + "data": { + "type": "ammo", + "playerAmmo": "ammoDetonator", + "amount": 1, + "message1": 203, + "asset": "IDET.FME" + } + }, + { + "name": "DETONATORS", + "data": { + "type": "ammo", + "playerAmmo": "ammoDetonator", + "amount": 5, + "message1": 204, + "asset": "IDETS.FME" + } + }, + { + "name": "SHELL", + "data": { + "type": "ammo", + "playerAmmo": "ammoShell", + "amount": 1, + "message1": 205, + "asset": "ISHELL.FME" + } + }, + { + "name": "SHELLS", + "data": { + "type": "ammo", + "playerAmmo": "ammoShell", + "amount": 5, + "message1": 206, + "asset": "ISHELLS.FME" + } + }, + { + "name": "MINE", + "data": { + "type": "ammo", + "playerAmmo": "ammoMine", + "amount": 1, + "message1": 207, + "asset": "IMINE.FME" + } + }, + { + "name": "MINES", + "data": { + "type": "ammo", + "playerAmmo": "ammoMine", + "amount": 5, + "message1": 208, + "asset": "IMINES.FME" + } + }, + { + "name": "MISSILE", + "data": { + "type": "ammo", + "playerAmmo": "ammoMissile", + "amount": 1, + "message1": 209, + "asset": "IMSL.FME" + } + }, + { + "name": "MISSILES", + "data": { + "type": "ammo", + "playerAmmo": "ammoMissile", + "amount": 5, + "message1": 210, + "asset": "IMSLS.FME" + } + }, + { + "name": "SHIELD", + "data": { + "type": "ammo", + "playerAmmo": "shields", + "amount": 20, + "message1": 114, + "asset": "IARMOR.WAX" + } + }, + { + "name": "RED_KEY", + "data": { + "type": "usable", + "playerItem": "itemRedKey", + "message1": 300, + "asset": "IKEYR.FME" + } + }, + { + "name": "YELLOW_KEY", + "data": { + "type": "usable", + "playerItem": "itemYellowKey", + "message1": 301, + "asset": "IKEYY.FME" + } + }, + { + "name": "BLUE_KEY", + "data": { + "type": "usable", + "playerItem": "itemBlueKey", + "message1": 302, + "asset": "IKEYB.FME" + } + }, + { + "name": "GOGGLES", + "data": { + "type": "usable", + "playerItem": "itemGoggles", + "playerAmmo": "batteryPower", + "message1": 303, + "amount": 50, + "asset": "IGOGGLES.FME" + } + }, + { + "name": "CLEATS", + "data": { + "type": "usable", + "playerItem": "itemCleats", + "message1": 304, + "asset": "ICLEATS.FME" + } + }, + { + "name": "MASK", + "data": { + "type": "usable", + "playerItem": "itemMask", + "playerAmmo": "batteryPower", + "message1": 305, + "amount": 50, + "asset": "IMASK.FME" + } + }, + { + "name": "BATTERY", + "data": { + "type": "ammo", + "playerAmmo": "batteryPower", + "message1": 211, + "amount": 50, + "asset": "IBATTERY.FME" + } + }, + { + "name": "CODE1", + "data": { + "type": "codekey", + "playerItem": "itemCode1", + "message1": 501, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE2", + "data": { + "type": "codekey", + "playerItem": "itemCode2", + "message1": 502, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE3", + "data": { + "type": "codekey", + "playerItem": "itemCode3", + "message1": 503, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE4", + "data": { + "type": "codekey", + "playerItem": "itemCode4", + "message1": 504, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE5", + "data": { + "type": "codekey", + "playerItem": "itemCode5", + "message1": 505, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE6", + "data": { + "type": "codekey", + "playerItem": "itemCode6", + "message1": 506, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE7", + "data": { + "type": "codekey", + "playerItem": "itemCode7", + "message1": 507, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE8", + "data": { + "type": "codekey", + "playerItem": "itemCode8", + "message1": 508, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "CODE9", + "data": { + "type": "codekey", + "playerItem": "itemCode9", + "message1": 509, + "fullbright": true, + "noRemove": true, + "asset": "DET_CODE.FME" + } + }, + { + "name": "INVINCIBLE", + "data": { + "type": "powerup", + "message1": 306, + "asset": "IINVINC.WAX" + } + }, + { + "name": "SUPERCHARGE", + "data": { + "type": "powerup", + "message1": 307, + "asset": "ICHARGE.FME" + } + }, + { + "name": "REVIVE", + "data": { + "type": "powerup", + "message1": 308, + "asset": "IREVIVE.WAX" + } + }, + { + "name": "LIFE", + "data": { + "type": "powerup", + "message1": 310, + "asset": "ILIFE.WAX" + } + }, + { + "name": "MEDKIT", + "data": { + "type": "ammo", + "playerAmmo": "health", + "amount": 20, + "message1": 311, + "asset": "IMEDKIT.FME" + } + }, + { + "name": "PILE", + "data": { + "type": "special", + "asset": "IPILE.FME" + } + } + ] +} diff --git a/TheForceEngine/ExternalData/DarkForces/projectiles.json b/TheForceEngine/ExternalData/DarkForces/projectiles.json new file mode 100644 index 000000000..be989a626 --- /dev/null +++ b/TheForceEngine/ExternalData/DarkForces/projectiles.json @@ -0,0 +1,442 @@ +{ + "projectiles": [ + { + "type": "PUNCH", + "data": { + "assetType": "spirit", + "updateFunc": "standard", + "damage": 6, + "falloffAmount": 0, + "force": 0.02, + "speed": 230, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": null, + "duration": 0 + } + }, + { + "type": "PISTOL_BOLT", + "data": { + "assetType": "3d", + "asset": "wrbolt.3do", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 10, + "minDamage": 1, + "falloffAmount": 1, + "nextFalloffTick": 14, + "damageFalloffDelta": 14, + "force": 0.01, + "speed": 250, + "horzBounciness": 1, + "vertBounciness": 1, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": "SMALL_EXP", + "hitEffect": "SMALL_EXP", + "duration": 3.0, + "cameraPassSound": "lasrby.voc", + "reflectSound": "boltref1.voc" + } + }, + { + "type": "RIFLE_BOLT", + "data": { + "assetType": "3d", + "asset": "wrbolt.3do", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 10, + "minDamage": 1, + "falloffAmount": 0.8, + "nextFalloffTick": 14, + "damageFalloffDelta": 14, + "force": 0.01, + "speed": 250, + "horzBounciness": 1, + "vertBounciness": 1, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": "SMALL_EXP", + "hitEffect": "SMALL_EXP", + "duration": 4.0, + "cameraPassSound": "lasrby.voc", + "reflectSound": "boltref1.voc" + } + }, + { + "type": "THERMAL_DET", + "data": { + "assetType": "frame", + "asset": "wdet.fme", + "zeroWidth": true, + "movable": true, + "updateFunc": "arcing", + "damage": 0, + "minDamage": 1, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.1, + "speed": 80, + "horzBounciness": 0.45, + "vertBounciness": 0.89, + "bounceCount": -1, + "reflectEffect": null, + "hitEffect": "thermdet_exp", + "duration": 3.0, + "cameraPassSound": null, + "reflectSound": "thermal1.voc", + "explodeOnTimeout": true + } + }, + { + "type": "REPEATER", + "data": { + "assetType": "frame", + "asset": "bullet.fme", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 10, + "minDamage": 1, + "falloffAmount": 0.3, + "nextFalloffTick": 19660, + "damageFalloffDelta": 19660, + "force": 0.03, + "speed": 270, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": null, + "hitEffect": "REPEATER_EXP", + "duration": 4.0, + "cameraPassSound": "lasrby.voc", + "reflectSound": "boltref1.voc" + } + }, + { + "type": "PLASMA", + "data": { + "assetType": "sprite", + "asset": "wemiss.wax", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 15, + "minDamage": 1, + "falloffAmount": 0.6, + "nextFalloffTick": 29, + "damageFalloffDelta": 29, + "force": 0.05, + "speed": 100, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": null, + "hitEffect": "PLASMA_EXP", + "duration": 10.0, + "cameraPassSound": "emisby.voc", + "reflectSound": "bigrefl1.voc" + } + }, + { + "type": "MORTAR", + "data": { + "assetType": "sprite", + "asset": "wshell.wax", + "zeroWidth": true, + "updateFunc": "arcing", + "damage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.2, + "speed": 110, + "horzBounciness": 0.4, + "vertBounciness": 0.6, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": "MORTAR_EXP", + "duration": 4.0 + } + }, + { + "type": "LAND_MINE", + "data": { + "assetType": "frame", + "asset": "wmine.fme", + "zeroWidth": true, + "movable": true, + "updateFunc": "arcing", + "damage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.2, + "speed": 0, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": -1, + "reflectEffect": null, + "hitEffect": "LARGE_EXP", + "duration": 3.0, + "explodeOnTimeout": true + } + }, + { + "type": "LAND_MINE_PROX", + "data": { + "assetType": "frame", + "asset": "wmine.fme", + "zeroWidth": true, + "movable": true, + "updateFunc": "arcing", + "damage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.2, + "speed": 0, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": -1, + "reflectEffect": null, + "hitEffect": "LARGE_EXP", + "duration": 3.0, + "explodeOnTimeout": true + } + }, + { + "type": "LAND_MINE_PLACED", + "data": { + "assetType": "frame", + "asset": "wlmine.fme", + "zeroWidth": true, + "movable": true, + "updateFunc": "landmine", + "damage": 0, + "falloffAmount": 0, + "force": 0.2, + "speed": 0, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": -1, + "reflectEffect": null, + "hitEffect": "LARGE_EXP", + "duration": 0, + "explodeOnTimeout": true + } + }, + { + "type": "CONCUSSION", + "data": { + "assetType": "spirit", + "zeroWidth": true, + "updateFunc": "standard", + "damage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.06, + "speed": 190, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": "concussion", + "duration": 5.0 + } + }, + { + "type": "CANNON", + "data": { + "assetType": "sprite", + "asset": "wplasma.wax", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 30, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0.061523438, + "speed": 100, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 3, + "reflectVariation": 18, + "reflectEffect": null, + "hitEffect": "CANNON_EXP", + "duration": 4.0, + "cameraPassSound": "emisby.voc", + "reflectSound": "bigrefl1.voc" + } + }, + { + "type": "MISSILE", + "data": { + "assetType": "sprite", + "asset": "wmsl.wax", + "zeroWidth": true, + "updateFunc": "standard", + "damage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 1, + "speed": 74, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": "MISSILE_EXP", + "duration": 7.0, + "flightSound": "rocket-1.voc" + } + }, + { + "type": "TURRET_BOLT", + "data": { + "assetType": "3d", + "asset": "wgbolt.3do", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 17, + "minDamage": 1, + "falloffAmount": 1, + "nextFalloffTick": 14, + "damageFalloffDelta": 14, + "force": 0.03, + "speed": 300, + "horzBounciness": 1, + "vertBounciness": 1, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": "SMALL_EXP", + "hitEffect": "SMALL_EXP", + "duration": 3.0, + "cameraPassSound": "lasrby.voc", + "reflectSound": "boltref1.voc" + } + }, + { + "type": "REMOTE_BOLT", + "data": { + "assetType": "3d", + "asset": "wgbolt.3do", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 1, + "minDamage": 0, + "falloffAmount": 1, + "nextFalloffTick": 14, + "damageFalloffDelta": 14, + "force": 0.01, + "speed": 300, + "horzBounciness": 1, + "vertBounciness": 1, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": "SMALL_EXP", + "hitEffect": "SMALL_EXP", + "duration": 3.0, + "cameraPassSound": "lasrby.voc", + "reflectSound": "boltref1.voc" + } + }, + { + "type": "EXP_BARREL", + "data": { + "assetType": "spirit", + "updateFunc": "standard", + "damage": 0, + "minDamage": 0, + "falloffAmount": 0, + "damageFalloffDelta": 0, + "force": 0, + "speed": 0, + "horzBounciness": 0, + "vertBounciness": 0, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": "EXP_BARREL", + "duration": 0, + "explodeOnTimeout": true + } + }, + { + "type": "HOMING_MISSILE", + "data": { + "assetType": "sprite", + "asset": "wdt3msl.wax", + "autoAim": true, + "updateFunc": "homing", + "damage": 0, + "minDamage": 1, + "falloffAmount": 0, + "damageFalloffDelta": 65536, + "force": 1, + "speed": 29, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 0, + "angularSpeed": 10, + "reflectEffect": null, + "hitEffect": "MISSILE_EXP", + "duration": 10.0, + "reflectSound": "", + "flightSound": "tracker.voc", + "explodeOnTimeout": true + } + }, + { + "type": "PROBE_PROJ", + "data": { + "assetType": "sprite", + "asset": "widball.wax", + "fullBright": true, + "zeroWidth": true, + "updateFunc": "standard", + "damage": 10, + "minDamage": 1, + "falloffAmount": 1, + "nextFalloffTick": 14, + "damageFalloffDelta": 14, + "force": 0.04, + "speed": 100, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 3, + "reflectVariation": 9, + "reflectEffect": null, + "hitEffect": "PLASMA_EXP", + "duration": 8.0, + "cameraPassSound": "emisby.voc", + "reflectSound": "bigrefl1.voc" + } + }, + { + "type": "BOBAFETT_BALL", + "data": { + "assetType": "sprite", + "asset": "bobaball.wax", + "zeroWidth": true, + "updateFunc": "standard", + "damage": 0, + "falloffAmount": 0, + "force": 1, + "speed": 90, + "horzBounciness": 0.9, + "vertBounciness": 0.9, + "bounceCount": 0, + "reflectEffect": null, + "hitEffect": "EXP_25", + "duration": 10.0, + "cameraPassSound": "fireball.voc" + } + } + ] +} diff --git a/TheForceEngine/ExternalData/DarkForces/weapons.json b/TheForceEngine/ExternalData/DarkForces/weapons.json new file mode 100644 index 000000000..4eadbd1ef --- /dev/null +++ b/TheForceEngine/ExternalData/DarkForces/weapons.json @@ -0,0 +1,388 @@ +{ + "gasmask": { + "texture": "gmask.bm", + "xPos": 105, + "yPos": 141 + }, + "weapons": [ + { + "name": "FIST", + "data": { + "frameCount": 4, + "textures": ["rhand1.bm", "punch1.bm", "punch2.bm", "punch3.bm"], + "xPos": [172, 55, 59, 56], + "yPos": [141, 167, 114, 141], + "ammo": "", + "wakeupRange": 0, + "variation": 0, + "animFrames": [ + { + "texture": 1, + "light": 0, + "durationSupercharge": 5, + "durationNormal": 10 + }, + { + "texture": 2, + "light": 0, + "durationSupercharge": 10, + "durationNormal": 21 + }, + { + "texture": 3, + "light": 0, + "durationSupercharge": 10, + "durationNormal": 21 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 10, + "durationNormal": 21 + } + ] + } + }, + { + "name": "PISTOL", + "data": { + "frameCount": 3, + "textures": ["pistol1.bm", "pistol2.bm", "pistol3.bm"], + "xPos": [165, 169, 169], + "yPos": [142, 136, 136], + "ammo": "ammoEnergy", + "wakeupRange": 45, + "variation": 22, + "primaryFireConsumption": 1, + "animFrames": [ + { + "texture": 1, + "light": 40, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 2, + "light": 0, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 21, + "durationNormal": 43 + } + ] + } + }, + { + "name": "RIFLE", + "data": { + "frameCount": 2, + "textures": ["rifle1.bm", "rifle2.bm"], + "xPos": [113, 112], + "yPos": [127, 114], + "ammo": "ammoEnergy", + "wakeupRange": 50, + "variation": 86, + "primaryFireConsumption": 2, + "animFrames": [ + { + "texture": 1, + "light": 40, + "durationSupercharge": 3, + "durationNormal": 7 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 7, + "durationNormal": 14 + } + ] + } + }, + { + "name": "THERMAL_DET", + "data": { + "frameCount": 4, + "textures": ["therm1.bm", "therm2.bm", "therm3.bm", "rhand1.bm"], + "xPos": [161, 179, 180, 132], + "yPos": [131, 165, 102, 150], + "ammo": "ammoDetonator", + "wakeupRange": 0, + "variation": 2949165, + "primaryFireConsumption": 1, + "animFrames": [ + { + "texture": 1, + "light": 0, + "durationSupercharge": 11, + "durationNormal": 11 + }, + { + "texture": 2, + "light": 0, + "durationSupercharge": 58, + "durationNormal": 58 + }, + { + "texture": 1, + "light": 0, + "durationSupercharge": 29, + "durationNormal": 29 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 29, + "durationNormal": 29 + }, + { + "texture": 3, + "light": 0, + "durationSupercharge": 29, + "durationNormal": 29 + } + ] + } + }, + { + "name": "REPEATER", + "data": { + "frameCount": 3, + "textures":["autogun1.bm", "autogun2.bm", "autogun3.bm"], + "xPos": [156, 163, 163], + "yPos": [138, 140, 140], + "ammo": "ammoPower", + "wakeupRange": 60, + "variation": 2949165, + "primaryFireConsumption": 1, + "secondaryFireConsumption": 3, + "animFrames": [ + { + "texture": 1, + "light": 0, + "durationSupercharge": 2, + "durationNormal": 5 + }, + { + "texture": 2, + "light": 33, + "durationSupercharge": 5, + "durationNormal": 11 + } + ], + "animFramesSecondary": [ + { + "texture": 1, + "light": 0, + "durationSupercharge": 4, + "durationNormal": 8 + }, + { + "texture": 2, + "light": 33, + "durationSupercharge": 5, + "durationNormal": 11 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 14, + "durationNormal": 29 + } + ] + } + }, + { + "name": "FUSION", + "data": { + "frameCount": 6, + "textures": ["fusion1.bm", "fusion2.bm", "fusion3.bm", "fusion4.bm", "fusion5.bm", "fusion6.bm"], + "xPos": [19, 23, 23, 23, 23, 23], + "yPos": [152, 155, 155, 155, 155, 155], + "ammo": "ammoPower", + "wakeupRange": 55, + "variation": 45, + "primaryFireConsumption": 1, + "secondaryFireConsumption": 8, + "animFrames": [ + { + "texture": 0, + "light": 0, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 1, + "light": 34, + "durationSupercharge": 10, + "durationNormal": 20 + }, + { + "texture": 2, + "light": 34, + "durationSupercharge": 10, + "durationNormal": 20 + }, + { + "texture": 3, + "light": 34, + "durationSupercharge": 10, + "durationNormal": 20 + }, + { + "texture": 4, + "light": 34, + "durationSupercharge": 10, + "durationNormal": 20 + } + ], + "animFramesSecondary": [ + { + "texture": 5, + "light": 34, + "durationSupercharge": 14, + "durationNormal": 29 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 19, + "durationNormal": 39 + } + ] + } + }, + { + "name": "MORTAR", + "data": { + "frameCount": 4, + "textures": ["mortar1.bm", "mortar2.bm", "mortar3.bm", "mortar4.bm"], + "xPos": [123, 126, 127, 123], + "yPos": [119, 117, 119, 116], + "ammo": "ammoShell", + "wakeupRange": 60, + "variation": 45, + "primaryFireConsumption": 1, + "animFrames": [ + { + "texture": 1, + "light": 36, + "durationSupercharge": 29, + "durationNormal": 58 + }, + { + "texture": 2, + "light": 36, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 3, + "light": 36, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 7, + "durationNormal": 14 + } + ] + } + }, + { + "name": "MINE", + "data": { + "frameCount": 3, + "textures": ["clay1.bm", "clay2.bm", "rhand1.bm"], + "xPos": [105, 105, 132], + "yPos": [153, 153, 150], + "ammo": "ammoMine", + "wakeupRange": 0, + "variation": 0, + "primaryFireConsumption": 1 + } + }, + { + "name": "CONCUSSION", + "data": { + "frameCount": 3, + "textures": ["concuss1.bm", "concuss2.bm", "concuss3.bm"], + "xPos": [130, 191, 188], + "yPos": [131, 129, 132], + "ammo": "ammoPower", + "wakeupRange": 70, + "variation": 0, + "primaryFireConsumption": 4, + "animFrames":[ + { + "texture": 1, + "light": 36, + "durationSupercharge": 21, + "durationNormal": 43 + }, + { + "texture": 2, + "light": 0, + "durationSupercharge": 7, + "durationNormal": 14 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 29, + "durationNormal": 58 + } + ] + } + }, + { + "name": "CANNON", + "data": { + "frameCount": 4, + "textures": ["assault1.bm", "assault2.bm", "assault3.bm", "assault4.bm"], + "xPos": [206, 208, 224, 230], + "yPos": [-74, -60, 81, 86], + "ammo": "ammoPlasma", + "secondaryAmmo": "ammoMissile", + "wakeupRange": 55, + "variation": 2949165, + "primaryFireConsumption": 1, + "secondaryFireConsumption": 1, + "animFrames": [ + { + "texture": 1, + "light": 34, + "durationSupercharge": 7, + "durationNormal": 14 + } + ], + "animFramesSecondary":[ + { + "texture": 2, + "light": 0, + "durationSupercharge": 14, + "durationNormal": 29 + }, + { + "texture": 3, + "light": 33, + "durationSupercharge": 43, + "durationNormal": 87 + }, + { + "texture": 0, + "light": 0, + "durationSupercharge": 29, + "durationNormal": 58 + } + ] + } + } + ] +} \ No newline at end of file diff --git a/TheForceEngine/ScriptTests/Readme.txt b/TheForceEngine/ScriptTests/Readme.txt new file mode 100644 index 000000000..d056ac2da --- /dev/null +++ b/TheForceEngine/ScriptTests/Readme.txt @@ -0,0 +1,2 @@ +This directory holds script unit tests, used to validate math operations, swizzling, and other operations. +Do not put gameplay related or library scripts here. \ No newline at end of file diff --git a/TheForceEngine/ScriptTests/Test_float2.fs b/TheForceEngine/ScriptTests/Test_float2.fs new file mode 100644 index 000000000..33a03f836 --- /dev/null +++ b/TheForceEngine/ScriptTests/Test_float2.fs @@ -0,0 +1,85 @@ +///////////////////////////////////////////////////////////// +// Vector Math Tests +///////////////////////////////////////////////////////////// + +// Swizzle +void test_float2_swizzle() +{ + test.beginTest("float2_swizzle"); + float2 a = float2(1, 2); + // float2 -> float2 swizzles. + test.expectEq(a.xy, float2(1,2)); + test.expectEq(a.yx, float2(2,1)); + test.expectEq(a.xx, float2(1,1)); + test.expectEq(a.yy, float2(2,2)); + // float2 -> scalar swizzles. + test.expectEq(a.x, 1.0f); + test.expectEq(a.y, 2.0f); + // float2 array indexing. + test.expectEq(a[0], 1.0f); + test.expectEq(a[1], 2.0f); +} + +// float2 x float2 math +void test_float2_float2_math() +{ + test.beginTest("float2_float2_math"); + float2 a = float2(1, 2); + float2 b = float2(3, 4); + float2 c = float2(0, 1); + test.expectEq(a + b, float2(4, 6)); + test.expectEq(a - b, float2(-2, -2)); + test.expectEq(a * b, float2(3, 8)); + test.expectEq(a / b, float2(1.0/3.0, 2.0/4.0)); + + const float neg45 = math.radians(-45.0); // -45 degrees in radians. + test.expectEq(math.distance(a, b), 2.828427125f); + test.expectEq(math.dot(a, b), 11.0f); + test.expectEq(math.length(a), 2.2360679775f); + test.expectEq(math.normalize(a), float2(0.4472135955, 0.894427191)); + test.expectEq(math.perp(a, b), -2.0f); + test.expectEq(math.sincos(neg45), float2(-0.7071067812, 0.7071067812)); + + // Test normalize with zero-vector (invalid). + test.expectEq(math.normalize(float2(0)), float2(0)); + + test.expectEq(math.clamp(a, float2(0.5,2.5), float2(2,3)), float2(1.0, 2.5)); + test.expectEq(math.clamp(a, 1.5, 3.0), float2(1.5, 2.0)); + test.expectEq(math.max(a, b), float2(3, 4)); + test.expectEq(math.max(a, 1.5), float2(1.5, 2)); + test.expectEq(math.min(a, b), float2(1, 2)); + test.expectEq(math.min(a, 1.5), float2(1, 1.5)); + test.expectEq(math.mix(a, b, 0.5), float2(2, 3)); + test.expectEq(math.mix(a, b, float2(0.5, 1.0)), float2(2, 4)); + + test.expectTrue(math.all(a)); + test.expectTrue(math.any(a)); + test.expectFalse(math.all(c)); + test.expectTrue(math.any(c)); +} + +// float2 x scalar math +void test_float2_scalar_math() +{ + test.beginTest("float2_scalar_math"); + float2 a = float2(3, 4); + test.expectEq(a * 0.0, float2(0)); + test.expectEq(a * 1.0, float2(3, 4)); + test.expectEq(a.yx * 0.5, float2(2.0, 1.5)); + + test.expectEq(a / 2.0, float2(1.5, 2)); + test.expectEq(a + 2.0, float2(5, 6)); + test.expectEq(a - 2.0, float2(1, 2)); + test.expectEq(a * 2.0, float2(6, 8)); +} + +void main() +{ + test.beginSystem("Float2"); + { + test_float2_swizzle(); + test_float2_float2_math(); + test_float2_scalar_math(); + } + test.report(); +} diff --git a/TheForceEngine/ScriptTests/Test_float2x2.fs b/TheForceEngine/ScriptTests/Test_float2x2.fs new file mode 100644 index 000000000..33ce35d12 --- /dev/null +++ b/TheForceEngine/ScriptTests/Test_float2x2.fs @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////// +// Vector Math Tests +///////////////////////////////////////////////////////////// + +// Swizzle +void test_float2x2_arrayAccess() +{ + test.beginTest("float2x2_arrayAccess"); + float2x2 m = float2x2(1, 2, 3, 4); + test.expectEq(m[0], float2(1, 2)); + test.expectEq(m[1], float2(3, 4)); +} + +// float2x2_math +void test_float2x2_math() +{ + test.beginTest("float2x2_math"); + float2x2 a = float2x2(1, 2, 3, 4); + float2x2 b = float2x2(5, 6, 7, 8); + + float2x2 ta = math.transpose(a); + float2x2 tb = math.transpose(b); + test.expectEq(ta[0], float2(1, 3)); + test.expectEq(ta[1], float2(2, 4)); + test.expectEq(tb[0], float2(5, 7)); + test.expectEq(tb[1], float2(6, 8)); + + // matrix multiply: float2x2 * float2x2 -> float2x2 + float2x2 ab = a * b; + test.expectEq(ab[0], float2(19, 22)); + test.expectEq(ab[1], float2(43, 50)); + + // scale: float2x2 * float -> float2x2 + float2x2 as = a * 2; + test.expectEq(as[0], float2(2, 4)); + test.expectEq(as[1], float2(6, 8)); + + // vector multiply: float2x2 * float2 -> float2 (float2 is column vector). + float2 v = a * float2(1, 2); + test.expectEq(v, float2(5, 11)); + + float2x2 rm = math.rotationMatrix2x2(math.radians(-45.0)); + test.expectEq(rm[0], float2(0.7071067812, 0.7071067812)); + test.expectEq(rm[1], float2(-0.7071067812, 0.7071067812)); +} + +void main() +{ + test.beginSystem("Float2x2"); + { + test_float2x2_arrayAccess(); + test_float2x2_math(); + } + test.report(); +} diff --git a/TheForceEngine/ScriptTests/Test_float3.fs b/TheForceEngine/ScriptTests/Test_float3.fs new file mode 100644 index 000000000..928fca45c --- /dev/null +++ b/TheForceEngine/ScriptTests/Test_float3.fs @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////// +// Vector Math Tests +///////////////////////////////////////////////////////////// + +// Swizzle +void test_float3_swizzle() +{ + test.beginTest("float3_swizzle"); + float3 a = float3(1, 2, 3); + // float3 -> float3 swizzles. + test.expectEq(a.xxx, float3(1,1,1)); + test.expectEq(a.xxy, float3(1,1,2)); + test.expectEq(a.xxz, float3(1,1,3)); + test.expectEq(a.xyx, float3(1,2,1)); + test.expectEq(a.xyy, float3(1,2,2)); + test.expectEq(a.xyz, float3(1,2,3)); + test.expectEq(a.xzx, float3(1,3,1)); + test.expectEq(a.xzy, float3(1,3,2)); + test.expectEq(a.xzz, float3(1,3,3)); + test.expectEq(a.yxx, float3(2,1,1)); + test.expectEq(a.yxy, float3(2,1,2)); + test.expectEq(a.yxz, float3(2,1,3)); + test.expectEq(a.yyx, float3(2,2,1)); + test.expectEq(a.yyy, float3(2,2,2)); + test.expectEq(a.yyz, float3(2,2,3)); + test.expectEq(a.yzx, float3(2,3,1)); + test.expectEq(a.yzy, float3(2,3,2)); + test.expectEq(a.yzz, float3(2,3,3)); + test.expectEq(a.zxx, float3(3,1,1)); + test.expectEq(a.zxy, float3(3,1,2)); + test.expectEq(a.zxz, float3(3,1,3)); + test.expectEq(a.zyx, float3(3,2,1)); + test.expectEq(a.zyy, float3(3,2,2)); + test.expectEq(a.zyz, float3(3,2,3)); + test.expectEq(a.zzx, float3(3,3,1)); + test.expectEq(a.zzy, float3(3,3,2)); + test.expectEq(a.zzz, float3(3,3,3)); + // float3 -> float2 swizzles. + test.expectEq(a.xx, float2(1,1)); + test.expectEq(a.xy, float2(1,2)); + test.expectEq(a.xz, float2(1,3)); + test.expectEq(a.yx, float2(2,1)); + test.expectEq(a.yy, float2(2,2)); + test.expectEq(a.yz, float2(2,3)); + test.expectEq(a.zx, float2(3,1)); + test.expectEq(a.zy, float2(3,2)); + test.expectEq(a.zz, float2(3,3)); + // float3 -> scalar swizzles. + test.expectEq(a.x, 1.0f); + test.expectEq(a.y, 2.0f); + test.expectEq(a.z, 3.0f); + // float3 array indexing. + test.expectEq(a[0], 1.0f); + test.expectEq(a[1], 2.0f); + test.expectEq(a[2], 3.0f); +} + +// float3 x float3 math +void test_float3_float3_math() +{ + test.beginTest("float3_float3_math"); + float3 a = float3(1, 2, 3); + float3 b = float3(3, 4, 5); + float3 c = float3(0, 1, 2); + test.expectEq(a + b, float3(4, 6, 8)); + test.expectEq(a - b, float3(-2, -2, -2)); + test.expectEq(a * b, float3(3, 8, 15)); + test.expectEq(a / b, float3(1.0/3.0, 2.0/4.0, 3.0/5.0)); + + test.expectEq(math.distance(a, b), 3.4641016f); + test.expectEq(math.dot(a, b), 26.0f); + test.expectEq(math.length(a), 3.741657387f); + test.expectEq(math.normalize(a), float3(0.267261237, 0.534522474, 0.801783681)); + test.expectEq(math.cross(a, b), float3(-2.0, 4.0, -2.0)); + + // Test normalize with zero-vector (invalid). + test.expectEq(math.normalize(float3(0)), float3(0)); + + test.expectEq(math.clamp(a, float3(0.5,2.5,1.5), float3(2,3,2.75)), float3(1.0, 2.5, 2.75)); + test.expectEq(math.clamp(a, 1.5, 3.0), float3(1.5, 2.0, 3.0)); + test.expectEq(math.max(a, b), float3(3, 4, 5)); + test.expectEq(math.max(a, 1.5), float3(1.5, 2, 3)); + test.expectEq(math.min(a, b), float3(1, 2, 3)); + test.expectEq(math.min(a, 1.5), float3(1, 1.5, 1.5)); + test.expectEq(math.mix(a, b, 0.5), float3(2, 3, 4)); + test.expectEq(math.mix(a, b, float3(0.5, 1.0, 2.0)), float3(2, 4, 7)); + + test.expectTrue(math.all(a)); + test.expectTrue(math.any(a)); + test.expectFalse(math.all(c)); + test.expectTrue(math.any(c)); +} + +// float3 x scalar math +void test_float3_scalar_math() +{ + test.beginTest("float3_scalar_math"); + float3 a = float3(3, 4, 5); + test.expectEq(a * 0.0, float3(0)); + test.expectEq(a * 1.0, float3(3, 4, 5)); + test.expectEq(a.zyx * 0.5, float3(2.5, 2.0, 1.5)); + + test.expectEq(a / 2.0, float3(1.5, 2, 2.5)); + test.expectEq(a + 2.0, float3(5, 6, 7)); + test.expectEq(a - 2.0, float3(1, 2, 3)); + test.expectEq(a * 2.0, float3(6, 8, 10)); +} + +void main() +{ + test.beginSystem("Float3"); + { + test_float3_swizzle(); + test_float3_float3_math(); + test_float3_scalar_math(); + } + test.report(); +} diff --git a/TheForceEngine/ScriptTests/Test_float4.fs b/TheForceEngine/ScriptTests/Test_float4.fs new file mode 100644 index 000000000..2531abae9 --- /dev/null +++ b/TheForceEngine/ScriptTests/Test_float4.fs @@ -0,0 +1,420 @@ +///////////////////////////////////////////////////////////// +// Vector Math Tests +///////////////////////////////////////////////////////////// + +// Swizzle +void test_float4_swizzle() +{ + test.beginTest("float4_swizzle"); + float4 a = float4(1, 2, 3, 4); + // float4 -> float4 swizzles. + test.expectEq(a.xxxx, float4(1,1,1,1)); + test.expectEq(a.xxxy, float4(1,1,1,2)); + test.expectEq(a.xxxz, float4(1,1,1,3)); + test.expectEq(a.xxxw, float4(1,1,1,4)); + test.expectEq(a.xxyx, float4(1,1,2,1)); + test.expectEq(a.xxyy, float4(1,1,2,2)); + test.expectEq(a.xxyz, float4(1,1,2,3)); + test.expectEq(a.xxyw, float4(1,1,2,4)); + test.expectEq(a.xxzx, float4(1,1,3,1)); + test.expectEq(a.xxzy, float4(1,1,3,2)); + test.expectEq(a.xxzz, float4(1,1,3,3)); + test.expectEq(a.xxzw, float4(1,1,3,4)); + test.expectEq(a.xxwx, float4(1,1,4,1)); + test.expectEq(a.xxwy, float4(1,1,4,2)); + test.expectEq(a.xxwz, float4(1,1,4,3)); + test.expectEq(a.xxww, float4(1,1,4,4)); + test.expectEq(a.xyxx, float4(1,2,1,1)); + test.expectEq(a.xyxy, float4(1,2,1,2)); + test.expectEq(a.xyxz, float4(1,2,1,3)); + test.expectEq(a.xyxw, float4(1,2,1,4)); + test.expectEq(a.xyyx, float4(1,2,2,1)); + test.expectEq(a.xyyy, float4(1,2,2,2)); + test.expectEq(a.xyyz, float4(1,2,2,3)); + test.expectEq(a.xyyw, float4(1,2,2,4)); + test.expectEq(a.xyzx, float4(1,2,3,1)); + test.expectEq(a.xyzy, float4(1,2,3,2)); + test.expectEq(a.xyzz, float4(1,2,3,3)); + test.expectEq(a.xyzw, float4(1,2,3,4)); + test.expectEq(a.xywx, float4(1,2,4,1)); + test.expectEq(a.xywy, float4(1,2,4,2)); + test.expectEq(a.xywz, float4(1,2,4,3)); + test.expectEq(a.xyww, float4(1,2,4,4)); + test.expectEq(a.xzxx, float4(1,3,1,1)); + test.expectEq(a.xzxy, float4(1,3,1,2)); + test.expectEq(a.xzxz, float4(1,3,1,3)); + test.expectEq(a.xzxw, float4(1,3,1,4)); + test.expectEq(a.xzyx, float4(1,3,2,1)); + test.expectEq(a.xzyy, float4(1,3,2,2)); + test.expectEq(a.xzyz, float4(1,3,2,3)); + test.expectEq(a.xzyw, float4(1,3,2,4)); + test.expectEq(a.xzzx, float4(1,3,3,1)); + test.expectEq(a.xzzy, float4(1,3,3,2)); + test.expectEq(a.xzzz, float4(1,3,3,3)); + test.expectEq(a.xzzw, float4(1,3,3,4)); + test.expectEq(a.xzwx, float4(1,3,4,1)); + test.expectEq(a.xzwy, float4(1,3,4,2)); + test.expectEq(a.xzwz, float4(1,3,4,3)); + test.expectEq(a.xzww, float4(1,3,4,4)); + test.expectEq(a.xwxx, float4(1,4,1,1)); + test.expectEq(a.xwxy, float4(1,4,1,2)); + test.expectEq(a.xwxz, float4(1,4,1,3)); + test.expectEq(a.xwxw, float4(1,4,1,4)); + test.expectEq(a.xwyx, float4(1,4,2,1)); + test.expectEq(a.xwyy, float4(1,4,2,2)); + test.expectEq(a.xwyz, float4(1,4,2,3)); + test.expectEq(a.xwyw, float4(1,4,2,4)); + test.expectEq(a.xwzx, float4(1,4,3,1)); + test.expectEq(a.xwzy, float4(1,4,3,2)); + test.expectEq(a.xwzz, float4(1,4,3,3)); + test.expectEq(a.xwzw, float4(1,4,3,4)); + test.expectEq(a.xwwx, float4(1,4,4,1)); + test.expectEq(a.xwwy, float4(1,4,4,2)); + test.expectEq(a.xwwz, float4(1,4,4,3)); + test.expectEq(a.xwww, float4(1,4,4,4)); + test.expectEq(a.yxxx, float4(2,1,1,1)); + test.expectEq(a.yxxy, float4(2,1,1,2)); + test.expectEq(a.yxxz, float4(2,1,1,3)); + test.expectEq(a.yxxw, float4(2,1,1,4)); + test.expectEq(a.yxyx, float4(2,1,2,1)); + test.expectEq(a.yxyy, float4(2,1,2,2)); + test.expectEq(a.yxyz, float4(2,1,2,3)); + test.expectEq(a.yxyw, float4(2,1,2,4)); + test.expectEq(a.yxzx, float4(2,1,3,1)); + test.expectEq(a.yxzy, float4(2,1,3,2)); + test.expectEq(a.yxzz, float4(2,1,3,3)); + test.expectEq(a.yxzw, float4(2,1,3,4)); + test.expectEq(a.yxwx, float4(2,1,4,1)); + test.expectEq(a.yxwy, float4(2,1,4,2)); + test.expectEq(a.yxwz, float4(2,1,4,3)); + test.expectEq(a.yxww, float4(2,1,4,4)); + test.expectEq(a.yyxx, float4(2,2,1,1)); + test.expectEq(a.yyxy, float4(2,2,1,2)); + test.expectEq(a.yyxz, float4(2,2,1,3)); + test.expectEq(a.yyxw, float4(2,2,1,4)); + test.expectEq(a.yyyx, float4(2,2,2,1)); + test.expectEq(a.yyyy, float4(2,2,2,2)); + test.expectEq(a.yyyz, float4(2,2,2,3)); + test.expectEq(a.yyyw, float4(2,2,2,4)); + test.expectEq(a.yyzx, float4(2,2,3,1)); + test.expectEq(a.yyzy, float4(2,2,3,2)); + test.expectEq(a.yyzz, float4(2,2,3,3)); + test.expectEq(a.yyzw, float4(2,2,3,4)); + test.expectEq(a.yywx, float4(2,2,4,1)); + test.expectEq(a.yywy, float4(2,2,4,2)); + test.expectEq(a.yywz, float4(2,2,4,3)); + test.expectEq(a.yyww, float4(2,2,4,4)); + test.expectEq(a.yzxx, float4(2,3,1,1)); + test.expectEq(a.yzxy, float4(2,3,1,2)); + test.expectEq(a.yzxz, float4(2,3,1,3)); + test.expectEq(a.yzxw, float4(2,3,1,4)); + test.expectEq(a.yzyx, float4(2,3,2,1)); + test.expectEq(a.yzyy, float4(2,3,2,2)); + test.expectEq(a.yzyz, float4(2,3,2,3)); + test.expectEq(a.yzyw, float4(2,3,2,4)); + test.expectEq(a.yzzx, float4(2,3,3,1)); + test.expectEq(a.yzzy, float4(2,3,3,2)); + test.expectEq(a.yzzz, float4(2,3,3,3)); + test.expectEq(a.yzzw, float4(2,3,3,4)); + test.expectEq(a.yzwx, float4(2,3,4,1)); + test.expectEq(a.yzwy, float4(2,3,4,2)); + test.expectEq(a.yzwz, float4(2,3,4,3)); + test.expectEq(a.yzww, float4(2,3,4,4)); + test.expectEq(a.ywxx, float4(2,4,1,1)); + test.expectEq(a.ywxy, float4(2,4,1,2)); + test.expectEq(a.ywxz, float4(2,4,1,3)); + test.expectEq(a.ywxw, float4(2,4,1,4)); + test.expectEq(a.ywyx, float4(2,4,2,1)); + test.expectEq(a.ywyy, float4(2,4,2,2)); + test.expectEq(a.ywyz, float4(2,4,2,3)); + test.expectEq(a.ywyw, float4(2,4,2,4)); + test.expectEq(a.ywzx, float4(2,4,3,1)); + test.expectEq(a.ywzy, float4(2,4,3,2)); + test.expectEq(a.ywzz, float4(2,4,3,3)); + test.expectEq(a.ywzw, float4(2,4,3,4)); + test.expectEq(a.ywwx, float4(2,4,4,1)); + test.expectEq(a.ywwy, float4(2,4,4,2)); + test.expectEq(a.ywwz, float4(2,4,4,3)); + test.expectEq(a.ywww, float4(2,4,4,4)); + test.expectEq(a.zxxx, float4(3,1,1,1)); + test.expectEq(a.zxxy, float4(3,1,1,2)); + test.expectEq(a.zxxz, float4(3,1,1,3)); + test.expectEq(a.zxxw, float4(3,1,1,4)); + test.expectEq(a.zxyx, float4(3,1,2,1)); + test.expectEq(a.zxyy, float4(3,1,2,2)); + test.expectEq(a.zxyz, float4(3,1,2,3)); + test.expectEq(a.zxyw, float4(3,1,2,4)); + test.expectEq(a.zxzx, float4(3,1,3,1)); + test.expectEq(a.zxzy, float4(3,1,3,2)); + test.expectEq(a.zxzz, float4(3,1,3,3)); + test.expectEq(a.zxzw, float4(3,1,3,4)); + test.expectEq(a.zxwx, float4(3,1,4,1)); + test.expectEq(a.zxwy, float4(3,1,4,2)); + test.expectEq(a.zxwz, float4(3,1,4,3)); + test.expectEq(a.zxww, float4(3,1,4,4)); + test.expectEq(a.zyxx, float4(3,2,1,1)); + test.expectEq(a.zyxy, float4(3,2,1,2)); + test.expectEq(a.zyxz, float4(3,2,1,3)); + test.expectEq(a.zyxw, float4(3,2,1,4)); + test.expectEq(a.zyyx, float4(3,2,2,1)); + test.expectEq(a.zyyy, float4(3,2,2,2)); + test.expectEq(a.zyyz, float4(3,2,2,3)); + test.expectEq(a.zyyw, float4(3,2,2,4)); + test.expectEq(a.zyzx, float4(3,2,3,1)); + test.expectEq(a.zyzy, float4(3,2,3,2)); + test.expectEq(a.zyzz, float4(3,2,3,3)); + test.expectEq(a.zyzw, float4(3,2,3,4)); + test.expectEq(a.zywx, float4(3,2,4,1)); + test.expectEq(a.zywy, float4(3,2,4,2)); + test.expectEq(a.zywz, float4(3,2,4,3)); + test.expectEq(a.zyww, float4(3,2,4,4)); + test.expectEq(a.zzxx, float4(3,3,1,1)); + test.expectEq(a.zzxy, float4(3,3,1,2)); + test.expectEq(a.zzxz, float4(3,3,1,3)); + test.expectEq(a.zzxw, float4(3,3,1,4)); + test.expectEq(a.zzyx, float4(3,3,2,1)); + test.expectEq(a.zzyy, float4(3,3,2,2)); + test.expectEq(a.zzyz, float4(3,3,2,3)); + test.expectEq(a.zzyw, float4(3,3,2,4)); + test.expectEq(a.zzzx, float4(3,3,3,1)); + test.expectEq(a.zzzy, float4(3,3,3,2)); + test.expectEq(a.zzzz, float4(3,3,3,3)); + test.expectEq(a.zzzw, float4(3,3,3,4)); + test.expectEq(a.zzwx, float4(3,3,4,1)); + test.expectEq(a.zzwy, float4(3,3,4,2)); + test.expectEq(a.zzwz, float4(3,3,4,3)); + test.expectEq(a.zzww, float4(3,3,4,4)); + test.expectEq(a.zwxx, float4(3,4,1,1)); + test.expectEq(a.zwxy, float4(3,4,1,2)); + test.expectEq(a.zwxz, float4(3,4,1,3)); + test.expectEq(a.zwxw, float4(3,4,1,4)); + test.expectEq(a.zwyx, float4(3,4,2,1)); + test.expectEq(a.zwyy, float4(3,4,2,2)); + test.expectEq(a.zwyz, float4(3,4,2,3)); + test.expectEq(a.zwyw, float4(3,4,2,4)); + test.expectEq(a.zwzx, float4(3,4,3,1)); + test.expectEq(a.zwzy, float4(3,4,3,2)); + test.expectEq(a.zwzz, float4(3,4,3,3)); + test.expectEq(a.zwzw, float4(3,4,3,4)); + test.expectEq(a.zwwx, float4(3,4,4,1)); + test.expectEq(a.zwwy, float4(3,4,4,2)); + test.expectEq(a.zwwz, float4(3,4,4,3)); + test.expectEq(a.zwww, float4(3,4,4,4)); + test.expectEq(a.wxxx, float4(4,1,1,1)); + test.expectEq(a.wxxy, float4(4,1,1,2)); + test.expectEq(a.wxxz, float4(4,1,1,3)); + test.expectEq(a.wxxw, float4(4,1,1,4)); + test.expectEq(a.wxyx, float4(4,1,2,1)); + test.expectEq(a.wxyy, float4(4,1,2,2)); + test.expectEq(a.wxyz, float4(4,1,2,3)); + test.expectEq(a.wxyw, float4(4,1,2,4)); + test.expectEq(a.wxzx, float4(4,1,3,1)); + test.expectEq(a.wxzy, float4(4,1,3,2)); + test.expectEq(a.wxzz, float4(4,1,3,3)); + test.expectEq(a.wxzw, float4(4,1,3,4)); + test.expectEq(a.wxwx, float4(4,1,4,1)); + test.expectEq(a.wxwy, float4(4,1,4,2)); + test.expectEq(a.wxwz, float4(4,1,4,3)); + test.expectEq(a.wxww, float4(4,1,4,4)); + test.expectEq(a.wyxx, float4(4,2,1,1)); + test.expectEq(a.wyxy, float4(4,2,1,2)); + test.expectEq(a.wyxz, float4(4,2,1,3)); + test.expectEq(a.wyxw, float4(4,2,1,4)); + test.expectEq(a.wyyx, float4(4,2,2,1)); + test.expectEq(a.wyyy, float4(4,2,2,2)); + test.expectEq(a.wyyz, float4(4,2,2,3)); + test.expectEq(a.wyyw, float4(4,2,2,4)); + test.expectEq(a.wyzx, float4(4,2,3,1)); + test.expectEq(a.wyzy, float4(4,2,3,2)); + test.expectEq(a.wyzz, float4(4,2,3,3)); + test.expectEq(a.wyzw, float4(4,2,3,4)); + test.expectEq(a.wywx, float4(4,2,4,1)); + test.expectEq(a.wywy, float4(4,2,4,2)); + test.expectEq(a.wywz, float4(4,2,4,3)); + test.expectEq(a.wyww, float4(4,2,4,4)); + test.expectEq(a.wzxx, float4(4,3,1,1)); + test.expectEq(a.wzxy, float4(4,3,1,2)); + test.expectEq(a.wzxz, float4(4,3,1,3)); + test.expectEq(a.wzxw, float4(4,3,1,4)); + test.expectEq(a.wzyx, float4(4,3,2,1)); + test.expectEq(a.wzyy, float4(4,3,2,2)); + test.expectEq(a.wzyz, float4(4,3,2,3)); + test.expectEq(a.wzyw, float4(4,3,2,4)); + test.expectEq(a.wzzx, float4(4,3,3,1)); + test.expectEq(a.wzzy, float4(4,3,3,2)); + test.expectEq(a.wzzz, float4(4,3,3,3)); + test.expectEq(a.wzzw, float4(4,3,3,4)); + test.expectEq(a.wzwx, float4(4,3,4,1)); + test.expectEq(a.wzwy, float4(4,3,4,2)); + test.expectEq(a.wzwz, float4(4,3,4,3)); + test.expectEq(a.wzww, float4(4,3,4,4)); + test.expectEq(a.wwxx, float4(4,4,1,1)); + test.expectEq(a.wwxy, float4(4,4,1,2)); + test.expectEq(a.wwxz, float4(4,4,1,3)); + test.expectEq(a.wwxw, float4(4,4,1,4)); + test.expectEq(a.wwyx, float4(4,4,2,1)); + test.expectEq(a.wwyy, float4(4,4,2,2)); + test.expectEq(a.wwyz, float4(4,4,2,3)); + test.expectEq(a.wwyw, float4(4,4,2,4)); + test.expectEq(a.wwzx, float4(4,4,3,1)); + test.expectEq(a.wwzy, float4(4,4,3,2)); + test.expectEq(a.wwzz, float4(4,4,3,3)); + test.expectEq(a.wwzw, float4(4,4,3,4)); + test.expectEq(a.wwwx, float4(4,4,4,1)); + test.expectEq(a.wwwy, float4(4,4,4,2)); + test.expectEq(a.wwwz, float4(4,4,4,3)); + test.expectEq(a.wwww, float4(4,4,4,4)); + // float4 -> float3 swizzles. + test.expectEq(a.xxx, float3(1,1,1)); + test.expectEq(a.xxy, float3(1,1,2)); + test.expectEq(a.xxz, float3(1,1,3)); + test.expectEq(a.xxw, float3(1,1,4)); + test.expectEq(a.xyx, float3(1,2,1)); + test.expectEq(a.xyy, float3(1,2,2)); + test.expectEq(a.xyz, float3(1,2,3)); + test.expectEq(a.xyw, float3(1,2,4)); + test.expectEq(a.xzx, float3(1,3,1)); + test.expectEq(a.xzy, float3(1,3,2)); + test.expectEq(a.xzz, float3(1,3,3)); + test.expectEq(a.xzw, float3(1,3,4)); + test.expectEq(a.xwx, float3(1,4,1)); + test.expectEq(a.xwy, float3(1,4,2)); + test.expectEq(a.xwz, float3(1,4,3)); + test.expectEq(a.xww, float3(1,4,4)); + test.expectEq(a.yxx, float3(2,1,1)); + test.expectEq(a.yxy, float3(2,1,2)); + test.expectEq(a.yxz, float3(2,1,3)); + test.expectEq(a.yxw, float3(2,1,4)); + test.expectEq(a.yyx, float3(2,2,1)); + test.expectEq(a.yyy, float3(2,2,2)); + test.expectEq(a.yyz, float3(2,2,3)); + test.expectEq(a.yyw, float3(2,2,4)); + test.expectEq(a.yzx, float3(2,3,1)); + test.expectEq(a.yzy, float3(2,3,2)); + test.expectEq(a.yzz, float3(2,3,3)); + test.expectEq(a.yzw, float3(2,3,4)); + test.expectEq(a.ywx, float3(2,4,1)); + test.expectEq(a.ywy, float3(2,4,2)); + test.expectEq(a.ywz, float3(2,4,3)); + test.expectEq(a.yww, float3(2,4,4)); + test.expectEq(a.zxx, float3(3,1,1)); + test.expectEq(a.zxy, float3(3,1,2)); + test.expectEq(a.zxz, float3(3,1,3)); + test.expectEq(a.zxw, float3(3,1,4)); + test.expectEq(a.zyx, float3(3,2,1)); + test.expectEq(a.zyy, float3(3,2,2)); + test.expectEq(a.zyz, float3(3,2,3)); + test.expectEq(a.zyw, float3(3,2,4)); + test.expectEq(a.zzx, float3(3,3,1)); + test.expectEq(a.zzy, float3(3,3,2)); + test.expectEq(a.zzz, float3(3,3,3)); + test.expectEq(a.zzw, float3(3,3,4)); + test.expectEq(a.zwx, float3(3,4,1)); + test.expectEq(a.zwy, float3(3,4,2)); + test.expectEq(a.zwz, float3(3,4,3)); + test.expectEq(a.zww, float3(3,4,4)); + test.expectEq(a.wxx, float3(4,1,1)); + test.expectEq(a.wxy, float3(4,1,2)); + test.expectEq(a.wxz, float3(4,1,3)); + test.expectEq(a.wxw, float3(4,1,4)); + test.expectEq(a.wyx, float3(4,2,1)); + test.expectEq(a.wyy, float3(4,2,2)); + test.expectEq(a.wyz, float3(4,2,3)); + test.expectEq(a.wyw, float3(4,2,4)); + test.expectEq(a.wzx, float3(4,3,1)); + test.expectEq(a.wzy, float3(4,3,2)); + test.expectEq(a.wzz, float3(4,3,3)); + test.expectEq(a.wzw, float3(4,3,4)); + test.expectEq(a.wwx, float3(4,4,1)); + test.expectEq(a.wwy, float3(4,4,2)); + test.expectEq(a.wwz, float3(4,4,3)); + test.expectEq(a.www, float3(4,4,4)); + // float4 -> float2 swizzles. + test.expectEq(a.xx, float2(1,1)); + test.expectEq(a.xy, float2(1,2)); + test.expectEq(a.xz, float2(1,3)); + test.expectEq(a.xw, float2(1,4)); + test.expectEq(a.yx, float2(2,1)); + test.expectEq(a.yy, float2(2,2)); + test.expectEq(a.yz, float2(2,3)); + test.expectEq(a.yw, float2(2,4)); + test.expectEq(a.zx, float2(3,1)); + test.expectEq(a.zy, float2(3,2)); + test.expectEq(a.zz, float2(3,3)); + test.expectEq(a.zw, float2(3,4)); + test.expectEq(a.wx, float2(4,1)); + test.expectEq(a.wy, float2(4,2)); + test.expectEq(a.wz, float2(4,3)); + test.expectEq(a.ww, float2(4,4)); + // float4 -> scalar swizzles. + test.expectEq(a.x, 1.0f); + test.expectEq(a.y, 2.0f); + test.expectEq(a.z, 3.0f); + test.expectEq(a.w, 4.0f); + // float4 array indexing. + test.expectEq(a[0], 1.0f); + test.expectEq(a[1], 2.0f); + test.expectEq(a[2], 3.0f); + test.expectEq(a[3], 4.0f); +} + +// float4 x float4 math +void test_float4_float4_math() +{ + test.beginTest("float4_float4_math"); + float4 a = float4(1, 2, 3, 4); + float4 b = float4(3, 4, 5, 6); + float4 c = float4(0, 1, 2, 3); + test.expectEq(a + b, float4(4, 6, 8, 10)); + test.expectEq(a - b, float4(-2, -2, -2, -2)); + test.expectEq(a * b, float4(3, 8, 15, 24)); + test.expectEq(a / b, float4(1.0/3.0, 2.0/4.0, 3.0/5.0, 4.0/6.0)); + + test.expectEq(math.distance(a, b), 4.0f); + test.expectEq(math.dot(a, b), 50.0f); + test.expectEq(math.length(a), 5.47722578f); + test.expectEq(math.normalize(a), float4(0.182574183, 0.365148365, 0.547722578, 0.730296731)); + + // Test normalize with zero-vector (invalid). + test.expectEq(math.normalize(float4(0)), float4(0)); + + test.expectEq(math.clamp(a, float4(0.5,2.5,1.5,3.5), float4(2,3,2.75,4)), float4(1.0, 2.5, 2.75, 4.0)); + test.expectEq(math.clamp(a, 1.5, 3.0), float4(1.5, 2.0, 3.0, 3.0)); + test.expectEq(math.max(a, b), float4(3, 4, 5, 6)); + test.expectEq(math.max(a, 1.5), float4(1.5, 2, 3, 4)); + test.expectEq(math.min(a, b), float4(1, 2, 3, 4)); + test.expectEq(math.min(a, 1.5), float4(1, 1.5, 1.5, 1.5)); + test.expectEq(math.mix(a, b, 0.5), float4(2, 3, 4, 5)); + test.expectEq(math.mix(a, b, float4(0.5, 1.0, 2.0, 1.0)), float4(2, 4, 7, 6)); + + test.expectTrue(math.all(a)); + test.expectTrue(math.any(a)); + test.expectFalse(math.all(c)); + test.expectTrue(math.any(c)); +} + +// float4 x scalar math +void test_float4_scalar_math() +{ + test.beginTest("float4_scalar_math"); + float4 a = float4(3, 4, 5, 6); + test.expectEq(a * 0.0, float4(0)); + test.expectEq(a * 1.0, float4(3, 4, 5, 6)); + test.expectEq(a.wzyx * 0.5, float4(3.0, 2.5, 2.0, 1.5)); + + test.expectEq(a / 2.0, float4(1.5, 2, 2.5, 3)); + test.expectEq(a + 2.0, float4(5, 6, 7, 8)); + test.expectEq(a - 2.0, float4(1, 2, 3, 4)); + test.expectEq(a * 2.0, float4(6, 8, 10, 12)); +} + +void main() +{ + test.beginSystem("Float4"); + { + test_float4_swizzle(); + test_float4_float4_math(); + test_float4_scalar_math(); + } + test.report(); +} diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp index 70d84a9e3..c590496e0 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.cpp @@ -29,8 +29,6 @@ #include #include #include -#include -#include using namespace TFE_Jedi; @@ -193,7 +191,7 @@ namespace TFE_DarkForces dispatch->vel = { 0 }; dispatch->lastPlayerPos = { 0 }; dispatch->freeTask = nullptr; - dispatch->flags = 4; + dispatch->flags = ACTOR_NPC; // this is later removed for barrels and scenery if (obj) { @@ -284,10 +282,11 @@ namespace TFE_DarkForces attackMod->fireSpread = FIXED(30); attackMod->accuracyNextTick = 0; attackMod->fireOffset.x = 0; - attackMod->fireOffset.z = 0; + attackMod->fireOffset.z = 0; + attackMod->target.flags &= ~TARGET_ALL; - attackMod->anim.flags |= 3; - attackMod->timing.nextTick = s_curTick + 0x4446; // ~120 seconds + attackMod->anim.flags |= (AFLAG_PLAYONCE | AFLAG_READY); // attack and damage module animations do not loop + attackMod->timing.nextTick = s_curTick + 0x4446; // ~120 seconds SecObject* obj = attackMod->header.obj; // world width and height was set from the sprite data. @@ -306,11 +305,12 @@ namespace TFE_DarkForces return attackMod->fireOffset.y; } + // Cause flying enemies fall to the ground when killed void actor_setDeathCollisionFlags() { ActorDispatch* logic = (ActorDispatch*)s_actorState.curLogic; MovementModule* moveMod = logic->moveMod; - moveMod->collisionFlags |= 2; + moveMod->collisionFlags |= ACTORCOL_GRAVITY; // Added to disable auto-aim when dying. logic->logic.obj->flags &= ~OBJ_FLAG_AIM; } @@ -386,7 +386,7 @@ namespace TFE_DarkForces SecObject* obj = moveMod->header.obj; if (moveMod->physics.responseStep || moveMod->collisionWall) { - if (!(moveMod->collisionFlags & 1)) + if (!(moveMod->collisionFlags & ACTORCOL_NO_Y_MOVE)) // i.e. actor is capable of vertical movement { RWall* wall = moveMod->physics.wall ? moveMod->physics.wall : moveMod->collisionWall; if (!wall) @@ -397,6 +397,7 @@ namespace TFE_DarkForces RSector* nextSector = wall->nextSector; if (nextSector) { + // Actor has collided with adjoined wall; test if it can fit (vertically) through the adjoin RSector* sector = wall->sector; fixed16_16 floorHeight = min(nextSector->floorHeight, sector->floorHeight); fixed16_16 ceilHeight = max(nextSector->ceilingHeight, sector->ceilingHeight); @@ -405,8 +406,9 @@ namespace TFE_DarkForces // If the object can fit. if (gap > objHeight) { + // Move vertically to the middle of the gap target->pos.y = floorHeight - (TFE_Jedi::abs(gap) >> 1) + (TFE_Jedi::abs(obj->worldHeight) >> 1); - target->flags |= 2; + target->flags |= TARGET_MOVE_Y; return JFALSE; } } @@ -416,10 +418,10 @@ namespace TFE_DarkForces return JFALSE; } - JBool actorLogic_isStopFlagSet() + JBool actorLogic_isVisibleFlagSet() { ActorDispatch* logic = (ActorDispatch*)s_actorState.curLogic; - return (logic->flags & 8) ? JTRUE : JFALSE; + return (logic->flags & ACTOR_PLAYER_VISIBLE) ? JTRUE : JFALSE; } void actor_changeDirFromCollision(MovementModule* moveMod, ActorTarget* target, Tick* prevColTick) @@ -564,7 +566,7 @@ namespace TFE_DarkForces ActorDispatch* logic = (ActorDispatch*)s_actorState.curLogic; // Update player visibility flag. - logic->flags &= ~8; + logic->flags &= ~ACTOR_PLAYER_VISIBLE; logic->flags |= ((playerVis & 1) << 3); // flag 8 = player visible. if (playerVis) @@ -585,7 +587,8 @@ namespace TFE_DarkForces AttackModule* attackMod = &damageMod->attackMod; SecObject* obj = attackMod->header.obj; RSector* sector = obj->sector; - if (!(attackMod->anim.flags & 2)) + + if (!(attackMod->anim.flags & AFLAG_READY)) { if (obj->type == OBJ_TYPE_SPRITE) { @@ -639,7 +642,7 @@ namespace TFE_DarkForces freeObject(item); } } - s32 animIndex = actor_getAnimationIndex(4); + s32 animIndex = actor_getAnimationIndex(ANIM_DEAD); if (animIndex != -1) { RSector* sector = obj->sector; @@ -668,6 +671,7 @@ namespace TFE_DarkForces AttackModule* attackMod = &damageMod->attackMod; SecObject* obj = attackMod->header.obj; RSector* sector = obj->sector; + if (msg == MSG_DAMAGE) { if (damageMod->hp > 0) @@ -700,14 +704,14 @@ namespace TFE_DarkForces actor_setDeathCollisionFlags(); sound_stop(logic->alertSndID); sound_playCued(damageMod->dieSndSrc, obj->posWS); - attackMod->anim.flags |= 8; + attackMod->anim.flags |= AFLAG_BIT3; if (proj->type == PROJ_PUNCH && obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(2, anim); + actor_setupAnimation(ANIM_DIE1, anim); } else if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(3, anim); + actor_setupAnimation(ANIM_DIE2, anim); } } else @@ -724,7 +728,7 @@ namespace TFE_DarkForces damageMod->hurtSndID = sound_playCued(damageMod->hurtSndSrc, obj->posWS); if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(12, anim); + actor_setupAnimation(ANIM_PAIN, anim); } } } @@ -761,10 +765,10 @@ namespace TFE_DarkForces actor_setDeathCollisionFlags(); sound_stop(logic->alertSndID); sound_playCued(damageMod->dieSndSrc, obj->posWS); - attackMod->target.flags |= 8; + attackMod->target.flags |= 8; // possibly an error? FREEZE (bit 3) has already been added to target flags above if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(3, anim); + actor_setupAnimation(ANIM_DIE2, anim); } } else @@ -774,7 +778,7 @@ namespace TFE_DarkForces damageMod->hurtSndID = sound_playCued(damageMod->hurtSndSrc, obj->posWS); if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(12, anim); + actor_setupAnimation(ANIM_PAIN, anim); } } } @@ -798,13 +802,14 @@ namespace TFE_DarkForces actor_setDeathCollisionFlags(); sound_stop(logic->alertSndID); sound_playCued(damageMod->dieSndSrc, obj->posWS); - attackMod->anim.flags |= 8; + attackMod->anim.flags |= AFLAG_BIT3; if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(3, anim); + actor_setupAnimation(ANIM_DIE2, anim); } } } + return JFALSE; } @@ -840,16 +845,16 @@ namespace TFE_DarkForces SecObject* obj = attackMod->header.obj; LogicAnimation* anim = &attackMod->anim; s32 state = attackMod->anim.state; - + switch (state) { case STATE_DELAY: { - if (attackMod->anim.flags & 2) + if (attackMod->anim.flags & AFLAG_READY) { // Clear attack based lighting. obj->flags &= ~OBJ_FLAG_FULLBRIGHT; - attackMod->anim.state = STATE_ANIMATEATTACK; + attackMod->anim.state = STATE_DECIDE; // Update the shooting accuracy. if (attackMod->accuracyNextTick < s_curTick) @@ -873,36 +878,37 @@ namespace TFE_DarkForces return s_curTick + random(attackMod->timing.delay); } } break; - case STATE_ANIMATEATTACK: + case STATE_DECIDE: { gameMusic_sustainFight(); if (s_playerDying) { if (s_curTick >= s_reviveTick) { - attackMod->anim.flags |= 2; + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; return attackMod->timing.delay; } } + // Check for player visibility if (!actor_canSeeObjFromDist(obj, s_playerObject)) { actor_updatePlayerVisiblity(JFALSE, 0, 0); - attackMod->anim.flags |= 2; + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; if (attackMod->timing.nextTick < s_curTick) { + // Lost sight of player for sufficient length of time (losDelay) - return to idle state attackMod->timing.delay = attackMod->timing.searchDelay; actor_setupInitAnimation(); } return attackMod->timing.delay; } - else + else // Player is visible { actor_updatePlayerVisiblity(JTRUE, s_eyePos.x, s_eyePos.z); attackMod->timing.nextTick = s_curTick + attackMod->timing.losDelay; - fixed16_16 dist = distApprox(s_playerObject->posWS.x, s_playerObject->posWS.z, obj->posWS.x, obj->posWS.z); fixed16_16 yDiff = TFE_Jedi::abs(obj->posWS.y - obj->worldHeight - s_eyePos.y); angle14_32 vertAngle = vec2ToAngle(yDiff, dist); @@ -916,42 +922,50 @@ namespace TFE_DarkForces { if (dist <= attackMod->meleeRange) { - attackMod->anim.state = STATE_FIRE1; + // Within melee range => Perform melee (primary attack) + attackMod->anim.state = STATE_ATTACK1; attackMod->timing.delay = attackMod->timing.meleeDelay; } else if (!(attackMod->attackFlags & ATTFLAG_RANGED) || dist < attackMod->minDist) { - attackMod->anim.flags |= 2; + // Outside melee range && cannot do ranged attack => quit out + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; return attackMod->timing.delay; } else { - attackMod->anim.state = STATE_FIRE2; + // Do ranged attack (secondary) + attackMod->anim.state = STATE_ATTACK2; attackMod->timing.delay = attackMod->timing.rangedDelay; } } - else + else // Actor does not have melee attack { if (dist < attackMod->minDist) { - attackMod->anim.flags |= 2; + // Too close for ranged attack => quit out + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; return attackMod->timing.delay; } - attackMod->anim.state = STATE_FIRE1; + + // Do ranged attack (primary) + attackMod->anim.state = STATE_ATTACK1; attackMod->timing.delay = attackMod->timing.rangedDelay; } if (obj->type == OBJ_TYPE_SPRITE) { - if (attackMod->anim.state == STATE_FIRE1) + if (attackMod->anim.state == STATE_ATTACK1) { - actor_setupAnimation(1, &attackMod->anim); + // Use primary attack animation + actor_setupAnimation(ANIM_ATTACK1, &attackMod->anim); } else { - actor_setupAnimation(7, &attackMod->anim); + // Use secondary attack animation + actor_setupAnimation(ANIM_ATTACK2, &attackMod->anim); } } @@ -962,17 +976,17 @@ namespace TFE_DarkForces attackMod->target.roll = obj->roll; attackMod->target.flags |= (TARGET_MOVE_XZ | TARGET_MOVE_ROT); } - else + else // Too far away or too high or too low to attack { - attackMod->anim.flags |= 2; + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; return attackMod->timing.delay; } } } break; - case STATE_FIRE1: + case STATE_ATTACK1: { - if (!(attackMod->anim.flags & 2)) + if (!(attackMod->anim.flags & AFLAG_READY)) { break; } @@ -1012,7 +1026,7 @@ namespace TFE_DarkForces SecObject* projObj = proj->logic.obj; projObj->yaw = obj->yaw; - // Vanilla DF did not handle arcing projectiles with STATE_FIRE1; this has been added + // Vanilla DF did not handle arcing projectiles with STATE_ATTACK1; this has been added if (attackMod->projType == PROJ_THERMAL_DET || attackMod->projType == PROJ_MORTAR) { // TDs are lobbed at an angle that depends on distance from target @@ -1053,13 +1067,13 @@ namespace TFE_DarkForces { if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(6, anim); + actor_setupAnimation(ANIM_ATTACK1_END, anim); } attackMod->anim.state = STATE_DELAY; } break; - case STATE_FIRE2: + case STATE_ATTACK2: { - if (!(attackMod->anim.flags & 2)) + if (!(attackMod->anim.flags & AFLAG_READY)) { break; } @@ -1112,7 +1126,7 @@ namespace TFE_DarkForces { if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(8, anim); + actor_setupAnimation(ANIM_ATTACK2_END, anim); } attackMod->anim.state = STATE_DELAY; } break; @@ -1135,7 +1149,7 @@ namespace TFE_DarkForces { attackMod->timing.nextTick = s_curTick + attackMod->timing.losDelay; } - if (logic->flags & 1) + if (logic->flags & ACTOR_IDLE) { return attackMod->timing.delay; } @@ -1173,7 +1187,7 @@ namespace TFE_DarkForces } else { - if (actorLogic_isStopFlagSet()) + if (actorLogic_isVisibleFlagSet()) { if (thinkerMod->playerLastSeen != 0xffffffff) { @@ -1191,7 +1205,7 @@ namespace TFE_DarkForces if (actor_handleSteps(moveMod, target)) { actor_changeDirFromCollision(moveMod, target, &thinkerMod->prevColTick); - if (!actorLogic_isStopFlagSet()) + if (!actorLogic_isVisibleFlagSet()) { thinkerMod->maxWalkTime += 218; if (thinkerMod->maxWalkTime > 1456) @@ -1219,7 +1233,7 @@ namespace TFE_DarkForces } fixed16_16 targetOffset; - if (!actorLogic_isStopFlagSet()) + if (!actorLogic_isVisibleFlagSet()) { // Offset the target by |dx| / 4 // This is obviously a typo and bug in the DOS code and should be min(|dx|, |dz|) @@ -1249,20 +1263,20 @@ namespace TFE_DarkForces thinkerMod->target.yaw = vec2ToAngle(dx, dz); thinkerMod->target.flags |= TARGET_MOVE_ROT; - if (!(logic->flags & 2)) + if (!(logic->flags & ACTOR_MOVING)) { if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(0, &thinkerMod->anim); + actor_setupAnimation(ANIM_MOVE, &thinkerMod->anim); } - logic->flags |= 2; + logic->flags |= ACTOR_MOVING; } thinkerMod->anim.state = STATE_MOVE; thinkerMod->nextTick = s_curTick + thinkerMod->maxWalkTime; if (obj->entityFlags & ETFLAG_REMOTE) { - if (!(logic->flags & 1) && (logic->flags & 2)) + if (!(logic->flags & ACTOR_IDLE) && (logic->flags & ACTOR_MOVING)) { sound_playCued(s_agentSndSrc[AGENTSND_REMOTE_2], obj->posWS); } @@ -1287,7 +1301,7 @@ namespace TFE_DarkForces thinkerMod->delay = 72; thinkerMod->nextTick = 0; thinkerMod->playerLastSeen = 0xffffffff; - thinkerMod->anim.state = STATE_FIRE1; + thinkerMod->anim.state = STATE_TURN; thinkerMod->maxWalkTime = 728; // ~5 seconds between decision points. thinkerMod->anim.frameRate = 5; thinkerMod->anim.frameCount = ONE_16; @@ -1316,11 +1330,11 @@ namespace TFE_DarkForces void actor_setupInitAnimation() { ActorDispatch* logic = (ActorDispatch*)s_actorState.curLogic; - logic->flags = (logic->flags | 1) & 0xfd; - logic->nextTick = s_curTick +logic->delay; + logic->flags = (logic->flags | ACTOR_IDLE) & ~ACTOR_MOVING; // remove flag bit 1 (ACTOR_MOVING) + logic->nextTick = s_curTick + logic->delay; SecObject* obj = logic->logic.obj; - obj->anim = actor_getAnimationIndex(5); + obj->anim = actor_getAnimationIndex(ANIM_IDLE); obj->frame = 0; } @@ -1351,7 +1365,7 @@ namespace TFE_DarkForces desiredMove.x = moveMod->target.pos.x - obj->posWS.x; desiredMove.z = moveMod->target.pos.z - obj->posWS.z; } - if (!(moveMod->collisionFlags & 1)) + if (!(moveMod->collisionFlags & ACTORCOL_NO_Y_MOVE)) // i.e. actor is capable of vertical movement { if (moveMod->target.flags & TARGET_MOVE_Y) { @@ -1414,7 +1428,7 @@ namespace TFE_DarkForces } } // Handles a single collision response + resolution step. - if (moveMod->collisionFlags & 4) + if (moveMod->collisionFlags & ACTORCOL_BIT2) { moveMod->collisionWall = wall; dirX = physics->responseDir.x; @@ -1435,7 +1449,7 @@ namespace TFE_DarkForces handleCollision(physics); // Handles a single collision response + resolution step from velocity delta. - if ((moveMod->collisionFlags & 4) && physics->responseStep) + if ((moveMod->collisionFlags & ACTORCOL_BIT2) && physics->responseStep) { moveMod->collisionWall = physics->wall; dirX = physics->responseDir.x; @@ -1528,9 +1542,9 @@ namespace TFE_DarkForces // Updates the actor target with the passed in target based on the flags. JBool defaultUpdateTargetFunc(MovementModule* moveMod, ActorTarget* target) { - if (target->flags & 8) + if (target->flags & TARGET_FREEZE) { - moveMod->target.flags |= 8; + moveMod->target.flags |= TARGET_FREEZE; } else { @@ -1577,7 +1591,7 @@ namespace TFE_DarkForces moveMod->collisionWall = nullptr; moveMod->unused = 0; - moveMod->collisionFlags = (moveMod->collisionFlags | 3) & 0xfffffffb; + moveMod->collisionFlags = (moveMod->collisionFlags | (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY)) & ~ACTORCOL_BIT2; // Set bits 0, 1 and clear bit 2. This creates a non-flying AI by default. obj->entityFlags |= ETFLAG_SMART_OBJ; } @@ -1663,6 +1677,7 @@ namespace TFE_DarkForces actor_initModule((ActorModule*)moveMod, (Logic*)dispatch); actor_setupSmartObj(moveMod); + moveMod->header.func = defaultActorFunc; moveMod->header.freeFunc = nullptr; moveMod->header.type = ACTMOD_MOVE; @@ -1710,11 +1725,12 @@ namespace TFE_DarkForces allocator_deleteItem(s_istate.actorDispatch, dispatch); } + // Returns JTRUE when animation is finished, otherwise JFALSE JBool actor_advanceAnimation(LogicAnimation* anim, SecObject* obj) { - if (!anim->prevTick) { + // Start the animation anim->prevTick = s_frameTicks[anim->frameRate]; anim->flags &= ~AFLAG_READY; @@ -1729,8 +1745,9 @@ namespace TFE_DarkForces fixed16_16 endFrame = anim->startFrame + anim->frameCount; if (anim->frame >= endFrame) { - if (anim->flags & AFLAG_PLAYED) + if (anim->flags & AFLAG_PLAYONCE) { + // Non-looping animation; animation will end at the final frame endFrame -= ONE_16; anim->frame = endFrame; obj->frame = floor16(endFrame); @@ -1739,6 +1756,7 @@ namespace TFE_DarkForces while (anim->frame >= endFrame) { + // Rewind to start of animation (looping animation) anim->frame -= anim->frameCount; } } @@ -1908,16 +1926,16 @@ namespace TFE_DarkForces if (msg == MSG_WAKEUP) { - if (dispatch->flags & 1) + if (dispatch->flags & ACTOR_IDLE) { gameMusic_startFight(); } - if ((dispatch->flags & 4) && (dispatch->flags & 1)) + if ((dispatch->flags & ACTOR_NPC) && (dispatch->flags & ACTOR_IDLE)) { if (s_actorState.nextAlertTick < s_curTick) { - if (dispatch->flags & 16) // Officer alert list. + if (dispatch->flags & ACTOR_OFFIC_ALERT) // Officer alert list. { dispatch->alertSndID = sound_playCued(s_officerAlertSndSrc[s_actorState.officerAlertIndex], obj->posWS); s_actorState.officerAlertIndex++; @@ -1926,7 +1944,7 @@ namespace TFE_DarkForces s_actorState.officerAlertIndex = 0; } } - else if (dispatch->flags & 32) // Storm trooper alert list + else if (dispatch->flags & ACTOR_TROOP_ALERT) // Storm trooper alert list { dispatch->alertSndID = sound_playCued(s_stormAlertSndSrc[s_actorState.stormtrooperAlertIndex], obj->posWS); s_actorState.stormtrooperAlertIndex++; @@ -1941,16 +1959,16 @@ namespace TFE_DarkForces } s_actorState.nextAlertTick = s_curTick + 291; // ~2 seconds between alerts } - dispatch->flags &= 0xfffffffe; + dispatch->flags &= ~ACTOR_IDLE; // remove flag bit 0 (ACTOR_IDLE) } } else if (msg == MSG_DAMAGE || msg == MSG_EXPLOSION) { - if (dispatch->flags & 1) + if (dispatch->flags & ACTOR_IDLE) { gameMusic_startFight(); } - dispatch->flags &= ~1; + dispatch->flags &= ~ACTOR_IDLE; s_actorState.curAnimation = nullptr; } } @@ -2040,6 +2058,8 @@ namespace TFE_DarkForces } } + // Task function for dispatch actors + // Iterates through all actors in s_istate.actorDispatch and updates them void actorLogicTaskFunc(MessageType msg) { task_begin; @@ -2053,35 +2073,34 @@ namespace TFE_DarkForces { SecObject* obj = dispatch->logic.obj; const u32 flags = dispatch->flags; - if ((flags & 1) && (flags & 4)) + if ((flags & ACTOR_IDLE) && (flags & ACTOR_NPC)) { if (dispatch->nextTick < s_curTick) { - dispatch->nextTick = s_curTick +dispatch->delay; + dispatch->nextTick = s_curTick + dispatch->delay; if (actor_isObjectVisible(obj, s_playerObject, dispatch->fov, dispatch->awareRange)) { + // Wake up, and alert other actors within a 150 unit range message_sendToObj(obj, MSG_WAKEUP, actor_hitEffectMsgFunc); gameMusic_startFight(); collision_effectObjectsInRangeXZ(obj->sector, FIXED(150), obj->posWS, hitEffectWakeupFunc, obj, ETFLAG_AI_ACTOR); } } } - else + else // actor is not idle { s_actorState.curLogic = (Logic*)dispatch; s_actorState.curAnimation = nullptr; for (s32 i = 0; i < ACTOR_MAX_MODULES; i++) { - ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; + ActorModule* module = dispatch->modules[ACTOR_MAX_MODULES - 1 - i]; if (module && module->func && module->nextTick < s_curTick) { - Tick newtick = module->func(module, dispatch->moveMod); - module->nextTick = newtick; + module->nextTick = module->func(module, dispatch->moveMod); } - } - if (s_actorState.curLogic && !(dispatch->flags & 1)) + if (s_actorState.curLogic && !(dispatch->flags & ACTOR_IDLE)) { MovementModule* moveMod = dispatch->moveMod; if (moveMod && moveMod->header.func) @@ -2089,7 +2108,7 @@ namespace TFE_DarkForces actor_handlePhysics(moveMod, &dispatch->vel); moveMod->header.func((ActorModule*)moveMod, moveMod); } - + if ((obj->type & OBJ_TYPE_SPRITE) && s_actorState.curAnimation) { obj->anim = s_actorState.curAnimation->animId; @@ -2109,7 +2128,8 @@ namespace TFE_DarkForces task_end; } - // This is really a list of bosses: + // Task function for bosses, mousebots, welders, turrets + // Iterates through all actors in s_physicsActors and updates them // TODO: Rename "physics" actor as appopriate. void actorPhysicsTaskFunc(MessageType msg) { @@ -2163,4 +2183,4 @@ namespace TFE_DarkForces } task_end; } -} // namespace TFE_DarkForces \ No newline at end of file +} // namespace TFE_DarkForces diff --git a/TheForceEngine/TFE_DarkForces/Actor/actor.h b/TheForceEngine/TFE_DarkForces/Actor/actor.h index 6dc88c993..d8f0a62cc 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actor.h +++ b/TheForceEngine/TFE_DarkForces/Actor/actor.h @@ -84,9 +84,14 @@ enum ACTOR_MAX_MODULES = 6, }; -enum ActorCollisionFlags +enum ActorDispatchFlags { - ACTORCOL_GRAVITY = FLAG_BIT(1), + ACTOR_IDLE = FLAG_BIT(0), + ACTOR_MOVING = FLAG_BIT(1), + ACTOR_NPC = FLAG_BIT(2), // set for AI characters, unset for scenery and exploders (these also use ActorDispatch) + ACTOR_PLAYER_VISIBLE = FLAG_BIT(3), + ACTOR_OFFIC_ALERT = FLAG_BIT(4), // use officer alert sounds + ACTOR_TROOP_ALERT = FLAG_BIT(5), // use stormtrooper alert sounds }; // Forward Declarations. @@ -101,7 +106,7 @@ struct ActorTarget; // Logic for 'actors' - // an Actor is something with animated 'actions' that can move around in the world. -// The "Dispatch" logic is the core actor, so rename to match. +// The "Dispatch" logic is the core actor used for ordinary enemies, as well as scenery and exploders. struct ActorDispatch { Logic logic; diff --git a/TheForceEngine/TFE_DarkForces/Actor/actorInternal.h b/TheForceEngine/TFE_DarkForces/Actor/actorInternal.h index 0962b0168..82157049f 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actorInternal.h +++ b/TheForceEngine/TFE_DarkForces/Actor/actorInternal.h @@ -49,8 +49,8 @@ namespace TFE_DarkForces Task* actorPhysicsTask; JBool objCollisionEnabled; }; - extern ActorInternalState s_istate; - extern List* s_physicsActors; + extern ActorInternalState s_istate; // Holds all the "Dispatch" actors + extern List* s_physicsActors; // Holds all the non-dispatch actors (bosses, mousebots, turrets...) void actorLogicCleanupFunc(Logic* logic); JBool defaultActorFunc(ActorModule* module, MovementModule* moveMod); diff --git a/TheForceEngine/TFE_DarkForces/Actor/actorModule.h b/TheForceEngine/TFE_DarkForces/Actor/actorModule.h index ce82360c0..06f84ae81 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/actorModule.h +++ b/TheForceEngine/TFE_DarkForces/Actor/actorModule.h @@ -24,8 +24,9 @@ struct Task; enum LogicAnimFlags { - AFLAG_PLAYED = FLAG_BIT(0), - AFLAG_READY = FLAG_BIT(1), + AFLAG_PLAYONCE = FLAG_BIT(0), // Indicates an animation will play through once only and not loop back to first frame + AFLAG_READY = FLAG_BIT(1), + AFLAG_BIT3 = FLAG_BIT(3), // Unknown, seems to be set but never read }; enum LogicAnimState : u32 @@ -33,11 +34,11 @@ enum LogicAnimState : u32 //GENERAL STATE_DELAY = 0u, //ATTACK MODULE - STATE_ANIMATEATTACK, - STATE_FIRE1, - STATE_ANIMATE1, - STATE_FIRE2, - STATE_ANIMATE2, + STATE_DECIDE, // Decide whether to attack and which attack to use + STATE_ATTACK1, // Primary attack (can be melee or ranged) + STATE_ANIMATE1, // Animation played after attack (eg. weapon recoil) + STATE_ATTACK2, // Secondary attack (always ranged) + STATE_ANIMATE2, // Animation played after secondary attack STATE_COUNT, //THINKER MODULE // Move towards our destination @@ -59,7 +60,7 @@ struct LogicAnimation Tick prevTick; fixed16_16 frame; fixed16_16 startFrame; - u32 flags; + u32 flags; // see LogicAnimFlags enum s32 animId; LogicAnimState state; }; @@ -106,6 +107,14 @@ enum AttackFlags ATTFLAG_ALL = ATTFLAG_MELEE | ATTFLAG_RANGED | ATTFLAG_LIT_MELEE | ATTFLAG_LIT_RNG }; +enum ActorCollisionFlags +{ + ACTORCOL_NO_Y_MOVE = FLAG_BIT(0), // When _not_ set, an actor can move vertically. Set for non-flying enemies. + ACTORCOL_GRAVITY = FLAG_BIT(1), + ACTORCOL_BIT2 = FLAG_BIT(2), // Alters the way collision is handled. This is generally set for flying enemies and bosses + ACTORCOL_ALL = ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY | ACTORCOL_BIT2 +}; + struct ActorModule { ActorModuleType type; @@ -165,7 +174,7 @@ struct MovementModule vec3_fixed delta; RWall* collisionWall; u32 unused; // member in structure but not actually used, other than being initialized. - u32 collisionFlags; + u32 collisionFlags; // see ActorCollisionFlags }; struct ThinkerModule @@ -203,6 +212,10 @@ struct DamageModule HitEffectID dieEffect; }; +// "PhysicsActor" is the core actor used for bosses, mousebots, turrets and welders +// It only has a movement module +// Damage, attack and other behaviour is coded specifically for each AI +// Consider changing to a more suitable name? struct PhysicsActor { MovementModule moveMod; @@ -218,4 +231,5 @@ struct PhysicsActor Task* actorTask; JBool alive; s32 state; -}; \ No newline at end of file +}; + diff --git a/TheForceEngine/TFE_DarkForces/Actor/animTables.cpp b/TheForceEngine/TFE_DarkForces/Actor/animTables.cpp index c65754187..8d0b423c3 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/animTables.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/animTables.cpp @@ -52,6 +52,7 @@ namespace TFE_DarkForces s_officerAnimTable, s_troopAnimTable, s_commandoAnimTable, + s_customAnimTable, }; static const s32 s_animTableCount = TFE_ARRAYSIZE(s_animTables); diff --git a/TheForceEngine/TFE_DarkForces/Actor/animTables.h b/TheForceEngine/TFE_DarkForces/Actor/animTables.h index 6c2a6fbf7..4a42d33c8 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/animTables.h +++ b/TheForceEngine/TFE_DarkForces/Actor/animTables.h @@ -13,6 +13,21 @@ namespace TFE_DarkForces { + enum + { + ANIM_MOVE = 0, + ANIM_ATTACK1 = 1, + ANIM_DIE1 = 2, // Standard enemies: death from melee + ANIM_DIE2 = 3, + ANIM_DEAD = 4, + ANIM_IDLE = 5, + ANIM_ATTACK1_END = 6, + ANIM_ATTACK2 = 7, + ANIM_ATTACK2_END = 8, + ANIM_PAIN = 12, + ANIM_SEARCH = 14, // Used by sewer creature + }; + // Accessors for serialization. s32 animTables_getIndex(const s32* table); const s32* animTables_getTable(s32 index); diff --git a/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp b/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp index 165d553e2..c32770826 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/bobaFett.cpp @@ -145,7 +145,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; local(anim)->frameRate = 6; actor_setupBossAnimation(local(obj), 12, local(anim)); @@ -203,7 +203,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; local(anim)->frameRate = 6; actor_setupBossAnimation(local(obj), 12, local(anim)); @@ -606,7 +606,7 @@ namespace TFE_DarkForces sound_stop(local(physicsActor)->moveSndId); sound_playCued(s_shared.boba4SndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; local(anim)->frameRate = 8; actor_setupBossAnimation(local(obj), 2, local(anim)); @@ -660,7 +660,7 @@ namespace TFE_DarkForces local(nextCheckForPlayerTick) = 0; local(changeStateTick) = s_curTick + 8739; local(nextChangePhaseTick) = s_curTick + 1456; - local(physicsActor)->moveMod.collisionFlags |= 4; + local(physicsActor)->moveMod.collisionFlags |= ACTORCOL_BIT2; while (local(physicsActor)->state == BOBASTATE_SEARCH) { @@ -712,7 +712,7 @@ namespace TFE_DarkForces } } // while (state == BOBASTATE_SEARCH) - local(physicsActor)->moveMod.collisionFlags |= 4; + local(physicsActor)->moveMod.collisionFlags |= ACTORCOL_BIT2; task_end; } @@ -946,8 +946,8 @@ namespace TFE_DarkForces physicsActor->moveMod.physics.width = FIXED(2); physicsActor->moveMod.physics.botOffset = 0; - physicsActor->moveMod.collisionFlags &= 0xfffffff8; - physicsActor->moveMod.collisionFlags |= 6; + physicsActor->moveMod.collisionFlags &= ~ACTORCOL_ALL; + physicsActor->moveMod.collisionFlags |= (ACTORCOL_GRAVITY | ACTORCOL_BIT2); physicsActor->moveMod.physics.yPos = COL_INFINITY; physicsActor->moveMod.physics.height = obj->worldHeight; @@ -956,7 +956,7 @@ namespace TFE_DarkForces anim->frameCount = ONE_16; anim->prevTick = 0; anim->flags |= AFLAG_READY; - anim->flags &= ~AFLAG_PLAYED; + anim->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(obj, 5, anim); ActorTarget* target = &physicsActor->moveMod.target; diff --git a/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp b/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp index e7a4bddf4..b5a93b9ac 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/dragon.cpp @@ -119,10 +119,10 @@ namespace TFE_DarkForces { task_localBlockBegin; ActorTarget* target = &local(physicsActor)->moveMod.target; - target->flags |= 8; + target->flags |= TARGET_FREEZE; memcpy(&local(tmpAnim), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); local(anim)->frameRate = 8; task_localBlockEnd; @@ -137,7 +137,7 @@ namespace TFE_DarkForces actor_setupBossAnimation(local(obj), local(anim)->animId, local(anim)); ActorTarget* target = &local(physicsActor)->moveMod.target; - target->flags &= 0xfffffff7; + target->flags &= ~TARGET_FREEZE; task_localBlockEnd; } msg = MSG_DAMAGE; @@ -209,7 +209,7 @@ namespace TFE_DarkForces memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); // Set the animation to 12. - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); local(anim)->frameRate = 8; @@ -305,13 +305,13 @@ namespace TFE_DarkForces fixed16_16 sinYaw, cosYaw; sinCosFixed(obj->yaw, &sinYaw, &cosYaw); - target->flags |= 4; + target->flags |= TARGET_MOVE_ROT; target->speedRotation = 6826; fixed16_16 animSpeed = s_kellDragonAnim[dragon->animIndex].speed; target->pos.x = obj->posWS.x + mul16(sinYaw, animSpeed); target->pos.z = obj->posWS.z + mul16(cosYaw, animSpeed); - target->flags |= 1; + target->flags |= TARGET_MOVE_XZ; } void kellDragon_handleState1(MessageType msg) @@ -336,7 +336,7 @@ namespace TFE_DarkForces local(prevColTick) = 0; local(yawAligned) = JFALSE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); task_localBlockBegin; @@ -462,7 +462,7 @@ namespace TFE_DarkForces local(physicsActor) = &local(dragon)->actor; local(anim) = &local(physicsActor)->anim; local(target) = &local(physicsActor)->moveMod.target; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; task_localBlockBegin; actor_setupBossAnimation(local(obj), 0, local(anim)); @@ -559,7 +559,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); if (local(physicsActor)->state != DRAGONSTATE_JUMPING) { break; } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 9, local(anim)); local(anim)->frameRate = 8; sound_playCued(s_shared.kellSound1, local(obj)->posWS); @@ -688,7 +688,7 @@ namespace TFE_DarkForces break; } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 1, local(anim)); local(anim)->frameRate = 8; @@ -751,7 +751,7 @@ namespace TFE_DarkForces local(target)->flags |= TARGET_FREEZE; sound_playCued(s_shared.kellSound3, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 2, local(anim)); local(anim)->frameRate = 8; @@ -819,7 +819,7 @@ namespace TFE_DarkForces local(delay) = 72; local(speedRotation) = local(target)->speedRotation; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); local(target)->flags &= ~TARGET_FREEZE; local(target)->speedRotation = 0x3000; @@ -1103,17 +1103,17 @@ namespace TFE_DarkForces physics->botOffset = 0x60000; physics->yPos = 0x80000; physics->width = obj->worldWidth; - physicsActor->moveMod.collisionFlags |= 7; + physicsActor->moveMod.collisionFlags |= ACTORCOL_ALL; physics->height = obj->worldHeight + HALF_16; ActorTarget* target = &physicsActor->moveMod.target; - target->flags &= 0xfffffff0; + target->flags &= ~TARGET_ALL; target->speedRotation = 43546; // 956.8 degrees per second. target->speed = FIXED(10); LogicAnimation* anim = &physicsActor->anim; anim->frameRate = 5; - anim->flags = (anim->flags | AFLAG_READY) & (~AFLAG_PLAYED); + anim->flags = (anim->flags | AFLAG_READY) & (~AFLAG_PLAYONCE); anim->frameCount = ONE_16; anim->prevTick = 0; actor_setupBossAnimation(obj, 5, anim); diff --git a/TheForceEngine/TFE_DarkForces/Actor/enemies.cpp b/TheForceEngine/TFE_DarkForces/Actor/enemies.cpp index 33c58665e..d75a4a057 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/enemies.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/enemies.cpp @@ -49,7 +49,7 @@ namespace TFE_DarkForces ThinkerModule* thinkerMod = actor_createThinkerModule(dispatch); thinkerMod->target.speedRotation = 0x7fff; thinkerMod->target.speed = FIXED(13); - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -57,7 +57,7 @@ namespace TFE_DarkForces dispatch->moveMod = moveMod; dispatch->animTable = s_reeyeesAnimTable; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -87,7 +87,7 @@ namespace TFE_DarkForces ThinkerModule* thinkerMod = actor_createThinkerModule(dispatch); thinkerMod->target.speedRotation = 0x7fff; thinkerMod->target.speed = FIXED(13); - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -95,7 +95,7 @@ namespace TFE_DarkForces dispatch->moveMod = moveMod; dispatch->animTable = s_reeyeesAnimTable; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -129,7 +129,7 @@ namespace TFE_DarkForces thinkerMod->delay = 0; thinkerMod->target.speedRotation = 0x3fff; thinkerMod->target.speed = FIXED(12); - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -137,7 +137,7 @@ namespace TFE_DarkForces dispatch->moveMod = moveMod; dispatch->animTable = s_gamorAnimTable; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -170,7 +170,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = 0x7fff; thinkerMod->target.speed = FIXED(9); thinkerMod->delay = 0; - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -178,7 +178,7 @@ namespace TFE_DarkForces dispatch->moveMod = moveMod; dispatch->animTable = s_bosskAnimTable; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -228,7 +228,7 @@ namespace TFE_DarkForces ThinkerModule* thinkerMod = actor_createThinkerModule(dispatch); thinkerMod->target.speedRotation = cust->rotationSpeed; thinkerMod->target.speed = FIXED(cust->speed); - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; // Ensures that walking animations will loop thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -248,12 +248,12 @@ namespace TFE_DarkForces moveMod->physics.width = obj->worldWidth; if (cust->isFlying) { - moveMod->collisionFlags = (moveMod->collisionFlags & 0xfffffff8) | 4; + moveMod->collisionFlags = (moveMod->collisionFlags & ~ACTORCOL_ALL) | ACTORCOL_BIT2; // Remove bits 0, 1 and set bit 2 moveMod->physics.yPos = FIXED(200); } else { - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; } dispatch->animTable = s_customAnimTable; diff --git a/TheForceEngine/TFE_DarkForces/Actor/exploders.cpp b/TheForceEngine/TFE_DarkForces/Actor/exploders.cpp index d0387cb18..e64624b64 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/exploders.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/exploders.cpp @@ -19,7 +19,7 @@ namespace TFE_DarkForces s_actorState.curAnimation = anim; return JFALSE; } - else if ((anim->flags & AFLAG_PLAYED) && damageMod->hp <= 0) + else if ((anim->flags & AFLAG_PLAYONCE) && damageMod->hp <= 0) { actor_kill(); return JFALSE; @@ -58,7 +58,7 @@ namespace TFE_DarkForces // TODO: Move to the correct location. actor_removeLogics(obj); - actor_setupAnimation(2/*animIndex*/, anim); + actor_setupAnimation(ANIM_DIE1/*animIndex*/, anim); moveMod->updateTargetFunc(moveMod, &damageMod->attackMod.target); retValue = JFALSE; } @@ -114,7 +114,7 @@ namespace TFE_DarkForces // I have to remove the logics here in order to get this to work, but this doesn't actually happen here in the original code. // TODO: Move to the correct location. actor_removeLogics(obj); - actor_setupAnimation(2/*animIndex*/, anim); + actor_setupAnimation(ANIM_DIE1/*animIndex*/, anim); retValue = JFALSE; } return retValue; @@ -124,7 +124,7 @@ namespace TFE_DarkForces { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags &= ~(1 | 4); + dispatch->flags &= ~(ACTOR_IDLE | ACTOR_NPC); dispatch->animTable = s_mineBarrelAnimTable; DamageModule* module = actor_createDamageModule(dispatch); @@ -136,7 +136,7 @@ namespace TFE_DarkForces MovementModule* moveMod = actor_createMovementModule(dispatch); dispatch->moveMod = moveMod; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; moveMod->target.flags = (moveMod->target.flags & ~TARGET_ALL_MOVE) | TARGET_FREEZE; moveMod->target.speed = 0; @@ -153,8 +153,8 @@ namespace TFE_DarkForces Logic* landmine_setup(SecObject* obj, LogicSetupFunc* setupFunc) { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags &= ~4; - dispatch->flags &= ~1; + dispatch->flags &= ~ACTOR_NPC; + dispatch->flags &= ~ACTOR_IDLE; dispatch->animTable = s_mineBarrelAnimTable; DamageModule* module = actor_createDamageModule(dispatch); @@ -166,12 +166,12 @@ namespace TFE_DarkForces MovementModule* moveMod = actor_createMovementModule(dispatch); dispatch->moveMod = moveMod; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; // This was cleared to 0 in createProjectile() moveMod->physics.width = obj->worldWidth; ActorTarget* target = &module->attackMod.target; - target->flags = (target->flags | 8) & 0xfffffff8; + target->flags = (target->flags | TARGET_FREEZE) & ~TARGET_ALL_MOVE; target->speed = 0; target->speedRotation = 0; @@ -179,7 +179,7 @@ namespace TFE_DarkForces moveMod->target.speed = 0; moveMod->target.speedRotation = 0; - dispatch->flags &= ~1; + dispatch->flags &= ~ACTOR_IDLE; dispatch->animTable = s_mineBarrelAnimTable; return (Logic*)dispatch; diff --git a/TheForceEngine/TFE_DarkForces/Actor/flyers.cpp b/TheForceEngine/TFE_DarkForces/Actor/flyers.cpp index b18e4d1db..da1da6b48 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/flyers.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/flyers.cpp @@ -144,7 +144,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = 0; thinkerMod->target.speed = FIXED(6); thinkerMod->delay = 116; - thinkerMod->anim.flags &= ~FLAG_BIT(0); + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); // (145.5)*2 actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -157,7 +157,7 @@ namespace TFE_DarkForces MovementModule* moveMod = actor_createMovementModule(dispatch); dispatch->moveMod = moveMod; - moveMod->collisionFlags = (moveMod->collisionFlags & 0xfffffff8) | 4; + moveMod->collisionFlags = (moveMod->collisionFlags & ~ACTORCOL_ALL) | ACTORCOL_BIT2; moveMod->physics.yPos = FIXED(200); moveMod->physics.width = obj->worldWidth; @@ -194,7 +194,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = 0; thinkerMod->target.speed = FIXED(6); thinkerMod->delay = 116; - thinkerMod->anim.flags &= ~FLAG_BIT(0); + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); // (145.5)*2 actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -207,7 +207,7 @@ namespace TFE_DarkForces MovementModule* moveMod = actor_createMovementModule(dispatch); dispatch->moveMod = moveMod; - moveMod->collisionFlags = (moveMod->collisionFlags & 0xfffffff8) | 4; + moveMod->collisionFlags = (moveMod->collisionFlags & ~ACTORCOL_ALL) | ACTORCOL_BIT2; moveMod->physics.yPos = FIXED(200); moveMod->physics.width = obj->worldWidth; @@ -261,8 +261,8 @@ namespace TFE_DarkForces MovementModule* moveMod = actor_createMovementModule(dispatch); dispatch->moveMod = moveMod; - moveMod->collisionFlags &= 0xfffffff8; - moveMod->collisionFlags |= 4; + moveMod->collisionFlags &= ~ACTORCOL_ALL; + moveMod->collisionFlags |= ACTORCOL_BIT2; moveMod->physics.yPos = FIXED(200); // should be: 0xa7ec diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp index d683521b3..37b18be8f 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.cpp @@ -48,7 +48,7 @@ namespace TFE_DarkForces static MouseBot* s_curMouseBot; static s32 s_mouseNum = 0; - void resetMouseNum() + void mousebot_resetNum() { s_mouseNum = 0; } @@ -115,7 +115,6 @@ namespace TFE_DarkForces MovementModule* moveMod; CollisionInfo* colInfo; Tick tick; - u32 frame; JBool odd; JBool flip; @@ -150,7 +149,6 @@ namespace TFE_DarkForces if (local(phyActor)->state != MBSTATE_ACTIVE) { break; } - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "CUR TICK = %d", s_curTick); // Go to sleep if the player hasn't been spotted in about 5 seconds. if (actor_isObjectVisible(local(obj), s_playerObject, 0x4000/*360 degrees*/, FIXED(25)/*closeDist*/)) { @@ -172,19 +170,15 @@ namespace TFE_DarkForces if (actorYaw == yaw) { local(odd) = (s_curTick & 1) ? JTRUE : JFALSE; - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM"); angle14_32 deltaYaw = random(16338); - local(moveMod)->target.yaw = local(odd) ? (local(obj)->yaw + deltaYaw) : (local(obj)->yaw - deltaYaw); - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM2"); + local(moveMod)->target.yaw = local(odd) ? (local(obj)->yaw + deltaYaw) : (local(obj)->yaw - deltaYaw); local(moveMod)->target.speedRotation = random(0x3555) + 0x555; -// TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "delta Yaw %d speedrotate = %d update = %d", deltaYaw, local(moveMod)->target.speedRotation, TFE_Input::getCounter()); local(moveMod)->target.flags |= TARGET_MOVE_ROT; local(flip) = JFALSE; } if (local(colInfo)->wall) { - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM3"); s32 rnd = random(100); if (rnd <= 10) // ~10% chance of playing a sound effect when hitting a wall. { @@ -198,7 +192,6 @@ namespace TFE_DarkForces { local(moveMod)->target.yaw -= 4096; } - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", "MOUSEBOT CALL RANDOM4"); local(moveMod)->target.speedRotation = random(0x3555); local(flip) = ~local(flip); } @@ -211,19 +204,6 @@ namespace TFE_DarkForces local(moveMod)->target.speed = FIXED(22); local(moveMod)->target.flags |= TARGET_MOVE_XZ; - //char botinfo[80]; - fixed16_16 x = local(moveMod)->target.pos.x; - fixed16_16 z = local(moveMod)->target.pos.z; - angle14_16 myaw = local(moveMod)->target.yaw; - fixed16_16 mspeed = local(moveMod)->target.speed; - fixed16_16 rotspeed = local(moveMod)->target.speedRotation; - Tick curtick = local(tick); - //sprintf(botinfo, "X: %d Z: %d YAW: %d SPEED: %d ROT: %d TICK: %d", x, z, myaw, mspeed, rotspeed, curtick); - - - //TFE_DarkForces::hud_sendTextMessage(botinfo, 1, false); - //TFE_System::logWrite(LOG_MSG, "MOUSEBOT", botinfo); - } task_end; } diff --git a/TheForceEngine/TFE_DarkForces/Actor/mousebot.h b/TheForceEngine/TFE_DarkForces/Actor/mousebot.h index 65571d9fd..b7c68b289 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/mousebot.h +++ b/TheForceEngine/TFE_DarkForces/Actor/mousebot.h @@ -11,7 +11,7 @@ namespace TFE_DarkForces { Logic* mousebot_setup(SecObject* obj, LogicSetupFunc* setupFunc); - void resetMouseNum(); + void mousebot_resetNum(); void mousebot_clear(); void mousebot_exit(); void mousebot_precache(); diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp index 76637da8a..e12ff3aa1 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseOne.cpp @@ -111,7 +111,7 @@ namespace TFE_DarkForces // Handle reflection. sound_stop(local(trooper)->reflectSndId); local(trooper)->reflectSndId = sound_playCued(s_shared.phase1ReflectSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 13, local(anim)); task_localBlockEnd; @@ -158,7 +158,7 @@ namespace TFE_DarkForces local(restoreAnim) = JTRUE; memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); // Wait for animation to finish. @@ -255,7 +255,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); // Wait for animation to finish. @@ -350,7 +350,7 @@ namespace TFE_DarkForces local(anim) = &local(physicsActor)->anim; local(prevColTick) = 0; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); while (local(physicsActor)->state == P1STATE_CHARGE) @@ -431,7 +431,7 @@ namespace TFE_DarkForces local(target) = &local(physicsActor)->moveMod.target; local(anim) = &local(physicsActor)->anim; local(odd) = (s_curTick & 1) ? JFALSE : JTRUE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); task_localBlockBegin; @@ -550,7 +550,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); if (local(physicsActor)->state != P1STATE_ATTACK) { break; } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 1, local(anim)); // Wait for the animation to finish. @@ -600,7 +600,7 @@ namespace TFE_DarkForces local(target)->flags |= TARGET_FREEZE; sound_playCued(s_shared.phase1cSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 2, local(anim)); // Wait for the animation to finish. @@ -665,7 +665,7 @@ namespace TFE_DarkForces local(nextTick) = s_curTick + 4369; local(delay) = 72; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); local(target)->flags &= ~TARGET_FREEZE; @@ -938,11 +938,11 @@ namespace TFE_DarkForces physicsActor->moveMod.physics.obj = obj; actor_setupSmartObj(&physicsActor->moveMod); - physicsActor->moveMod.collisionFlags |= 3; - physicsActor->moveMod.collisionFlags &= 0xfffffffb; + physicsActor->moveMod.collisionFlags |= ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY; + physicsActor->moveMod.collisionFlags &= ~ACTORCOL_BIT2; ActorTarget* target = &physicsActor->moveMod.target; - target->flags &= 0xfffffff0; + target->flags &= ~TARGET_ALL; target->speed = FIXED(30); target->speedRotation = 0x3000; @@ -951,7 +951,7 @@ namespace TFE_DarkForces anim->frameCount = ONE_16; anim->prevTick = 0; anim->flags |= AFLAG_READY; - anim->flags &= (~AFLAG_PLAYED); + anim->flags &= (~AFLAG_PLAYONCE); actor_setupBossAnimation(obj, 5, anim); obj_addLogic(obj, (Logic*)trooper, LOGIC_PHASE_ONE, task, phaseOneCleanupFunc); diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp index 5bc9c93e3..da8d2a452 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseThree.cpp @@ -133,7 +133,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 10, local(anim)); // Wait for animation to finish. @@ -213,7 +213,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= 1; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 10, local(anim)); // Wait for animation to finish. @@ -296,7 +296,7 @@ namespace TFE_DarkForces if (random(100) <= 40) { local(trooper)->rocketSndId = sound_playCued(s_shared.phase3RocketSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 13, local(anim)); actor_setAnimFrameRange(local(anim), 0, 1); @@ -313,15 +313,15 @@ namespace TFE_DarkForces local(target)->speed = FIXED(70); local(flying) = JTRUE; - local(physicsActor)->moveMod.collisionFlags &= (~3); + local(physicsActor)->moveMod.collisionFlags &= (~(ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY)); local(physicsActor)->moveMod.physics.yPos = COL_INFINITY; } else { - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); local(target)->speed = FIXED(25); local(flying) = JFALSE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); } @@ -344,7 +344,7 @@ namespace TFE_DarkForces local(physicsActor)->state = (random(100) > 40) ? P3STATE_FIRE_PLASMA : P3STATE_FIRE_MISSILES; if (local(flying)) { - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 13, local(anim)); actor_setAnimFrameRange(local(anim), 3, 3); do @@ -354,7 +354,7 @@ namespace TFE_DarkForces task_callTaskFunc(phaseThree_handleMsg); } while (msg != MSG_RUN_TASK || !(local(anim)->flags & AFLAG_READY)); - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); local(physicsActor)->moveMod.physics.yPos = COL_INFINITY; local(target)->speed = FIXED(25); local(target)->flags &= ~TARGET_MOVE_Y; @@ -429,7 +429,7 @@ namespace TFE_DarkForces local(target) = &local(physicsActor)->moveMod.target; local(anim) = &local(physicsActor)->anim; local(odd) = (s_curTick & 1) ? JFALSE : JTRUE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); task_localBlockBegin; @@ -536,7 +536,7 @@ namespace TFE_DarkForces local(anim) = &local(physicsActor)->anim; local(target)->flags &= ~TARGET_MOVE_XZ; - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); while (local(physicsActor)->state == P3STATE_FIRE_MISSILES) { do @@ -547,7 +547,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); if (local(physicsActor)->state != P3STATE_FIRE_MISSILES) { break; } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 11, local(anim)); // Wait for the animation to finish. @@ -576,7 +576,7 @@ namespace TFE_DarkForces proj_setTransform(proj, 0, projObj->yaw); } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); task_localBlockEnd; @@ -615,9 +615,9 @@ namespace TFE_DarkForces local(anim) = &local(physicsActor)->anim; local(target)->flags &= ~TARGET_MOVE_XZ; - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 1, local(anim)); // Wait for the animation to finish. @@ -658,7 +658,7 @@ namespace TFE_DarkForces } } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 6, local(anim)); // Wait for the animation to finish. @@ -695,9 +695,9 @@ namespace TFE_DarkForces sound_stop(local(trooper)->rocketSndId); sound_playCued(s_shared.phase3DieSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 2, local(anim)); - local(physicsActor)->moveMod.collisionFlags |= 3; // ground, gravity + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); // ground, gravity // Wait for the animation to finish. do @@ -762,13 +762,13 @@ namespace TFE_DarkForces local(nextTick) = s_curTick + 4369; local(delay) = 72; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; local(forceContinue) = JFALSE; actor_setupBossAnimation(local(obj), 13, local(anim)); local(target)->flags &= ~TARGET_FREEZE; local(target)->speed = FIXED(30); - local(physicsActor)->moveMod.collisionFlags &= (~3); + local(physicsActor)->moveMod.collisionFlags &= (~(ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY)); while (local(physicsActor)->state == P3STATE_SEARCH || local(forceContinue)) { @@ -1048,7 +1048,7 @@ namespace TFE_DarkForces physicsActor->moveMod.physics.obj = obj; actor_setupSmartObj(&physicsActor->moveMod); - physicsActor->moveMod.collisionFlags |= 7; + physicsActor->moveMod.collisionFlags |= ACTORCOL_ALL; physicsActor->moveMod.physics.yPos = COL_INFINITY; ActorTarget* target = &physicsActor->moveMod.target; @@ -1062,7 +1062,7 @@ namespace TFE_DarkForces anim->frameCount = ONE_16; anim->prevTick = 0; anim->flags |= AFLAG_READY; - anim->flags &= ~AFLAG_PLAYED; + anim->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(obj, 3, anim); obj_addLogic(obj, (Logic*)trooper, LOGIC_PHASE_THREE, task, phaseThreeCleanupFunc); diff --git a/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp b/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp index d42cb6989..a3d4087f8 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/phaseTwo.cpp @@ -126,7 +126,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); // Wait for animation to finish. @@ -206,7 +206,7 @@ namespace TFE_DarkForces // Save Animation memcpy(&local(tmp), local(anim), sizeof(LogicAnimation) - 4); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 12, local(anim)); // Wait for animation to finish. @@ -289,7 +289,7 @@ namespace TFE_DarkForces if (random(100) <= 40) { local(trooper)->rocketSndId = sound_playCued(s_shared.phase2RocketSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 13, local(anim)); actor_setAnimFrameRange(local(anim), 0, 2); @@ -306,15 +306,15 @@ namespace TFE_DarkForces local(target)->speed = FIXED(60); local(flying) = JTRUE; - local(physicsActor)->moveMod.collisionFlags &= 0xfffffffc; + local(physicsActor)->moveMod.collisionFlags &= (~(ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY)); // flying local(physicsActor)->moveMod.physics.yPos = COL_INFINITY; } else { - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); local(target)->speed = FIXED(15); local(flying) = JFALSE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); } @@ -336,7 +336,7 @@ namespace TFE_DarkForces local(physicsActor)->state = (random(100) > 40) ? P2STATE_FIRE_PLASMA : P2STATE_FIRE_MISSILES; if (local(flying)) { - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 13, local(anim)); actor_setAnimFrameRange(local(anim), 4, 4); do @@ -346,7 +346,7 @@ namespace TFE_DarkForces task_callTaskFunc(phaseTwo_handleMsg); } while (msg != MSG_RUN_TASK || !(local(anim)->flags & AFLAG_READY)); - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); local(physicsActor)->moveMod.physics.yPos = COL_INFINITY; local(target)->speed = FIXED(15); local(target)->flags &= ~TARGET_MOVE_Y; @@ -421,7 +421,7 @@ namespace TFE_DarkForces local(target) = &local(physicsActor)->moveMod.target; local(anim) = &local(physicsActor)->anim; local(odd) = (s_curTick & 1) ? JFALSE : JTRUE; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 0, local(anim)); task_localBlockBegin; @@ -529,7 +529,7 @@ namespace TFE_DarkForces local(anim) = &local(physicsActor)->anim; local(target)->flags &= ~TARGET_MOVE_XZ; - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); while (local(physicsActor)->state == P2STATE_FIRE_MISSILES) { do @@ -540,7 +540,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); if (local(physicsActor)->state != P2STATE_FIRE_MISSILES) { break; } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 1, local(anim)); // Wait for the animation to finish. @@ -568,7 +568,7 @@ namespace TFE_DarkForces vec3_fixed target = { s_eyePos.x, s_eyePos.y + ONE_16, s_eyePos.z }; proj_aimAtTarget(proj, target); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 6, local(anim)); task_localBlockEnd; @@ -607,9 +607,9 @@ namespace TFE_DarkForces local(anim) = &local(physicsActor)->anim; local(target)->flags &= ~TARGET_MOVE_XZ; - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 1, local(anim)); // Wait for the animation to finish. @@ -650,7 +650,7 @@ namespace TFE_DarkForces } } - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 6, local(anim)); // Wait for the animation to finish. @@ -687,9 +687,9 @@ namespace TFE_DarkForces sound_stop(local(trooper)->rocketSndId); sound_playCued(s_shared.phase2cSndID, local(obj)->posWS); - local(anim)->flags |= AFLAG_PLAYED; + local(anim)->flags |= AFLAG_PLAYONCE; actor_setupBossAnimation(local(obj), 2, local(anim)); - local(physicsActor)->moveMod.collisionFlags |= 3; + local(physicsActor)->moveMod.collisionFlags |= (ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY); // Wait for the animation to finish. do @@ -803,13 +803,13 @@ namespace TFE_DarkForces local(nextTick) = s_curTick + 4369; local(delay) = 72; - local(anim)->flags &= ~AFLAG_PLAYED; + local(anim)->flags &= ~AFLAG_PLAYONCE; local(forceContinue) = JFALSE; actor_setupBossAnimation(local(obj), 13, local(anim)); local(target)->flags &= ~TARGET_FREEZE; local(target)->speed = FIXED(30); - local(physicsActor)->moveMod.collisionFlags &= 0xfffffffc; + local(physicsActor)->moveMod.collisionFlags &= (~(ACTORCOL_NO_Y_MOVE | ACTORCOL_GRAVITY)); while (local(physicsActor)->state == P2STATE_SEARCH || local(forceContinue)) { @@ -1088,11 +1088,11 @@ namespace TFE_DarkForces physicsActor->moveMod.physics.obj = obj; actor_setupSmartObj(&physicsActor->moveMod); - physicsActor->moveMod.collisionFlags |= 7; + physicsActor->moveMod.collisionFlags |= ACTORCOL_ALL; physicsActor->moveMod.physics.yPos = COL_INFINITY; ActorTarget* target = &physicsActor->moveMod.target; - target->flags &= 0xfffffff0; + target->flags &= ~TARGET_ALL; target->speed = FIXED(15); target->speedVert = FIXED(10); target->speedRotation = 0x3000; @@ -1102,7 +1102,7 @@ namespace TFE_DarkForces anim->frameCount = ONE_16; anim->prevTick = 0; anim->flags |= AFLAG_READY; - anim->flags &= (~AFLAG_PLAYED); + anim->flags &= (~AFLAG_PLAYONCE); actor_setupBossAnimation(obj, 5, anim); obj_addLogic(obj, (Logic*)trooper, LOGIC_PHASE_TWO, task, phaseTwoCleanupFunc); diff --git a/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp b/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp index 737912200..d9a8017b9 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/scenery.cpp @@ -23,12 +23,12 @@ namespace TFE_DarkForces s_actorState.curAnimation = anim; return JFALSE; } - else if (damageMod->hp <= 0 && actor_getAnimationIndex(4) != -1) + else if (damageMod->hp <= 0 && actor_getAnimationIndex(ANIM_DEAD) != -1) { SecObject* newObj = allocateObject(); sprite_setData(newObj, obj->wax); newObj->frame = 0; - newObj->anim = actor_getAnimationIndex(4); + newObj->anim = actor_getAnimationIndex(ANIM_DEAD); newObj->posWS = obj->posWS; newObj->worldWidth = obj->worldWidth; @@ -62,7 +62,7 @@ namespace TFE_DarkForces } else { - actor_setupAnimation(4, anim); + actor_setupAnimation(ANIM_DEAD, anim); actor_removeLogics(obj); retValue = JFALSE; } @@ -83,7 +83,7 @@ namespace TFE_DarkForces } else { - actor_setupAnimation(4, anim); + actor_setupAnimation(ANIM_DEAD, anim); actor_removeLogics(obj); retValue = JFALSE; } @@ -94,7 +94,7 @@ namespace TFE_DarkForces Logic* scenery_setup(SecObject* obj, LogicSetupFunc* setupFunc) { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags &= ~(FLAG_BIT(0) | FLAG_BIT(2)); + dispatch->flags &= ~(ACTOR_IDLE | ACTOR_NPC); dispatch->animTable = s_sceneryAnimTable; DamageModule* module = actor_createDamageModule(dispatch); diff --git a/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp b/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp index 119f28bf5..49ea0e2dc 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/sewer.cpp @@ -45,7 +45,7 @@ namespace TFE_DarkForces } // Creature die. - s32 animIndex = actor_getAnimationIndex(4); + s32 animIndex = actor_getAnimationIndex(ANIM_DEAD); if (animIndex != -1) { SecObject* corpse = allocateObject(); @@ -130,25 +130,25 @@ namespace TFE_DarkForces { case STATE_DELAY: { - if (attackMod->anim.flags & 2) + if (attackMod->anim.flags & AFLAG_READY) { obj->flags &= ~OBJ_FLAG_NEEDS_TRANSFORM; obj->worldWidth = 0; obj->posWS.y = sector->floorHeight + sector->secHeight; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; attackMod->target.flags &= ~TARGET_ALL; - attackMod->anim.state = STATE_ANIMATEATTACK; + attackMod->anim.state = STATE_DECIDE; return s_curTick + random(attackMod->timing.delay); } } break; - case STATE_ANIMATEATTACK: + case STATE_DECIDE: { gameMusic_sustainFight(); if (!actor_canSeeObjFromDist(obj, s_playerObject)) { actor_updatePlayerVisiblity(JFALSE, 0, 0); - attackMod->anim.flags |= 2; + attackMod->anim.flags |= AFLAG_READY; attackMod->anim.state = STATE_DELAY; if (s_curTick > attackMod->timing.nextTick) { @@ -159,14 +159,14 @@ namespace TFE_DarkForces } actor_updatePlayerVisiblity(JTRUE, s_eyePos.x, s_eyePos.z); - moveMod->collisionFlags &= ~1; + moveMod->collisionFlags &= ~ACTORCOL_NO_Y_MOVE; obj->posWS.y = sector->floorHeight; fixed16_16 dy = TFE_Jedi::abs(obj->posWS.y - s_playerObject->posWS.y); fixed16_16 dist = dy + distApprox(s_playerObject->posWS.x, s_playerObject->posWS.z, obj->posWS.x, obj->posWS.z); if (dist <= attackMod->meleeRange) { - attackMod->anim.state = STATE_FIRE1; + attackMod->anim.state = STATE_ATTACK1; attackMod->timing.delay = attackMod->timing.meleeDelay; attackMod->target.pos.x = obj->posWS.x; attackMod->target.pos.z = obj->posWS.z; @@ -191,19 +191,19 @@ namespace TFE_DarkForces obj->flags |= OBJ_FLAG_NEEDS_TRANSFORM; if (obj->type == OBJ_TYPE_SPRITE) { - if (attackMod->anim.state == STATE_FIRE1) // Attack animation + if (attackMod->anim.state == STATE_ATTACK1) // Attack animation { - actor_setupAnimation(1, &attackMod->anim); + actor_setupAnimation(ANIM_ATTACK1, &attackMod->anim); } else // Look around animation { - actor_setupAnimation(14, &attackMod->anim); + actor_setupAnimation(ANIM_SEARCH, &attackMod->anim); } } } break; - case STATE_FIRE1: + case STATE_ATTACK1: { - if (attackMod->anim.flags & 2) + if (attackMod->anim.flags & AFLAG_READY) { attackMod->anim.state = STATE_ANIMATE1; sound_playCued(attackMod->attackSecSndSrc, obj->posWS); @@ -218,7 +218,7 @@ namespace TFE_DarkForces } break; case STATE_ANIMATE1: { - actor_setupAnimation(6, &attackMod->anim); + actor_setupAnimation(ANIM_ATTACK1_END, &attackMod->anim); attackMod->anim.state = STATE_DELAY; } break; } @@ -262,7 +262,7 @@ namespace TFE_DarkForces thinkerMod->target.speed = FIXED(18); thinkerMod->delay = 58; thinkerMod->startDelay = 72; - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; actor_addModule(dispatch, (ActorModule*)thinkerMod); MovementModule* moveMod = actor_createMovementModule(dispatch); @@ -270,7 +270,7 @@ namespace TFE_DarkForces dispatch->animTable = s_sewerCreatureAnimTable; obj->entityFlags &= ~ETFLAG_SMART_OBJ; - moveMod->collisionFlags = (moveMod->collisionFlags | 1) & 0xfffffffd; + moveMod->collisionFlags = (moveMod->collisionFlags | ACTORCOL_NO_Y_MOVE) & ~ACTORCOL_GRAVITY; // gravity is removed so they remain on the surface of water (floor height) rather than sinking down (second height) moveMod->physics.yPos = 0; moveMod->physics.botOffset = 0; moveMod->physics.width = obj->worldWidth; @@ -291,17 +291,17 @@ namespace TFE_DarkForces sound_playCued(module->dieSndSrc, obj->posWS); attackMod->target.flags |= TARGET_FREEZE; - if ((obj->anim == 1 || obj->anim == 6) && obj->type == OBJ_TYPE_SPRITE) + if ((obj->anim == ANIM_ATTACK1 || obj->anim == ANIM_ATTACK1_END) && obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(2, &attackMod->anim); + actor_setupAnimation(ANIM_DIE1, &attackMod->anim); } else if (obj->type == OBJ_TYPE_SPRITE) { - actor_setupAnimation(3, &attackMod->anim); + actor_setupAnimation(ANIM_DIE2, &attackMod->anim); } obj->posWS.y = sector->floorHeight; - moveMod->collisionFlags &= 0xfffffffd; + moveMod->collisionFlags &= ~ACTORCOL_GRAVITY; if (obj->type == OBJ_TYPE_SPRITE) { diff --git a/TheForceEngine/TFE_DarkForces/Actor/troopers.cpp b/TheForceEngine/TFE_DarkForces/Actor/troopers.cpp index dfb399b9a..99bca5134 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/troopers.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/troopers.cpp @@ -42,7 +42,7 @@ namespace TFE_DarkForces Logic* officer_setup(SecObject* obj, LogicSetupFunc* setupFunc, KEYWORD logicId) { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags |= FLAG_BIT(4); // Use Officer alert table. + dispatch->flags |= ACTOR_OFFIC_ALERT; // Use Officer alert table. dispatch->alertSndSrc = 0; DamageModule* module = actor_createDamageModule(dispatch); @@ -64,7 +64,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = HALF_16 - 1; thinkerMod->target.speed = FIXED(7); thinkerMod->delay = 0; - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -73,7 +73,7 @@ namespace TFE_DarkForces dispatch->animTable = s_officerAnimTable; s_actorState.curLogic = (Logic*)dispatch; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -83,7 +83,7 @@ namespace TFE_DarkForces Logic* trooper_setup(SecObject* obj, LogicSetupFunc* setupFunc) { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags |= FLAG_BIT(5); // Use Stormtrooper alert table. + dispatch->flags |= ACTOR_TROOP_ALERT; // Use Stormtrooper alert table. dispatch->alertSndSrc = 0; DamageModule* module = actor_createDamageModule(dispatch); @@ -104,7 +104,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = HALF_16 - 1; thinkerMod->target.speed = FIXED(8); thinkerMod->delay = 116; - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(2); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -113,7 +113,7 @@ namespace TFE_DarkForces dispatch->animTable = s_troopAnimTable; s_actorState.curLogic = (Logic*)dispatch; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); @@ -123,7 +123,7 @@ namespace TFE_DarkForces Logic* commando_setup(SecObject* obj, LogicSetupFunc* setupFunc) { ActorDispatch* dispatch = actor_createDispatch(obj, setupFunc); - dispatch->flags |= FLAG_BIT(5); // Use Stormtrooper alert table. + dispatch->flags |= ACTOR_TROOP_ALERT; // Use Stormtrooper alert table. dispatch->alertSndSrc = 0; DamageModule* module = actor_createDamageModule(dispatch); @@ -144,7 +144,7 @@ namespace TFE_DarkForces thinkerMod->target.speedRotation = HALF_16 - 1; thinkerMod->target.speed = FIXED(9); thinkerMod->delay = 116; - thinkerMod->anim.flags &= 0xfffffffe; + thinkerMod->anim.flags &= ~AFLAG_PLAYONCE; thinkerMod->startDelay = TICKS(1); actor_addModule(dispatch, (ActorModule*)thinkerMod); @@ -153,7 +153,7 @@ namespace TFE_DarkForces dispatch->animTable = s_commandoAnimTable; s_actorState.curLogic = (Logic*)dispatch; - moveMod->collisionFlags |= 1; + moveMod->collisionFlags |= ACTORCOL_NO_Y_MOVE; moveMod->physics.width = obj->worldWidth; actor_setupInitAnimation(); diff --git a/TheForceEngine/TFE_DarkForces/Actor/turret.cpp b/TheForceEngine/TFE_DarkForces/Actor/turret.cpp index ddf28b0b0..44e510f33 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/turret.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/turret.cpp @@ -83,7 +83,7 @@ namespace TFE_DarkForces s_turretRes.sound1 = sound_load("TURRET-1.VOC", SOUND_PRIORITY_MED2); } - void resetTurretNum() + void turret_resetNum() { s_turretNum = 0; } @@ -331,7 +331,7 @@ namespace TFE_DarkForces // Set a new random target. target->yaw = random(ANGLE_MAX); target->pitch = (-random(TURRET_PITCH_RANGE)) & ANGLE_MASK; - target->flags = (target->flags | 4) & 0xfffffffe; + target->flags = (target->flags | TARGET_MOVE_ROT) & (~TARGET_MOVE_XZ); target->speedRotation = TURRET_OUT_OF_CONTROL_ROTATE_SPD; } } @@ -607,11 +607,11 @@ namespace TFE_DarkForces MovementModule* moveMod = &physicsActor->moveMod; moveMod->header.obj = obj; moveMod->delta = { 0, 0, 0 }; - moveMod->collisionFlags &= 0xfffffff8; + moveMod->collisionFlags &= ~ACTORCOL_ALL; ActorTarget* target = &physicsActor->moveMod.target; target->speed = 0; - target->flags &= 0xfffffff0; + target->flags &= ~TARGET_ALL; target->speedRotation = TURRET_ROTATION_SPD; target->pitch = obj->pitch; target->yaw = obj->yaw; diff --git a/TheForceEngine/TFE_DarkForces/Actor/turret.h b/TheForceEngine/TFE_DarkForces/Actor/turret.h index baa1e7fdb..02213bf5b 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/turret.h +++ b/TheForceEngine/TFE_DarkForces/Actor/turret.h @@ -9,7 +9,7 @@ namespace TFE_DarkForces { - void resetTurretNum(); + void turret_resetNum(); void turret_exit(); void turret_precache(); Logic* turret_setup(SecObject* obj, LogicSetupFunc* setupFunc); diff --git a/TheForceEngine/TFE_DarkForces/Actor/welder.cpp b/TheForceEngine/TFE_DarkForces/Actor/welder.cpp index 3c64031a3..2e25f01cb 100644 --- a/TheForceEngine/TFE_DarkForces/Actor/welder.cpp +++ b/TheForceEngine/TFE_DarkForces/Actor/welder.cpp @@ -537,11 +537,11 @@ namespace TFE_DarkForces physics->height = obj->worldHeight + HALF_16; physicsActor->moveMod.delta = { 0, 0, 0 }; - physicsActor->moveMod.collisionFlags &= 0xfffffff8; + physicsActor->moveMod.collisionFlags &= ~ACTORCOL_ALL; target->speed = 0; target->speedRotation = 4551; // ~100 degrees/second. - target->flags &= 0xfffffff0; + target->flags &= ~TARGET_ALL; target->pitch = obj->pitch; target->yaw = obj->yaw; target->roll = obj->roll; diff --git a/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp b/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp index 7a44ddb81..91a97bd73 100644 --- a/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp +++ b/TheForceEngine/TFE_DarkForces/GameUI/pda.cpp @@ -124,6 +124,8 @@ namespace TFE_DarkForces /////////////////////////////////////////// void pda_start(const char* levelName) { + TFE_System::logWrite(LOG_MSG, "PDA", "Opening PDA"); + // TFE reticle_enable(false); s_mouseAccum = { 0 }; @@ -254,6 +256,8 @@ namespace TFE_DarkForces TFE_Jedi::renderer_setType(RendererType(graphics->rendererIndex)); TFE_Jedi::renderer_setLimits(); } + + TFE_System::logWrite(LOG_MSG, "PDA", "Closing PDA"); } void pda_update() diff --git a/TheForceEngine/TFE_DarkForces/Scripting/CMakeLists.txt b/TheForceEngine/TFE_DarkForces/Scripting/CMakeLists.txt new file mode 100644 index 000000000..cf0824deb --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB SOURCES "*.cpp") +target_sources(tfe PRIVATE ${SOURCES}) diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.cpp b/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.cpp new file mode 100644 index 000000000..59a16aef9 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.cpp @@ -0,0 +1,58 @@ +#include "gameScripts.h" +#include "gs_system.h" +#include "gs_level.h" +#include "gs_game.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; +using namespace TFE_ForceScript; + +namespace TFE_DarkForces +{ + bool s_gameScriptRegistered = false; + + GS_System s_gsSystem; + GS_Level s_gsLevel; + GS_Game s_gsGame; + + // Print any script messages, warnings or errors to the editor output. + void scriptCallback(LogWriteType type, const char* section, s32 row, s32 col, const char* msg) + { + TFE_System::logWrite(type, "Dark Forces Game Script", "%s (%d, %d) : %s", section, row, col, msg); + } + + void registerScriptFunctions(ScriptAPI api) + { + if (s_gameScriptRegistered) { return; } + s_gameScriptRegistered = true; + TFE_ForceScript::overrideCallback(scriptCallback); + + s_gsSystem.scriptRegister(api); + s_gsLevel.scriptRegister(api); + s_gsGame.scriptRegister(api); + } +} +#else +namespace TFE_DarkForces +{ + void registerScriptFunctions(ScriptAPI api) {} +} +#endif diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.h b/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.h new file mode 100644 index 000000000..d4883bcdb --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gameScripts.h @@ -0,0 +1,15 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#include + +namespace TFE_DarkForces +{ + void registerScriptFunctions(ScriptAPI api); +} diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_game.cpp b/TheForceEngine/TFE_DarkForces/Scripting/gs_game.cpp new file mode 100644 index 000000000..3496b0622 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_game.cpp @@ -0,0 +1,29 @@ +#include "gs_game.h" +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + const f32 c_timeScale = 1.0f / f32(TICKS_PER_SECOND); + + f32 GS_Game::getGameTime() + { + return f32(s_curTick) * c_timeScale; + } + + bool GS_Game::scriptRegister(ScriptAPI api) + { + ScriptClassBegin("Game", "game", api); + { + // Functions + ScriptObjMethod("float getGameTime()", getGameTime); + } + ScriptClassEnd(); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_game.h b/TheForceEngine/TFE_DarkForces/Scripting/gs_game.h new file mode 100644 index 000000000..2490fcd99 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_game.h @@ -0,0 +1,25 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include + +namespace TFE_DarkForces +{ + class GS_Game : public ScriptAPIClass + { + public: + // System + bool scriptRegister(ScriptAPI api) override; + + f32 getGameTime(); + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp new file mode 100644 index 000000000..d2c9fbd8a --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.cpp @@ -0,0 +1,238 @@ +#include "gs_level.h" +#include "scriptTexture.h" +#include "scriptElev.h" +#include "scriptWall.h" +#include "scriptSector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + static u32 s_lsSearchKey = 1002; // any non-zero value really. + + enum SectorProperty + { + SECTORPROP_NONE = 0, + SECTORPROP_FLOOR_HEIGHT = FLAG_BIT(0), + SECTORPROP_CEIL_HEIGHT = FLAG_BIT(1), + SECTORPROP_SECOND_HEIGHT = FLAG_BIT(2), + SECTORPROP_FLOOR_TEX = FLAG_BIT(3), + SECTORPROP_CEIL_TEX = FLAG_BIT(4), + SECTORPROP_AMBIENT = FLAG_BIT(5), + SECTORPROP_START = SECTORPROP_FLOOR_HEIGHT, + SECTORPROP_END = SECTORPROP_AMBIENT << 1, + }; + + ScriptSector GS_Level::getSectorById(s32 id) + { + if (id < 0 || id >= (s32)s_levelState.sectorCount) + { + TFE_System::logWrite(LOG_ERROR, "Level Script", "Runtime error, invalid sectorID %d.", id); + id = -1; // Invalid ID, sector.isValid() == false + } + ScriptSector sector(id); + return sector; + } + + ScriptElev GS_Level::getElevator(s32 id) + { + if (id < 0 || id >= allocator_getCount(s_infSerState.infElevators)) + { + TFE_System::logWrite(LOG_ERROR, "Level Script", "Runtime error, invalid elevator ID %d.", id); + id = -1; // Invalid ID, elevator.isValid() == false + } + ScriptElev elev(id); + return elev; + } + + bool doSectorPropMatch(RSector* s0, RSector* s1, u32 prop) + { + switch (prop) + { + case SECTORPROP_FLOOR_HEIGHT: + { + return s0->floorHeight == s1->floorHeight; + } break; + case SECTORPROP_CEIL_HEIGHT: + { + return s0->ceilingHeight == s1->ceilingHeight; + } break; + case SECTORPROP_SECOND_HEIGHT: + { + return s0->secHeight == s1->secHeight; + } break; + case SECTORPROP_FLOOR_TEX: + { + return s0->floorTex == s1->floorTex; + } break; + case SECTORPROP_CEIL_TEX: + { + return s0->ceilTex == s1->ceilTex; + } break; + case SECTORPROP_AMBIENT: + { + return s0->ambient == s1->ambient; + } break; + default: + { + // Invalid property. + assert(0); + } + } + return false; + } + + void GS_Level::findConnectedSectors(ScriptSector initSector, u32 matchProp, CScriptArray& results) + { + results.Resize(0); + // Return an empty array if the init sector is invalid. + if (!isScriptSectorValid(&initSector)) { return; } + + // Push the initial sector onto the array as the first element. + results.InsertLast(&initSector); + + s_lsSearchKey++; + std::vector stack; + RSector* baseSector = &s_levelState.sectors[initSector.m_id]; + baseSector->searchKey = s_lsSearchKey; + + stack.push_back(baseSector); + while (!stack.empty()) + { + RSector* sector = stack.back(); + stack.pop_back(); + + const s32 wallCount = sector->wallCount; + RWall* wall = sector->walls; + for (s32 w = 0; w < wallCount; w++, wall++) + { + if (!wall->nextSector) { continue; } + + RSector* next = wall->nextSector; + // Don't search sectors already touched. + if (next->searchKey == s_lsSearchKey) { continue; } + next->searchKey = s_lsSearchKey; + + bool propMatch = true; + u32 prop = SECTORPROP_START; + while (prop < SECTORPROP_END && propMatch) + { + if (prop & matchProp) + { + propMatch = propMatch && doSectorPropMatch(next, baseSector, prop); + } + prop <<= 1; + } + + if (propMatch) + { + stack.push_back(next); + + ScriptSector nextSector(next->id); + results.InsertLast(&nextSector); + } + } + } + } + + bool GS_Level::scriptRegister(ScriptAPI api) + { + ScriptElev scriptElev; + ScriptTexture scriptTex; + ScriptSector scriptSector; + ScriptWall scriptWall; + scriptElev.registerType(); + scriptTex.registerType(); + scriptWall.registerType(); + scriptSector.registerType(); + + ScriptClassBegin("Level", "level", api); + { + // Enums + ScriptEnumRegister("SectorFlags1"); + ScriptEnum("SECTOR_FLAG1_EXTERIOR", SEC_FLAGS1_EXTERIOR); + ScriptEnum("SECTOR_FLAG1_DOOR", SEC_FLAGS1_DOOR); + ScriptEnum("SECTOR_FLAG1_MAG_SEAL", SEC_FLAGS1_MAG_SEAL); + ScriptEnum("SECTOR_FLAG1_EXTERIOR_ADJOIN", SEC_FLAGS1_EXT_ADJ); + ScriptEnum("SECTOR_FLAG1_ICE_FLOOR", SEC_FLAGS1_ICE_FLOOR); + ScriptEnum("SECTOR_FLAG1_SNOW_FLOOR", SEC_FLAGS1_SNOW_FLOOR); + ScriptEnum("SECTOR_FLAG1_EXPLODING_WALL", SEC_FLAGS1_EXP_WALL); + ScriptEnum("SECTOR_FLAG1_PIT", SEC_FLAGS1_PIT); + ScriptEnum("SECTOR_FLAG1_PIT_ADJOIN", SEC_FLAGS1_EXT_FLOOR_ADJ); + ScriptEnum("SECTOR_FLAG1_CRUSHING", SEC_FLAGS1_CRUSHING); + ScriptEnum("SECTOR_FLAG1_NOWALL_DRAW", SEC_FLAGS1_NOWALL_DRAW); + ScriptEnum("SECTOR_FLAG1_LOW_DAMAGE", SEC_FLAGS1_LOW_DAMAGE); + ScriptEnum("SECTOR_FLAG1_HIGH_DAMAGE", SEC_FLAGS1_HIGH_DAMAGE); + ScriptEnum("SECTOR_FLAG1_NO_SMART_OBJ", SEC_FLAGS1_NO_SMART_OBJ); + ScriptEnum("SECTOR_FLAG1_SMART_OBJ", SEC_FLAGS1_SMART_OBJ); + ScriptEnum("SECTOR_FLAG1_SAFE_SECTOR", SEC_FLAGS1_SAFESECTOR); + ScriptEnum("SECTOR_FLAG1_PLAYER", SEC_FLAGS1_PLAYER); + ScriptEnum("SECTOR_FLAG1_SECRET", SEC_FLAGS1_SECRET); + + ScriptEnumRegister("WallFlags1"); + ScriptEnum("WALL_FLAGS1_TRANSPARENT_MIDTEX", WF1_ADJ_MID_TEX); + ScriptEnum("WALL_FLAGS1_ILLUM_SIGN", WF1_ILLUM_SIGN); + ScriptEnum("WALL_FLAGS1_TEX_FLIP", WF1_FLIP_HORIZ); + ScriptEnum("WALL_FLAGS1_CHANGE_WALL_LIGHT", WF1_CHANGE_WALL_LIGHT); + ScriptEnum("WALL_FLAGS1_TEX_ANCHORED", WF1_TEX_ANCHORED); + ScriptEnum("WALL_FLAGS1_WALL_MORPHS", WF1_WALL_MORPHS); + ScriptEnum("WALL_FLAGS1_SCROLL_TOP_TEX", WF1_SCROLL_TOP_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_MID_TEX", WF1_SCROLL_MID_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_BOT_TEX", WF1_SCROLL_BOT_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_SIGN_TEX", WF1_SCROLL_SIGN_TEX); + ScriptEnum("WALL_FLAGS1_HIDE_ON_MAP", WF1_HIDE_ON_MAP); + ScriptEnum("WALL_FLAGS1_SHOW_NORMAL_ON_MAP", WF1_SHOW_NORMAL_ON_MAP); + ScriptEnum("WALL_FLAGS1_SIGN_ANCHORED", WF1_SIGN_ANCHORED); + ScriptEnum("WALL_FLAGS1_DAMAGE_WALL", WF1_DAMAGE_WALL); + ScriptEnum("WALL_FLAGS1_SHOW_AS_LEDGE_ON_MAP", WF1_SHOW_AS_LEDGE_ON_MAP); + ScriptEnum("WALL_FLAGS1_SHOW_AS_DOOR_ON_MAP", WF1_SHOW_AS_DOOR_ON_MAP); + + ScriptEnumRegister("WallFlags3"); + ScriptEnum("WALL_FLAGS3_ALWAYS_WALK", WF3_ALWAYS_WALK); + ScriptEnum("WALL_FLAGS3_SOLID", WF3_SOLID_WALL); + ScriptEnum("WALL_FLAGS3_PLAYER_WALK_ONLY", WF3_PLAYER_WALK_ONLY); + ScriptEnum("WALL_FLAGS3_CANNOT_FIRE_THRU", WF3_CANNOT_FIRE_THROUGH); + + ScriptEnumRegister("SectorProp"); + ScriptEnumStr(SECTORPROP_FLOOR_HEIGHT); + ScriptEnumStr(SECTORPROP_CEIL_HEIGHT); + ScriptEnumStr(SECTORPROP_SECOND_HEIGHT); + ScriptEnumStr(SECTORPROP_FLOOR_TEX); + ScriptEnumStr(SECTORPROP_CEIL_TEX); + ScriptEnumStr(SECTORPROP_AMBIENT); + + // Functions + ScriptObjMethod("Sector getSector(int)", getSectorById); + ScriptObjMethod("Elevator getElevator(int)", getElevator); + ScriptObjMethod("void findConnectedSectors(Sector initSector, uint, array&)", findConnectedSectors); + // -- Getters -- + ScriptLambdaPropertyGet("int get_minLayer()", s32, { return s_levelState.minLayer; }); + ScriptLambdaPropertyGet("int get_maxLayer()", s32, { return s_levelState.maxLayer; }); + ScriptLambdaPropertyGet("int get_sectorCount()", s32, { return (s32)s_levelState.sectorCount; }); + ScriptLambdaPropertyGet("int get_secretCount()", s32, { return s_levelState.secretCount; }); + ScriptLambdaPropertyGet("int get_textureCount()", s32, { return s_levelState.textureCount; }); + ScriptLambdaPropertyGet("int get_elevatorCount()", s32, { return allocator_getCount(s_infSerState.infElevators); }); + ScriptLambdaPropertyGet("float2 get_parallax()", TFE_ForceScript::float2, { return TFE_ForceScript::float2(fixed16ToFloat(s_levelState.parallax0), fixed16ToFloat(s_levelState.parallax1)); }); + + // Gameplay sector pointers. + ScriptLambdaPropertyGet("Sector get_bossSector()", ScriptSector, { ScriptSector sector(-1); if (s_levelState.bossSector) { sector.m_id = s_levelState.bossSector->id; } return sector; }); + ScriptLambdaPropertyGet("Sector get_mohcSector()", ScriptSector, { ScriptSector sector(-1); if (s_levelState.mohcSector) { sector.m_id = s_levelState.mohcSector->id; } return sector; }); + ScriptLambdaPropertyGet("Sector get_completeSector()", ScriptSector, { ScriptSector sector(-1); if (s_levelState.completeSector) { sector.m_id = s_levelState.completeSector->id; } return sector; }); + } + ScriptClassEnd(); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h new file mode 100644 index 000000000..fc2141933 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_level.h @@ -0,0 +1,33 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include +#include + +class CScriptArray; + +namespace TFE_DarkForces +{ + class ScriptSector; + class ScriptElev; + + class GS_Level : public ScriptAPIClass + { + public: + // System + bool scriptRegister(ScriptAPI api) override; + + ScriptSector getSectorById(s32 id); + ScriptElev getElevator(s32 id); + void findConnectedSectors(ScriptSector initSector, u32 matchProp, CScriptArray& results); + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_system.cpp b/TheForceEngine/TFE_DarkForces/Scripting/gs_system.cpp new file mode 100644 index 000000000..6d62678a4 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_system.cpp @@ -0,0 +1,361 @@ +#include "gs_system.h" +#include "gameScripts.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_DarkForces +{ + namespace // Static helper functions. + { + std::string toString(CScriptArray* arr) + { + char output[4096] = ""; + s32 typeId = arr->GetElementTypeId(); + char tmp[256]; + + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_ARRAY)) + { + // Can't handle multi-dimensional arrays yet. + return "?"; + } + + const u32 len = arr->GetSize(); + for (u32 i = 0; i < len; i++) + { + const void* data = arr->At(i); + std::string value; + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_STRING)) + { + value = *(std::string*)data; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) + { + value = toString(*(TFE_ForceScript::float2*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + value = toStringF3(*(TFE_ForceScript::float3*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4)) + { + value = toStringF4(*(TFE_ForceScript::float4*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2x2)) + { + value = toStringF2x2(*(TFE_ForceScript::float2x2*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3x3)) + { + value = toStringF3x3(*(TFE_ForceScript::float3x3*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4x4)) + { + value = toStringF4x4(*(TFE_ForceScript::float4x4*)data); + } + else + { + switch (typeId) + { + case 2: + { + char local = *(char*)(data); + sprintf(tmp, "%c", local); + value = tmp; + break; + } + case 3: + { + s16 local = *(s16*)(data); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 4: + { + s32 local = *(s32*)(data); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 5: + { + s64 local = *(s64*)(data); + sprintf(tmp, "%lld", local); + value = tmp; + break; + } + case 6: + { + u8 local = *(u8*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 7: + { + u16 local = *(u16*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 8: + { + u32 local = *(u32*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 9: + { + u64 local = *(u64*)(data); + sprintf(tmp, "%llu", local); + value = tmp; + break; + } + case 10: + { + f32 local = *(f32*)(data); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + case 11: + { + f64 local = *(f64*)(data); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + default: + { + return "?"; + } + } + } + + char fmt[256]; + if (len == 1) + { + sprintf(fmt, "(%s)", value.c_str()); + } + else if (i == 0) + { + sprintf(fmt, "(%s, ", value.c_str()); + } + else if (i < len - 1) + { + sprintf(fmt, "%s, ", value.c_str()); + } + else + { + sprintf(fmt, "%s)", value.c_str()); + } + strcat(output, fmt); + } + return output; + } + + std::string formatValue(s32 typeId, void* ref) + { + std::string value; + char tmp[256]; + + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_STRING)) + { + value = *(std::string*)ref; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_ARRAY)) + { + value = toString((CScriptArray*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) + { + value = toString(*(TFE_ForceScript::float2*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + value = toStringF3(*(TFE_ForceScript::float3*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4)) + { + value = toStringF4(*(TFE_ForceScript::float4*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2x2)) + { + value = toStringF2x2(*(TFE_ForceScript::float2x2*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3x3)) + { + value = toStringF3x3(*(TFE_ForceScript::float3x3*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4x4)) + { + value = toStringF4x4(*(TFE_ForceScript::float4x4*)ref); + } + else + { + switch (typeId) + { + case 2: + { + char local = *(char*)(ref); + sprintf(tmp, "%c", local); + value = tmp; + break; + } + case 3: + { + s16 local = *(s16*)(ref); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 4: + { + s32 local = *(s32*)(ref); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 5: + { + s64 local = *(s64*)(ref); + sprintf(tmp, "%lld", local); + value = tmp; + break; + } + case 6: + { + u8 local = *(u8*)(ref); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 7: + { + u16 local = *(u16*)(ref); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 8: + { + u32 local = *(u32*)(ref); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 9: + { + u64 local = *(u64*)(ref); + sprintf(tmp, "%llu", local); + value = tmp; + break; + } + case 10: + { + f32 local = *(f32*)(ref); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + case 11: + { + f64 local = *(f64*)(ref); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + default: + { + value = "?"; + } + } + } + return value; + } + + void outputPrintFormat(asIScriptGeneric *gen) + { + const s32 argCount = gen->GetArgCount(); + + void* ref = gen->GetArgAddress(0); + s32 typeId = gen->GetArgTypeId(0); + std::string fmt = *((std::string*)ref); + std::string output = fmt; + + // parse the string for {} pairs. + const char* key = "{}"; + const size_t keyLen = strlen(key); + size_t len = output.length(); + + s32 index = 1; + for (size_t pos = 0; pos < len && index < argCount;) + { + pos = output.find(key, pos); + if (pos == std::string::npos) + { + break; + } + + ref = gen->GetArgAddress(index); + typeId = gen->GetArgTypeId(index); + index++; + + output.replace(pos, keyLen, formatValue(typeId, ref)); + len = output.length(); + pos += 2; + } + TFE_FrontEndUI::logToConsole(output.c_str()); + } + } + + void GS_System::error(asIScriptGeneric *gen) + { + outputPrintFormat(gen); + } + + void GS_System::warning(asIScriptGeneric *gen) + { + outputPrintFormat(gen); + } + + void GS_System::print(asIScriptGeneric *gen) + { + outputPrintFormat(gen); + } + + bool GS_System::scriptRegister(ScriptAPI api) + { + ScriptClassBegin("System", "system", api); + { + // Member Variables + ScriptMemberVariable("int version", m_version); + + // Functions + // Currently support up to 10 arguments + format string. + // This has to be a static member of the class since it use the GENERIC calling convention, but it is still added as an object method + // so the script API follows the desired `system.func()` pattern. + ScriptGenericMethod( + "void error(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + error); + ScriptGenericMethod( + "void warning(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + warning); + ScriptGenericMethod( + "void print(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + print); + } + ScriptClassEnd(); + } +} + +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/gs_system.h b/TheForceEngine/TFE_DarkForces/Scripting/gs_system.h new file mode 100644 index 000000000..caa13d1a9 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/gs_system.h @@ -0,0 +1,30 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include +#include + +namespace TFE_DarkForces +{ + class GS_System : public ScriptAPIClass + { + public: + // Properties + s32 m_version = 1; + // Functions + static void error(asIScriptGeneric* gen); + static void warning(asIScriptGeneric* gen); + static void print(asIScriptGeneric* gen); + // System + bool scriptRegister(ScriptAPI api) override; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.cpp b/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.cpp new file mode 100644 index 000000000..2085f5d42 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.cpp @@ -0,0 +1,42 @@ +#include "scriptElev.h" +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + bool ScriptElev::isScriptElevValid(ScriptElev* elev) + { + return elev->m_id >= 0 && elev->m_id < allocator_getCount(s_infSerState.infElevators); + } + // TODO- Replace by updateFlags and enum? + bool getElevMaster(ScriptElev* elev) + { + if (!ScriptElev::isScriptElevValid(elev)) { return false; } + InfElevator* data = (InfElevator*)allocator_getByIndex(s_infSerState.infElevators, elev->m_id); + return (data->updateFlags & ELEV_MASTER_ON) != 0; + } + + void ScriptElev::registerType() + { + s32 res = 0; + asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); + + ScriptValueType("Elevator"); + // Variables + ScriptMemberVariable("int id", m_id); + // Functions + ScriptObjFunc("bool isValid()", isScriptElevValid); + // Properties + ScriptPropertyGetFunc("bool get_master()", getElevMaster); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.h b/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.h new file mode 100644 index 000000000..79da58558 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptElev.h @@ -0,0 +1,28 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_DarkForces +{ + class ScriptElev + { + public: + ScriptElev() : m_id(-1) {}; + ScriptElev(s32 id) : m_id(id) {}; + void registerType(); + + static bool isScriptElevValid(ScriptElev* elev); + + public: + s32 m_id; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.cpp b/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.cpp new file mode 100644 index 000000000..4d2913299 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.cpp @@ -0,0 +1,204 @@ +#include "scriptSector.h" +#include "scriptWall.h" +#include "scriptTexture.h" +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + f32 getFloorHeight(ScriptSector* sector) { return sector->m_id >= 0 ? fixed16ToFloat(s_levelState.sectors[sector->m_id].floorHeight) : 0; } + f32 getCeilHeight(ScriptSector* sector) { return sector->m_id >= 0 ? fixed16ToFloat(s_levelState.sectors[sector->m_id].ceilingHeight) : 0; } + f32 getSecondHeight(ScriptSector* sector) { return sector->m_id >= 0 ? fixed16ToFloat(s_levelState.sectors[sector->m_id].secHeight) : 0; } + s32 getWallCount(ScriptSector* sector) { return sector->m_id >= 0 ? s_levelState.sectors[sector->m_id].wallCount : 0; } + f32 getAmbient(ScriptSector* sector) { return sector->m_id >= 0 ? fixed16ToFloat(s_levelState.sectors[sector->m_id].ambient) : 0; } + + void setFloorHeight(f32 height, ScriptSector* sector) + { + if (sector->m_id < 0) { return; } + RSector* lvlSector = &s_levelState.sectors[sector->m_id]; + const fixed16_16 offset = floatToFixed16(height) - lvlSector->floorHeight; + if (offset != 0) + { + sector_adjustTextureWallOffsets_Floor(lvlSector, offset); + sector_adjustTextureMirrorOffsets_Floor(lvlSector, offset); + sector_adjustHeights(lvlSector, offset, 0, 0); + } + } + void setCeilHeight(f32 height, ScriptSector* sector) + { + if (sector->m_id < 0) { return; } + const fixed16_16 offset = floatToFixed16(height) - s_levelState.sectors[sector->m_id].ceilingHeight; + sector_adjustHeights(&s_levelState.sectors[sector->m_id], 0, offset, 0); + } + void setSecondHeight(f32 height, ScriptSector* sector) + { + if (sector->m_id < 0) { return; } + const fixed16_16 offset = floatToFixed16(height) - s_levelState.sectors[sector->m_id].secHeight; + sector_adjustHeights(&s_levelState.sectors[sector->m_id], 0, 0, offset); + } + void setAmbient(f32 ambient, ScriptSector* sector) + { + if (sector->m_id < 0) { return; } + s_levelState.sectors[sector->m_id].ambient = floatToFixed16(ambient); + s_levelState.sectors[sector->m_id].dirtyFlags |= SDF_AMBIENT; + } + bool isScriptSectorValid(ScriptSector* sector) + { + return sector->m_id >= 0 && sector->m_id < (s32)s_levelState.sectorCount; + } + bool isSectorFlagSet(s32 index, u32 flag, ScriptSector* sector) + { + if (!isScriptSectorValid(sector)) { return false; } + if (index == 1) { return (s_levelState.sectors[sector->m_id].flags1 & flag) != 0u; } + else if (index == 2) { return (s_levelState.sectors[sector->m_id].flags2 & flag) != 0u; } + else if (index == 3) { return (s_levelState.sectors[sector->m_id].flags3 & flag) != 0u; } + return false; + } + void clearSectorFlag(s32 index, u32 flag, ScriptSector* sector) + { + if (!isScriptSectorValid(sector)) { return; } + if (index == 1) { s_levelState.sectors[sector->m_id].flags1 &= ~flag; } + else if (index == 2) { s_levelState.sectors[sector->m_id].flags2 &= ~flag; } + else if (index == 3) { s_levelState.sectors[sector->m_id].flags3 &= ~flag; } + } + void setSectorFlag(s32 index, u32 flag, ScriptSector* sector) + { + if (!isScriptSectorValid(sector)) { return; } + if (index == 1) { s_levelState.sectors[sector->m_id].flags1 |= flag; } + else if (index == 2) { s_levelState.sectors[sector->m_id].flags2 |= flag; } + else if (index == 3) { s_levelState.sectors[sector->m_id].flags3 |= flag; } + } + TFE_ForceScript::float2 getCenterXZ(ScriptSector* sector) + { + TFE_ForceScript::float2 cen(0.0f, 0.0f); + if (!isScriptSectorValid(sector)) { return cen; } + cen.x = fixed16ToFloat(s_levelState.sectors[sector->m_id].boundsMin.x + s_levelState.sectors[sector->m_id].boundsMax.x) * 0.5f; + cen.y = fixed16ToFloat(s_levelState.sectors[sector->m_id].boundsMin.z + s_levelState.sectors[sector->m_id].boundsMax.z) * 0.5f; + return cen; + } + ScriptWall getWall(s32 index, ScriptSector* sector) + { + ScriptWall wall(-1, -1); + if (isScriptSectorValid(sector)) + { + if (index >= 0 && index < s_levelState.sectors[sector->m_id].wallCount) + { + wall.m_sectorId = sector->m_id; + wall.m_wallId = index; + } + } + return wall; + } + ScriptTexture getFloorTexture(ScriptSector* sector) + { + ScriptTexture tex(-1); + if (isScriptSectorValid(sector)) + { + tex.m_id = ScriptTexture::getTextureIdFromData(s_levelState.sectors[sector->m_id].floorTex); + } + return tex; + } + ScriptTexture getCeilTexture(ScriptSector* sector) + { + ScriptTexture tex(-1); + if (isScriptSectorValid(sector)) + { + tex.m_id = ScriptTexture::getTextureIdFromData(s_levelState.sectors[sector->m_id].ceilTex); + } + return tex; + } + TFE_ForceScript::float2 getFloorTextureOffset(ScriptSector* sector) + { + TFE_ForceScript::float2 offset(0.0f, 0.0f); + if (isScriptSectorValid(sector)) + { + offset.x = fixed16ToFloat(s_levelState.sectors[sector->m_id].floorOffset.x); + offset.y = fixed16ToFloat(s_levelState.sectors[sector->m_id].floorOffset.z); + } + return offset; + } + TFE_ForceScript::float2 getCeilTextureOffset(ScriptSector* sector) + { + TFE_ForceScript::float2 offset(0.0f, 0.0f); + if (isScriptSectorValid(sector)) + { + offset.x = fixed16ToFloat(s_levelState.sectors[sector->m_id].ceilOffset.x); + offset.y = fixed16ToFloat(s_levelState.sectors[sector->m_id].ceilOffset.z); + } + return offset; + } + void setFloorTexture(ScriptTexture tex, ScriptSector* sector) + { + if (isScriptSectorValid(sector) && ScriptTexture::isTextureValid(&tex)) + { + s_levelState.sectors[sector->m_id].floorTex = &s_levelState.textures[tex.m_id]; + } + } + void setCeilTexture(ScriptTexture tex, ScriptSector* sector) + { + if (isScriptSectorValid(sector) && ScriptTexture::isTextureValid(&tex)) + { + s_levelState.sectors[sector->m_id].ceilTex = &s_levelState.textures[tex.m_id]; + } + } + void setFloorTextureOffset(TFE_ForceScript::float2 offset, ScriptSector* sector) + { + if (isScriptSectorValid(sector)) + { + s_levelState.sectors[sector->m_id].floorOffset.x = floatToFixed16(offset.x); + s_levelState.sectors[sector->m_id].floorOffset.z = floatToFixed16(offset.y); + } + } + void setCeilTextureOffset(TFE_ForceScript::float2 offset, ScriptSector* sector) + { + if (isScriptSectorValid(sector)) + { + s_levelState.sectors[sector->m_id].ceilOffset.x = floatToFixed16(offset.x); + s_levelState.sectors[sector->m_id].ceilOffset.z = floatToFixed16(offset.y); + } + } + + void ScriptSector::registerType() + { + s32 res = 0; + asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); + // TODO: Objects + + ScriptValueType("Sector"); + // Variables + ScriptMemberVariable("int id", m_id); + // Functions + ScriptObjFunc("bool isValid()", isScriptSectorValid); + ScriptObjFunc("bool isFlagSet(int, uint)", isSectorFlagSet); + ScriptObjFunc("void clearFlag(int, uint)", clearSectorFlag); + ScriptObjFunc("void setFlag(int, uint)", setSectorFlag); + ScriptObjFunc("float2 getCenterXZ()", getCenterXZ); + ScriptObjFunc("Wall getWall(int)", getWall); + // Properties + ScriptPropertyGetFunc("float get_floorHeight()", getFloorHeight); + ScriptPropertyGetFunc("float get_ceilHeight()", getCeilHeight); + ScriptPropertyGetFunc("float get_secondHeight()", getSecondHeight); + ScriptPropertyGetFunc("float get_ambient()", getAmbient); + ScriptPropertyGetFunc("int get_wallCount()", getWallCount); + ScriptPropertyGetFunc("LevelTexture get_floorTexture()", getFloorTexture); + ScriptPropertyGetFunc("LevelTexture get_ceilTexture()", getCeilTexture); + ScriptPropertyGetFunc("float2 get_floorTextureOffset()", getFloorTextureOffset); + ScriptPropertyGetFunc("float2 get_ceilTextureOffset()", getCeilTextureOffset); + ScriptPropertySetFunc("void set_floorHeight(float)", setFloorHeight); + ScriptPropertySetFunc("void set_ceilHeight(float)", setCeilHeight); + ScriptPropertySetFunc("void set_secondHeight(float)", setSecondHeight); + ScriptPropertySetFunc("void set_ambient(float)", setAmbient); + ScriptPropertySetFunc("void set_floorTexture(LevelTexture)", setFloorTexture); + ScriptPropertySetFunc("void set_ceilTexture(LevelTexture)", setCeilTexture); + ScriptPropertySetFunc("void set_floorTextureOffset(float2)", setFloorTextureOffset); + ScriptPropertySetFunc("void set_ceilTextureOffset(float2)", setCeilTextureOffset); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.h b/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.h new file mode 100644 index 000000000..978bf4527 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptSector.h @@ -0,0 +1,29 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include + +namespace TFE_DarkForces +{ + class ScriptSector + { + public: + ScriptSector() : m_id(-1) {}; + ScriptSector(s32 id) : m_id(id) {}; + void registerType(); + + public: + s32 m_id; + }; + + extern bool isScriptSectorValid(ScriptSector* sector); +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.cpp b/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.cpp new file mode 100644 index 000000000..91758dc3a --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.cpp @@ -0,0 +1,40 @@ +#include "scriptTexture.h" +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + s32 ScriptTexture::getTextureIdFromData(TextureData** texData) + { + if (!texData || !*texData) { return -1; } + + s32 index; + AssetPool pool; + if (!bitmap_getTextureIndex(*texData, &index, &pool)) + { + return -1; + } + return index; + } + bool ScriptTexture::isTextureValid(ScriptTexture* tex) + { + return tex->m_id >= 0; + } + + void ScriptTexture::registerType() + { + s32 res = 0; + asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); + + ScriptValueType("LevelTexture"); + // Variables + ScriptMemberVariable("int id", m_id); + // Functions + ScriptObjFunc("bool isValid()", isTextureValid); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.h b/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.h new file mode 100644 index 000000000..71123e003 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptTexture.h @@ -0,0 +1,30 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include + +namespace TFE_DarkForces +{ + class ScriptTexture + { + public: + ScriptTexture() : m_id(-1) {}; + ScriptTexture(s32 id) : m_id(id) {}; + void registerType(); + + static s32 getTextureIdFromData(TextureData** texData); + static bool isTextureValid(ScriptTexture* tex); + + public: + s32 m_id; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.cpp b/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.cpp new file mode 100644 index 000000000..5b06920d0 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.cpp @@ -0,0 +1,259 @@ +#include "scriptWall.h" +#include "scriptTexture.h" +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_DarkForces +{ + bool isScriptWallValid(ScriptWall* wall) + { + if (wall->m_sectorId < 0 || wall->m_sectorId >= (s32)s_levelState.sectorCount) { return false; } + if (wall->m_wallId < 0 || wall->m_wallId >= s_levelState.sectors[wall->m_sectorId].wallCount) { return false; } + return true; + } + bool isWallFlagSet(s32 index, u32 flag, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return false; } + if (index == 1) { return (s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags1 & flag) != 0u; } + else if (index == 2) { return (s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags2 & flag) != 0u; } + else if (index == 3) { return (s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags3 & flag) != 0u; } + return false; + } + void clearWallFlag(s32 index, u32 flag, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + if (index == 1) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags1 &= ~flag; } + else if (index == 2) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags2 &= ~flag; } + else if (index == 3) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags3 &= ~flag; } + } + void setWallFlag(s32 index, u32 flag, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + if (index == 1) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags1 |= flag; } + else if (index == 2) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags2 |= flag; } + else if (index == 3) { s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].flags3 |= flag; } + } + s32 getAdjoin(ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return -1; } + RSector* next = s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].nextSector; + return next ? next->id : -1; + } + s32 getMirror(ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return -1; } + RWall* next = s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].mirrorWall; + return next ? next->id : -1; + } + f32 getWallLight(ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return 0; } + return fixed16ToFloat(s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId].wallLight); + } + ScriptTexture getWallTexture(WallPart part, ScriptWall* wall) + { + ScriptTexture tex(-1); + if (isScriptWallValid(wall)) + { + RWall* lvlWall = &s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId]; + switch (part) + { + case WP_MIDDLE: tex.m_id = ScriptTexture::getTextureIdFromData(lvlWall->midTex); break; + case WP_TOP: tex.m_id = ScriptTexture::getTextureIdFromData(lvlWall->topTex); break; + case WP_BOTTOM: tex.m_id = ScriptTexture::getTextureIdFromData(lvlWall->botTex); break; + case WP_SIGN: tex.m_id = ScriptTexture::getTextureIdFromData(lvlWall->signTex); break; + } + } + return tex; + } + TFE_ForceScript::float2 getWallTextureOffset(WallPart part, ScriptWall* wall) + { + TFE_ForceScript::float2 offset(0.0f, 0.0f); + if (isScriptWallValid(wall)) + { + RWall* lvlWall = &s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId]; + switch (part) + { + case WP_MIDDLE: + { + offset.x = fixed16ToFloat(lvlWall->midOffset.x); + offset.y = fixed16ToFloat(lvlWall->midOffset.z); + } break; + case WP_TOP: + { + offset.x = fixed16ToFloat(lvlWall->topOffset.x); + offset.y = fixed16ToFloat(lvlWall->topOffset.z); + } break; + case WP_BOTTOM: + { + offset.x = fixed16ToFloat(lvlWall->botOffset.x); + offset.y = fixed16ToFloat(lvlWall->botOffset.z); + } break; + case WP_SIGN: + { + offset.x = fixed16ToFloat(lvlWall->signOffset.x); + offset.y = fixed16ToFloat(lvlWall->signOffset.z); + } break; + } + } + return offset; + } + TFE_ForceScript::float2 getVertex(s32 index, ScriptWall* wall) + { + TFE_ForceScript::float2 vtx(0.0f, 0.0f); + if (!isScriptWallValid(wall)) { return vtx; } + + RWall* lvlWall = &s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId]; + vtx.x = fixed16ToFloat(index == 0 ? lvlWall->w0->x : lvlWall->w1->x); + vtx.y = fixed16ToFloat(index == 0 ? lvlWall->w0->z : lvlWall->w1->z); + return vtx; + } + TFE_ForceScript::float2 getVertexBase(ScriptWall* wall) + { + TFE_ForceScript::float2 vtx(0.0f, 0.0f); + if (!isScriptWallValid(wall)) { return vtx; } + + RWall* lvlWall = &s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId]; + vtx.x = fixed16ToFloat(lvlWall->worldPos0.x); + vtx.y = fixed16ToFloat(lvlWall->worldPos0.z); + return vtx; + } + void setAdjoin(s32 id, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + RSector* sector = &s_levelState.sectors[wall->m_sectorId]; + RWall* lvlWall = §or->walls[wall->m_wallId]; + if (id < 0 || id >= (s32)s_levelState.sectorCount) + { + lvlWall->nextSector = nullptr; + + sector_setupWallDrawFlags(sector); + } + else + { + lvlWall->nextSector = &s_levelState.sectors[id]; + + sector_setupWallDrawFlags(sector); + sector_setupWallDrawFlags(lvlWall->nextSector); + } + } + void setMirror(s32 id, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + RSector* sector = &s_levelState.sectors[wall->m_sectorId]; + RWall* lvlWall = §or->walls[wall->m_wallId]; + RSector* next = lvlWall->nextSector; + if (!next) { return; } + + if (id < 0 || id >= next->wallCount) + { + lvlWall->mirror = -1; + lvlWall->mirrorWall = nullptr; + } + else + { + lvlWall->mirror = id; + lvlWall->mirrorWall = &next->walls[id]; + sector_setupWallDrawFlags(lvlWall->nextSector); + } + sector_setupWallDrawFlags(sector); + } + void setWallLight(f32 light, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + RSector* sector = &s_levelState.sectors[wall->m_sectorId]; + sector->walls[wall->m_wallId].wallLight = floatToFixed16(light); + sector->dirtyFlags |= SDF_AMBIENT; + } + void setWallTexture(WallPart part, ScriptTexture tex, ScriptWall* wall) + { + if (isScriptWallValid(wall) && ScriptTexture::isTextureValid(&tex)) + { + RSector* sector = &s_levelState.sectors[wall->m_sectorId]; + RWall* lvlWall = §or->walls[wall->m_wallId]; + TextureData** data = &s_levelState.textures[tex.m_id]; + switch (part) + { + case WP_MIDDLE: lvlWall->midTex = data; break; + case WP_TOP: lvlWall->topTex = data; break; + case WP_BOTTOM: lvlWall->botTex = data; break; + case WP_SIGN: lvlWall->signTex = data; break; + } + } + } + void setWallTextureOffset(WallPart part, TFE_ForceScript::float2 offset, ScriptWall* wall) + { + if (isScriptWallValid(wall)) + { + RWall* lvlWall = &s_levelState.sectors[wall->m_sectorId].walls[wall->m_wallId]; + const vec2_fixed fixedOffset = { floatToFixed16(offset.x), floatToFixed16(offset.y) }; + switch (part) + { + case WP_MIDDLE: lvlWall->midOffset = fixedOffset; break; + case WP_TOP: lvlWall->topOffset = fixedOffset; break; + case WP_BOTTOM: lvlWall->botOffset = fixedOffset; break; + case WP_SIGN: lvlWall->signOffset = fixedOffset; break; + } + } + } + void setVertex(TFE_ForceScript::float2 vtx, s32 index, ScriptWall* wall) + { + if (!isScriptWallValid(wall)) { return; } + + RSector* sector = &s_levelState.sectors[wall->m_sectorId]; + RWall* lvlWall = §or->walls[wall->m_wallId]; + if (index == 0) + { + lvlWall->w0->x = floatToFixed16(vtx.x); + lvlWall->w0->z = floatToFixed16(vtx.y); + } + else + { + lvlWall->w1->x = floatToFixed16(vtx.x); + lvlWall->w1->z = floatToFixed16(vtx.y); + } + sector->dirtyFlags |= (SDF_VERTICES | SDF_WALL_SHAPE); + } + + void ScriptWall::registerType() + { + s32 res = 0; + asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); + + ScriptEnumRegister("WallPart"); + ScriptEnumStr(WP_MIDDLE); + ScriptEnumStr(WP_TOP); + ScriptEnumStr(WP_BOTTOM); + ScriptEnumStr(WP_SIGN); + + ScriptValueType("Wall"); + // Variables + ScriptMemberVariable("int sectorId", m_sectorId); + ScriptMemberVariable("int wallId", m_wallId); + // Functions + ScriptObjFunc("bool isValid()", isScriptWallValid); + ScriptObjFunc("bool isFlagSet(int, uint)", isWallFlagSet); + ScriptObjFunc("void clearFlag(int, uint)", clearWallFlag); + ScriptObjFunc("void setFlag(int, uint)", setWallFlag); + ScriptObjFunc("float2 getVertex(int index = 0)", getVertex); + ScriptObjFunc("float2 getVertexBase()", getVertexBase); + ScriptObjFunc("void setVertex(float2, int index = 0)", setVertex); + ScriptObjFunc("LevelTexture getTexture(WallPart)", getWallTexture); + ScriptObjFunc("float2 getTextureOffset(WallPart)", getWallTextureOffset); + ScriptObjFunc("void setTexture(WallPart, LevelTexture)", setWallTexture); + ScriptObjFunc("void setTextureOffset(WallPart, float2)", setWallTextureOffset); + // Properties + ScriptPropertyGetFunc("int get_adjoin()", getAdjoin); + ScriptPropertyGetFunc("int get_mirror()", getMirror); + ScriptPropertyGetFunc("float get_wallLight()", getWallLight); + ScriptPropertySetFunc("void set_adjoin(int)", setAdjoin); + ScriptPropertySetFunc("void set_mirror(int)", setMirror); + ScriptPropertySetFunc("void set_wallLight(float)", setWallLight); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.h b/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.h new file mode 100644 index 000000000..d36f881d9 --- /dev/null +++ b/TheForceEngine/TFE_DarkForces/Scripting/scriptWall.h @@ -0,0 +1,36 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include + +namespace TFE_DarkForces +{ + enum WallPart + { + WP_MIDDLE = 0, + WP_TOP, + WP_BOTTOM, + WP_SIGN + }; + + class ScriptWall + { + public: + ScriptWall() : m_sectorId(-1), m_wallId(-1) {}; + ScriptWall(s32 sectorId, s32 wallId) : m_sectorId(sectorId), m_wallId(wallId) {}; + void registerType(); + + public: + s32 m_sectorId; + s32 m_wallId; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/agent.cpp b/TheForceEngine/TFE_DarkForces/agent.cpp index f3dd7387a..1a0a22396 100644 --- a/TheForceEngine/TFE_DarkForces/agent.cpp +++ b/TheForceEngine/TFE_DarkForces/agent.cpp @@ -271,16 +271,16 @@ namespace TFE_DarkForces memset(&saveData, 0, sizeof(LevelSaveData)); memset(data, 0, sizeof(AgentData)); - saveData.inv[0] = 0xff; - saveData.inv[2] = 0xff; - saveData.inv[6] = 0xff; - saveData.inv[30] = WPN_PISTOL; - saveData.inv[31] = 3; - - saveData.ammo[0] = 100; - saveData.ammo[7] = 100; - saveData.ammo[8] = 100; - saveData.ammo[9] = FIXED(2); + saveData.inv[0] = 0xff; // bryar pistol + saveData.inv[2] = 0xff; // s_itemUnknown1 + saveData.inv[6] = 0xff; // s_itemUnknown2 + saveData.inv[30] = WPN_PISTOL; // current weapon + saveData.inv[31] = 3; // lives + + saveData.ammo[0] = min(s_ammoEnergyMax, 100); // energy + saveData.ammo[7] = 100; // shields + saveData.ammo[8] = 100; // health + saveData.ammo[9] = FIXED(2); // battery strCopyAndZero(data->name, name, 32); data->difficulty = 1; diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp index 69feb5af2..9f9dbf416 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,8 @@ #include #include #include +#include +#include #include // Add texture callbacks. @@ -335,6 +338,11 @@ namespace TFE_DarkForces s_sharedState.gameStarted = JTRUE; sound_setLevelStart(); + + // TFE + TFE_ScriptInterface::registerScriptInterface(API_GAME); + TFE_ScriptInterface::setAPI(API_GAME, nullptr); + return true; } @@ -380,13 +388,17 @@ namespace TFE_DarkForces TFE_Model_Jedi::freeAll(); reticle_enable(false); texturepacker_reset(); + freeLevelScript(); TFE_MidiPlayer::resume(); TFE_Audio::resume(); // Reset state. - s_sharedState = {}; + s_sharedState = SharedGameState{}; s_runGameState = {}; + + // TFE - Script system. + TFE_ScriptInterface::reset(); } void DarkForces::pauseGame(bool pause) @@ -551,8 +563,6 @@ namespace TFE_DarkForces ****************************************************/ void DarkForces::loopGame() { - //TFE_System::logWrite(LOG_MSG, "LOOP", "FRAME COUNT"); - updateTime(); switch (s_runGameState.state) @@ -562,6 +572,7 @@ namespace TFE_DarkForces s_runGameState.state = GSTATE_CUTSCENE; s_invalidLevelIndex = JTRUE; + // Always force cutscenes off for demo playbac for cutscenes. if (isDemoPlayback()) { s_runGameState.cutscenesEnabled = JFALSE; @@ -680,6 +691,8 @@ namespace TFE_DarkForces // TFE reticle_enable(false); + // TFE - Script system. + TFE_ScriptInterface::reset(); if (!s_levelComplete) { @@ -801,7 +814,8 @@ namespace TFE_DarkForces task_reset(); inf_clearState(); - TFE_Settings_Game * gameSettings = TFE_Settings::getGameSettings(); + + TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); // Entry point to replay a demo if (gameSettings->df_enableReplay && !isDemoPlayback()) @@ -856,7 +870,7 @@ namespace TFE_DarkForces { c = arg[1]; if (c == 'c' || c == 'C') - { + { enableCutscenes(arg[2] == '1' ? JTRUE : JFALSE); } else if ((c == 'l' || c == 'L') && arg[2]) @@ -923,6 +937,17 @@ namespace TFE_DarkForces s_runGameState.startLevel = agent_getLevelIndexFromName(levelName); } + char* extractTextFileFromZip(ZipArchive& zip, u32 fileIndex) + { + u32 bufferLen = (u32)zip.getFileLength(fileIndex); + char* buffer = (char*)malloc(bufferLen); + zip.openFile(fileIndex); + zip.readFile(buffer, bufferLen); + zip.closeFile(); + + return buffer; + } + void loadCustomGob(const char* gobName) { FilePath archivePath; @@ -939,17 +964,19 @@ namespace TFE_DarkForces // Is this really a gob? const size_t len = strlen(gobName); const char* ext = &gobName[len - 3]; - if (strcasecmp(ext, "zip") == 0 || strcasecmp(ext, "pk3") == 0) + const char* ext4 = &gobName[len - 4]; + if (strcasecmp(ext, "zip") == 0 || strcasecmp(ext, "pk3") == 0 || strcasecmp(ext4, "gobx") == 0) { // In the case of a zip file, we want to extract the GOB into an in-memory format and use that directly. - ZipArchive zipArchive; - if (zipArchive.open(archivePath.path)) + // Note that the archive will be deleted on exit, so we can safely allocate here and pass it along. + ZipArchive* zipArchive = new ZipArchive(); + if (zipArchive->open(archivePath.path)) { s32 gobIndex = -1; - const u32 count = zipArchive.getFileCount(); + const u32 count = zipArchive->getFileCount(); for (u32 i = 0; i < count; i++) { - const char* name = zipArchive.getFileName(i); + const char* name = zipArchive->getFileName(i); const size_t nameLen = strlen(name); const char* zext = &name[nameLen - 3]; const char* zext4 = &name[nameLen - 4]; @@ -976,21 +1003,48 @@ namespace TFE_DarkForces } else if (strcasecmp(zext4, "json") == 0) { - char name2[TFE_MAX_PATH]; - strcpy(name2, name); - const char* subdir = strtok(name2, "/"); + // Load external data overrides + char fname[TFE_MAX_PATH]; + FileUtil::getFileNameFromPath(name, fname, true); - // If in logics subdirectory, attempt to load logics from JSON - if (strcasecmp(subdir, "logics") == 0) + if (strcasecmp(fname, "projectiles.json") == 0) { - u32 bufferLen = (u32)zipArchive.getFileLength(i); - char* buffer = (char*)malloc(bufferLen); - zipArchive.openFile(i); - zipArchive.readFile(buffer, bufferLen); - zipArchive.closeFile(); - - TFE_ExternalData::ExternalLogics* logics = TFE_ExternalData::getExternalLogics(); - TFE_ExternalData::parseLogicData(buffer, name, logics->actorLogics); + char* buffer = extractTextFileFromZip(*zipArchive, i); + TFE_ExternalData::parseExternalProjectiles(buffer, true); + free(buffer); + } + else if (strcasecmp(fname, "effects.json") == 0) + { + char* buffer = extractTextFileFromZip(*zipArchive, i); + TFE_ExternalData::parseExternalEffects(buffer, true); + free(buffer); + } + else if (strcasecmp(fname, "pickups.json") == 0) + { + char* buffer = extractTextFileFromZip(*zipArchive, i); + TFE_ExternalData::parseExternalPickups(buffer, true); + free(buffer); + } + else if (strcasecmp(fname, "weapons.json") == 0) + { + char* buffer = extractTextFileFromZip(*zipArchive, i); + TFE_ExternalData::parseExternalWeapons(buffer, true); + free(buffer); + } + else + { + char name2[TFE_MAX_PATH]; + strcpy(name2, name); + const char* subdir = strtok(name2, "/"); + + // If in logics subdirectory, attempt to load logics from JSON + if (strcasecmp(subdir, "logics") == 0) + { + char* buffer = extractTextFileFromZip(*zipArchive, i); + TFE_ExternalData::ExternalLogics* logics = TFE_ExternalData::getExternalLogics(); + TFE_ExternalData::parseLogicData(buffer, name, logics->actorLogics); + free(buffer); + } } } } @@ -1004,14 +1058,14 @@ namespace TFE_DarkForces if (gobIndex >= 0) { - u32 bufferLen = (u32)zipArchive.getFileLength(gobIndex); + u32 bufferLen = (u32)zipArchive->getFileLength(gobIndex); u8* buffer = (u8*)malloc(bufferLen); - zipArchive.openFile(gobIndex); - zipArchive.readFile(buffer, bufferLen); - zipArchive.closeFile(); + zipArchive->openFile(gobIndex); + zipArchive->readFile(buffer, bufferLen); + zipArchive->closeFile(); GobMemoryArchive* gobArchive = new GobMemoryArchive(); - gobArchive->setName(zipArchive.getFileName(gobIndex)); + gobArchive->setName(zipArchive->getFileName(gobIndex)); gobArchive->open(buffer, bufferLen); TFE_Paths::addLocalArchive(gobArchive); } @@ -1021,11 +1075,11 @@ namespace TFE_DarkForces // Extract and copy the briefing. if (briefingIndex >= 0) { - u32 bufferLen = (u32)zipArchive.getFileLength(briefingIndex); + u32 bufferLen = (u32)zipArchive->getFileLength(briefingIndex); u8* buffer = (u8*)malloc(bufferLen); - zipArchive.openFile(briefingIndex); - zipArchive.readFile(buffer, bufferLen); - zipArchive.closeFile(); + zipArchive->openFile(briefingIndex); + zipArchive->readFile(buffer, bufferLen); + zipArchive->closeFile(); char lfdPath[TFE_MAX_PATH]; sprintf(lfdPath, "%sdfbrief.lfd", tempPath); @@ -1042,11 +1096,11 @@ namespace TFE_DarkForces // Extract and copy the LFD. for (s32 i = 0; i < lfdCount; i++) { - u32 bufferLen = (u32)zipArchive.getFileLength(lfdIndex[i]); + u32 bufferLen = (u32)zipArchive->getFileLength(lfdIndex[i]); u8* buffer = (u8*)malloc(bufferLen); - zipArchive.openFile(lfdIndex[i]); - zipArchive.readFile(buffer, bufferLen); - zipArchive.closeFile(); + zipArchive->openFile(lfdIndex[i]); + zipArchive->readFile(buffer, bufferLen); + zipArchive->closeFile(); char lfdPath[TFE_MAX_PATH]; sprintf(lfdPath, "%scutscenes%d.lfd", tempPath, i); @@ -1058,10 +1112,16 @@ namespace TFE_DarkForces } free(buffer); - TFE_Paths::addSingleFilePath(zipArchive.getFileName(lfdIndex[i]), lfdPath); + TFE_Paths::addSingleFilePath(zipArchive->getFileName(lfdIndex[i]), lfdPath); } - zipArchive.close(); + // Add the ZIP archive itself. + TFE_Paths::addLocalArchive(zipArchive); + } + else + { + // Delete on read failure since the allocation is not added to TFE_Paths in this case. + delete zipArchive; } } else @@ -1129,6 +1189,81 @@ namespace TFE_DarkForces sprintf(lfdPath, "%s%s", modPath, lfdName[i]); TFE_Paths::addSingleFilePath(lfdName[i], lfdPath); } + + // Load external data overrides + char jsonPath[TFE_MAX_PATH]; + + sprintf(jsonPath, "%s%s", modPath, "projectiles.json"); + if (FileUtil::exists(jsonPath)) + { + FileStream file; + if (!file.open(jsonPath, FileStream::MODE_READ)) { return; } + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + + if (size > 0 && data) + { + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + TFE_ExternalData::parseExternalProjectiles(data, true); + free(data); + } + } + + sprintf(jsonPath, "%s%s", modPath, "effects.json"); + if (FileUtil::exists(jsonPath)) + { + FileStream file; + if (!file.open(jsonPath, FileStream::MODE_READ)) { return; } + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + + if (size > 0 && data) + { + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + TFE_ExternalData::parseExternalEffects(data, true); + free(data); + } + } + + sprintf(jsonPath, "%s%s", modPath, "pickups.json"); + if (FileUtil::exists(jsonPath)) + { + FileStream file; + if (!file.open(jsonPath, FileStream::MODE_READ)) { return; } + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + + if (size > 0 && data) + { + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + TFE_ExternalData::parseExternalPickups(data, true); + free(data); + } + } + + sprintf(jsonPath, "%s%s", modPath, "weapons.json"); + if (FileUtil::exists(jsonPath)) + { + FileStream file; + if (!file.open(jsonPath, FileStream::MODE_READ)) { return; } + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + + if (size > 0 && data) + { + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + TFE_ExternalData::parseExternalWeapons(data, true); + free(data); + } + } } } } @@ -1273,17 +1408,42 @@ namespace TFE_DarkForces loadMapNumFont(); inf_loadSounds(); actor_loadSounds(); - item_loadData(); - player_init(); actor_allocatePhysicsActorList(); loadCutsceneList(); - projectile_startup(); - hitEffect_startup(); - weapon_startup(); loadLangHotkeys(); TFE_ExternalData::loadCustomLogics(); + TFE_ExternalData::loadExternalPickups(); + if (!TFE_ExternalData::validateExternalPickups()) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Warning: Pickup data is incomplete. PICKUPS.JSON may have been altered. Pickups may not behave as expected."); + } + + TFE_ExternalData::loadExternalProjectiles(); + if (!TFE_ExternalData::validateExternalProjectiles()) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Warning: Projectile data is incomplete. PROJECTILES.JSON may have been altered. Projectiles may not behave as expected."); + } + + TFE_ExternalData::loadExternalEffects(); + if (!TFE_ExternalData::validateExternalEffects()) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Warning: Effect data is incomplete. EFFECTS.JSON may have been altered. Effects may not behave as expected."); + } + + TFE_ExternalData::loadExternalWeapons(); + if (!TFE_ExternalData::validateExternalWeapons()) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Warning: Weapon data is incomplete. WEAPONS.JSON may have been altered. Weapons may not behave as expected."); + } + + projectile_startup(); + hitEffect_startup(); + weapon_startup(); + item_loadData(); + player_init(); + FilePath filePath; TFE_Paths::getFilePath("swfont1.fnt", &filePath); s_sharedState.swFont1 = font_load(&filePath); @@ -1364,18 +1524,7 @@ namespace TFE_DarkForces SERIALIZE(SaveVersionInit, s_runGameState.argCount, 0); for (s32 i = 0; i < s_runGameState.argCount; i++) { - u32 length = 0; - if (serialization_getMode() == SMODE_WRITE) - { - length = (u32)strlen(s_runGameState.args[i]); - } - SERIALIZE(SaveVersionInit, length, 0); - if (serialization_getMode() == SMODE_READ) - { - s_runGameState.args[i] = (char*)game_alloc(length + 1); - } - SERIALIZE_BUF(SaveVersionInit, s_runGameState.args[i], length); - s_runGameState.args[i][length] = 0; + SERIALIZE_CSTRING_GAME_ALLOC(SaveVersionInit, s_runGameState.args[i]); } SERIALIZE(SaveVersionInit, s_runGameState.cutscenesEnabled, JTRUE); @@ -1392,15 +1541,14 @@ namespace TFE_DarkForces } } - void DarkForces::serializeVersion(Stream* stream) + void serializeVersion(Stream* stream) { - SERIALIZE_VERSION(SaveVersionInit); + SERIALIZE_VERSION(SaveVersionCur); } bool DarkForces::serializeGameState(Stream* stream, const char* filename, bool writeState) { if (!stream) { return false; } - SERIALIZE_VERSION(SaveVersionInit); if (writeState && filename) { // Write the save message. @@ -1424,6 +1572,8 @@ namespace TFE_DarkForces } serializeVersion(stream); + const u32 curVersion = serialization_getVersion(); + serializeLoopState(stream, this); agent_serialize(stream); time_serialize(stream); @@ -1442,6 +1592,10 @@ namespace TFE_DarkForces pickupLogic_serializeTasks(stream); mission_serialize(stream); + // TFE - Scripting. + serialization_setVersion(curVersion); + TFE_ForceScript::serialize(stream); + if (!writeState) { agent_restartEndLevelTask(); @@ -1455,4 +1609,4 @@ namespace TFE_DarkForces } return true; } -} \ No newline at end of file +} diff --git a/TheForceEngine/TFE_DarkForces/darkForcesMain.h b/TheForceEngine/TFE_DarkForces/darkForcesMain.h index 935ead3f5..0222be4d2 100644 --- a/TheForceEngine/TFE_DarkForces/darkForcesMain.h +++ b/TheForceEngine/TFE_DarkForces/darkForcesMain.h @@ -17,8 +17,7 @@ namespace TFE_DarkForces void restartMusic() override; void exitGame() override; void loopGame() override; - bool serializeGameState(Stream* stream, const char* filename, bool writeState) override; - void serializeVersion(Stream* stream); + bool serializeGameState(Stream* stream, const char* filename, bool writeState) override; bool canSave() override; bool isPaused() override; void getLevelName(char* name) override; diff --git a/TheForceEngine/TFE_DarkForces/generator.cpp b/TheForceEngine/TFE_DarkForces/generator.cpp index e5b9878b4..d9df0407f 100644 --- a/TheForceEngine/TFE_DarkForces/generator.cpp +++ b/TheForceEngine/TFE_DarkForces/generator.cpp @@ -41,7 +41,7 @@ namespace TFE_DarkForces Wax* wax; JBool active; - string logicName; // JK: added to store a custom logic name + char logicName[64]; // JK: added to store a custom logic name }; void generatorTaskFunc(MessageType msg) @@ -108,8 +108,6 @@ namespace TFE_DarkForces assert(gen->entities && allocator_validate(gen->entities)); - TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); - fixed16_16 dy = TFE_Jedi::abs(s_playerObject->posWS.y - spawn->posWS.y); fixed16_16 dist = dy + distApprox(spawn->posWS.x, spawn->posWS.z, s_playerObject->posWS.x, s_playerObject->posWS.z); if (dist >= gen->minDist && dist <= gen->maxDist && !actor_canSeeObject(spawn, s_playerObject)) @@ -118,8 +116,8 @@ namespace TFE_DarkForces // Search the externally defined logics for a match TFE_ExternalData::CustomActorLogic* customLogic; - customLogic = tryFindCustomActorLogic(gen->logicName.c_str()); - if (customLogic && gameSettings->df_jsonAiLogics) + customLogic = tryFindCustomActorLogic(gen->logicName); + if (customLogic && TFE_Settings::jsonAiLogics()) { obj_setCustomActorLogic(spawn, customLogic); } @@ -236,7 +234,7 @@ namespace TFE_DarkForces memset(generator, 0, sizeof(Generator)); generator->type = genType; - generator->logicName = string(logicName); + strncpy(generator->logicName, logicName, 63); generator->active = 1; generator->delay = 0; @@ -363,5 +361,14 @@ namespace TFE_DarkForces SERIALIZE(ObjState_InitVersion, gen->wanderTime, 0); serialization_serializeWaxPtr(stream, ObjState_InitVersion, gen->wax); SERIALIZE(ObjState_InitVersion, gen->active, 0); + + u32 len = 0; + if (serialization_getMode() == SMODE_WRITE) + { + len = (u32)strlen(gen->logicName); + } + SERIALIZE(ObjState_CustomLogics, len, 0); + SERIALIZE_BUF(ObjState_CustomLogics, gen->logicName, len); + gen->logicName[len] = 0; } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/hitEffect.cpp b/TheForceEngine/TFE_DarkForces/hitEffect.cpp index 56d89a663..f74a15eca 100644 --- a/TheForceEngine/TFE_DarkForces/hitEffect.cpp +++ b/TheForceEngine/TFE_DarkForces/hitEffect.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace TFE_Jedi; @@ -40,6 +41,7 @@ namespace TFE_DarkForces vec3_fixed s_explodePos; EffectData* s_curEffectData = nullptr; + EffectData setEffectData(HitEffectID type, TFE_ExternalData::ExternalEffect* extEffects); void hitEffectWakeupFunc(SecObject* obj); void hitEffectExplodeFunc(SecObject* obj); void hitEffectTaskFunc(MessageType msg); @@ -54,192 +56,44 @@ namespace TFE_DarkForces void hitEffect_startup() { - // TODO: Move Hit Effect data to an external file instead of hardcoding here. - s_effectData[HEFFECT_SMALL_EXP] = - { - HEFFECT_SMALL_EXP, // type - TFE_Sprite_Jedi::getWax("exptiny.wax", POOL_GAME), - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(40), // wakeupRange - sound_load("ex-tiny1.voc", SOUND_PRIORITY_LOW0), // soundEffect - }; - s_effectData[HEFFECT_THERMDET_EXP] = - { - HEFFECT_THERMDET_EXP, // type - TFE_Sprite_Jedi::getWax("detexp.wax", POOL_GAME), - FIXED(50), // force - FIXED(60), // damage - FIXED(30), // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-small.voc", SOUND_PRIORITY_LOW0), // soundEffect - }; - s_effectData[HEFFECT_PLASMA_EXP] = - { - HEFFECT_PLASMA_EXP, // type - TFE_Sprite_Jedi::getWax("emisexp.wax", POOL_GAME), - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(40), // wakeupRange - sound_load("ex-tiny1.voc", SOUND_PRIORITY_LOW0), // soundEffect - }; - s_effectData[HEFFECT_MORTAR_EXP] = - { - HEFFECT_MORTAR_EXP, // type - TFE_Sprite_Jedi::getWax("mortexp.wax", POOL_GAME), - FIXED(35), // force - FIXED(50), // damage - FIXED(40), // explosiveRange - FIXED(60), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_CONCUSSION] = - { - HEFFECT_CONCUSSION, // type - TFE_Sprite_Jedi::getWax("concexp.wax", POOL_GAME), - FIXED(30), // force - FIXED(30), // damage - FIXED(25), // explosiveRange - FIXED(60), // wakeupRange - sound_load("ex-lrg1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_CONCUSSION2] = - { - HEFFECT_CONCUSSION2, // type - nullptr, // spriteData - FIXED(30), // force - FIXED(30), // damage - FIXED(25), // explosiveRange - FIXED(60), // wakeupRange - sound_load("ex-lrg1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_MISSILE_EXP] = - { - HEFFECT_MISSILE_EXP, // type - TFE_Sprite_Jedi::getWax("missexp.wax", POOL_GAME), - FIXED(70), // force - FIXED(70), // damage - FIXED(40), // explosiveRange - FIXED(70), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_MISSILE_WEAK] = - { - HEFFECT_MISSILE_WEAK, // type - TFE_Sprite_Jedi::getWax("missexp.wax", POOL_GAME), - FIXED(50), // force - FIXED(25), // damage - FIXED(40), // explosiveRange - FIXED(70), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_PUNCH] = - { - HEFFECT_PUNCH, // type - nullptr, // spriteData - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(10), // wakeupRange - sound_load("punch.voc", SOUND_PRIORITY_LOW1), // soundEffect - }; - s_effectData[HEFFECT_CANNON_EXP] = - { - HEFFECT_CANNON_EXP, // type - TFE_Sprite_Jedi::getWax("plasexp.wax", POOL_GAME), - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_LOW0), // soundEffect - }; - s_effectData[HEFFECT_REPEATER_EXP] = - { - HEFFECT_REPEATER_EXP, // type - TFE_Sprite_Jedi::getWax("bullexp.wax", POOL_GAME), - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-tiny1.voc", SOUND_PRIORITY_LOW0), // soundEffect - }; - s_effectData[HEFFECT_LARGE_EXP] = - { - HEFFECT_LARGE_EXP, // type - TFE_Sprite_Jedi::getWax("mineexp.wax", POOL_GAME), - FIXED(80), // force - FIXED(90), // damage - FIXED(45), // explosiveRange - FIXED(90), // wakeupRange - sound_load("ex-lrg1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_EXP_BARREL] = - { - HEFFECT_EXP_BARREL, // type - nullptr, // spriteData - FIXED(120), // force - FIXED(60), // damage - FIXED(40), // explosiveRange - FIXED(60), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_MED3), // soundEffect - }; - s_effectData[HEFFECT_EXP_INVIS] = - { - HEFFECT_EXP_INVIS, // type - nullptr, // spriteData - FIXED(60), // force - FIXED(40), // damage - FIXED(20), // explosiveRange - FIXED(60), // wakeupRange - 0, // soundEffect - }; - s_effectData[HEFFECT_SPLASH] = - { - HEFFECT_SPLASH, // type - TFE_Sprite_Jedi::getWax("splash.wax", POOL_GAME), - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(40), // wakeupRange - sound_load("swim-in.voc", SOUND_PRIORITY_LOW3), // soundEffect - }; + // TFE: Effect data is now defined externally. These were hardcoded in vanilla DF. + TFE_ExternalData::ExternalEffect* externalEffects = TFE_ExternalData::getExternalEffects(); - s_genExplosion = TFE_Sprite_Jedi::getWax("genexp.wax", POOL_GAME); - // Different types of explosions that use the above animation. - s_effectData[HEFFECT_EXP_35] = - { - HEFFECT_EXP_35, // type - s_genExplosion, // spriteData - FIXED(30), // force - FIXED(35), // damage - FIXED(30), // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_EXP_NO_DMG] = - { - HEFFECT_EXP_NO_DMG, // type - s_genExplosion, // spriteData - 0, // force - 0, // damage - 0, // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect - }; - s_effectData[HEFFECT_EXP_25] = + s_effectData[HEFFECT_SMALL_EXP] = setEffectData(HEFFECT_SMALL_EXP, externalEffects); + s_effectData[HEFFECT_THERMDET_EXP] = setEffectData(HEFFECT_THERMDET_EXP, externalEffects); + s_effectData[HEFFECT_PLASMA_EXP] = setEffectData(HEFFECT_PLASMA_EXP, externalEffects); + s_effectData[HEFFECT_MORTAR_EXP] = setEffectData(HEFFECT_MORTAR_EXP, externalEffects); + s_effectData[HEFFECT_CONCUSSION] = setEffectData(HEFFECT_CONCUSSION, externalEffects); + s_effectData[HEFFECT_CONCUSSION2] = setEffectData(HEFFECT_CONCUSSION2, externalEffects); + s_effectData[HEFFECT_MISSILE_EXP] = setEffectData(HEFFECT_MISSILE_EXP, externalEffects); + s_effectData[HEFFECT_MISSILE_WEAK] = setEffectData(HEFFECT_MISSILE_WEAK, externalEffects); + s_effectData[HEFFECT_PUNCH] = setEffectData(HEFFECT_PUNCH, externalEffects); + s_effectData[HEFFECT_CANNON_EXP] = setEffectData(HEFFECT_CANNON_EXP, externalEffects); + s_effectData[HEFFECT_REPEATER_EXP] = setEffectData(HEFFECT_REPEATER_EXP, externalEffects); + s_effectData[HEFFECT_LARGE_EXP] = setEffectData(HEFFECT_LARGE_EXP, externalEffects); + s_effectData[HEFFECT_EXP_BARREL] = setEffectData(HEFFECT_EXP_BARREL, externalEffects); + s_effectData[HEFFECT_EXP_INVIS] = setEffectData(HEFFECT_EXP_INVIS, externalEffects); + s_effectData[HEFFECT_SPLASH] = setEffectData(HEFFECT_SPLASH, externalEffects); + s_effectData[HEFFECT_EXP_35] = setEffectData(HEFFECT_EXP_35, externalEffects); + s_effectData[HEFFECT_EXP_NO_DMG] = setEffectData(HEFFECT_EXP_NO_DMG, externalEffects); + s_effectData[HEFFECT_EXP_25] = setEffectData(HEFFECT_EXP_25, externalEffects); + } + + // TFE: Set up effect from external data. + EffectData setEffectData(HitEffectID type, TFE_ExternalData::ExternalEffect* extEffects) + { + return { - HEFFECT_EXP_25, // type - s_genExplosion, // spriteData - FIXED(20), // force - FIXED(25), // damage - FIXED(20), // explosiveRange - FIXED(50), // wakeupRange - sound_load("ex-med1.voc", SOUND_PRIORITY_HIGH2), // soundEffect + type, // type + TFE_Sprite_Jedi::getWax(extEffects[type].wax, POOL_GAME), // spriteData + FIXED(extEffects[type].force), // force + FIXED(extEffects[type].damage), // damage + FIXED(extEffects[type].explosiveRange), // explosiveRange + FIXED(extEffects[type].wakeupRange), // wakeupRange + sound_load(extEffects[type].soundEffect, SoundPriority(extEffects[type].soundPriority)), // soundEffect & priority }; } - + void spawnHitEffect(HitEffectID hitEffectId, RSector* sector, vec3_fixed pos, SecObject* excludeObj) { if (hitEffectId != HEFFECT_NONE) @@ -276,7 +130,7 @@ namespace TFE_DarkForces SERIALIZE(SaveVersionInit, hasTask, 0); if (hasTask && s_hitEffectTask) { - task_serializeState(stream, s_hitEffectTask); + task_serializeState(stream, s_hitEffectTask, nullptr, nullptr, true/*resetIP*/); } } diff --git a/TheForceEngine/TFE_DarkForces/hud.cpp b/TheForceEngine/TFE_DarkForces/hud.cpp index dbbc7a22b..af8079a20 100644 --- a/TheForceEngine/TFE_DarkForces/hud.cpp +++ b/TheForceEngine/TFE_DarkForces/hud.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #define TFE_CONVERT_CAPS 0 #if TFE_CONVERT_CAPS @@ -527,27 +526,6 @@ namespace TFE_DarkForces } offscreenBuffer_drawTexture(s_cachedHudRight, s_hudLightOff, 19, 0); } - - string hud_getDataStr() - { - fixed16_16 x, z; - getCameraXZ(&x, &z); - char* dataStr = new char[64]; - s32 xPos = floor16(x); - s32 yPos = -fixed16ToFloat(s_playerEye->posWS.y); - s32 zPos = floor16(z); - s32 h = fixed16ToFloat(s_playerEye->worldHeight); - s32 s = s_secretsPercent; - angle14_16 y, r, p; - y = s_playerEye->yaw; - r = s_playerEye->roll; - p = s_playerEye->pitch; - - string format = "X:%04d Y:%.1f Z:%04d H:%.1f S:%d"; - sprintf((char*)dataStr, format.c_str(), xPos, yPos, zPos, h, s); - std::string result = string(dataStr); - return result; - } void hud_drawMessage(u8* framebuffer) { @@ -564,11 +542,13 @@ namespace TFE_DarkForces if (s_showData && s_playerEye) { + fixed16_16 x, z; + getCameraXZ(&x, &z); + s32 xOffset = floor16(div16(intToFixed16(vfb_getWidescreenOffset()), vfb_getXScale())); + u8 dataStr[64]; - string result = TFE_DarkForces::hud_getDataStr(); - std::copy(result.begin(), result.end(), dataStr); - dataStr[result.size()] = '\0'; + sprintf((char*)dataStr, "X:%04d Y:%.1f Z:%04d H:%.1f S:%d%%", floor16(x), -fixed16ToFloat(s_playerEye->posWS.y), floor16(z), fixed16ToFloat(s_playerEye->worldHeight), s_secretsPercent); displayHudMessage(s_hudFont, (DrawRect*)vfb_getScreenRect(VFB_RECT_UI), 164 + xOffset, 10, dataStr, framebuffer); } } @@ -731,7 +711,7 @@ namespace TFE_DarkForces s_hudWorkPalette[3] = 55; s_hudWorkPalette[4] = 55; - strcpy(shieldStr, "200"); + sprintf(shieldStr, "%03d", s_shieldsMax); } else { @@ -1009,7 +989,7 @@ namespace TFE_DarkForces s_hudWorkPalette[3] = 55; s_hudWorkPalette[4] = 55; - strcpy(shieldStr, "200"); + sprintf(shieldStr, "%03d", s_shieldsMax); } else { diff --git a/TheForceEngine/TFE_DarkForces/hud.h b/TheForceEngine/TFE_DarkForces/hud.h index 269df5f01..f48ae55e4 100644 --- a/TheForceEngine/TFE_DarkForces/hud.h +++ b/TheForceEngine/TFE_DarkForces/hud.h @@ -18,7 +18,6 @@ namespace TFE_DarkForces void hud_sendTextMessage(s32 msgId); void hud_sendTextMessage(const char* msg, s32 priority, bool skipPriority=true); void hud_clearMessage(); - std::string hud_getDataStr(); void hud_loadGameMessages(); void hud_loadGraphics(); diff --git a/TheForceEngine/TFE_DarkForces/item.cpp b/TheForceEngine/TFE_DarkForces/item.cpp index 9b1f7be36..5c56861a9 100644 --- a/TheForceEngine/TFE_DarkForces/item.cpp +++ b/TheForceEngine/TFE_DarkForces/item.cpp @@ -4,6 +4,7 @@ #include "pickup.h" #include "animLogic.h" #include +#include using namespace TFE_Jedi; @@ -12,7 +13,8 @@ namespace TFE_DarkForces /////////////////////////////////////////// // Constants /////////////////////////////////////////// - // TODO: Move into data file for TFE, rather than hardcoding here. + + /* TFE: Moved to external data static const char* c_itemResoure[ITEM_COUNT] = { "IDPLANS.WAX", // ITEM_PLANS @@ -61,14 +63,14 @@ namespace TFE_DarkForces "IMEDKIT.FME", // ITEM_MEDKIT "IPILE.FME", // ITEM_PILE "ITEM10.WAX", // ITEM_UNUSED - }; + }; */ /////////////////////////////////////////// // Shared State /////////////////////////////////////////// SoundSourceId s_powerupPickupSnd; - SoundSourceId s_invItemPickupSnd; - SoundSourceId s_wpnPickupSnd; + SoundSourceId s_objectivePickupSnd; + SoundSourceId s_itemPickupSnd; ItemData s_itemData[ITEM_COUNT]; /////////////////////////////////////////// @@ -77,22 +79,26 @@ namespace TFE_DarkForces void item_loadData() { s_powerupPickupSnd = sound_load("bonus.voc", SOUND_PRIORITY_HIGH4); - s_invItemPickupSnd = sound_load("complete.voc", SOUND_PRIORITY_HIGH5); - s_wpnPickupSnd = sound_load("key.voc", SOUND_PRIORITY_MED5); + s_objectivePickupSnd = sound_load("complete.voc", SOUND_PRIORITY_HIGH5); + s_itemPickupSnd = sound_load("key.voc", SOUND_PRIORITY_MED5); + TFE_ExternalData::ExternalPickup* externalPickups = TFE_ExternalData::getExternalPickups(); + char ext[16]; for (s32 i = 0; i < ITEM_COUNT; i++) { - const char* item = c_itemResoure[i]; - if (strstr(item, ".WAX")) + const char* item = externalPickups[i].asset; + FileUtil::getFileExtension(item, ext); + if (strcasecmp(ext, "WAX") == 0) { s_itemData[i].wax = TFE_Sprite_Jedi::getWax(item, POOL_GAME); s_itemData[i].isWax = JTRUE; } - else + else if (strcasecmp(ext, "FME") == 0) { s_itemData[i].frame = TFE_Sprite_Jedi::getFrame(item, POOL_GAME); s_itemData[i].isWax = JFALSE; } + // TODO: should we also add support for 3DO dropitems?? } } @@ -108,6 +114,7 @@ namespace TFE_DarkForces { frame_setData(newObj, s_itemData[itemId].frame); } + // TODO: should we also add support for 3DO dropitems?? obj_createPickup(newObj, itemId); if (s_itemData[itemId].isWax) diff --git a/TheForceEngine/TFE_DarkForces/item.h b/TheForceEngine/TFE_DarkForces/item.h index f5cd42579..0acee2c1c 100644 --- a/TheForceEngine/TFE_DarkForces/item.h +++ b/TheForceEngine/TFE_DarkForces/item.h @@ -67,8 +67,8 @@ namespace TFE_DarkForces ITYPE_OBJECTIVE= 1, // Mission objective item. ITYPE_WEAPON = 2, // Weapons ITYPE_AMMO = 4, // Pickups that refill ammo, health, shields, etc. - ITYPE_KEY_ITEM = 8, // Keys and usable inventory items. - ITYPE_INV_ITEM = 16, // Non-usable Inventory Items + ITYPE_USABLE = 8, // Keys and usable inventory items. + ITYPE_CODEKEY = 16, // Code keys (Non-usable Inventory Items) ITYPE_POWERUP = 32, // Powerups & Extra lives. ITYPE_SPECIAL = 64, // Special - only a single item fits in this type (ITEM_PILE). }; @@ -84,8 +84,8 @@ namespace TFE_DarkForces }; extern SoundSourceId s_powerupPickupSnd; - extern SoundSourceId s_invItemPickupSnd; - extern SoundSourceId s_wpnPickupSnd; + extern SoundSourceId s_objectivePickupSnd; + extern SoundSourceId s_itemPickupSnd; extern ItemData s_itemData[ITEM_COUNT]; void item_loadData(); diff --git a/TheForceEngine/TFE_DarkForces/logic.cpp b/TheForceEngine/TFE_DarkForces/logic.cpp index 91cf1d0e6..675c86558 100644 --- a/TheForceEngine/TFE_DarkForces/logic.cpp +++ b/TheForceEngine/TFE_DarkForces/logic.cpp @@ -110,8 +110,6 @@ namespace TFE_DarkForces JBool object_parseSeq(SecObject* obj, TFE_Parser* parser, size_t* bufferPos) { - TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); - LogicSetupFunc setupFunc = nullptr; const char* line = parser->readLine(*bufferPos); @@ -133,12 +131,11 @@ namespace TFE_DarkForces KEYWORD logicId = getKeywordIndex(s_objSeqArg1); // First, search the externally defined logics for a match (if the setting is enabled) - TFE_Settings_Game* gameSettings = TFE_Settings::getGameSettings(); TFE_ExternalData::CustomActorLogic* customLogic = (logicId != KW_PLAYER) ? tryFindCustomActorLogic(s_objSeqArg1) : nullptr; // do not allow "LOGIC: PLAYER" to be overridden !! - if (gameSettings->df_jsonAiLogics && customLogic) + if (TFE_Settings::jsonAiLogics() && customLogic) { newLogic = obj_setCustomActorLogic(obj, customLogic); setupFunc = nullptr; @@ -186,7 +183,7 @@ namespace TFE_DarkForces obj_createPickup(obj, itemId); setupFunc = nullptr; } - else if (gameSettings->df_enableUnusedItem && strcasecmp(s_objSeqArg1, "ITEM10") == 0) + else if (TFE_Settings::enableUnusedItem() && strcasecmp(s_objSeqArg1, "ITEM10") == 0) { obj_createPickup(obj, ITEM_UNUSED); setupFunc = nullptr; @@ -480,7 +477,7 @@ namespace TFE_DarkForces TFE_ExternalData::CustomActorLogic* tryFindCustomActorLogic(const char* logicName) { TFE_ExternalData::ExternalLogics* externalLogics = TFE_ExternalData::getExternalLogics(); - u32 actorCount = externalLogics->actorLogics.size(); + u32 actorCount = (u32)externalLogics->actorLogics.size(); for (u32 a = 0; a < actorCount; a++) { diff --git a/TheForceEngine/TFE_DarkForces/mission.cpp b/TheForceEngine/TFE_DarkForces/mission.cpp index b411f359e..618832062 100644 --- a/TheForceEngine/TFE_DarkForces/mission.cpp +++ b/TheForceEngine/TFE_DarkForces/mission.cpp @@ -54,8 +54,6 @@ namespace TFE_DarkForces JBool s_canTeleport = JTRUE; GameMissionMode s_missionMode = MISSION_MODE_MAIN; - JBool stopRecordEscOnly = JTRUE; - TextureData* s_loadScreen = nullptr; u8 s_loadingScreenPal[768]; u8 s_levelPalette[768]; @@ -76,7 +74,6 @@ namespace TFE_DarkForces JBool s_lumMaskChanged = JFALSE; JBool s_loadingFromSave = JFALSE; - JBool s_inMission = JFALSE; s32 s_flashFxLevel = 0; s32 s_healthFxLevel = 0; @@ -409,11 +406,7 @@ namespace TFE_DarkForces s_playerTick = s_curTick; s_levelComplete = JFALSE; } - - - - s_mainTask = createTask("main task", mission_mainTaskFunc); - + s_mainTask = createTask("main task", mission_mainTaskFunc); s_invalidLevelIndex = JFALSE; s_exitLevel = JFALSE; @@ -449,7 +442,6 @@ namespace TFE_DarkForces hud_startup(JFALSE); reticle_enable(true); - s_inMission = JTRUE; } s_flatLighting = JFALSE; // Note: I am not sure why this is there but it overrides all player settings @@ -475,18 +467,11 @@ namespace TFE_DarkForces // Cleanup - shut down all tasks. task_freeAll(); - s_inMission = JFALSE; // End the task. task_end; } - JBool isMissionRunning() - { - return s_inMission; - - } - void mission_setLoadMissionTask(Task* task) { s_missionLoadTask = task; @@ -519,6 +504,7 @@ namespace TFE_DarkForces void mission_exitLevel() { + // Force the game to exit the replay modes in case you try to exit through the menu / console if (isDemoPlayback()) { endReplay(); @@ -590,6 +576,9 @@ namespace TFE_DarkForces } else if (s_missionMode == MISSION_MODE_MAIN) { + // TFE - Level Script Support. + updateLevelScript(fixed16ToFloat(s_deltaTime)); + // Dark Forces Draw. updateScreensize(); if (s_playerEye) { @@ -689,7 +678,7 @@ namespace TFE_DarkForces } } while (msg != MSG_FREE_TASK && msg != MSG_RUN_TASK); } - if (TFE_Input::isRecording() && !stopRecordEscOnly) + if (TFE_Input::isRecording()) { endRecording(); } @@ -1302,8 +1291,6 @@ namespace TFE_DarkForces void handleGeneralInput() { - //TFE_Input::playbackKeyState(); - // Early out if the player is dying. if (s_playerDying) { diff --git a/TheForceEngine/TFE_DarkForces/mission.h b/TheForceEngine/TFE_DarkForces/mission.h index 020c0a9c4..62ca59218 100644 --- a/TheForceEngine/TFE_DarkForces/mission.h +++ b/TheForceEngine/TFE_DarkForces/mission.h @@ -51,8 +51,6 @@ namespace TFE_DarkForces void cheat_toggleData(); void cheat_toggleFullBright(); void cheat_levelSkip(); - - JBool isMissionRunning(); extern JBool s_gamePaused; extern GameMissionMode s_missionMode; diff --git a/TheForceEngine/TFE_DarkForces/pickup.cpp b/TheForceEngine/TFE_DarkForces/pickup.cpp index fedac693b..5c2c9a95f 100644 --- a/TheForceEngine/TFE_DarkForces/pickup.cpp +++ b/TheForceEngine/TFE_DarkForces/pickup.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include using namespace TFE_Jedi; @@ -154,18 +155,20 @@ namespace TFE_DarkForces if (pickup->type == ITYPE_WEAPON) { s32 maxAmount = pickup->maxAmount; - if (!(*pickup->item) || *pickup->value < maxAmount) + if (!(*pickup->playerItem) || *pickup->playerAmmo < maxAmount) { - *pickup->value = pickup_addToValue(*pickup->value, pickup->amount, maxAmount); - if (*pickup->item) + *pickup->playerAmmo = pickup_addToValue(*pickup->playerAmmo, pickup->amount, maxAmount); + if (*pickup->playerItem) { + // Player already has the weapon, so show ammo message hud_sendTextMessage(pickup->msgId[1]); } else { - *pickup->item = JTRUE; + // Add the weapon + *pickup->playerItem = JTRUE; hud_sendTextMessage(pickup->msgId[0]); - s_playerInfo.newWeapon = pickup->index; + s_playerInfo.newWeapon = pickup->weaponIndex; } } else @@ -180,10 +183,10 @@ namespace TFE_DarkForces { pickedUpItem = JFALSE; } - s32 curValue = *pickup->value; + s32 curValue = *pickup->playerAmmo; if (pickedUpItem && curValue < pickup->maxAmount) { - *pickup->value = pickup_addToValue(*pickup->value, pickup->amount, pickup->maxAmount); + *pickup->playerAmmo = pickup_addToValue(*pickup->playerAmmo, pickup->amount, pickup->maxAmount); hud_sendTextMessage(pickup->msgId[0]); } else @@ -191,14 +194,16 @@ namespace TFE_DarkForces pickedUpItem = JFALSE; } } - else if (pickup->type == ITYPE_KEY_ITEM) + else if (pickup->type == ITYPE_USABLE) { - *pickup->item = JTRUE; + *pickup->playerItem = JTRUE; hud_sendTextMessage(pickup->msgId[0]); - if (pickup->value) + if (pickup->playerAmmo) { - *pickup->value = pickup_addToValue(*pickup->value, pickup->amount, pickup->maxAmount); + *pickup->playerAmmo = pickup_addToValue(*pickup->playerAmmo, pickup->amount, pickup->maxAmount); } + + // Wear cleats immediately if (pickup->id == ITEM_CLEATS && s_wearingCleats == 0) { s_wearingCleats = JTRUE; @@ -206,13 +211,16 @@ namespace TFE_DarkForces } else if (pickup->type == ITYPE_OBJECTIVE) { - *pickup->item = JTRUE; + *pickup->playerItem = JTRUE; hud_sendTextMessage(pickup->msgId[0]); + + // Trigger complete elevator if (s_levelState.completeSector) { message_sendToSector(s_levelState.completeSector, nullptr, 0, MSG_TRIGGER); } + // Mark objective as complete switch (pickup->id) { case ITEM_PLANS: @@ -241,9 +249,9 @@ namespace TFE_DarkForces } break; } } - else if (pickup->type == ITYPE_INV_ITEM) + else if (pickup->type == ITYPE_CODEKEY) { - *pickup->item = JTRUE; + *pickup->playerItem = JTRUE; hud_sendTextMessage(pickup->msgId[0]); } else if (pickup->type == ITYPE_POWERUP) @@ -260,11 +268,11 @@ namespace TFE_DarkForces } break; case ITEM_REVIVE: { - if (s_playerInfo.health < 100 || s_playerInfo.shields < 200) + if (s_playerInfo.health < s_healthMax || s_playerInfo.shields < s_shieldsMax) { player_revive(); // The function sets shields to 100, so set it to the proper value here. - s_playerInfo.shields = 200; + s_playerInfo.shields = s_shieldsMax; } else { @@ -310,17 +318,17 @@ namespace TFE_DarkForces pickupObj->worldWidth = 0; // Play pickup sound. - if (pickup->type == ITYPE_KEY_ITEM || pickup->type == ITYPE_POWERUP) + if (pickup->type == ITYPE_USABLE || pickup->type == ITYPE_POWERUP) { sound_play(s_powerupPickupSnd); } else if (pickup->type == ITYPE_OBJECTIVE || pickup->type == ITYPE_SPECIAL) { - sound_play(s_invItemPickupSnd); + sound_play(s_objectivePickupSnd); } else { - sound_play(s_wpnPickupSnd); + sound_play(s_itemPickupSnd); } // Initialize effect @@ -375,7 +383,75 @@ namespace TFE_DarkForces task_end; } - // TODO: Move pickup data to an external data file to avoid hardcoding. + // TFE: Pickups are set from external data instead of hardcoded as in vanilla DF + void setPickup(Pickup*& pickup, SecObject* obj, ItemId itemId, TFE_ExternalData::ExternalPickup* externalPickups) + { + // itemId will correspond to position in externalPickups array + pickup->type = ItemType(externalPickups[itemId].type); + pickup->weaponIndex = externalPickups[itemId].weaponIndex; + pickup->playerItem = externalPickups[itemId].playerItem; + pickup->playerAmmo = externalPickups[itemId].playerAmmo; + pickup->amount = pickup->playerAmmo == s_playerBatteryPower + ? s32(externalPickups[itemId].amount / 100.0 * 2 * ONE_16) // convert battery from a percentage + : externalPickups[itemId].amount; + + pickup->msgId[0] = externalPickups[itemId].message1; + pickup->msgId[1] = externalPickups[itemId].message2; + + if (externalPickups[itemId].fullBright) + { + obj->flags |= OBJ_FLAG_FULLBRIGHT; + } + + if (externalPickups[itemId].noRemove) + { + obj->flags |= OBJ_FLAG_NO_REMOVE; + } + + // Set max amounts based on ammo type + TFE_ExternalData::MaxAmounts* maxAmounts = TFE_ExternalData::getMaxAmounts(); + if (externalPickups[itemId].playerAmmo == s_playerAmmoEnergy) + { + pickup->maxAmount = maxAmounts->ammoEnergyMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoPower) + { + pickup->maxAmount = maxAmounts->ammoPowerMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoPlasma) + { + pickup->maxAmount = maxAmounts->ammoPlasmaMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoShell) + { + pickup->maxAmount = maxAmounts->ammoShellMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoDetonators) + { + pickup->maxAmount = maxAmounts->ammoDetonatorMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoMines) + { + pickup->maxAmount = maxAmounts->ammoMineMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerAmmoMissiles) + { + pickup->maxAmount = maxAmounts->ammoMissileMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerShields) + { + pickup->maxAmount = maxAmounts->shieldsMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerHealth) + { + pickup->maxAmount = maxAmounts->healthMax; + } + else if (externalPickups[itemId].playerAmmo == s_playerBatteryPower) + { + pickup->maxAmount = maxAmounts->batteryPowerMax; + } + } + Logic* obj_createPickup(SecObject* obj, ItemId id) { Pickup* pickup = (Pickup*)level_alloc(sizeof(Pickup)); @@ -386,388 +462,15 @@ namespace TFE_DarkForces // Setup the pickup based on the ItemId. pickup->id = id; - pickup->index = -1; - pickup->item = nullptr; - pickup->value = nullptr; + pickup->weaponIndex = -1; + pickup->playerItem = nullptr; + pickup->playerAmmo = nullptr; pickup->amount = 0; pickup->msgId[0] = -1; pickup->msgId[1] = -1; + pickup->maxAmount = 999; - switch (id) - { - // MISSION ITEMS - case ITEM_PLANS: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemPlans; - pickup->msgId[0] = 400; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_PHRIK: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemPhrik; - pickup->msgId[0] = 401; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_NAVA: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemNava; - pickup->msgId[0] = 402; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_DT_WEAPON: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemDtWeapon; - pickup->msgId[0] = 405; - - obj->flags |= OBJ_FLAG_MISSION; - } break; - case ITEM_DATATAPE: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemDatatape; - pickup->msgId[0] = 406; - - obj->flags |= OBJ_FLAG_MISSION; - } break; - case ITEM_UNUSED: - { - pickup->type = ITYPE_OBJECTIVE; - pickup->item = &s_playerInfo.itemUnused; - pickup->msgId[0] = 403; - - obj->flags |= OBJ_FLAG_MISSION; - } break; - // WEAPONS - case ITEM_RIFLE: - { - pickup->index = 2; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemRifle; - pickup->value = &s_playerInfo.ammoEnergy; - pickup->amount = 15; - pickup->msgId[0] = 100; - pickup->msgId[1] = 101; - pickup->maxAmount = 500; - } break; - case ITEM_AUTOGUN: - { - pickup->index = 4; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemAutogun; - pickup->value = &s_playerInfo.ammoPower; - pickup->amount = 30; - pickup->msgId[0] = 103; - pickup->msgId[1] = 104; - pickup->maxAmount = 500; - } break; - case ITEM_MORTAR: - { - pickup->index = 6; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemMortar; - pickup->value = &s_playerInfo.ammoShell; - pickup->amount = 3; - pickup->msgId[0] = 105; - pickup->msgId[1] = 106; - pickup->maxAmount = 50; - } break; - case ITEM_FUSION: - { - pickup->index = 5; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemFusion; - pickup->value = &s_playerInfo.ammoPower; - pickup->amount = 50; - pickup->msgId[0] = 107; - pickup->msgId[1] = 108; - pickup->maxAmount = 500; - } break; - case ITEM_CONCUSSION: - { - pickup->index = 8; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemConcussion; - pickup->value = &s_playerInfo.ammoPower; - pickup->amount = 100; - pickup->msgId[0] = 110; - pickup->msgId[1] = 111; - pickup->maxAmount = 500; - } break; - case ITEM_CANNON: - { - pickup->index = 9; - pickup->type = ITYPE_WEAPON; - pickup->item = &s_playerInfo.itemCannon; - pickup->value = &s_playerInfo.ammoPlasma; - pickup->amount = 30; - pickup->msgId[0] = 112; - pickup->msgId[1] = 113; - pickup->maxAmount = 400; - } break; - // AMMO - case ITEM_ENERGY: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoEnergy; - pickup->amount = 15; - pickup->msgId[0] = 200; - pickup->maxAmount = 500; - } break; - case ITEM_POWER: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoPower; - pickup->amount = 10; - pickup->msgId[0] = 201; - pickup->maxAmount = 500; - } break; - case ITEM_PLASMA: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoPlasma; - pickup->amount = 20; - pickup->msgId[0] = 202; - pickup->maxAmount = 400; - } break; - case ITEM_DETONATOR: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoDetonator; - pickup->amount = 1; - pickup->msgId[0] = 203; - pickup->maxAmount = 50; - } break; - case ITEM_DETONATORS: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoDetonator; - pickup->amount = 5; - pickup->msgId[0] = 204; - pickup->maxAmount = 50; - } break; - case ITEM_SHELL: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoShell; - pickup->amount = 1; - pickup->msgId[0] = 205; - pickup->maxAmount = 50; - } break; - case ITEM_SHELLS: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoShell; - pickup->amount = 5; - pickup->msgId[0] = 206; - pickup->maxAmount = 50; - } break; - case ITEM_MINE: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoMine; - pickup->amount = 1; - pickup->msgId[0] = 207; - pickup->maxAmount = 30; - } break; - case ITEM_MINES: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoMine; - pickup->amount = 5; - pickup->msgId[0] = 208; - pickup->maxAmount = 30; - } break; - case ITEM_MISSILE: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoMissile; - pickup->amount = 1; - pickup->msgId[0] = 209; - pickup->maxAmount = 20; - } break; - case ITEM_MISSILES: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.ammoMissile; - pickup->amount = 5; - pickup->msgId[0] = 210; - pickup->maxAmount = 20; - } break; - // PICKUPS & KEYS - case ITEM_SHIELD: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.shields; - pickup->amount = 20; - pickup->msgId[0] = 114; - pickup->maxAmount = 200; - } break; - case ITEM_RED_KEY: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemRedKey; - pickup->msgId[0] = 300; - } break; - case ITEM_YELLOW_KEY: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemYellowKey; - pickup->msgId[0] = 301; - } break; - case ITEM_BLUE_KEY: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemBlueKey; - pickup->msgId[0] = 302; - } break; - case ITEM_GOGGLES: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemGoggles; - pickup->value = &s_batteryPower; - pickup->msgId[0] = 303; - pickup->amount = ONE_16; - pickup->maxAmount = 2 * ONE_16; - } break; - case ITEM_CLEATS: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemCleats; - pickup->msgId[0] = 304; - } break; - case ITEM_MASK: - { - pickup->type = ITYPE_KEY_ITEM; - pickup->item = &s_playerInfo.itemMask; - pickup->value = &s_batteryPower; - pickup->msgId[0] = 305; - pickup->amount = ONE_16; - pickup->maxAmount = 2 * ONE_16; - } break; - case ITEM_BATTERY: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_batteryPower; - pickup->msgId[0] = 211; - pickup->amount = ONE_16; - pickup->maxAmount = 2 * ONE_16; - } break; - case ITEM_CODE1: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode1; - pickup->msgId[0] = 501; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE2: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode2; - pickup->msgId[0] = 502; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE3: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode3; - pickup->msgId[0] = 503; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE4: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode4; - pickup->msgId[0] = 504; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE5: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode5; - pickup->msgId[0] = 505; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE6: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode6; - pickup->msgId[0] = 506; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE7: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode7; - pickup->msgId[0] = 507; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE8: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode8; - pickup->msgId[0] = 508; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_CODE9: - { - pickup->type = ITYPE_INV_ITEM; - pickup->item = &s_playerInfo.itemCode9; - pickup->msgId[0] = 509; - - obj->flags |= (OBJ_FLAG_FULLBRIGHT | OBJ_FLAG_MISSION); - } break; - case ITEM_INVINCIBLE: - { - pickup->type = ITYPE_POWERUP; - pickup->item = nullptr; - pickup->msgId[0] = 306; - } break; - case ITEM_SUPERCHARGE: - { - pickup->type = ITYPE_POWERUP; - pickup->item = nullptr; - pickup->msgId[0] = 307; - } break; - case ITEM_REVIVE: - { - pickup->type = ITYPE_POWERUP; - pickup->item = nullptr; - pickup->msgId[0] = 308; - } break; - case ITEM_LIFE: - { - pickup->type = ITYPE_POWERUP; - pickup->item = nullptr; - pickup->msgId[0] = 310; - } break; - case ITEM_MEDKIT: - { - pickup->type = ITYPE_AMMO; - pickup->value = &s_playerInfo.health; - pickup->amount = 20; - pickup->msgId[0] = 311; - pickup->maxAmount = 100; - } break; - case ITEM_PILE: - { - pickup->type = ITYPE_SPECIAL; - } break; - } + setPickup(pickup, obj, id, TFE_ExternalData::getExternalPickups()); return (Logic*)pickup; } @@ -788,183 +491,183 @@ namespace TFE_DarkForces // Serialization void pickupLogic_setItemValue(Pickup* pickup) { - pickup->item = nullptr; - pickup->value = nullptr; + pickup->playerItem = nullptr; + pickup->playerAmmo = nullptr; switch (pickup->id) { case ITEM_PLANS: { - pickup->item = &s_playerInfo.itemPlans; + pickup->playerItem = &s_playerInfo.itemPlans; } break; case ITEM_PHRIK: { - pickup->item = &s_playerInfo.itemPhrik; + pickup->playerItem = &s_playerInfo.itemPhrik; } break; case ITEM_NAVA: { - pickup->item = &s_playerInfo.itemNava; + pickup->playerItem = &s_playerInfo.itemNava; } break; case ITEM_DT_WEAPON: { - pickup->item = &s_playerInfo.itemDtWeapon; + pickup->playerItem = &s_playerInfo.itemDtWeapon; } break; case ITEM_DATATAPE: { - pickup->item = &s_playerInfo.itemDatatape; + pickup->playerItem = &s_playerInfo.itemDatatape; } break; case ITEM_UNUSED: { - pickup->item = &s_playerInfo.itemUnused; + pickup->playerItem = &s_playerInfo.itemUnused; } break; // WEAPONS case ITEM_RIFLE: { - pickup->item = &s_playerInfo.itemRifle; - pickup->value = &s_playerInfo.ammoEnergy; + pickup->playerItem = &s_playerInfo.itemRifle; + pickup->playerAmmo = &s_playerInfo.ammoEnergy; } break; case ITEM_AUTOGUN: { - pickup->item = &s_playerInfo.itemAutogun; - pickup->value = &s_playerInfo.ammoPower; + pickup->playerItem = &s_playerInfo.itemAutogun; + pickup->playerAmmo = &s_playerInfo.ammoPower; } break; case ITEM_MORTAR: { - pickup->item = &s_playerInfo.itemMortar; - pickup->value = &s_playerInfo.ammoShell; + pickup->playerItem = &s_playerInfo.itemMortar; + pickup->playerAmmo = &s_playerInfo.ammoShell; } break; case ITEM_FUSION: { - pickup->item = &s_playerInfo.itemFusion; - pickup->value = &s_playerInfo.ammoPower; + pickup->playerItem = &s_playerInfo.itemFusion; + pickup->playerAmmo = &s_playerInfo.ammoPower; } break; case ITEM_CONCUSSION: { - pickup->item = &s_playerInfo.itemConcussion; - pickup->value = &s_playerInfo.ammoPower; + pickup->playerItem = &s_playerInfo.itemConcussion; + pickup->playerAmmo = &s_playerInfo.ammoPower; } break; case ITEM_CANNON: { - pickup->item = &s_playerInfo.itemCannon; - pickup->value = &s_playerInfo.ammoPlasma; + pickup->playerItem = &s_playerInfo.itemCannon; + pickup->playerAmmo = &s_playerInfo.ammoPlasma; } break; case ITEM_ENERGY: { - pickup->value = &s_playerInfo.ammoEnergy; + pickup->playerAmmo = &s_playerInfo.ammoEnergy; } break; case ITEM_POWER: { - pickup->value = &s_playerInfo.ammoPower; + pickup->playerAmmo = &s_playerInfo.ammoPower; } break; case ITEM_PLASMA: { - pickup->value = &s_playerInfo.ammoPlasma; + pickup->playerAmmo = &s_playerInfo.ammoPlasma; } break; case ITEM_DETONATOR: { - pickup->value = &s_playerInfo.ammoDetonator; + pickup->playerAmmo = &s_playerInfo.ammoDetonator; } break; case ITEM_DETONATORS: { - pickup->value = &s_playerInfo.ammoDetonator; + pickup->playerAmmo = &s_playerInfo.ammoDetonator; } break; case ITEM_SHELL: { - pickup->value = &s_playerInfo.ammoShell; + pickup->playerAmmo = &s_playerInfo.ammoShell; } break; case ITEM_SHELLS: { - pickup->value = &s_playerInfo.ammoShell; + pickup->playerAmmo = &s_playerInfo.ammoShell; } break; case ITEM_MINE: { - pickup->value = &s_playerInfo.ammoMine; + pickup->playerAmmo = &s_playerInfo.ammoMine; } break; case ITEM_MINES: { - pickup->value = &s_playerInfo.ammoMine; + pickup->playerAmmo = &s_playerInfo.ammoMine; } break; case ITEM_MISSILE: { - pickup->value = &s_playerInfo.ammoMissile; + pickup->playerAmmo = &s_playerInfo.ammoMissile; } break; case ITEM_MISSILES: { - pickup->value = &s_playerInfo.ammoMissile; + pickup->playerAmmo = &s_playerInfo.ammoMissile; } break; case ITEM_SHIELD: { - pickup->value = &s_playerInfo.shields; + pickup->playerAmmo = &s_playerInfo.shields; } break; case ITEM_RED_KEY: { - pickup->item = &s_playerInfo.itemRedKey; + pickup->playerItem = &s_playerInfo.itemRedKey; } break; case ITEM_YELLOW_KEY: { - pickup->item = &s_playerInfo.itemYellowKey; + pickup->playerItem = &s_playerInfo.itemYellowKey; } break; case ITEM_BLUE_KEY: { - pickup->item = &s_playerInfo.itemBlueKey; + pickup->playerItem = &s_playerInfo.itemBlueKey; } break; case ITEM_GOGGLES: { - pickup->item = &s_playerInfo.itemGoggles; - pickup->value = &s_batteryPower; + pickup->playerItem = &s_playerInfo.itemGoggles; + pickup->playerAmmo = &s_batteryPower; } break; case ITEM_CLEATS: { - pickup->item = &s_playerInfo.itemCleats; + pickup->playerItem = &s_playerInfo.itemCleats; } break; case ITEM_MASK: { - pickup->item = &s_playerInfo.itemMask; - pickup->value = &s_batteryPower; + pickup->playerItem = &s_playerInfo.itemMask; + pickup->playerAmmo = &s_batteryPower; } break; case ITEM_BATTERY: { - pickup->value = &s_batteryPower; + pickup->playerAmmo = &s_batteryPower; } break; case ITEM_CODE1: { - pickup->item = &s_playerInfo.itemCode1; + pickup->playerItem = &s_playerInfo.itemCode1; } break; case ITEM_CODE2: { - pickup->item = &s_playerInfo.itemCode2; + pickup->playerItem = &s_playerInfo.itemCode2; } break; case ITEM_CODE3: { - pickup->item = &s_playerInfo.itemCode3; + pickup->playerItem = &s_playerInfo.itemCode3; } break; case ITEM_CODE4: { - pickup->item = &s_playerInfo.itemCode4; + pickup->playerItem = &s_playerInfo.itemCode4; } break; case ITEM_CODE5: { - pickup->item = &s_playerInfo.itemCode5; + pickup->playerItem = &s_playerInfo.itemCode5; } break; case ITEM_CODE6: { - pickup->item = &s_playerInfo.itemCode6; + pickup->playerItem = &s_playerInfo.itemCode6; } break; case ITEM_CODE7: { - pickup->item = &s_playerInfo.itemCode7; + pickup->playerItem = &s_playerInfo.itemCode7; } break; case ITEM_CODE8: { - pickup->item = &s_playerInfo.itemCode8; + pickup->playerItem = &s_playerInfo.itemCode8; } break; case ITEM_CODE9: { - pickup->item = &s_playerInfo.itemCode9; + pickup->playerItem = &s_playerInfo.itemCode9; } break; case ITEM_MEDKIT: { - pickup->value = &s_playerInfo.health; + pickup->playerAmmo = &s_playerInfo.health; } break; } } @@ -1025,7 +728,7 @@ namespace TFE_DarkForces logic = (Logic*)pickup; } SERIALIZE(ObjState_InitVersion, pickup->id, ITEM_NONE); - SERIALIZE(ObjState_InitVersion, pickup->index, 0); + SERIALIZE(ObjState_InitVersion, pickup->weaponIndex, 0); SERIALIZE(ObjState_InitVersion, pickup->type, ITYPE_NONE); // item and value need to be setup based on type. SERIALIZE(ObjState_InitVersion, pickup->amount, 0); @@ -1079,13 +782,13 @@ namespace TFE_DarkForces for (taskCtx->i = 4; taskCtx->i >= 0; taskCtx->i--) { sound_play(s_invCountdownSound); - s_playerInfo.shields = 200; + s_playerInfo.shields = s_shieldsMax; task_waitWhileIdNotZero(87); // 0.6 seconds. s_playerInfo.shields = 0xffffffff; task_waitWhileIdNotZero(87); // 0.6 seconds. } - s_playerInfo.shields = 200; + s_playerInfo.shields = s_shieldsMax; s_invincibility = 0; s_invincibilityTask = nullptr; @@ -1126,7 +829,7 @@ namespace TFE_DarkForces void pickupInvincibility() { s_invincibility = -1; - s_playerInfo.shields = JTRUE; // This seems like a bug... + s_playerInfo.shields = -1; // When set to -1 the HUD will display shields as a special colour (eg. bright yellow) // Free old invincibility task and create a new invincibility task. if (s_invincibilityTask) { @@ -1178,13 +881,13 @@ namespace TFE_DarkForces level_free(s_playerInvSaved); s_playerInvSaved = nullptr; // Clamp ammo values. - s_playerInfo.ammoEnergy = pickup_addToValue(s_playerInfo.ammoEnergy, 0, 500); - s_playerInfo.ammoPower = pickup_addToValue(s_playerInfo.ammoPower, 0, 500); - s_playerInfo.ammoPlasma = pickup_addToValue(s_playerInfo.ammoPlasma, 0, 400); - s_playerInfo.ammoDetonator = pickup_addToValue(s_playerInfo.ammoDetonator, 0, 50); - s_playerInfo.ammoShell = pickup_addToValue(s_playerInfo.ammoShell, 0, 50); - s_playerInfo.ammoMine = pickup_addToValue(s_playerInfo.ammoMine, 0, 30); - s_playerInfo.ammoMissile = pickup_addToValue(s_playerInfo.ammoMissile, 0, 20); + s_playerInfo.ammoEnergy = pickup_addToValue(s_playerInfo.ammoEnergy, 0, s_ammoEnergyMax); + s_playerInfo.ammoPower = pickup_addToValue(s_playerInfo.ammoPower, 0, s_ammoPowerMax); + s_playerInfo.ammoPlasma = pickup_addToValue(s_playerInfo.ammoPlasma, 0, s_ammoPlasmaMax); + s_playerInfo.ammoDetonator = pickup_addToValue(s_playerInfo.ammoDetonator, 0, s_ammoDetonatorMax); + s_playerInfo.ammoShell = pickup_addToValue(s_playerInfo.ammoShell, 0, s_ammoShellMax); + s_playerInfo.ammoMine = pickup_addToValue(s_playerInfo.ammoMine, 0, s_ammoMineMax); + s_playerInfo.ammoMissile = pickup_addToValue(s_playerInfo.ammoMissile, 0, s_ammoMissileMax); } } } // TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_DarkForces/pickup.h b/TheForceEngine/TFE_DarkForces/pickup.h index 787d25635..7b5bba876 100644 --- a/TheForceEngine/TFE_DarkForces/pickup.h +++ b/TheForceEngine/TFE_DarkForces/pickup.h @@ -19,10 +19,10 @@ namespace TFE_DarkForces { Logic logic; // Logic header. ItemId id; - s32 index; + s32 weaponIndex; ItemType type; - JBool* item; - s32* value; + JBool* playerItem; // Points to s_playerInfo inventory item + s32* playerAmmo; // Points to s_playerInfo ammo type, shields, or health s32 amount; s32 msgId[2]; s32 maxAmount; diff --git a/TheForceEngine/TFE_DarkForces/player.cpp b/TheForceEngine/TFE_DarkForces/player.cpp index d1dfade3e..6867bb79e 100644 --- a/TheForceEngine/TFE_DarkForces/player.cpp +++ b/TheForceEngine/TFE_DarkForces/player.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -219,7 +220,7 @@ namespace TFE_DarkForces SecObject* s_playerObject = nullptr; SecObject* s_playerEye = nullptr; vec3_fixed s_eyePos = { 0 }; // s_camX, s_camY, s_camZ in the DOS code. - angle14_32 s_pitch = 0, s_yaw = 0, s_roll = 0; + angle14_32 s_eyePitch = 0, s_eyeYaw = 0, s_eyeRoll = 0; u32 s_playerEyeFlags = OBJ_FLAG_NEEDS_TRANSFORM; Tick s_playerTick; Tick s_prevPlayerTick; @@ -235,8 +236,10 @@ namespace TFE_DarkForces angle14_32 s_camOffsetRoll = 0; angle14_32 s_playerYaw; - JBool s_itemUnknown1; // 0x282428 - JBool s_itemUnknown2; // 0x28242c + // Based on positioning in inventory, these were probably meant to represent thermal + // detonators and mines - see player_readInfo() and player_writeInfo() + JBool s_itemUnknown1; // 0x282428 + JBool s_itemUnknown2; // 0x28242c SoundSourceId s_landSplashSound; SoundSourceId s_landSolidSound; @@ -261,6 +264,30 @@ namespace TFE_DarkForces s32 s_onMovingSurface = 0; // Other s32 s_playerCrouch = 0; + + // Pointers to player ammo stores + s32* s_playerAmmoEnergy; + s32* s_playerAmmoPower; + s32* s_playerAmmoPlasma; + s32* s_playerAmmoShell; + s32* s_playerAmmoDetonators; + s32* s_playerAmmoMines; + s32* s_playerAmmoMissiles; + s32* s_playerShields; + s32* s_playerHealth; + fixed16_16* s_playerBatteryPower; + + // Maximum values for ammo, etc. + s32 s_ammoEnergyMax; + s32 s_ammoPowerMax; + s32 s_ammoShellMax; + s32 s_ammoPlasmaMax; + s32 s_ammoDetonatorMax; + s32 s_ammoMineMax; + s32 s_ammoMissileMax; + s32 s_shieldsMax; + fixed16_16 s_batteryPowerMax; + s32 s_healthMax; /////////////////////////////////////////// // Forward Declarations @@ -303,14 +330,39 @@ namespace TFE_DarkForces s_kyleScreamSoundSource = sound_load("fall.voc", SOUND_PRIORITY_MED4); s_playerShieldHitSoundSource = sound_load("shield1.voc", SOUND_PRIORITY_MED5); + s_playerAmmoEnergy = &s_playerInfo.ammoEnergy; + s_playerAmmoPower = &s_playerInfo.ammoPower; + s_playerAmmoPlasma = &s_playerInfo.ammoPlasma; + s_playerAmmoShell = &s_playerInfo.ammoShell; + s_playerAmmoDetonators = &s_playerInfo.ammoDetonator; + s_playerAmmoMines = &s_playerInfo.ammoMine; + s_playerAmmoMissiles = &s_playerInfo.ammoMissile; + s_playerShields = &s_playerInfo.shields; + s_playerHealth = &s_playerInfo.health; + s_playerBatteryPower = &s_batteryPower; + + TFE_ExternalData::MaxAmounts* maxAmounts = TFE_ExternalData::getMaxAmounts(); + s_ammoEnergyMax = maxAmounts->ammoEnergyMax; + s_ammoPowerMax = maxAmounts->ammoPowerMax; + s_ammoShellMax = maxAmounts->ammoShellMax; + s_ammoPlasmaMax = maxAmounts->ammoPlasmaMax; + s_ammoDetonatorMax = maxAmounts->ammoDetonatorMax; + s_ammoMineMax = maxAmounts->ammoMineMax; + s_ammoMissileMax = maxAmounts->ammoMissileMax; + s_shieldsMax = maxAmounts->shieldsMax; + s_batteryPowerMax = maxAmounts->batteryPowerMax; + s_healthMax = maxAmounts->healthMax; + s_playerInfo = { 0 }; // Make sure this is clear... - s_playerInfo.ammoEnergy = pickup_addToValue(0, 100, 999); - s_playerInfo.ammoPower = pickup_addToValue(0, 100, 999); - s_playerInfo.ammoPlasma = pickup_addToValue(0, 100, 999); - s_playerInfo.shields = pickup_addToValue(0, 100, 200); - s_playerInfo.health = pickup_addToValue(0, 100, 100); + s_playerInfo.ammoEnergy = pickup_addToValue(0, 100, s_ammoEnergyMax); + s_playerInfo.ammoPower = pickup_addToValue(0, 100, s_ammoPowerMax); + s_playerInfo.ammoPlasma = pickup_addToValue(0, 100, s_ammoPlasmaMax); + s_playerInfo.shields = pickup_addToValue(0, 100, s_shieldsMax); + s_playerInfo.health = pickup_addToValue(0, 100, s_healthMax); s_playerInfo.healthFract = 0; - s_batteryPower = FIXED(2); + s_batteryPower = s_batteryPowerMax; + + // Always reset player ticks on init for replay consistency s_playerTick = 0; s_prevPlayerTick = 0; s_reviveTick = 0; @@ -782,11 +834,11 @@ namespace TFE_DarkForces s_instaDeathEnabled = JFALSE; // The player will always start a level with at least 100 shields, though if they have more it carries over. - s_playerInfo.shields = max(100, s_playerInfo.shields); + s_playerInfo.shields = min(max(100, s_playerInfo.shields), s_shieldsMax); // The player starts a new level with full health and energy. - s_playerInfo.health = 100; + s_playerInfo.health = s_healthMax; s_playerInfo.healthFract = 0; - s_batteryPower = FIXED(2); + s_batteryPower = s_batteryPowerMax; s_wearingGasmask = JFALSE; s_wearingCleats = JFALSE; @@ -797,8 +849,6 @@ namespace TFE_DarkForces const char* levelName = agent_getLevelName(); TFE_System::logWrite(LOG_MSG, "Player", "Setting up level '%s'", levelName); - //resetCounter(); - // Handle custom level player overrides ModSettingLevelOverride modLevelOverride = TFE_Settings::getLevelOverrides(levelName); if (!modLevelOverride.levName.empty()) @@ -871,8 +921,8 @@ namespace TFE_DarkForces { s_playerObject = obj; obj_addLogic(obj, (Logic*)&s_playerLogic, LOGIC_PLAYER, s_playerTask, playerLogicCleanupFunc); - - // Wipe out the player logic. + + // Wipe out the player logic for replay consistency s_playerLogic.move = {}; s_playerLogic.dir = {}; @@ -915,20 +965,20 @@ namespace TFE_DarkForces s_eyePos.y = s_playerEye->posWS.y; s_eyePos.z = s_playerEye->posWS.z; - s_pitch = s_playerEye->pitch; - s_yaw = s_playerEye->yaw; - s_roll = s_playerEye->roll; + s_eyePitch = s_playerEye->pitch; + s_eyeYaw = s_playerEye->yaw; + s_eyeRoll = s_playerEye->roll; setCameraOffset(0, 0, 0); - setCameraAngleOffset(0, 0, 0); + setCameraAngleOffset(0, 0, 0); } void player_revive() { // player_revive() is called when the player respawns, which is why it sets 100 for shields here. // In the case of picking up the item, the value is then set to 200 after the function call. - s_playerInfo.shields = 100; - s_playerInfo.health = 100; + s_playerInfo.shields = min(100, s_shieldsMax); + s_playerInfo.health = s_healthMax; s_playerInfo.healthFract = 0; s_playerDying = 0; } @@ -952,10 +1002,10 @@ namespace TFE_DarkForces void giveAllWeaponsAndHealth() { - s_playerInfo.health = 100; + s_playerInfo.health = s_healthMax; s_playerInfo.healthFract = 0; s_playerDying = 0; - s_playerInfo.shields = 200; + s_playerInfo.shields = s_shieldsMax; s_playerInfo.itemPistol = JTRUE; s_playerInfo.itemRifle = JTRUE; @@ -970,54 +1020,54 @@ namespace TFE_DarkForces s_playerInfo.itemCleats = JTRUE; s_playerInfo.itemMask = JTRUE; - s_playerInfo.ammoEnergy = 500; - s_playerInfo.ammoPower = 500; - s_playerInfo.ammoDetonator = 50; - s_playerInfo.ammoShell = 50; - s_playerInfo.ammoPlasma = 400; - s_playerInfo.ammoMine = 30; - s_playerInfo.ammoMissile = 20; + s_playerInfo.ammoEnergy = s_ammoEnergyMax; + s_playerInfo.ammoPower = s_ammoPowerMax; + s_playerInfo.ammoDetonator = s_ammoDetonatorMax; + s_playerInfo.ammoShell = s_ammoShellMax; + s_playerInfo.ammoPlasma = s_ammoPlasmaMax; + s_playerInfo.ammoMine = s_ammoMineMax; + s_playerInfo.ammoMissile = s_ammoMissileMax; - s_batteryPower = FIXED(2); - weapon_fixupAnim(); + s_batteryPower = s_batteryPowerMax; + weapon_emptyAnim(); } void giveHealthAndFullAmmo() { - s_playerInfo.health = 100; + s_playerInfo.health = s_healthMax; s_playerInfo.healthFract = 0; s_playerDying = 0; - s_playerInfo.shields = 200; - s_playerInfo.ammoEnergy = 500; + s_playerInfo.shields = s_shieldsMax; + s_playerInfo.ammoEnergy = s_ammoEnergyMax; if (s_playerInfo.itemAutogun || s_playerInfo.itemFusion || s_playerInfo.itemConcussion) { - s_playerInfo.ammoPower = 500; + s_playerInfo.ammoPower = s_ammoPowerMax; } if (s_playerInfo.itemCannon) { - s_playerInfo.ammoPlasma = 400; + s_playerInfo.ammoPlasma = s_ammoPlasmaMax; } - s_playerInfo.ammoDetonator = 50; + s_playerInfo.ammoDetonator = s_ammoDetonatorMax; if (s_playerInfo.itemMortar) { - s_playerInfo.ammoShell = 50; + s_playerInfo.ammoShell = s_ammoShellMax; } - s_playerInfo.ammoMine = 30; + s_playerInfo.ammoMine = s_ammoMineMax; if (s_playerInfo.itemCannon) { - s_playerInfo.ammoMissile = 20; + s_playerInfo.ammoMissile = s_ammoMissileMax; } - s_batteryPower = FIXED(2); - weapon_fixupAnim(); + s_batteryPower = s_batteryPowerMax; + weapon_emptyAnim(); } void giveAllInventoryAndHealth() { - s_playerInfo.health = 100; + s_playerInfo.health = s_healthMax; s_playerInfo.healthFract = 0; s_playerDying = 0; - s_playerInfo.shields = 200; + s_playerInfo.shields = s_shieldsMax; s_playerInfo.itemPistol = JTRUE; s_playerInfo.itemRifle = JTRUE; // s_282428 = JTRUE; @@ -1048,16 +1098,16 @@ namespace TFE_DarkForces s_playerInfo.itemCode7 = JTRUE; s_playerInfo.itemCode8 = JTRUE; s_playerInfo.itemCode9 = JTRUE; - s_playerInfo.ammoEnergy = 500; - s_playerInfo.ammoPower = 500; - s_playerInfo.ammoDetonator = 50; - s_playerInfo.ammoShell = 50; - s_playerInfo.ammoMissile = 20; - s_playerInfo.ammoPlasma = 400; - s_playerInfo.ammoMine = 30; - s_batteryPower = FIXED(2); + s_playerInfo.ammoEnergy = s_ammoEnergyMax; + s_playerInfo.ammoPower = s_ammoPowerMax; + s_playerInfo.ammoDetonator = s_ammoDetonatorMax; + s_playerInfo.ammoShell = s_ammoShellMax; + s_playerInfo.ammoMissile = s_ammoMissileMax; + s_playerInfo.ammoPlasma = s_ammoPlasmaMax; + s_playerInfo.ammoMine = s_ammoMineMax; + s_batteryPower = s_batteryPowerMax; - weapon_fixupAnim(); + weapon_emptyAnim(); } void giveKeys() @@ -1273,13 +1323,13 @@ namespace TFE_DarkForces s_eyePos.y = s_playerEye->posWS.y + s_camOffset.y - s_playerEye->worldHeight; s_eyePos.z = s_playerEye->posWS.z + s_camOffset.z; - s_pitch = s_playerEye->pitch + s_camOffsetPitch; - s_yaw = s_playerEye->yaw + s_camOffsetYaw; - s_roll = s_playerEye->roll + s_camOffsetRoll; + s_eyePitch = s_playerEye->pitch + s_camOffsetPitch; + s_eyeYaw = s_playerEye->yaw + s_camOffsetYaw; + s_eyeRoll = s_playerEye->roll + s_camOffsetRoll; if (s_playerEye->sector) { - renderer_computeCameraTransform(s_playerEye->sector, s_pitch, s_yaw, s_eyePos.x, s_eyePos.y, s_eyePos.z); + renderer_computeCameraTransform(s_playerEye->sector, s_eyePitch, s_eyeYaw, s_eyePos.x, s_eyePos.y, s_eyePos.z); } renderer_setWorldAmbient(s_playerLight); } @@ -1328,7 +1378,7 @@ namespace TFE_DarkForces s_playerVelX += pushVel.x; s_playerUpVel2 += pushVel.y; s_playerVelZ += pushVel.z; - + if (s_invincibility || s_config.superShield) { // TODO @@ -1352,7 +1402,7 @@ namespace TFE_DarkForces s_playerVelX += mul16(force, pushDir.x); s_playerUpVel2 += mul16(force, pushDir.y); s_playerVelZ += mul16(force, pushDir.z); - + if (s_invincibility || s_config.superShield) { // Return because no damage is applied. @@ -1514,11 +1564,14 @@ namespace TFE_DarkForces s_playerObjSector = s_playerObject->sector; s_playerTick = s_curTick; s_prevPlayerTick = s_curTick; + TFE_System::logWrite(LOG_MSG, "PLAYER", "Task msg = %d", msg); while (msg != MSG_FREE_TASK) { if (msg == MSG_RUN_TASK) { + TFE_System::logWrite(LOG_MSG, "PLAYER", "Curtick = %d s_gamePaued = %d", s_curTick, s_gamePaused); + if (!s_gamePaused) { // TFE: Add pitch limit setting. @@ -1543,7 +1596,8 @@ namespace TFE_DarkForces } } } - + TFE_System::logWrite(LOG_MSG, "PLAYER", "Ending task s_playerTick = %d", s_playerTick); + s_prevPlayerTick = s_playerTick; task_yield(TASK_NO_DELAY); } @@ -1578,6 +1632,8 @@ namespace TFE_DarkForces s32 mdx, mdy; TFE_Input::getAccumulatedMouseMove(&mdx, &mdy); + + InputConfig* inputConfig = TFE_Input::inputMapping_get(); // Yaw change @@ -1622,7 +1678,6 @@ namespace TFE_DarkForces } } - if (settings->df_autorun) // TFE: Optional feature. { s_playerRun = 1; @@ -1713,7 +1768,6 @@ namespace TFE_DarkForces s_playerUpVel2 = 0; } - ////////////////////////////////////////// // Pitch and Roll controls. ////////////////////////////////////////// @@ -1824,7 +1878,6 @@ namespace TFE_DarkForces s_forwardSpd >>= airControl; s_strafeSpd >>= airControl; } - } fixed16_16 adjustForwardSpeed(fixed16_16 spd) @@ -1901,7 +1954,7 @@ namespace TFE_DarkForces // Lower friction means the player will stop sliding sooner. friction = FRICTION_CLEATS; } - else if (s_playerSector->secHeight - 1 >= 0) // In water + else if (s_playerSector->secHeight - 1 >= 0 && !s_flyMode) // In water { friction = FRICTION_DEFAULT; s_playerRun = wearingCleats; // Once you get cleats, you move faster through water. @@ -1909,10 +1962,12 @@ namespace TFE_DarkForces } } + TFE_System::logWrite(LOG_MSG, "PLAYER", "Player tick = %d prevplayerTick = %d s_playerVelX = %d s_playerVelZ = %d", s_playerTick, s_prevPlayerTick, s_playerVelX, s_playerVelZ); + // Apply friction to existing velocity. if (s_playerVelX || s_playerVelZ) { - Tick dt = s_playerTick - s_prevPlayerTick; + Tick dt = s_playerTick - s_prevPlayerTick; // Exponential friction, this is the same as vel * friction^dt for (Tick i = 0; i < dt; i++) { @@ -1959,6 +2014,7 @@ namespace TFE_DarkForces fixed16_16 angularSpd; vec3_fixed vel; inf_getMovingElevatorVelocity(elev, &vel, &angularSpd); + if (!angularSpd && secHeight > 0) { // liquid behavior - dampens velocity. @@ -2007,11 +2063,11 @@ namespace TFE_DarkForces } fixed16_16 speed = adjustForwardSpeed(s_forwardSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw, speed); - + // Add 90 degrees to get the strafe direction. speed = adjustStrafeSpeed(s_strafeSpd); computeMoveFromAngleAndSpeed(&s_playerVelX, &s_playerVelZ, player->yaw + 4095, speed); - limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); + limitVectorLength(&s_playerVelX, &s_playerVelZ, s_maxMoveDist); } // Then convert from player velocity to per-frame movement. @@ -2032,7 +2088,7 @@ namespace TFE_DarkForces s_playerSlideWall = nullptr; // Up to 4 iterations, to handle sliding on walls. if (s_noclip) - { + { if (s_playerLogic.move.x || s_playerLogic.move.y) { moved = JTRUE; @@ -2120,9 +2176,11 @@ namespace TFE_DarkForces s_playerVelX = 0; s_playerVelZ = 0; } - s_playerSector = s_colMinSector; + TFE_System::logWrite(LOG_MSG, "PLAYER", "friction Player tick = %d prevplayerTick = %d s_playerVelX = %d s_playerVelZ = %d", s_playerTick, s_prevPlayerTick, s_playerVelX, s_playerVelZ); + + if (s_externalVelX || s_externalVelZ) { fixed16_16 friction = FRICTION_DEFAULT; @@ -2156,7 +2214,7 @@ namespace TFE_DarkForces } s_playerSecMoved = JFALSE; } - + if (origSector != player->sector) { RSector* newSector = player->sector; @@ -2210,11 +2268,11 @@ namespace TFE_DarkForces { s_playerUpVel2 += gravityAccelDt; } - s_playerUpVel = s_playerUpVel2; s_playerYPos += mul16(s_playerUpVel, s_deltaTime); s_playerLogic.move.y = s_playerYPos - player->posWS.y; - player->posWS.y = s_playerYPos; + player->posWS.y = s_playerYPos; + if (s_playerYPos >= s_colCurLowestFloor && (!s_noclip || !s_flyMode)) { if (s_kyleScreamSoundId) @@ -2266,7 +2324,6 @@ namespace TFE_DarkForces fixed16_16 newUpVel = min(0, s_playerUpVel2); s_playerUpVel = newUpVel; s_playerUpVel2 = newUpVel; - } else { @@ -2305,6 +2362,7 @@ namespace TFE_DarkForces eyeToCeil = min(ONE_16, eyeToCeil); // the player eye should be clamped to 1 unit below the ceiling if possible. eyeHeight -= eyeToCeil; eyeHeight = min(PLAYER_HEIGHT, eyeHeight); + RSector* sector = player->sector; fixed16_16 minEyeDistFromFloor = (s_smallModeEnabled) ? PLAYER_SIZE_SMALL : s_minEyeDistFromFloor; secHeight = sector->secHeight; @@ -2328,7 +2386,6 @@ namespace TFE_DarkForces } player->worldHeight = minDistToFloor; } - // Make sure eye height is clamped. if (!s_noclip || !s_flyMode) { @@ -2489,8 +2546,6 @@ namespace TFE_DarkForces } } - - weapon->rollOffset = -s_playerRoll / 13; weapon->pchOffset = s_playerPitch / 64; @@ -2555,7 +2610,7 @@ namespace TFE_DarkForces } // Now take the other half away. shields = max(0, shields - halfShieldDmg); - s_playerInfo.shields = pickup_addToValue(floor16(shields), 0, 200); + s_playerInfo.shields = pickup_addToValue(floor16(shields), 0, s_shieldsMax); if (playHitSound) { sound_play(s_playerShieldHitSoundSource); @@ -2573,7 +2628,7 @@ namespace TFE_DarkForces { s_playerInfo.healthFract = 0; // We could just set the health to 0 here... - s_playerInfo.health = pickup_addToValue(0, 0, 100); + s_playerInfo.health = pickup_addToValue(0, 0, s_healthMax); if (playHitSound) { sound_play(s_playerDeathSoundSource); @@ -3236,9 +3291,9 @@ namespace TFE_DarkForces } SERIALIZE(ObjState_InitVersion, s_eyePos, defV3); - SERIALIZE(ObjState_InitVersion, s_pitch, 0); - SERIALIZE(ObjState_InitVersion, s_yaw, 0); - SERIALIZE(ObjState_InitVersion, s_roll, 0); + SERIALIZE(ObjState_InitVersion, s_eyePitch, 0); + SERIALIZE(ObjState_InitVersion, s_eyeYaw, 0); + SERIALIZE(ObjState_InitVersion, s_eyeRoll, 0); SERIALIZE(ObjState_InitVersion, s_playerEyeFlags, 0); SERIALIZE(ObjState_InitVersion, s_playerTick, 0); SERIALIZE(ObjState_InitVersion, s_prevPlayerTick, 0); @@ -3272,7 +3327,7 @@ namespace TFE_DarkForces { s_playerInfo.healthFract = 0; // We could just set the health to 0 here... - s_playerInfo.health = pickup_addToValue(0, 0, 100); + s_playerInfo.health = pickup_addToValue(0, 0, s_healthMax); if (s_gasSectorTask) { task_free(s_gasSectorTask); diff --git a/TheForceEngine/TFE_DarkForces/player.h b/TheForceEngine/TFE_DarkForces/player.h index 3be4a83b7..f300f3076 100644 --- a/TheForceEngine/TFE_DarkForces/player.h +++ b/TheForceEngine/TFE_DarkForces/player.h @@ -105,7 +105,7 @@ namespace TFE_DarkForces extern fixed16_16 s_playerHeight; extern fixed16_16 s_playerYPos; extern vec3_fixed s_eyePos; // s_camX, s_camY, s_camZ in the DOS code. - extern angle14_32 s_pitch, s_yaw, s_roll; + extern angle14_32 s_eyePitch, s_eyeYaw, s_eyeRoll; extern angle14_32 s_playerYaw; extern Tick s_playerTick; extern Tick s_prevPlayerTick; @@ -138,6 +138,28 @@ namespace TFE_DarkForces extern SoundSourceId s_kyleScreamSoundSource; extern SoundSourceId s_playerShieldHitSoundSource; + extern s32* s_playerAmmoEnergy; + extern s32* s_playerAmmoPower; + extern s32* s_playerAmmoPlasma; + extern s32* s_playerAmmoShell; + extern s32* s_playerAmmoDetonators; + extern s32* s_playerAmmoMines; + extern s32* s_playerAmmoMissiles; + extern s32* s_playerShields; + extern s32* s_playerHealth; + extern fixed16_16* s_playerBatteryPower; + + extern s32 s_ammoEnergyMax; + extern s32 s_ammoPowerMax; + extern s32 s_ammoShellMax; + extern s32 s_ammoPlasmaMax; + extern s32 s_ammoDetonatorMax; + extern s32 s_ammoMineMax; + extern s32 s_ammoMissileMax; + extern s32 s_shieldsMax; + extern fixed16_16 s_batteryPowerMax; + extern s32 s_healthMax; + void player_init(); void player_readInfo(u8* inv, s32* ammo); void player_writeInfo(u8* inv, s32* ammo); diff --git a/TheForceEngine/TFE_DarkForces/projectile.cpp b/TheForceEngine/TFE_DarkForces/projectile.cpp index 99a372959..6eaaa8f6b 100644 --- a/TheForceEngine/TFE_DarkForces/projectile.cpp +++ b/TheForceEngine/TFE_DarkForces/projectile.cpp @@ -3,6 +3,7 @@ #include "random.h" #include "player.h" #include "sound.h" +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include using namespace TFE_Jedi; @@ -40,6 +42,15 @@ namespace TFE_DarkForces ////////////////////////////////////////////////////////////// static Allocator* s_projectiles = nullptr; + // Arrays to hold projectile assets + static JediModel* s_projectileModels[PROJ_COUNT]; + static WaxFrame* s_projectileFrames[PROJ_COUNT]; + static Wax* s_projectileWaxes[PROJ_COUNT]; + + static SoundSourceId s_projectileCameraSnd[PROJ_COUNT]; + static SoundSourceId s_projectileReflectSnd[PROJ_COUNT]; + static SoundSourceId s_projectileFlightSnd[PROJ_COUNT]; + static JediModel* s_boltModel; static JediModel* s_greenBoltModel; static Wax* s_plasmaProj; @@ -98,6 +109,8 @@ namespace TFE_DarkForces ProjectileHitType landMineUpdateFunc(ProjectileLogic* logic); ProjectileHitType arcingProjectileUpdateFunc(ProjectileLogic* logic); ProjectileHitType homingMissileProjectileUpdateFunc(ProjectileLogic* logic); + + ProjectileFunc getUpdateFunc(const char* type); void projectileTaskFunc(MessageType msg); @@ -114,29 +127,58 @@ namespace TFE_DarkForces ////////////////////////////////////////////////////////////// void projectile_startup() { - s_boltModel = TFE_Model_Jedi::get("wrbolt.3do", POOL_GAME); - s_thermalDetProj = TFE_Sprite_Jedi::getFrame("wdet.fme", POOL_GAME); - s_repeaterProj = TFE_Sprite_Jedi::getFrame("bullet.fme", POOL_GAME); - s_plasmaProj = TFE_Sprite_Jedi::getWax("wemiss.wax", POOL_GAME); - s_mortarProj = TFE_Sprite_Jedi::getWax("wshell.wax", POOL_GAME); - s_landmineWpnFrame = TFE_Sprite_Jedi::getFrame("wmine.fme", POOL_GAME); - s_cannonProj = TFE_Sprite_Jedi::getWax("wplasma.wax", POOL_GAME); - s_missileProj = TFE_Sprite_Jedi::getWax("wmsl.wax", POOL_GAME); - s_landmineFrame = TFE_Sprite_Jedi::getFrame("wlmine.fme", POOL_GAME); - s_greenBoltModel = TFE_Model_Jedi::get("wgbolt.3do", POOL_GAME); - s_probeProj = TFE_Sprite_Jedi::getWax("widball.wax", POOL_GAME); - s_homingMissileProj = TFE_Sprite_Jedi::getWax("wdt3msl.wax", POOL_GAME); - s_bobafetBall = TFE_Sprite_Jedi::getWax("bobaball.wax", POOL_GAME); - - s_stdProjReflectSnd = sound_load("boltref1.voc", SOUND_PRIORITY_LOW1); - s_thermalDetReflectSnd = sound_load("thermal1.voc", SOUND_PRIORITY_LOW1); - s_plasmaReflectSnd = sound_load("bigrefl1.voc", SOUND_PRIORITY_LOW1); - s_stdProjCameraSnd = sound_load("lasrby.voc", SOUND_PRIORITY_LOW3); - s_plasmaCameraSnd = sound_load("emisby.voc", SOUND_PRIORITY_LOW3); - s_missileLoopingSnd = sound_load("rocket-1.voc", SOUND_PRIORITY_LOW3); - s_homingMissileFlightSnd = sound_load("tracker.voc", SOUND_PRIORITY_LOW3); - s_bobaBallCameraSnd = sound_load("fireball.voc", SOUND_PRIORITY_LOW3); - s_landMineTriggerSnd = sound_load("beep-10.voc", SOUND_PRIORITY_HIGH3); + // TFE: Assets are now listed externally. + TFE_ExternalData::ExternalProjectile* externalProjectiles = TFE_ExternalData::getExternalProjectiles(); + for (u32 p = 0; p < PROJ_COUNT; p++) + { + // Frame + if (strcasecmp(externalProjectiles[p].assetType, "frame") == 0) + { + s_projectileFrames[p] = TFE_Sprite_Jedi::getFrame(externalProjectiles[p].asset, POOL_GAME); + } + + // Wax + if (strcasecmp(externalProjectiles[p].assetType, "sprite") == 0) + { + s_projectileWaxes[p] = TFE_Sprite_Jedi::getWax(externalProjectiles[p].asset, POOL_GAME); + } + + // 3D model + if (strcasecmp(externalProjectiles[p].assetType, "3d") == 0) + { + s_projectileModels[p] = TFE_Model_Jedi::get(externalProjectiles[p].asset, POOL_GAME); + } + + // Sounds + s_projectileCameraSnd[p] = sound_load(externalProjectiles[p].cameraPassSound, SOUND_PRIORITY_LOW3); + s_projectileReflectSnd[p] = sound_load(externalProjectiles[p].reflectSound, SOUND_PRIORITY_LOW1); + s_projectileFlightSnd[p] = sound_load(externalProjectiles[p].flightSound, SOUND_PRIORITY_LOW3); + } + + // This is the original list of hardcoded assets. + // s_boltModel = TFE_Model_Jedi::get("wrbolt.3do", POOL_GAME); + // s_thermalDetProj = TFE_Sprite_Jedi::getFrame("wdet.fme", POOL_GAME); + // s_repeaterProj = TFE_Sprite_Jedi::getFrame("bullet.fme", POOL_GAME); + // s_plasmaProj = TFE_Sprite_Jedi::getWax("wemiss.wax", POOL_GAME); + // s_mortarProj = TFE_Sprite_Jedi::getWax("wshell.wax", POOL_GAME); + // s_landmineWpnFrame = TFE_Sprite_Jedi::getFrame("wmine.fme", POOL_GAME); + // s_cannonProj = TFE_Sprite_Jedi::getWax("wplasma.wax", POOL_GAME); + // s_missileProj = TFE_Sprite_Jedi::getWax("wmsl.wax", POOL_GAME); + // s_landmineFrame = TFE_Sprite_Jedi::getFrame("wlmine.fme", POOL_GAME); + // s_greenBoltModel = TFE_Model_Jedi::get("wgbolt.3do", POOL_GAME); + // s_probeProj = TFE_Sprite_Jedi::getWax("widball.wax", POOL_GAME); + // s_homingMissileProj = TFE_Sprite_Jedi::getWax("wdt3msl.wax", POOL_GAME); + // s_bobafetBall = TFE_Sprite_Jedi::getWax("bobaball.wax", POOL_GAME); + // + // s_stdProjReflectSnd = sound_load("boltref1.voc", SOUND_PRIORITY_LOW1); + // s_thermalDetReflectSnd = sound_load("thermal1.voc", SOUND_PRIORITY_LOW1); + // s_plasmaReflectSnd = sound_load("bigrefl1.voc", SOUND_PRIORITY_LOW1); + // s_stdProjCameraSnd = sound_load("lasrby.voc", SOUND_PRIORITY_LOW3); + // s_plasmaCameraSnd = sound_load("emisby.voc", SOUND_PRIORITY_LOW3); + // s_missileLoopingSnd = sound_load("rocket-1.voc", SOUND_PRIORITY_LOW3); + // s_homingMissileFlightSnd = sound_load("tracker.voc", SOUND_PRIORITY_LOW3); + // s_bobaBallCameraSnd = sound_load("fireball.voc", SOUND_PRIORITY_LOW3); + s_landMineTriggerSnd = sound_load("beep-10.voc", SOUND_PRIORITY_HIGH3); // still used! } void projectile_clearState() @@ -153,7 +195,93 @@ namespace TFE_DarkForces s_projectileTask = createSubTask("projectiles", projectileTaskFunc); } - // TODO: Move projectile data to an external file to avoid hardcoding it for TFE. + // TFE: Set the Projectile object from external data. These were hardcoded in vanilla DF. + void setProjectileObject(SecObject*& projObj, ProjectileType type, TFE_ExternalData::ExternalProjectile* externalProjectiles) + { + if (strcasecmp(externalProjectiles[type].assetType, "spirit") == 0) + { + spirit_setData(projObj); + } + else if (strcasecmp(externalProjectiles[type].assetType, "frame") == 0) + { + if (s_projectileFrames[type]) + { + frame_setData(projObj, s_projectileFrames[type]); + } + } + else if (strcasecmp(externalProjectiles[type].assetType, "sprite") == 0) + { + if (s_projectileWaxes[type]) + { + sprite_setData(projObj, s_projectileWaxes[type]); + obj_setSpriteAnim(projObj); // Setup the looping wax animation. + } + } + else if (strcasecmp(externalProjectiles[type].assetType, "3d") == 0) + { + if (s_projectileModels[type]) + { + obj3d_setData(projObj, s_projectileModels[type]); + } + } + else + { + spirit_setData(projObj); // default to spirit + } + + if (externalProjectiles[type].fullBright) + { + projObj->flags |= OBJ_FLAG_FULLBRIGHT; + } + + if (externalProjectiles[type].autoAim) + { + projObj->flags |= OBJ_FLAG_AIM; // this is only meaningful for homing missiles, which can be shot down + } + + if (externalProjectiles[type].movable) + { + projObj->flags |= OBJ_FLAG_MOVABLE; // thermal detonators & mines will be moved by elevators + } + + if (externalProjectiles[type].zeroWidth) + { + projObj->worldWidth = 0; + } + } + + // TFE: Set the Projectile logic from external data. These were hardcoded in vanilla DF. + void setProjectileLogic(ProjectileLogic*& projLogic, ProjectileType type, TFE_ExternalData::ExternalProjectile* externalProjectiles) + { + projLogic->type = type; + projLogic->updateFunc = getUpdateFunc(externalProjectiles[type].updateFunc); + projLogic->dmg = FIXED(externalProjectiles[type].damage); + projLogic->falloffAmt = externalProjectiles[type].falloffAmount; + projLogic->nextFalloffTick = s_curTick + externalProjectiles[type].nextFalloffTick; + projLogic->dmgFalloffDelta = externalProjectiles[type].damageFalloffDelta; + projLogic->minDmg = FIXED(externalProjectiles[type].minDamage); + + projLogic->projForce = externalProjectiles[type].force; + projLogic->speed = FIXED(externalProjectiles[type].speed); + projLogic->horzBounciness = externalProjectiles[type].horzBounciness; + projLogic->vertBounciness = externalProjectiles[type].vertBounciness; + projLogic->bounceCnt = externalProjectiles[type].bounceCount; + projLogic->reflVariation = externalProjectiles[type].reflectVariation; + projLogic->reflectEffectId = (HitEffectID)externalProjectiles[type].reflectEffectId; + projLogic->hitEffectId = (HitEffectID)externalProjectiles[type].hitEffectId;; + projLogic->duration = s_curTick + externalProjectiles[type].duration; + projLogic->homingAngleSpd = externalProjectiles[type].homingAngularSpeed; // Starting homing rate + + projLogic->cameraPassSnd = s_projectileCameraSnd[type]; + projLogic->reflectSnd = s_projectileReflectSnd[type]; + projLogic->flightSndSource = s_projectileFlightSnd[type]; + + if (externalProjectiles[type].explodeOnTimeout) + { + projLogic->flags |= PROJFLAG_EXPLODE; + } + } + Logic* createProjectile(ProjectileType type, RSector* sector, fixed16_16 x, fixed16_16 y, fixed16_16 z, SecObject* obj) { ProjectileLogic* projLogic = (ProjectileLogic*)allocator_newItem(s_projectiles); @@ -186,508 +314,112 @@ namespace TFE_DarkForces obj_addLogic(projObj, (Logic*)projLogic, LOGIC_PROJECTILE, s_projectileTask, projectileLogicCleanupFunc); + TFE_ExternalData::ExternalProjectile* externalProjectiles = TFE_ExternalData::getExternalProjectiles(); + switch (type) { case PROJ_PUNCH: { - spirit_setData(projObj); - - projLogic->type = PROJ_PUNCH; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(6); - projLogic->falloffAmt = 0; - projLogic->projForce = 1310; - projLogic->speed = FIXED(230); - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_NONE; - projLogic->duration = s_curTick; + setProjectileObject(projObj, PROJ_PUNCH, externalProjectiles); + setProjectileLogic(projLogic, PROJ_PUNCH, externalProjectiles); } break; case PROJ_PISTOL_BOLT: { - if (s_boltModel) - { - obj3d_setData(projObj, s_boltModel); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - - projLogic->type = PROJ_PISTOL_BOLT; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(10); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = ONE_16; - projLogic->dmgFalloffDelta = 14; - - projLogic->projForce = 655; - projLogic->speed = FIXED(250); - projLogic->horzBounciness = ONE_16; - projLogic->vertBounciness = ONE_16; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 14; // ~0.1 seconds - projLogic->reflectEffectId = HEFFECT_SMALL_EXP; - projLogic->hitEffectId = HEFFECT_SMALL_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds - projLogic->cameraPassSnd = s_stdProjCameraSnd; - projLogic->reflectSnd = s_stdProjReflectSnd; + setProjectileObject(projObj, PROJ_PISTOL_BOLT, externalProjectiles); + setProjectileLogic(projLogic, PROJ_PISTOL_BOLT, externalProjectiles); } break; case PROJ_RIFLE_BOLT: { - if (s_boltModel) - { - obj3d_setData(projObj, s_boltModel); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - - projLogic->type = PROJ_RIFLE_BOLT; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(10); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = 52428; // 0.8 damage - projLogic->dmgFalloffDelta = 14; - - projLogic->projForce = 655; - projLogic->speed = FIXED(250); - projLogic->horzBounciness = ONE_16; - projLogic->vertBounciness = ONE_16; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 14; // ~0.1 seconds - projLogic->reflectEffectId = HEFFECT_SMALL_EXP; - projLogic->hitEffectId = HEFFECT_SMALL_EXP; - projLogic->duration = s_curTick + 582; // ~4 seconds - projLogic->cameraPassSnd = s_stdProjCameraSnd; - projLogic->reflectSnd = s_stdProjReflectSnd; + setProjectileObject(projObj, PROJ_RIFLE_BOLT, externalProjectiles); + setProjectileLogic(projLogic, PROJ_RIFLE_BOLT, externalProjectiles); } break; case PROJ_THERMAL_DET: { - if (s_thermalDetProj) - { - frame_setData(projObj, s_thermalDetProj); - } - projObj->flags |= OBJ_FLAG_MOVABLE; - projObj->worldWidth = 0; - - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->type = PROJ_THERMAL_DET; - projLogic->updateFunc = arcingProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 6553; - projLogic->speed = FIXED(80); - projLogic->horzBounciness = 29491; // 0.45 - projLogic->vertBounciness = 58327; // 0.89 - projLogic->bounceCnt = -1; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_THERMDET_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds - projLogic->cameraPassSnd = NULL_SOUND; - projLogic->reflectSnd = s_thermalDetReflectSnd; + setProjectileObject(projObj, PROJ_THERMAL_DET, externalProjectiles); + setProjectileLogic(projLogic, PROJ_THERMAL_DET, externalProjectiles); } break; case PROJ_REPEATER: { - if (s_repeaterProj) - { - frame_setData(projObj, s_repeaterProj); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - - projLogic->type = PROJ_REPEATER; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(10); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = 19660; // 0.3 damage - projLogic->dmgFalloffDelta = 19660; // 135 seconds, this probably should have been 0.3 seconds, or 43 - - projLogic->projForce = 1966; - projLogic->speed = FIXED(270); - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 19660; // ~135 seconds, should have been ~0.3 seconds. - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_REPEATER_EXP; - projLogic->duration = s_curTick + 582; // ~4 seconds - projLogic->cameraPassSnd = s_stdProjCameraSnd; - projLogic->reflectSnd = s_stdProjReflectSnd; + setProjectileObject(projObj, PROJ_REPEATER, externalProjectiles); + setProjectileLogic(projLogic, PROJ_REPEATER, externalProjectiles); + + // dmgFalloffDelta was 19660 in the code; this probably should have been 0.3 seconds, or 43 ticks } break; case PROJ_PLASMA: { - if (s_plasmaProj) - { - sprite_setData(projObj, s_plasmaProj); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - - projLogic->type = PROJ_PLASMA; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(15); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = 39321; // 0.6 damage - projLogic->dmgFalloffDelta = 29; // 0.2 seconds - - projLogic->projForce = 3276; - projLogic->speed = FIXED(100); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 29; // ~0.2 second - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_PLASMA_EXP; - projLogic->duration = s_curTick + 1456; // ~10 seconds - projLogic->cameraPassSnd = s_plasmaCameraSnd; - projLogic->reflectSnd = s_plasmaReflectSnd; + setProjectileObject(projObj, PROJ_PLASMA, externalProjectiles); + setProjectileLogic(projLogic, PROJ_PLASMA, externalProjectiles); } break; case PROJ_MORTAR: { - if (s_mortarProj) - { - sprite_setData(projObj, s_mortarProj); - } - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - projObj->worldWidth = 0; - - projLogic->type = PROJ_MORTAR; - projLogic->updateFunc = arcingProjectileUpdateFunc; - projLogic->dmg = 0; // Damage is set to 0 for some reason. - projLogic->falloffAmt = 0; // No falloff - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 13107; - projLogic->speed = FIXED(110); - projLogic->horzBounciness = 26214; // 0.4 - projLogic->vertBounciness = 39321; // 0.6 - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_MORTAR_EXP; - projLogic->duration = s_curTick + 582; // ~4 seconds -> ~440 units + setProjectileObject(projObj, PROJ_MORTAR, externalProjectiles); + setProjectileLogic(projLogic, PROJ_MORTAR, externalProjectiles); } break; case PROJ_LAND_MINE: { - if (s_landmineWpnFrame) - { - frame_setData(projObj, s_landmineWpnFrame); - } + setProjectileObject(projObj, PROJ_LAND_MINE, externalProjectiles); projObj->entityFlags |= ETFLAG_LANDMINE_WPN; - projObj->flags |= OBJ_FLAG_MOVABLE; - projObj->worldWidth = 0; - - projLogic->type = PROJ_LAND_MINE; - projLogic->updateFunc = arcingProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 13107; - projLogic->speed = 0; - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = -1; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->hitEffectId = HEFFECT_LARGE_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds + setProjectileLogic(projLogic, PROJ_LAND_MINE, externalProjectiles); } break; case PROJ_LAND_MINE_PROX: { - if (s_landmineWpnFrame) - { - frame_setData(projObj, s_landmineWpnFrame); - } + setProjectileObject(projObj, PROJ_LAND_MINE_PROX, externalProjectiles); projObj->entityFlags |= ETFLAG_LANDMINE_WPN; - projObj->flags |= OBJ_FLAG_MOVABLE; - projObj->worldWidth = 0; - - projLogic->type = PROJ_LAND_MINE_PROX; - projLogic->updateFunc = arcingProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 13107; - projLogic->speed = 0; - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = -1; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->hitEffectId = HEFFECT_LARGE_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds + setProjectileLogic(projLogic, PROJ_LAND_MINE_PROX, externalProjectiles); } break; case PROJ_LAND_MINE_PLACED: { - if (s_landmineFrame) - { - frame_setData(projObj, s_landmineFrame); - } - projLogic->type = PROJ_LAND_MINE_PLACED; - projObj->flags |= OBJ_FLAG_MOVABLE; - projObj->worldWidth = 0; - - projLogic->updateFunc = landMineUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - projLogic->projForce = 13107; - projLogic->speed = 0; - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = -1; - projLogic->duration = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->hitEffectId = HEFFECT_LARGE_EXP; + setProjectileObject(projObj, PROJ_LAND_MINE_PLACED, externalProjectiles); + setProjectileLogic(projLogic, PROJ_LAND_MINE_PLACED, externalProjectiles); } break; case PROJ_CONCUSSION: { - spirit_setData(projObj); - projObj->worldWidth = 0; - - projLogic->type = PROJ_CONCUSSION; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 3932; - projLogic->speed = FIXED(190); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_CONCUSSION; - projLogic->duration = s_curTick + 728; // ~5 seconds + setProjectileObject(projObj, PROJ_CONCUSSION, externalProjectiles); + setProjectileLogic(projLogic, PROJ_CONCUSSION, externalProjectiles); } break; case PROJ_CANNON: { - if (s_cannonProj) - { - sprite_setData(projObj, s_cannonProj); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - - projLogic->type = PROJ_CANNON; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(30); - projLogic->falloffAmt = 0; // No falloff - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 4032; - projLogic->speed = FIXED(100); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 18; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_CANNON_EXP; - projLogic->duration = s_curTick + 582; // ~4 seconds -> ~400 units - projLogic->cameraPassSnd = s_plasmaCameraSnd; - projLogic->reflectSnd = s_plasmaReflectSnd; + setProjectileObject(projObj, PROJ_CANNON, externalProjectiles); + setProjectileLogic(projLogic, PROJ_CANNON, externalProjectiles); } break; case PROJ_MISSILE: { - if (s_missileProj) - { - sprite_setData(projObj, s_missileProj); - } - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - projObj->worldWidth = 0; - - projLogic->type = PROJ_MISSILE; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = 0; // Damage is set to 0 for some reason. - projLogic->falloffAmt = 0; // No falloff - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = ONE_16; - projLogic->speed = FIXED(74); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_MISSILE_EXP; - projLogic->duration = s_curTick + 1019; // ~7 seconds -> ~518 units - projLogic->flightSndSource = s_missileLoopingSnd; + setProjectileObject(projObj, PROJ_MISSILE, externalProjectiles); + setProjectileLogic(projLogic, PROJ_MISSILE, externalProjectiles); } break; case PROJ_TURRET_BOLT: { - if (s_greenBoltModel) - { - obj3d_setData(projObj, s_greenBoltModel); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - - projLogic->type = PROJ_TURRET_BOLT; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(17); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = ONE_16; - projLogic->dmgFalloffDelta = 14; - - projLogic->projForce = 1966; - projLogic->speed = FIXED(300); - projLogic->horzBounciness = ONE_16; - projLogic->vertBounciness = ONE_16; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 14; // ~0.1 seconds - projLogic->reflectEffectId = HEFFECT_SMALL_EXP; - projLogic->hitEffectId = HEFFECT_SMALL_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds - projLogic->cameraPassSnd = s_stdProjCameraSnd; - projLogic->reflectSnd = s_stdProjReflectSnd; + setProjectileObject(projObj, PROJ_TURRET_BOLT, externalProjectiles); + setProjectileLogic(projLogic, PROJ_TURRET_BOLT, externalProjectiles); } break; case PROJ_REMOTE_BOLT: { - if (s_greenBoltModel) - { - obj3d_setData(projObj, s_greenBoltModel); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - - projLogic->type = PROJ_REMOTE_BOLT; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = ONE_16; - projLogic->minDmg = 0; - projLogic->falloffAmt = ONE_16; - projLogic->dmgFalloffDelta = 14; - - projLogic->projForce = 655; - projLogic->speed = FIXED(300); - projLogic->horzBounciness = ONE_16; - projLogic->vertBounciness = ONE_16; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 14; // ~0.1 seconds - projLogic->reflectEffectId = HEFFECT_SMALL_EXP; - projLogic->hitEffectId = HEFFECT_SMALL_EXP; - projLogic->duration = s_curTick + 436; // ~3 seconds - projLogic->cameraPassSnd = s_stdProjCameraSnd; - projLogic->reflectSnd = s_stdProjReflectSnd; + setProjectileObject(projObj, PROJ_REMOTE_BOLT, externalProjectiles); + setProjectileLogic(projLogic, PROJ_REMOTE_BOLT, externalProjectiles); } break; case PROJ_EXP_BARREL: { - spirit_setData(projObj); - - projLogic->type = PROJ_EXP_BARREL; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = 0; - - projLogic->projForce = 0; - projLogic->speed = 0; - projLogic->horzBounciness = 0; - projLogic->vertBounciness = 0; - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->hitEffectId = HEFFECT_EXP_BARREL; - projLogic->duration = s_curTick; + setProjectileObject(projObj, PROJ_EXP_BARREL, externalProjectiles); + setProjectileLogic(projLogic, PROJ_EXP_BARREL, externalProjectiles); } break; case PROJ_HOMING_MISSILE: { - if (s_homingMissileProj) - { - sprite_setData(projObj, s_homingMissileProj); - } - obj_setSpriteAnim(projObj); - projObj->flags |= OBJ_FLAG_AIM; - - projLogic->flags |= PROJFLAG_EXPLODE; - projLogic->type = PROJ_HOMING_MISSILE; - projLogic->updateFunc = homingMissileProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = 0; - projLogic->dmgFalloffDelta = ONE_16; - - projLogic->projForce = ONE_16; - projLogic->speed = FIXED(58) / 2; // try slowing them down...; the value is correct according to the code, but they are slower in DOS. - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 0; - projLogic->homingAngleSpd = 455; // Starting homing rate = 10 degrees / second - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->flightSndSource = s_homingMissileFlightSnd; - projLogic->hitEffectId = HEFFECT_MISSILE_EXP; - projLogic->duration = s_curTick + 1456; - projLogic->reflectSnd = 0; + setProjectileObject(projObj, PROJ_HOMING_MISSILE, externalProjectiles); + setProjectileLogic(projLogic, PROJ_HOMING_MISSILE, externalProjectiles); + + // projLogic->speed = FIXED(58) / 2; + // speed will be set to 29 externally; the value is correct according to the code, but they are slower in DOS. + } break; case PROJ_PROBE_PROJ: { - if (s_probeProj) - { - sprite_setData(projObj, s_probeProj); - } - projObj->flags |= OBJ_FLAG_FULLBRIGHT; - projObj->worldWidth = 0; - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - - projLogic->type = PROJ_PROBE_PROJ; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = FIXED(10); - projLogic->minDmg = ONE_16; - projLogic->falloffAmt = ONE_16; // 1.0 damage - projLogic->dmgFalloffDelta = 14; - - projLogic->projForce = 2621; - projLogic->speed = FIXED(100); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 3; - projLogic->reflVariation = 9; - projLogic->nextFalloffTick = s_curTick + 14; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_PLASMA_EXP; - projLogic->duration = s_curTick + 1165; // ~8 seconds - projLogic->cameraPassSnd = s_plasmaCameraSnd; - projLogic->reflectSnd = s_plasmaReflectSnd; + setProjectileObject(projObj, PROJ_PROBE_PROJ, externalProjectiles); + setProjectileLogic(projLogic, PROJ_PROBE_PROJ, externalProjectiles); } break; case PROJ_BOBAFET_BALL: { - if (s_bobafetBall) - { - sprite_setData(projObj, s_bobafetBall); - } - // Setup the looping wax animation. - obj_setSpriteAnim(projObj); - projObj->worldWidth = 0; - - projLogic->type = PROJ_BOBAFET_BALL; - projLogic->updateFunc = stdProjectileUpdateFunc; - projLogic->dmg = 0; - projLogic->falloffAmt = 0; - - projLogic->projForce = ONE_16; - projLogic->speed = FIXED(90); - projLogic->horzBounciness = 58982; // 0.9 - projLogic->vertBounciness = 58982; - projLogic->bounceCnt = 0; - projLogic->reflectEffectId = HEFFECT_NONE; - projLogic->hitEffectId = HEFFECT_EXP_25; - projLogic->duration = s_curTick + 1456; - projLogic->cameraPassSnd = s_bobaBallCameraSnd; + setProjectileObject(projObj, PROJ_BOBAFET_BALL, externalProjectiles); + setProjectileLogic(projLogic, PROJ_BOBAFET_BALL, externalProjectiles); } break; } projLogic->col_speed = projLogic->speed; @@ -858,6 +590,31 @@ namespace TFE_DarkForces // Bug: allocator_release() is never called } + ProjectileFunc getUpdateFunc(const char* type) + { + if (strcasecmp(type, "standard") == 0) + { + return stdProjectileUpdateFunc; + } + + if (strcasecmp(type, "arcing") == 0) + { + return arcingProjectileUpdateFunc; + } + + if (strcasecmp(type, "landmine") == 0) + { + return landMineUpdateFunc; + } + + if (strcasecmp(type, "homing") == 0) + { + return homingMissileProjectileUpdateFunc; + } + + return stdProjectileUpdateFunc; + } + // The "standard" update function - projectiles travel in a straight line with a fixed velocity. ProjectileHitType stdProjectileUpdateFunc(ProjectileLogic* projLogic) { diff --git a/TheForceEngine/TFE_DarkForces/projectile.h b/TheForceEngine/TFE_DarkForces/projectile.h index 3b7e1aa33..f2a8ce429 100644 --- a/TheForceEngine/TFE_DarkForces/projectile.h +++ b/TheForceEngine/TFE_DarkForces/projectile.h @@ -62,7 +62,7 @@ namespace TFE_DarkForces enum ProjectileFlags { PROJFLAG_CAMERA_PASS_SOUND = FLAG_BIT(0), - PROJFLAG_EXPLODE = FLAG_BIT(1), + PROJFLAG_EXPLODE = FLAG_BIT(1), // Explodes when reaches end of life. }; struct ProjectileLogic; @@ -90,10 +90,10 @@ namespace TFE_DarkForces fixed16_16 vertBounciness; SecObject* prevColObj; SecObject* prevObj; - SecObject* excludeObj; + SecObject* excludeObj; // Object which is excluded from damage (used to prevent AI from being hurt by their own weapons) Tick duration; // How long the projectile continues to move before going out of range (and being destroyed). angle14_32 homingAngleSpd; // How quickly a homing projectile lines up in angle units / second. - s32 bounceCnt; + s32 bounceCnt; // Number of remaining bounces / reflections. Used for magseal (positive numbers) and thermal detonator secondary fire (set to -1) s32 reflVariation; SoundEffectId flightSndId; // Looping sound instance played while the projectile moves. SoundSourceId flightSndSource; // Source looping sound. diff --git a/TheForceEngine/TFE_DarkForces/random.cpp b/TheForceEngine/TFE_DarkForces/random.cpp index 5d2ca16c8..b627ec169 100644 --- a/TheForceEngine/TFE_DarkForces/random.cpp +++ b/TheForceEngine/TFE_DarkForces/random.cpp @@ -23,11 +23,8 @@ namespace TFE_DarkForces s_seed = seed; } - s32 random_next() { - //TFE_System::logWrite(LOG_MSG, "RANDOM", "SEED START: %d", s_seed); - // Shift the seed by 1 (divide by two), and // xor the top 8 bits with b10100011 (163) if the starting seed is odd. // This has the effect of increasing the size of the seed if it gets too small due to the shifts, and increasing "randomness" @@ -39,7 +36,6 @@ namespace TFE_DarkForces { s_seed = (s_seed >> 1); } - //TFE_System::logWrite(LOG_MSG, "RANDOM", "SEED END: %d", s_seed); return s32(s_seed); } @@ -49,7 +45,6 @@ namespace TFE_DarkForces s32 random(s32 value) { s32 newValue = random_next(); - //TFE_System::logWrite(LOG_MSG, "RANDOM", "NEW VALUE %d", newValue); if (newValue > value || newValue < 0) { // Note the value is cast to fixed16_16 but is not actually converted. @@ -61,7 +56,6 @@ namespace TFE_DarkForces void random_seed(u32 seed) { - TFE_System::logWrite(LOG_MSG, "RANDOM", "Setting SEED to %d", seed); s_seed = seed; } diff --git a/TheForceEngine/TFE_DarkForces/sound.cpp b/TheForceEngine/TFE_DarkForces/sound.cpp index ea87e8e49..bb3f5eee1 100644 --- a/TheForceEngine/TFE_DarkForces/sound.cpp +++ b/TheForceEngine/TFE_DarkForces/sound.cpp @@ -421,7 +421,7 @@ namespace TFE_DarkForces // Calculate the pan. angle14_32 angle = vec2ToAngle(x - s_eyePos.x, z - s_eyePos.z); - angle = getAngleDifference(s_yaw, angle); + angle = getAngleDifference(s_eyeYaw, angle); *pan = s_tPan[((angle + 8192) / 512) & 31]; // TFE subtitles/captions diff --git a/TheForceEngine/TFE_DarkForces/time.cpp b/TheForceEngine/TFE_DarkForces/time.cpp index 8c460eb9c..e62e981cb 100644 --- a/TheForceEngine/TFE_DarkForces/time.cpp +++ b/TheForceEngine/TFE_DarkForces/time.cpp @@ -3,7 +3,6 @@ #include #include #include -#include using namespace TFE_Jedi; @@ -46,8 +45,7 @@ namespace TFE_DarkForces } void updateTime() - { - + { if (!s_pauseTimeUpdate) { s_timeAccum += TFE_System::getDeltaTime() * TIMER_FREQ; diff --git a/TheForceEngine/TFE_DarkForces/vueLogic.cpp b/TheForceEngine/TFE_DarkForces/vueLogic.cpp index 6903bd35b..1ceb893d2 100644 --- a/TheForceEngine/TFE_DarkForces/vueLogic.cpp +++ b/TheForceEngine/TFE_DarkForces/vueLogic.cpp @@ -213,6 +213,12 @@ namespace TFE_DarkForces size_t bufferPos = 0; if (!strcasecmp(transformName, "camera")) { + // TFE: allocate a VFRAME_FIRST for camera transforms, same as with ordinary transforms + VueFrame* frame = (VueFrame*)allocator_newItem(vueList); + if (!frame) + return; + frame->flags = VFRAME_FIRST; + while (1) { const char* line = parser->readLine(bufferPos); @@ -230,7 +236,12 @@ namespace TFE_DarkForces frame->offset.y = floatToFixed16(y1); frame->offset.z = floatToFixed16(z1); frame->yaw = vec2ToAngle(floatToFixed16(x2 - x1), floatToFixed16(z2 - z1)); - frame->pitch = 0; + + // TFE: calculate pitch (this was not done in vanilla) + f32 dx = x2 - x1; + f32 dz = z2 - z1; + f64 xzVec = sqrt(dx * dx + dz * dz); + frame->pitch = vec2ToAngle(floatToFixed16(y1 - y2), floatToFixed16((f32)xzVec)); frame->roll = angle14_32(r * 16383.0f / 360.0f); } } @@ -586,6 +597,8 @@ namespace TFE_DarkForces local(obj)->posWS = local(frame)->offset; local(obj)->yaw = local(frame)->yaw; + local(obj)->pitch = local(frame)->pitch; // TFE: copy pitch and roll to object (not done in vanilla) + local(obj)->roll = local(frame)->roll; task_localBlockEnd; entity_yield(TASK_NO_DELAY); diff --git a/TheForceEngine/TFE_DarkForces/weapon.cpp b/TheForceEngine/TFE_DarkForces/weapon.cpp index 8b010cd53..51852fc6e 100644 --- a/TheForceEngine/TFE_DarkForces/weapon.cpp +++ b/TheForceEngine/TFE_DarkForces/weapon.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace TFE_DarkForces { @@ -22,6 +23,8 @@ namespace TFE_DarkForces static TextureData* s_rhand1 = nullptr; static TextureData* s_gasmaskTexture = nullptr; + static s32 s_gasMaskXpos; + static s32 s_gasMaskYpos; static PlayerWeapon s_playerWeaponList[WPN_COUNT]; static Tick s_weaponDelayPrimary; @@ -79,11 +82,10 @@ namespace TFE_DarkForces // Forward Declarations /////////////////////////////////////////// TextureData* loadWeaponTexture(const char* texName); - void weapon_loadTextures(); void weapon_setFireRateInternal(WeaponFireMode mode, Tick delay, s32* canFire); void weapon_playerWeaponTaskFunc(MessageType msg); void weapon_handleOnAnimation(MessageType msg); - void weapon_prepareToFire(); + void weapon_setIdle(); static WeaponFireFunc s_weaponFireFunc[WPN_COUNT] = { @@ -171,222 +173,57 @@ namespace TFE_DarkForces } } - void weapon_startup() + // TFE: Set weapon data from external data + void setWeaponData(WeaponID id, TFE_ExternalData::ExternalWeapon* extWeapons) { - // TODO: Move this into data instead of hard coding it like vanilla Dark Forces. - s_playerWeaponList[WPN_FIST] = - { - 4, // frameCount - {nullptr}, // frames[] - 0, // frame - {172, 55, 59, 56}, // xPos[] - {141, 167, 114, 141}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - nullptr, // ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - 0, // wakeupRange - 0, // variation - }; - s_playerWeaponList[WPN_PISTOL] = - { - 3, // frameCount - {nullptr}, // frames[] - 0, // frame - {0xa5, 0xa9, 0xa9}, // xPos[] - {0x8e, 0x88, 0x88}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoEnergy,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(45), // wakeupRange - 22, // variation - }; - s_playerWeaponList[WPN_RIFLE] = - { - 2, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x71, 0x70}, // xPos[] - {0x7f, 0x72}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoEnergy,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(50), // wakeupRange - 0x56, // variation - }; - s_playerWeaponList[WPN_THERMAL_DET] = - { - 4, // frameCount - {nullptr}, // frames[] - 0, // frame - {0xa1, 0xb3, 0xb4, 0x84},// xPos[] - {0x83, 0xa5, 0x66, 0x96},// yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoDetonator,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - 0, // wakeupRange - 0x2d002d, // variation - }; - s_playerWeaponList[WPN_REPEATER] = - { - 3, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x9c, 0xa3, 0xa3}, // xPos[] - {0x8a, 0x8c, 0x8c}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoPower,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(60), // wakeupRange - 0x2d002d, // variation - }; - s_playerWeaponList[WPN_FUSION] = - { - 6, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x13, 0x17, 0x17, 0x17, 0x17, 0x17},// xPos[] - {0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b},// yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoPower,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(55), // wakeupRange - 0x2d, // variation - }; - s_playerWeaponList[WPN_MORTAR] = - { - 4, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x7b, 0x7e, 0x7f, 0x7b},// xPos[] - {0x77, 0x75, 0x77, 0x74},// yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoShell,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(60), // wakeupRange - 0x2d, // variation - }; - s_playerWeaponList[WPN_MINE] = - { - 3, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x69, 0x69, 0x84}, // xPos[] - {0x99, 0x99, 0x96}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoMine, // ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - 0, // wakeupRange - 0, // variation - }; - s_playerWeaponList[WPN_CONCUSSION] = - { - 3, // frameCount - {nullptr}, // frames[] - 0, // frame - {0x82, 0xbf, 0xbc}, // xPos[] - {0x83, 0x81, 0x84}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoPower,// ammo - nullptr, // secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(70), // wakeupRange - 0, // variation - }; - s_playerWeaponList[WPN_CANNON] = + s_playerWeaponList[id].frameCount = min(extWeapons[id].frameCount, WEAPON_NUM_TEXTURES); + s_playerWeaponList[id].frame = 0; + + for (s32 f = 0; f < WEAPON_NUM_TEXTURES; f++) { - 4, // frameCount - {nullptr}, // frames[] - 0, // frame - {206, 208, 224, 230}, // xPos[] - {-74, -60, 81, 86}, // yPos[] - 7, // flags - 0, // rollOffset - 0, // pchOffset - 0, // xWaveOffset - 0, // yWaveOffset - 0, // xOffset - 0, // yOffset - &s_playerInfo.ammoPlasma,// ammo - &s_playerInfo.ammoMissile,// secondaryAmmo - 0, // u8c - 0, // u90 - FIXED(55), // wakeupRange - 0x2d002d, // variation - }; + const char* tex = extWeapons[id].textures[f]; + s_playerWeaponList[id].frames[f] = tex ? loadWeaponTexture(tex) : nullptr; + s_playerWeaponList[id].xPos[f] = extWeapons[id].xPos[f]; + s_playerWeaponList[id].yPos[f] = extWeapons[id].yPos[f]; + } - // Load Textures. - weapon_loadTextures(); + s_playerWeaponList[id].flags = 7; + s_playerWeaponList[id].rollOffset = 0; + s_playerWeaponList[id].pchOffset = 0; + s_playerWeaponList[id].xWaveOffset = 0; + s_playerWeaponList[id].yWaveOffset = 0; + s_playerWeaponList[id].xOffset = 0; + s_playerWeaponList[id].yOffset = 0; + s_playerWeaponList[id].ammo = extWeapons[id].ammo; + s_playerWeaponList[id].secondaryAmmo = extWeapons[id].secondaryAmmo; + s_playerWeaponList[id].u8c = 0; + s_playerWeaponList[id].u90 = 0; + s_playerWeaponList[id].wakeupRange = FIXED(extWeapons[id].wakeupRange); + s_playerWeaponList[id].variation = extWeapons[id].variation; + s_playerWeaponList[id].primaryFireConsumption = extWeapons[id].primaryFireConsumption; + s_playerWeaponList[id].secondaryFireConsumption = extWeapons[id].secondaryFireConsumption; + + setupAnimationFrames(id, extWeapons[id].numAnimFrames, extWeapons[id].animFrames, extWeapons[id].numSecondaryAnimFrames, extWeapons[id].animFramesSecondary); + } + + void weapon_startup() + { + // TFE: Weapon data is set from external data instead of being hardcoded as in vanilla DF + TFE_ExternalData::ExternalWeapon* externalWeapons = TFE_ExternalData::getExternalWeapons(); + setWeaponData(WPN_FIST, externalWeapons); + setWeaponData(WPN_PISTOL, externalWeapons); + setWeaponData(WPN_RIFLE, externalWeapons); + setWeaponData(WPN_THERMAL_DET, externalWeapons); + setWeaponData(WPN_REPEATER, externalWeapons); + setWeaponData(WPN_FUSION, externalWeapons); + setWeaponData(WPN_MORTAR, externalWeapons); + setWeaponData(WPN_MINE, externalWeapons); + setWeaponData(WPN_CONCUSSION, externalWeapons); + setWeaponData(WPN_CANNON, externalWeapons); + + s_gasmaskTexture = loadWeaponTexture(TFE_ExternalData::getExternalGasmask()->texture); + s_gasMaskXpos = TFE_ExternalData::getExternalGasmask()->xPos; + s_gasMaskYpos = TFE_ExternalData::getExternalGasmask()->yPos; // Load Sounds. s_punchSwingSndSrc = sound_load("swing.voc", SOUND_PRIORITY_HIGH3); @@ -560,6 +397,7 @@ namespace TFE_DarkForces weapon_setFireRateInternal(WFIRE_PRIMARY, 0, nullptr); weapon_setFireRateInternal(WFIRE_SECONDARY, 0, nullptr); + // These values are set but never read. Actual fire rate is determined by the animation frames durations. switch (s_prevWeapon) { case WPN_PISTOL: @@ -633,7 +471,8 @@ namespace TFE_DarkForces s_superchargeTask = nullptr; } - void weapon_fixupAnim() + // If using TDs or mines and no ammo, show empty right hand + void weapon_emptyAnim() { PlayerWeapon* weapon = s_curPlayerWeapon; s32 ammo = weapon->ammo ? *weapon->ammo : 0; @@ -684,6 +523,7 @@ namespace TFE_DarkForces } } + // Added for the early GPU renderer. No longer used void weapon_addTexture(TextureInfoList& texList, TextureData* tex) { TextureInfo texInfo = {}; @@ -692,6 +532,7 @@ namespace TFE_DarkForces texList.push_back(texInfo); } + // Added for the early GPU renderer. No longer used bool weapon_getTextures(TextureInfoList& texList) { // Get weapon textures. @@ -707,63 +548,6 @@ namespace TFE_DarkForces return true; } - void weapon_loadTextures() - { - if (!s_weaponTexturesLoaded) - { - s_rhand1 = loadWeaponTexture("rhand1.bm"); - s_gasmaskTexture = loadWeaponTexture("gmask.bm"); - - s_playerWeaponList[WPN_FIST].frames[0] = s_rhand1; - s_playerWeaponList[WPN_FIST].frames[1] = loadWeaponTexture("punch1.bm"); - s_playerWeaponList[WPN_FIST].frames[2] = loadWeaponTexture("punch2.bm"); - s_playerWeaponList[WPN_FIST].frames[3] = loadWeaponTexture("punch3.bm"); - - s_playerWeaponList[WPN_PISTOL].frames[0] = loadWeaponTexture("pistol1.bm"); - s_playerWeaponList[WPN_PISTOL].frames[1] = loadWeaponTexture("pistol2.bm"); - s_playerWeaponList[WPN_PISTOL].frames[2] = loadWeaponTexture("pistol3.bm"); - - s_playerWeaponList[WPN_RIFLE].frames[0] = loadWeaponTexture("rifle1.bm"); - s_playerWeaponList[WPN_RIFLE].frames[1] = loadWeaponTexture("rifle2.bm"); - - s_playerWeaponList[WPN_THERMAL_DET].frames[0] = loadWeaponTexture("therm1.bm"); - s_playerWeaponList[WPN_THERMAL_DET].frames[1] = loadWeaponTexture("therm2.bm"); - s_playerWeaponList[WPN_THERMAL_DET].frames[2] = loadWeaponTexture("therm3.bm"); - s_playerWeaponList[WPN_THERMAL_DET].frames[3] = s_rhand1; - - s_playerWeaponList[WPN_REPEATER].frames[0] = loadWeaponTexture("autogun1.bm"); - s_playerWeaponList[WPN_REPEATER].frames[1] = loadWeaponTexture("autogun2.bm"); - s_playerWeaponList[WPN_REPEATER].frames[2] = loadWeaponTexture("autogun3.bm"); - - s_playerWeaponList[WPN_FUSION].frames[0] = loadWeaponTexture("fusion1.bm"); - s_playerWeaponList[WPN_FUSION].frames[1] = loadWeaponTexture("fusion2.bm"); - s_playerWeaponList[WPN_FUSION].frames[2] = loadWeaponTexture("fusion3.bm"); - s_playerWeaponList[WPN_FUSION].frames[3] = loadWeaponTexture("fusion4.bm"); - s_playerWeaponList[WPN_FUSION].frames[4] = loadWeaponTexture("fusion5.bm"); - s_playerWeaponList[WPN_FUSION].frames[5] = loadWeaponTexture("fusion6.bm"); - - s_playerWeaponList[WPN_MORTAR].frames[0] = loadWeaponTexture("mortar1.bm"); - s_playerWeaponList[WPN_MORTAR].frames[1] = loadWeaponTexture("mortar2.bm"); - s_playerWeaponList[WPN_MORTAR].frames[2] = loadWeaponTexture("mortar3.bm"); - s_playerWeaponList[WPN_MORTAR].frames[3] = loadWeaponTexture("mortar4.bm"); - - s_playerWeaponList[WPN_MINE].frames[0] = loadWeaponTexture("clay1.bm"); - s_playerWeaponList[WPN_MINE].frames[1] = loadWeaponTexture("clay2.bm"); - s_playerWeaponList[WPN_MINE].frames[2] = s_rhand1; - - s_playerWeaponList[WPN_CONCUSSION].frames[0] = loadWeaponTexture("concuss1.bm"); - s_playerWeaponList[WPN_CONCUSSION].frames[1] = loadWeaponTexture("concuss2.bm"); - s_playerWeaponList[WPN_CONCUSSION].frames[2] = loadWeaponTexture("concuss3.bm"); - - s_playerWeaponList[WPN_CANNON].frames[0] = loadWeaponTexture("assault1.bm"); - s_playerWeaponList[WPN_CANNON].frames[1] = loadWeaponTexture("assault2.bm"); - s_playerWeaponList[WPN_CANNON].frames[2] = loadWeaponTexture("assault3.bm"); - s_playerWeaponList[WPN_CANNON].frames[3] = loadWeaponTexture("assault4.bm"); - - s_weaponTexturesLoaded = JTRUE; - } - } - TextureData* loadWeaponTexture(const char* texName) { TextureData* weaponTex = bitmap_load(texName, 1, POOL_GAME); @@ -813,7 +597,7 @@ namespace TFE_DarkForces if (msg == MSG_STOP_FIRING) { s_isShooting = JFALSE; - weapon_prepareToFire(); + weapon_setIdle(); s_curPlayerWeapon->flags |= 2; } else if (msg == MSG_START_FIRING) @@ -1008,7 +792,8 @@ namespace TFE_DarkForces } } - void weapon_prepareToFire() + // Set weapons back to their idle frame; stop looping sound + void weapon_setIdle() { PlayerWeapon* weapon = s_curPlayerWeapon; if (s_prevWeapon == WPN_REPEATER) @@ -1090,13 +875,13 @@ namespace TFE_DarkForces } s_curPlayerWeapon->flags &= ~2; - weapon_prepareToFire(); + weapon_setIdle(); weapon_setFireRate(); } else if (msg == MSG_STOP_FIRING) { s_isShooting = JFALSE; - weapon_prepareToFire(); + weapon_setIdle(); s_curPlayerWeapon->flags |= 2; } else if (msg == MSG_HOLSTER) @@ -1107,7 +892,7 @@ namespace TFE_DarkForces if (!s_weaponOffAnim) { - weapon_prepareToFire(); + weapon_setIdle(); s_weaponAnimState = { @@ -1128,7 +913,7 @@ namespace TFE_DarkForces sound_play(s_weaponChangeSnd); } - weapon_fixupAnim(); + weapon_emptyAnim(); s_weaponAnimState = { @@ -1281,8 +1066,8 @@ namespace TFE_DarkForces if (s_wearingGasmask) { - s32 x = 105; - s32 y = 141; + s32 x = s_gasMaskXpos; + s32 y = s_gasMaskYpos; if (weapon) { x -= (weapon->xWaveOffset >> 3); diff --git a/TheForceEngine/TFE_DarkForces/weapon.h b/TheForceEngine/TFE_DarkForces/weapon.h index 3e2032a68..efb02a437 100644 --- a/TheForceEngine/TFE_DarkForces/weapon.h +++ b/TheForceEngine/TFE_DarkForces/weapon.h @@ -15,13 +15,18 @@ namespace TFE_Jedi namespace TFE_DarkForces { + enum + { + WEAPON_NUM_TEXTURES = 16, // Originally 8 in vanilla DF + }; + struct PlayerWeapon { s32 frameCount; - TextureData* frames[8]; + TextureData* frames[WEAPON_NUM_TEXTURES]; s32 frame; - s32 xPos[8]; - s32 yPos[8]; + s32 xPos[WEAPON_NUM_TEXTURES]; + s32 yPos[WEAPON_NUM_TEXTURES]; u32 flags; s32 rollOffset; s32 pchOffset; @@ -35,6 +40,10 @@ namespace TFE_DarkForces s32 u90; fixed16_16 wakeupRange; s32 variation; + + // TFE + s32 primaryFireConsumption; + s32 secondaryFireConsumption; }; struct WeaponAnimState @@ -87,7 +96,7 @@ namespace TFE_DarkForces void weapon_createPlayerWeaponTask(); void weapon_holster(); void weapon_draw(u8* display, DrawRect* rect); - void weapon_fixupAnim(); + void weapon_emptyAnim(); void weapon_stopFiring(); void player_cycleWeapons(s32 change); void weapon_computeMatrix(fixed16_16* mtx, angle14_32 pitch, angle14_32 yaw); diff --git a/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp b/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp index 399eec742..ee8995191 100644 --- a/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp +++ b/TheForceEngine/TFE_DarkForces/weaponFireFunc.cpp @@ -15,6 +15,7 @@ namespace TFE_DarkForces enum WeaponFireConst { MAX_AUTOAIM_DIST = COL_INFINITY, + WPN_NUM_ANIMFRAMES = 16, }; static SoundEffectId s_punchSwingSndId = 0; @@ -65,52 +66,33 @@ namespace TFE_DarkForces Tick delaySupercharge; Tick delayNormal; }; - static const WeaponAnimFrame c_punchAnim[4] = - { - { 1, 0, 5, 10 }, - { 2, 0, 10, 21 }, - { 3, 0, 10, 21 }, - { 0, 0, 10, 21 }, - }; - static const WeaponAnimFrame c_pistolAnim[3] = - { - { 1, 40, 7, 14 }, - { 2, 0, 7, 14 }, - { 0, 0, 21, 43 }, - }; - static const WeaponAnimFrame c_rifleAnim[2] = - { - { 1, 40, 3, 7 }, - { 0, 0, 7, 14 }, - }; - static const WeaponAnimFrame c_mortarAnim[4] = - { - { 1, 36, 29, 58 }, - { 2, 36, 7, 14 }, - { 3, 36, 7, 14 }, - { 0, 0, 7, 14 }, - }; - static const WeaponAnimFrame c_thermalDetAnim[5] = - { - { 1, 0, 11, 11 }, - { 2, 0, 58, 58 }, - { 1, 0, 29, 29 }, - { 0, 0, 29, 29 }, - { 3, 0, 29, 29 }, - }; - static const WeaponAnimFrame c_repeaterAnim[4] = - { - { 1, 0, 2, 5 }, // primary - { 1, 0, 4, 8 }, // secondary - { 2, 33, 5, 11 }, - { 0, 0, 14, 29 }, - }; - static const WeaponAnimFrame c_concussionAnim[] = - { - { 1, 36, 21, 43 }, - { 2, 0, 7, 14 }, - { 0, 0, 29, 58 }, - }; + + static s32 s_punchNumFrames = 0; + static s32 s_pistolNumFrames = 0; + static s32 s_rifleNumFrames = 0; + static s32 s_thermalDetNumFrames = 0; + static s32 s_repeaterPrimaryNumFrames = 0; + static s32 s_repeaterSecondaryNumFrames = 0; + static s32 s_fusionPrimaryNumFrames = 0; + static s32 s_fusionSecondaryNumFrames = 0; + static s32 s_mortarNumFrames = 0; + static s32 s_concussionNumFrames = 0; + static s32 s_cannonPrimaryNumFrames = 0; + static s32 s_cannonSecondaryNumFrames = 0; + + static WeaponAnimFrame s_punchAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_pistolAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_rifleAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_thermalDetAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_repeaterPrimaryAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_repeaterSecondaryAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_fusionPrimaryAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_fusionSecondaryAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_mortarAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_concussionAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_cannonPrimaryAnim[WPN_NUM_ANIMFRAMES]; + static WeaponAnimFrame s_cannonSecondaryAnim[WPN_NUM_ANIMFRAMES]; + static const angle14_32 c_repeaterYawOffset[3] = { 0, 136, -136 }; static const angle14_32 c_repeaterPitchOffset[3] = { 136, -136, -136 }; // Deltas for repeater - note the order is ZXY @@ -141,12 +123,95 @@ namespace TFE_DarkForces extern void weapon_animateOnOrOffscreen(MessageType msg); JBool computeAutoaim(fixed16_16 xPos, fixed16_16 yPos, fixed16_16 zPos, angle14_32 pitch, angle14_32 yaw, s32 variation); - // Reset for replay consistency. + // Ensure that the weapon is reset to the default states for replays void resetWeaponFunc() { s_weaponFirePitch = 0; s_weaponFireYaw = 0; } + + // TFE: Set up animation frames from external data (these were hardcoded in vanilla DF) + void setupAnimationFrames(WeaponID weaponId, s32 numPrimFrames, TFE_ExternalData::WeaponAnimFrame* extPrimFrames, s32 numSecFrames, TFE_ExternalData::WeaponAnimFrame* extSecFrames) + { + WeaponAnimFrame* primaryFrames = nullptr; + WeaponAnimFrame* secondaryFrames = nullptr; + + switch (weaponId) + { + case WPN_FIST: + s_punchNumFrames = numPrimFrames; + primaryFrames = s_punchAnim; + break; + + case WPN_PISTOL: + s_pistolNumFrames = numPrimFrames; + primaryFrames = s_pistolAnim; + break; + + case WPN_RIFLE: + s_rifleNumFrames = numPrimFrames; + primaryFrames = s_rifleAnim; + break; + + case WPN_THERMAL_DET: + s_thermalDetNumFrames = numPrimFrames; + primaryFrames = s_thermalDetAnim; + break; + + case WPN_REPEATER: + s_repeaterPrimaryNumFrames = numPrimFrames; + s_repeaterSecondaryNumFrames = numSecFrames; + primaryFrames = s_repeaterPrimaryAnim; + secondaryFrames = s_repeaterSecondaryAnim; + break; + + case WPN_FUSION: + s_fusionPrimaryNumFrames = numPrimFrames; + s_fusionSecondaryNumFrames = numSecFrames; + primaryFrames = s_fusionPrimaryAnim; + secondaryFrames = s_fusionSecondaryAnim; + break; + + case WPN_MORTAR: + s_mortarNumFrames = numPrimFrames; + primaryFrames = s_mortarAnim; + break; + + case WPN_CONCUSSION: + s_concussionNumFrames = numPrimFrames; + primaryFrames = s_concussionAnim; + break; + + case WPN_CANNON: + s_cannonPrimaryNumFrames = numPrimFrames; + s_cannonSecondaryNumFrames = numSecFrames; + primaryFrames = s_cannonPrimaryAnim; + secondaryFrames = s_cannonSecondaryAnim; + break; + } + + if (primaryFrames) + { + for (s32 i = 0; i < numPrimFrames; i++) + { + primaryFrames[i].waxFrame = extPrimFrames[i].texture; + primaryFrames[i].weaponLight = extPrimFrames[i].light; + primaryFrames[i].delaySupercharge = extPrimFrames[i].durationSupercharge; + primaryFrames[i].delayNormal = extPrimFrames[i].durationNormal; + } + } + + if (secondaryFrames) + { + for (s32 i = 0; i < numSecFrames; i++) + { + secondaryFrames[i].waxFrame = extSecFrames[i].texture; + secondaryFrames[i].weaponLight = extSecFrames[i].light; + secondaryFrames[i].delaySupercharge = extSecFrames[i].durationSupercharge; + secondaryFrames[i].delayNormal = extSecFrames[i].durationNormal; + } + } + } // Adjust the speed - in TFE the framerate may be higher than expected by the original code, which means that this // step (proj->delta) is too large for the 'speed'. @@ -170,6 +235,46 @@ namespace TFE_DarkForces proj->dmg = FIXED(9999); } + s32 getMaxAmmo(s32* ammo) + { + if (ammo == s_playerAmmoEnergy) + { + return s_ammoEnergyMax; + } + + if (ammo == s_playerAmmoPower) + { + return s_ammoPowerMax; + } + + if (ammo == s_playerAmmoPlasma) + { + return s_ammoPlasmaMax; + } + + if (ammo == s_playerAmmoShell) + { + return s_ammoShellMax; + } + + if (ammo == s_playerAmmoDetonators) + { + return s_ammoDetonatorMax; + } + + if (ammo == s_playerAmmoMines) + { + return s_ammoMineMax; + } + + if (ammo == s_playerAmmoMissiles) + { + return s_ammoMissileMax; + } + + return 0; + } + void weaponFire_fist(MessageType msg) { struct LocalContext @@ -179,21 +284,24 @@ namespace TFE_DarkForces }; task_begin_ctx; - taskCtx->delay = (s_superCharge) ? c_punchAnim[0].delaySupercharge : c_punchAnim[0].delayNormal; - s_curPlayerWeapon->frame = c_punchAnim[0].waxFrame; - s_weaponLight = c_punchAnim[0].weaponLight; + // Initial animation frame + taskCtx->delay = (s_superCharge) ? s_punchAnim[0].delaySupercharge : s_punchAnim[0].delayNormal; + s_curPlayerWeapon->frame = s_punchAnim[0].waxFrame; + s_weaponLight = s_punchAnim[0].weaponLight; do { task_yield(taskCtx->delay); task_callTaskFunc(weapon_handleState); } while (msg != MSG_RUN_TASK); + // Sound effect if (s_punchSwingSndId) { sound_stop(s_punchSwingSndId); } s_punchSwingSndId = sound_play(s_punchSwingSndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -201,6 +309,7 @@ namespace TFE_DarkForces } task_localBlockBegin; + // Aim and spawn projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -237,11 +346,11 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation. - for (taskCtx->iFrame = 1; taskCtx->iFrame < TFE_ARRAYSIZE(c_punchAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 1; taskCtx->iFrame < s_punchNumFrames; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_punchAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_punchAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_punchAnim[taskCtx->iFrame].delaySupercharge : c_punchAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_punchAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_punchAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_punchAnim[taskCtx->iFrame].delaySupercharge : s_punchAnim[taskCtx->iFrame].delayNormal; do { @@ -267,18 +376,21 @@ namespace TFE_DarkForces if (*s_curPlayerWeapon->ammo) { task_localBlockBegin; + // Sound effect if (s_pistolSndId) { sound_stop(s_pistolSndId); } s_pistolSndId = sound_play(s_pistolSndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } + // Aim projectile fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -330,7 +442,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoEnergy, -1, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } ProjectileLogic* projLogic; @@ -397,11 +512,11 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation. - for (taskCtx->iFrame = 0; taskCtx->iFrame < TFE_ARRAYSIZE(c_pistolAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_pistolNumFrames; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_pistolAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_pistolAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_pistolAnim[taskCtx->iFrame].delaySupercharge : c_pistolAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_pistolAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_pistolAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_pistolAnim[taskCtx->iFrame].delaySupercharge : s_pistolAnim[taskCtx->iFrame].delayNormal; do { @@ -452,18 +567,21 @@ namespace TFE_DarkForces if (*s_curPlayerWeapon->ammo) { task_localBlockBegin; + // Sound effect if (s_rifleSndId) { sound_stop(s_rifleSndId); } s_rifleSndId = sound_play(s_rifleSndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } + // Aim projectile fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -515,7 +633,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoEnergy, -2, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } ProjectileLogic* projLogic; @@ -582,11 +703,11 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation. - for (taskCtx->iFrame = 0; taskCtx->iFrame < TFE_ARRAYSIZE(c_rifleAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_rifleNumFrames; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_rifleAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_rifleAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_rifleAnim[taskCtx->iFrame].delaySupercharge : c_rifleAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_rifleAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_rifleAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_rifleAnim[taskCtx->iFrame].delaySupercharge : s_rifleAnim[taskCtx->iFrame].delayNormal; do { @@ -639,13 +760,16 @@ namespace TFE_DarkForces { s_canFireWeaponPrim = 0; s_canFireWeaponSec = 0; - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoDetonator, -1, 50); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); // Weapon Frame 0. - s_curPlayerWeapon->frame = c_thermalDetAnim[0].waxFrame; + s_curPlayerWeapon->frame = s_thermalDetAnim[0].waxFrame; do { - task_yield(c_thermalDetAnim[0].delayNormal); + task_yield(s_thermalDetAnim[0].delayNormal); task_callTaskFunc(weapon_handleState); } while (msg != MSG_RUN_TASK); @@ -666,6 +790,7 @@ namespace TFE_DarkForces task_callTaskFunc(weapon_handleState2); fixed16_16 dt = intToFixed16(s_curTick - taskCtx->startTick); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -720,9 +845,9 @@ namespace TFE_DarkForces s_curPlayerWeapon->yOffset = 0; // The initial frame is the same regardless of ammo. - s_curPlayerWeapon->frame = c_thermalDetAnim[1].waxFrame; - s_weaponLight = c_thermalDetAnim[1].weaponLight; - taskCtx->delay = c_thermalDetAnim[1].delayNormal; + s_curPlayerWeapon->frame = s_thermalDetAnim[1].waxFrame; + s_weaponLight = s_thermalDetAnim[1].weaponLight; + taskCtx->delay = s_thermalDetAnim[1].delayNormal; do { task_yield(taskCtx->delay); @@ -733,9 +858,9 @@ namespace TFE_DarkForces { for (taskCtx->iFrame = 2; taskCtx->iFrame <= 3; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_thermalDetAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_thermalDetAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = c_thermalDetAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_thermalDetAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_thermalDetAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = s_thermalDetAnim[taskCtx->iFrame].delayNormal; do { task_yield(taskCtx->delay); @@ -743,11 +868,11 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); } } - else + else // no more ammo, show empty hand { - s_curPlayerWeapon->frame = c_thermalDetAnim[4].waxFrame; - s_weaponLight = c_thermalDetAnim[4].weaponLight; - taskCtx->delay = c_thermalDetAnim[4].delayNormal; + s_curPlayerWeapon->frame = s_thermalDetAnim[4].waxFrame; + s_weaponLight = s_thermalDetAnim[4].weaponLight; + taskCtx->delay = s_thermalDetAnim[4].delayNormal; do { task_yield(taskCtx->delay); @@ -793,20 +918,23 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->ammo) { + // Sound effect if (s_repeaterFireSndID1) { sound_stop(s_repeaterFireSndID1); } s_repeaterFireSndID1 = sound_play(s_repeater1SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } - taskCtx->delay = s_superCharge ? c_repeaterAnim[1].delaySupercharge : c_repeaterAnim[1].delayNormal; - s_curPlayerWeapon->frame = c_repeaterAnim[1].waxFrame; + // Initial animation frame + taskCtx->delay = s_superCharge ? s_repeaterSecondaryAnim[0].delaySupercharge : s_repeaterSecondaryAnim[0].delayNormal; + s_curPlayerWeapon->frame = s_repeaterSecondaryAnim[0].waxFrame; do { task_yield(taskCtx->delay); @@ -814,6 +942,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); task_localBlockBegin; + // Aim projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -833,6 +962,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectiles (x3) fixed16_16 fire = intToFixed16(canFire); while (canFire) { @@ -847,7 +977,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPower, -3, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->secondaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } fixed16_16 yPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -887,11 +1020,11 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation - for (taskCtx->iFrame = 2; taskCtx->iFrame < TFE_ARRAYSIZE(c_repeaterAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 1; taskCtx->iFrame < s_repeaterSecondaryNumFrames; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_repeaterAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_repeaterAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_repeaterAnim[taskCtx->iFrame].delaySupercharge : c_repeaterAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_repeaterSecondaryAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_repeaterSecondaryAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_repeaterSecondaryAnim[taskCtx->iFrame].delaySupercharge : s_repeaterSecondaryAnim[taskCtx->iFrame].delayNormal; do { task_yield(taskCtx->delay); @@ -900,7 +1033,7 @@ namespace TFE_DarkForces } s_canFireWeaponSec = 1; } - else + else // out of ammo { if (s_repeaterFireSndID) { @@ -935,6 +1068,7 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->ammo) { + // Sound effect (includes looping sound effect) if (!s_repeaterFireSndID) { if (s_repeaterFireSndID1) @@ -945,14 +1079,16 @@ namespace TFE_DarkForces s_repeaterFireSndID = sound_play(s_repeaterSndSrc); } + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } - taskCtx->delay = s_superCharge ? c_repeaterAnim[0].delaySupercharge : c_repeaterAnim[0].delayNormal; - s_curPlayerWeapon->frame = c_repeaterAnim[0].waxFrame; + // Initial animation frame + taskCtx->delay = s_superCharge ? s_repeaterPrimaryAnim[0].delaySupercharge : s_repeaterPrimaryAnim[0].delayNormal; + s_curPlayerWeapon->frame = s_repeaterPrimaryAnim[0].waxFrame; do { task_yield(taskCtx->delay); @@ -960,6 +1096,7 @@ namespace TFE_DarkForces } while (msg != MSG_RUN_TASK); task_localBlockBegin; + // Aim projectile fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -995,6 +1132,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectile fixed16_16 fire = intToFixed16(canFire); while (canFire) { @@ -1009,7 +1147,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPower, -1, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } fixed16_16 yPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -1049,14 +1190,18 @@ namespace TFE_DarkForces } task_localBlockEnd; - taskCtx->delay = s_superCharge ? c_repeaterAnim[2].delaySupercharge : c_repeaterAnim[2].delayNormal; - s_weaponLight = c_repeaterAnim[2].weaponLight; - s_curPlayerWeapon->frame = c_repeaterAnim[2].waxFrame; - do + // Animation + for (taskCtx->iFrame = 1; taskCtx->iFrame < s_repeaterPrimaryNumFrames; taskCtx->iFrame++) { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); + taskCtx->delay = s_superCharge ? s_repeaterPrimaryAnim[taskCtx->iFrame].delaySupercharge : s_repeaterPrimaryAnim[taskCtx->iFrame].delayNormal; + s_weaponLight = s_repeaterPrimaryAnim[taskCtx->iFrame].weaponLight; + s_curPlayerWeapon->frame = s_repeaterPrimaryAnim[taskCtx->iFrame].waxFrame; + do + { + task_yield(taskCtx->delay); + task_callTaskFunc(weapon_handleState); + } while (msg != MSG_RUN_TASK); + } s_weaponLight = 0; @@ -1073,7 +1218,7 @@ namespace TFE_DarkForces s_canFireWeaponPrim = 1; } - else + else // out of ammo { if (s_repeaterFireSndID) { @@ -1121,12 +1266,14 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->ammo) { + // Sound effect if (s_fusionFireSndID) { sound_stop(s_fusionFireSndID); } s_fusionFireSndID = sound_play(s_fusion1SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -1134,6 +1281,7 @@ namespace TFE_DarkForces } task_localBlockBegin; + // Aim projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -1153,6 +1301,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectiles fixed16_16 fire = intToFixed16(canFire); while (canFire) { @@ -1167,7 +1316,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPower, -8, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->secondaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } fixed16_16 yPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -1207,28 +1359,21 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation - // TODO: not using the tables for this due to differences - refactor later. - s_curPlayerWeapon->frame = 5; - s_weaponLight = 34; - taskCtx->delay = (s_superCharge) ? 14 : 29; - do - { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); - - s_curPlayerWeapon->frame = 0; - s_weaponLight = 0; - taskCtx->delay = (s_superCharge) ? 19 : 39; - do + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_fusionSecondaryNumFrames; taskCtx->iFrame++) { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); + s_curPlayerWeapon->frame = s_fusionSecondaryAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_fusionSecondaryAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_fusionSecondaryAnim[taskCtx->iFrame].delaySupercharge : s_fusionSecondaryAnim[taskCtx->iFrame].delayNormal; + do + { + task_yield(taskCtx->delay); + task_callTaskFunc(weapon_handleState); + } while (msg != MSG_RUN_TASK); + } s_canFireWeaponSec = 1; } - else + else // out of ammo { if (s_fusionOutOfAmmoSndID) { @@ -1258,12 +1403,14 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->ammo) { + // Sound effect if (s_fusionFireSndID) { sound_stop(s_fusionFireSndID); } s_fusionFireSndID = sound_play(s_fusion1SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -1271,6 +1418,7 @@ namespace TFE_DarkForces } task_localBlockBegin; + // Aim projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -1282,6 +1430,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectiles fixed16_16 fire = intToFixed16(canFire); while (canFire) { @@ -1321,7 +1470,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPower, -1, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } fixed16_16 yPlayerPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -1361,9 +1513,10 @@ namespace TFE_DarkForces } task_localBlockEnd; - taskCtx->delay = s_superCharge ? 10 : 20; - s_weaponLight = 34; - s_curPlayerWeapon->frame = s_fusionCylinder; + // Animation + taskCtx->delay = s_superCharge ? s_fusionPrimaryAnim[s_fusionCylinder].delaySupercharge : s_fusionPrimaryAnim[s_fusionCylinder].delayNormal; + s_weaponLight = s_fusionPrimaryAnim[s_fusionCylinder].weaponLight; + s_curPlayerWeapon->frame = s_fusionPrimaryAnim[s_fusionCylinder].waxFrame; do { task_yield(taskCtx->delay); @@ -1389,9 +1542,9 @@ namespace TFE_DarkForces } } - taskCtx->delay = s_superCharge ? 7 : 14; - s_weaponLight = 0; - s_curPlayerWeapon->frame = 0; + taskCtx->delay = s_superCharge ? s_fusionPrimaryAnim[0].delaySupercharge : s_fusionPrimaryAnim[0].delayNormal; + s_weaponLight = s_fusionPrimaryAnim[0].weaponLight; + s_curPlayerWeapon->frame = s_fusionPrimaryAnim[0].waxFrame; do { task_yield(taskCtx->delay); @@ -1400,7 +1553,7 @@ namespace TFE_DarkForces s_canFireWeaponPrim = 1; } - else + else // out of ammo { if (s_fusionOutOfAmmoSndID) { @@ -1441,6 +1594,7 @@ namespace TFE_DarkForces if (*s_curPlayerWeapon->ammo) { + // Sound effect task_localBlockBegin; if (s_mortarFireSndID) { @@ -1448,12 +1602,14 @@ namespace TFE_DarkForces } s_mortarFireSndID = sound_play(s_mortarFireSndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } + // Aim projectile fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -1506,7 +1662,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoShell, -1, 50); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } ProjectileLogic* projLogic; @@ -1561,7 +1720,7 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation. - for (taskCtx->iFrame = 0; taskCtx->iFrame < TFE_ARRAYSIZE(c_mortarAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_mortarNumFrames; taskCtx->iFrame++) { if (taskCtx->iFrame == 1) { @@ -1572,9 +1731,9 @@ namespace TFE_DarkForces s_mortarFireSndID2 = sound_play(s_mortarFireSndSrc2); } - s_curPlayerWeapon->frame = c_mortarAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_mortarAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_mortarAnim[taskCtx->iFrame].delaySupercharge : c_mortarAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_mortarAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_mortarAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_mortarAnim[taskCtx->iFrame].delaySupercharge : s_mortarAnim[taskCtx->iFrame].delayNormal; do { @@ -1618,13 +1777,19 @@ namespace TFE_DarkForces task_begin; if (*s_curPlayerWeapon->ammo) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoMine, -1, 30); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); + + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } + // Animate (going down) s_weaponAnimState = { 1, // frame @@ -1642,12 +1807,14 @@ namespace TFE_DarkForces ProjectileLogic* mine = (ProjectileLogic*)createProjectile(type, s_playerObject->sector, s_playerObject->posWS.x, floorHeight, s_playerObject->posWS.z, s_playerObject); mine->vel = { 0, 0, 0 }; + // Sound effect if (s_mineSndId) { sound_stop(s_mineSndId); } s_mineSndId = sound_play(s_mineSndSrc); + // Animate (come back up; empty hand if no more ammo) s32 frame = (*s_curPlayerWeapon->ammo) ? 0 : 2; s_weaponAnimState = { @@ -1661,7 +1828,7 @@ namespace TFE_DarkForces task_yield(2); } - else + else // out of ammo { if (s_weaponAutoMount2) { @@ -1694,18 +1861,21 @@ namespace TFE_DarkForces if (*s_curPlayerWeapon->ammo) { task_localBlockBegin; + // Sound effect if (s_concussionFireSndID) { sound_stop(s_concussionFireSndID); } s_concussionFireSndID = sound_play(s_concussion6SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; collision_effectObjectsInRangeXZ(s_playerObject->sector, s_curPlayerWeapon->wakeupRange, origin, hitEffectWakeupFunc, s_playerObject, ETFLAG_AI_ACTOR); } + // Aim projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -1756,7 +1926,10 @@ namespace TFE_DarkForces // it is true every other frame. if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPower, -4, 500); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } ProjectileLogic* projLogic; @@ -1803,11 +1976,11 @@ namespace TFE_DarkForces } s_concussionFireSndID1 = sound_play(s_concussion5SndSrc); - for (taskCtx->iFrame = 0; taskCtx->iFrame < TFE_ARRAYSIZE(c_concussionAnim); taskCtx->iFrame++) + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_concussionNumFrames; taskCtx->iFrame++) { - s_curPlayerWeapon->frame = c_concussionAnim[taskCtx->iFrame].waxFrame; - s_weaponLight = c_concussionAnim[taskCtx->iFrame].weaponLight; - taskCtx->delay = (s_superCharge) ? c_concussionAnim[taskCtx->iFrame].delaySupercharge : c_concussionAnim[taskCtx->iFrame].delayNormal; + s_curPlayerWeapon->frame = s_concussionAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_concussionAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_concussionAnim[taskCtx->iFrame].delaySupercharge : s_concussionAnim[taskCtx->iFrame].delayNormal; do { @@ -1859,20 +2032,24 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->secondaryAmmo) { - taskCtx->delay = (s_superCharge) ? 14 : 29; - s_curPlayerWeapon->frame = 2; + // Initial animation frame + taskCtx->delay = (s_superCharge) ? s_cannonSecondaryAnim[0].delaySupercharge : s_cannonSecondaryAnim[0].delayNormal; + s_weaponLight = s_cannonSecondaryAnim[0].weaponLight; + s_curPlayerWeapon->frame = s_cannonSecondaryAnim[0].waxFrame; do { task_yield(taskCtx->delay); task_callTaskFunc(weapon_handleState); } while (msg != MSG_RUN_TASK); + // Sound effect if (s_cannonFireSndID1) { sound_stop(s_cannonFireSndID1); } s_cannonFireSndID1 = sound_play(s_missile1SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -1880,6 +2057,7 @@ namespace TFE_DarkForces } task_localBlockBegin; + // Aim projectile fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -1916,6 +2094,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectile fixed16_16 fire = intToFixed16(canFire); while (canFire && *s_curPlayerWeapon->secondaryAmmo) { @@ -1925,7 +2104,10 @@ namespace TFE_DarkForces s32 superChargeFrame = s_superCharge ? 0 : 1; if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->secondaryAmmo = pickup_addToValue(s_playerInfo.ammoMissile, -1, 20); + *s_curPlayerWeapon->secondaryAmmo = pickup_addToValue( + *s_curPlayerWeapon->secondaryAmmo, + -s_curPlayerWeapon->secondaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->secondaryAmmo)); } fixed16_16 yPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -1965,27 +2147,21 @@ namespace TFE_DarkForces task_localBlockEnd; // Animation - s_curPlayerWeapon->frame = 3; - s_weaponLight = 33; - taskCtx->delay = (s_superCharge) ? 43 : 87; - do + for (taskCtx->iFrame = 1; taskCtx->iFrame < s_cannonSecondaryNumFrames; taskCtx->iFrame++) { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); - - s_curPlayerWeapon->frame = 0; - s_weaponLight = 0; - taskCtx->delay = (s_superCharge) ? 29 : 58; - do - { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); + s_curPlayerWeapon->frame = s_cannonSecondaryAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_cannonSecondaryAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_cannonSecondaryAnim[taskCtx->iFrame].delaySupercharge : s_cannonSecondaryAnim[taskCtx->iFrame].delayNormal; + do + { + task_yield(taskCtx->delay); + task_callTaskFunc(weapon_handleState); + } while (msg != MSG_RUN_TASK); + } s_canFireWeaponSec = 1; } - else + else // out of ammo { s_curPlayerWeapon->frame = 0; taskCtx->delay = (s_superCharge) ? 3 : 7; @@ -2002,12 +2178,14 @@ namespace TFE_DarkForces { if (*s_curPlayerWeapon->ammo) { + // Sound effect if (s_cannonFireSndID) { sound_stop(s_cannonFireSndID); } s_cannonFireSndID = sound_play(s_plasma4SndSrc); + // Wakeup AI within range if (s_curPlayerWeapon->wakeupRange) { vec3_fixed origin = { s_playerObject->posWS.x, s_playerObject->posWS.y, s_playerObject->posWS.z }; @@ -2015,6 +2193,7 @@ namespace TFE_DarkForces } task_localBlockBegin; + // Aim projectiles fixed16_16 mtx[9]; weapon_computeMatrix(mtx, -s_playerObject->pitch, -s_playerObject->yaw); @@ -2050,6 +2229,7 @@ namespace TFE_DarkForces weapon_computeMatrix(mtx2, -s_weaponFirePitch, -s_weaponFireYaw); } + // Spawn projectiles fixed16_16 fire = intToFixed16(canFire); while (canFire && *s_curPlayerWeapon->ammo) { @@ -2059,7 +2239,10 @@ namespace TFE_DarkForces s32 superChargeFrame = s_superCharge ? 0 : 1; if (superChargeFrame | (s_fireFrame & 1)) { - *s_curPlayerWeapon->ammo = pickup_addToValue(s_playerInfo.ammoPlasma, -1, 400); + *s_curPlayerWeapon->ammo = pickup_addToValue( + *s_curPlayerWeapon->ammo, + -s_curPlayerWeapon->primaryFireConsumption, + getMaxAmmo(s_curPlayerWeapon->ammo)); } fixed16_16 yPos = s_playerObject->posWS.y - s_playerObject->worldHeight + s_headwaveVerticalOffset; @@ -2100,18 +2283,23 @@ namespace TFE_DarkForces } task_localBlockEnd; - s_curPlayerWeapon->frame = 1; - s_weaponLight = 34; - taskCtx->delay = ((s_superCharge) ? 14 : 29) >> 1; - do + // Animate + for (taskCtx->iFrame = 0; taskCtx->iFrame < s_cannonPrimaryNumFrames; taskCtx->iFrame++) { - task_yield(taskCtx->delay); - task_callTaskFunc(weapon_handleState); - } while (msg != MSG_RUN_TASK); + s_curPlayerWeapon->frame = s_cannonPrimaryAnim[taskCtx->iFrame].waxFrame; + s_weaponLight = s_cannonPrimaryAnim[taskCtx->iFrame].weaponLight; + taskCtx->delay = (s_superCharge) ? s_cannonPrimaryAnim[taskCtx->iFrame].delaySupercharge : s_cannonPrimaryAnim[taskCtx->iFrame].delayNormal; + do + { + task_yield(taskCtx->delay); + task_callTaskFunc(weapon_handleState); + } while (msg != MSG_RUN_TASK); + } s_weaponLight = 0; if (s_isShooting) { + taskCtx->delay = ((s_superCharge) ? 14 : 29) >> 1; // this extra delay is equal to the delay of the single animation frame; it should be preserved if the animation is modded do { task_yield(taskCtx->delay); @@ -2121,7 +2309,7 @@ namespace TFE_DarkForces s_canFireWeaponPrim = 1; } - else + else // out of ammo { if (s_cannonOutOfAmmoSndID) { diff --git a/TheForceEngine/TFE_DarkForces/weaponFireFunc.h b/TheForceEngine/TFE_DarkForces/weaponFireFunc.h index dd412cf1e..e5df5c16f 100644 --- a/TheForceEngine/TFE_DarkForces/weaponFireFunc.h +++ b/TheForceEngine/TFE_DarkForces/weaponFireFunc.h @@ -3,13 +3,17 @@ // Dark Forces Weapon Fire Functions // Logic for player weapons (not enemy weapons) ////////////////////////////////////////////////////////////////////// +#include "weapon.h" #include #include +#include namespace TFE_DarkForces { typedef void(*WeaponFireFunc)(MessageType msg); + void setupAnimationFrames(WeaponID weaponId, s32 numPrimFrames, TFE_ExternalData::WeaponAnimFrame* extPrimFrames, s32 numSecondaryFrames, TFE_ExternalData::WeaponAnimFrame* extSecFrames); + void resetWeaponFunc(); void weaponFire_fist(MessageType msg); void weaponFire_pistol(MessageType msg); void weaponFire_rifle(MessageType msg); @@ -20,5 +24,4 @@ namespace TFE_DarkForces void weaponFire_mine(MessageType msg); void weaponFire_concussion(MessageType msg); void weaponFire_cannon(MessageType msg); - void resetWeaponFunc(); } // namespace TFE_DarkForces \ No newline at end of file diff --git a/TheForceEngine/TFE_Editor/AssetBrowser/assetBrowser.cpp b/TheForceEngine/TFE_Editor/AssetBrowser/assetBrowser.cpp index 696c10340..6842531aa 100644 --- a/TheForceEngine/TFE_Editor/AssetBrowser/assetBrowser.cpp +++ b/TheForceEngine/TFE_Editor/AssetBrowser/assetBrowser.cpp @@ -123,7 +123,7 @@ namespace AssetBrowser s_assetPalette.clear(); s_defaultPal = 0; - s_viewInfo = {}; + s_viewInfo = ViewerInfo{}; s_viewAssetList.clear(); for (s32 i = 0; i < TYPE_COUNT; i++) { diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorFrame.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorFrame.cpp index f27cce49a..067bbf4bf 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorFrame.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorFrame.cpp @@ -25,7 +25,7 @@ namespace TFE_Editor if (strcasecmp(frame->name, name) == 0) { TFE_RenderBackend::freeTexture(frame->texGpu); - *frame = {}; + *frame = EditorFrame{}; break; } } @@ -45,7 +45,7 @@ namespace TFE_Editor s32 allocateFrame(const char* name) { s32 index = (s32)s_frameList.size(); - s_frameList.push_back({}); + s_frameList.emplace_back(); return index; } diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorLevelPreview.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorLevelPreview.cpp index 3c88f25ca..480cdf222 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorLevelPreview.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorLevelPreview.cpp @@ -25,7 +25,7 @@ namespace TFE_Editor if (strcasecmp(lev->name, name) == 0) { TFE_RenderBackend::freeTexture(lev->thumbnail); - *lev = {}; + *lev = EditorLevelPreview{}; break; } } @@ -45,7 +45,7 @@ namespace TFE_Editor s32 allocateLevelPreview(const char* name) { s32 index = (s32)s_levelPreviewList.size(); - s_levelPreviewList.push_back({}); + s_levelPreviewList.emplace_back(); return index; } diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorObj3D.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorObj3D.cpp index 00e3747b8..95b5c4f6d 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorObj3D.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorObj3D.cpp @@ -37,7 +37,7 @@ namespace TFE_Editor if (strcasecmp(obj3D[i]->name, name) == 0) { TFE_RenderBackend::freeTexture(obj3D[i]->thumbnail); - *obj3D[i] = {}; + *obj3D[i] = EditorObj3D{}; break; } } @@ -50,7 +50,7 @@ namespace TFE_Editor for (size_t i = 0; i < count; i++) { TFE_RenderBackend::freeTexture(obj3D[i]->thumbnail); - *obj3D[i] = {}; + *obj3D[i] = EditorObj3D{}; } s_obj3DList.clear(); } diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorSound.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorSound.cpp index 5da722ad0..f18137fd2 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorSound.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorSound.cpp @@ -37,7 +37,7 @@ namespace TFE_Editor alreadyLoaded = false; // Create a new sound. s32 index = (s32)s_soundList.size(); - s_soundList.push_back({}); + s_soundList.emplace_back(); return index; } diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorSprite.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorSprite.cpp index 24d856c60..50a1d7f2e 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorSprite.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorSprite.cpp @@ -29,7 +29,7 @@ namespace TFE_Editor if (strcasecmp(sprite->name, name) == 0) { TFE_RenderBackend::freeTexture(sprite->texGpu); - *sprite = {}; + *sprite = EditorSprite{}; break; } } @@ -49,7 +49,7 @@ namespace TFE_Editor s32 allocateSprite(const char* name) { s32 index = (s32)s_spriteList.size(); - s_spriteList.push_back({}); + s_spriteList.emplace_back(); return index; } diff --git a/TheForceEngine/TFE_Editor/EditorAsset/editorTexture.cpp b/TheForceEngine/TFE_Editor/EditorAsset/editorTexture.cpp index 287ed6a38..03f745eea 100644 --- a/TheForceEngine/TFE_Editor/EditorAsset/editorTexture.cpp +++ b/TheForceEngine/TFE_Editor/EditorAsset/editorTexture.cpp @@ -32,7 +32,7 @@ namespace TFE_Editor TFE_RenderBackend::freeTexture(texture->frames[f]); } texture->frames.clear(); - *texture = {}; + *texture = EditorTexture{}; break; } } @@ -55,7 +55,7 @@ namespace TFE_Editor s32 allocateTexture(const char* name) { s32 index = (s32)s_textureList.size(); - s_textureList.push_back({}); + s_textureList.emplace_back(); return index; } diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.cpp b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.cpp index b0548e13f..e11e91147 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,11 +25,11 @@ using namespace TFE_Editor; using namespace TFE_Jedi; +using namespace TFE_ForceScript; namespace LevelEditor { static char s_scriptBuffer[4096]; - static std::vector s_scriptsToRun; bool s_levelScriptRegistered = false; bool s_execFromOutput = false; @@ -60,18 +60,17 @@ namespace LevelEditor infoPanelAddMsg(msgType[type], "%s %d, %d: %s", section, row, col, msg); } } - - void registerScriptFunctions() + + void registerScriptFunctions(ScriptAPI api) { if (s_levelScriptRegistered) { return; } s_levelScriptRegistered = true; TFE_ForceScript::overrideCallback(scriptCallback); - - asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); - s_lsDraw.scriptRegister(engine); - s_lsSelection.scriptRegister(engine); - s_lsSystem.scriptRegister(engine); - s_lsLevel.scriptRegister(engine); + + s_lsDraw.scriptRegister(api); + s_lsSelection.scriptRegister(api); + s_lsSystem.scriptRegister(api); + s_lsLevel.scriptRegister(api); // Math/Intrinsics. } @@ -89,20 +88,15 @@ namespace LevelEditor strcat(s_scriptBuffer, line); strcat(s_scriptBuffer, ";}\n"); - TFE_ForceScript::ModuleHandle lineMod = TFE_ForceScript::createModule("LineMod", "LineMod", s_scriptBuffer); + TFE_ForceScript::ModuleHandle lineMod = TFE_ForceScript::createModule("LineMod", "LineMod", s_scriptBuffer, API_SHARED | API_LEVEL_EDITOR); if (lineMod) { - TFE_ForceScript::FunctionHandle func = TFE_ForceScript::findScriptFunc(lineMod, "void main()"); + TFE_ForceScript::FunctionHandle func = TFE_ForceScript::findScriptFuncByName(lineMod, "main"); TFE_ForceScript::execFunc(func); } s_execFromOutput = false; } - - void runLevelScript(const char* scriptName) - { - s_scriptsToRun.push_back(scriptName); - } void showLevelScript(const char* scriptName) { @@ -123,25 +117,6 @@ namespace LevelEditor TFE_Editor::showMessageBox(title, "%s", text.data()); } } - - void levelScript_update() - { - char scriptPath[TFE_MAX_PATH]; - const s32 count = (s32)s_scriptsToRun.size(); - const std::string* scriptName = s_scriptsToRun.data(); - for (s32 i = 0; i < count; i++, scriptName++) - { - sprintf(scriptPath, "EditorDef/Scripts/%s.fs", scriptName->c_str()); - - TFE_ForceScript::ModuleHandle scriptMod = TFE_ForceScript::createModule(scriptName->c_str(), scriptPath); - if (scriptMod) - { - TFE_ForceScript::FunctionHandle func = TFE_ForceScript::findScriptFunc(scriptMod, "void main()"); - TFE_ForceScript::execFunc(func); - } - } - s_scriptsToRun.clear(); - } } #else namespace LevelEditor diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.h b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.h index 93ac3a616..5516339c0 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/levelEditorScripts.h @@ -7,14 +7,11 @@ // to "play" the game as intended. ////////////////////////////////////////////////////////////////////// #include +#include namespace LevelEditor { - void registerScriptFunctions(); - + void registerScriptFunctions(ScriptAPI api); void executeLine(const char* line); - void runLevelScript(const char* scriptName); void showLevelScript(const char* scriptName); - - void levelScript_update(); } diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.cpp b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.cpp index 6f7147e31..35fee1356 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #ifdef ENABLE_FORCE_SCRIPT @@ -102,24 +102,16 @@ namespace LevelEditor createSectorFromShape(); } - bool LS_Draw::scriptRegister(asIScriptEngine* engine) + bool LS_Draw::scriptRegister(ScriptAPI api) { - s32 res = 0; - // Object Type - res = engine->RegisterObjectType("Draw", sizeof(LS_Draw), asOBJ_VALUE | asOBJ_POD); assert(res >= 0); - // Properties - - //------------------------------------ - // Functions - //------------------------------------ - res = engine->RegisterObjectMethod("Draw", "void begin(const float2 &in = 0, float = 1)", asMETHOD(LS_Draw, begin), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Draw", "void moveForward(float)", asMETHOD(LS_Draw, moveForward), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Draw", "void rect(float, float, bool = false)", asMETHOD(LS_Draw, rect), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Draw", "void polygon(float, int = 8, float = 0, bool = false)", asMETHOD(LS_Draw, polygon), asCALL_THISCALL); assert(res >= 0); - - // Script variable. - res = engine->RegisterGlobalProperty("Draw draw", this); assert(res >= 0); - return res >= 0; + ScriptClassBegin("Draw", "draw", api); + { + ScriptObjMethod("void begin(const float2 &in = 0, float = 1)", begin); + ScriptObjMethod("void moveForward(float)", moveForward); + ScriptObjMethod("void rect(float, float, bool = false)", rect); + ScriptObjMethod("void polygon(float, int = 8, float = 0, bool = false)", polygon); + } + ScriptClassEnd(); } } diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.h b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.h index 1a8eab7ff..b3d410707 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_draw.h @@ -9,14 +9,12 @@ #include #ifdef ENABLE_FORCE_SCRIPT #include -#include -#include "ls_api.h" -#include +#include #include namespace LevelEditor { - class LS_Draw : public LS_API + class LS_Draw : public ScriptAPIClass { public: void begin(TFE_ForceScript::float2 pos, f32 scale); @@ -29,7 +27,7 @@ namespace LevelEditor // Skip sector/wall properties for now. // System - bool scriptRegister(asIScriptEngine* engine) override; + bool scriptRegister(ScriptAPI api) override; private: TFE_ForceScript::float2 m_startPos; TFE_ForceScript::float2 m_cursor; diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.cpp b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.cpp index b796df5fa..e2731b63c 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.cpp @@ -12,68 +12,6 @@ namespace LevelEditor #define Vec2f_to_float2(v) TFE_ForceScript::float2(v.x, v.z) #define float2_to_Vec2f(v) Vec2f{v.x, v.y}; - // Getters - std::string LS_Level::getName() - { - return s_level.name; - } - - std::string LS_Level::getSlot() - { - return s_level.slot; - } - - std::string LS_Level::getPalette() - { - return s_level.palette; - } - - s32 LS_Level::getSectorCount() - { - return (s32)s_level.sectors.size(); - } - - s32 LS_Level::getEntityCount() - { - return (s32)s_level.entities.size(); - } - - s32 LS_Level::getLevelNoteCount() - { - return (s32)s_level.notes.size(); - } - - s32 LS_Level::getGuidelineCount() - { - return (s32)s_level.guidelines.size(); - } - - s32 LS_Level::getMinLayer() - { - return s_level.layerRange[0]; - } - - s32 LS_Level::getMaxLayer() - { - return s_level.layerRange[1]; - } - - TFE_ForceScript::float2 LS_Level::getParallax() - { - return Vec2f_to_float2(s_level.parallax); - } - - // Setters - void LS_Level::setName(std::string& name) - { - s_level.name = name; - } - - void LS_Level::setPalette(std::string& palette) - { - s_level.palette = palette; - } - // Creation // TODO Sector drawing. // TODO Guideline drawing. @@ -106,85 +44,85 @@ namespace LevelEditor } } - bool LS_Level::scriptRegister(asIScriptEngine* engine) + bool LS_Level::scriptRegister(ScriptAPI api) { - s32 res = 0; - // Object Type - res = engine->RegisterObjectType("Level", sizeof(LS_Level), asOBJ_VALUE | asOBJ_POD); assert(res >= 0); - // Enums - res = engine->RegisterEnum("WallPart"); assert(res >= 0); - res = engine->RegisterEnumValue("WallPart", "PART_MID", WP_MID); assert(res >= 0); - res = engine->RegisterEnumValue("WallPart", "PART_TOP", WP_TOP); assert(res >= 0); - res = engine->RegisterEnumValue("WallPart", "PART_BOT", WP_BOT); assert(res >= 0); - res = engine->RegisterEnumValue("WallPart", "PART_SIGN", WP_SIGN); assert(res >= 0); - - res = engine->RegisterEnum("SectorFlags1"); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_EXTERIOR", SEC_FLAGS1_EXTERIOR); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_DOOR", SEC_FLAGS1_DOOR); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_MAG_SEAL", SEC_FLAGS1_MAG_SEAL); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_EXTERIOR_ADJOIN", SEC_FLAGS1_EXT_ADJ); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_ICE_FLOOR", SEC_FLAGS1_ICE_FLOOR); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_SNOW_FLOOR", SEC_FLAGS1_SNOW_FLOOR); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_EXPLODING_WALL", SEC_FLAGS1_EXP_WALL); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_PIT", SEC_FLAGS1_PIT); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_PIT_ADJOIN", SEC_FLAGS1_EXT_FLOOR_ADJ); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_CRUSHING", SEC_FLAGS1_CRUSHING); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_NOWALL_DRAW", SEC_FLAGS1_NOWALL_DRAW); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_LOW_DAMAGE", SEC_FLAGS1_LOW_DAMAGE); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_HIGH_DAMAGE", SEC_FLAGS1_HIGH_DAMAGE); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_NO_SMART_OBJ", SEC_FLAGS1_NO_SMART_OBJ); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_SMART_OBJ", SEC_FLAGS1_SMART_OBJ); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_SAFE_SECTOR", SEC_FLAGS1_SAFESECTOR); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_PLAYER", SEC_FLAGS1_PLAYER); assert(res >= 0); - res = engine->RegisterEnumValue("SectorFlags1", "SECTOR_FLAG1_SECRET", SEC_FLAGS1_SECRET); assert(res >= 0); - - res = engine->RegisterEnum("WallFlags1"); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_TRANSPARENT_MIDTEX", WF1_ADJ_MID_TEX); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_ILLUM_SIGN", WF1_ILLUM_SIGN); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_TEX_FLIP", WF1_FLIP_HORIZ); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_CHANGE_WALL_LIGHT", WF1_CHANGE_WALL_LIGHT); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_TEX_ANCHORED", WF1_TEX_ANCHORED); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_WALL_MORPHS", WF1_WALL_MORPHS); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SCROLL_TOP_TEX", WF1_SCROLL_TOP_TEX); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SCROLL_MID_TEX", WF1_SCROLL_MID_TEX); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SCROLL_BOT_TEX", WF1_SCROLL_BOT_TEX); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SCROLL_SIGN_TEX", WF1_SCROLL_SIGN_TEX); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_HIDE_ON_MAP", WF1_HIDE_ON_MAP); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SHOW_NORMAL_ON_MAP", WF1_SHOW_NORMAL_ON_MAP); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SIGN_ANCHORED", WF1_SIGN_ANCHORED); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_DAMAGE_WALL", WF1_DAMAGE_WALL); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SHOW_AS_LEDGE_ON_MAP", WF1_SHOW_AS_LEDGE_ON_MAP); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags1", "WALL_FLAGS1_SHOW_AS_DOOR_ON_MAP", WF1_SHOW_AS_DOOR_ON_MAP); assert(res >= 0); - - res = engine->RegisterEnum("WallFlags3"); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags3", "WALL_FLAGS3_ALWAYS_WALK", WF3_ALWAYS_WALK); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags3", "WALL_FLAGS3_SOLID", WF3_SOLID_WALL); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags3", "WALL_FLAGS3_PLAYER_WALK_ONLY", WF3_PLAYER_WALK_ONLY); assert(res >= 0); - res = engine->RegisterEnumValue("WallFlags3", "WALL_FLAGS3_CANNOT_FIRE_THRU", WF3_CANNOT_FIRE_THROUGH); assert(res >= 0); - - // Properties - // Functions - // -- Getters -- - res = engine->RegisterObjectMethod("Level", "string getName()", asMETHOD(LS_Level, getName), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "string getSlot()", asMETHOD(LS_Level, getSlot), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "string getPalette()", asMETHOD(LS_Level, getPalette), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getSectorCount()", asMETHOD(LS_Level, getSectorCount), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getEntityCount()", asMETHOD(LS_Level, getEntityCount), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getLevelNoteCount()", asMETHOD(LS_Level, getLevelNoteCount), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getGuidelineCount()", asMETHOD(LS_Level, getGuidelineCount), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getMinLayer()", asMETHOD(LS_Level, getMinLayer), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "int getMaxLayer()", asMETHOD(LS_Level, getMaxLayer), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "float2 getParallax()", asMETHOD(LS_Level, getParallax), asCALL_THISCALL); assert(res >= 0); - // TODO Vec3f s_level.bounds[2] - // Types: Sector, Entity, LevelNote, Guideline - // -- Setters -- - res = engine->RegisterObjectMethod("Level", "void setName(const string &in)", asMETHOD(LS_Level, setName), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "void setPalette(const string &in)", asMETHOD(LS_Level, setPalette), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "void findSector(const string &in)", asMETHOD(LS_Level, findSector), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("Level", "void findSectorById(int)", asMETHOD(LS_Level, findSectorById), asCALL_THISCALL); assert(res >= 0); - // Script variable. - res = engine->RegisterGlobalProperty("Level level", this); assert(res >= 0); - return res >= 0; + ScriptClassBegin("Level", "level", api); + { + // Enums + ScriptEnumRegister("WallPart"); + ScriptEnumStr(WP_MID); + ScriptEnumStr(WP_TOP); + ScriptEnumStr(WP_BOT); + ScriptEnumStr(WP_SIGN); + + ScriptEnumRegister("SectorFlags1"); + ScriptEnum("SECTOR_FLAG1_EXTERIOR", SEC_FLAGS1_EXTERIOR); + ScriptEnum("SECTOR_FLAG1_DOOR", SEC_FLAGS1_DOOR); + ScriptEnum("SECTOR_FLAG1_MAG_SEAL", SEC_FLAGS1_MAG_SEAL); + ScriptEnum("SECTOR_FLAG1_EXTERIOR_ADJOIN", SEC_FLAGS1_EXT_ADJ); + ScriptEnum("SECTOR_FLAG1_ICE_FLOOR", SEC_FLAGS1_ICE_FLOOR); + ScriptEnum("SECTOR_FLAG1_SNOW_FLOOR", SEC_FLAGS1_SNOW_FLOOR); + ScriptEnum("SECTOR_FLAG1_EXPLODING_WALL", SEC_FLAGS1_EXP_WALL); + ScriptEnum("SECTOR_FLAG1_PIT", SEC_FLAGS1_PIT); + ScriptEnum("SECTOR_FLAG1_PIT_ADJOIN", SEC_FLAGS1_EXT_FLOOR_ADJ); + ScriptEnum("SECTOR_FLAG1_CRUSHING", SEC_FLAGS1_CRUSHING); + ScriptEnum("SECTOR_FLAG1_NOWALL_DRAW", SEC_FLAGS1_NOWALL_DRAW); + ScriptEnum("SECTOR_FLAG1_LOW_DAMAGE", SEC_FLAGS1_LOW_DAMAGE); + ScriptEnum("SECTOR_FLAG1_HIGH_DAMAGE", SEC_FLAGS1_HIGH_DAMAGE); + ScriptEnum("SECTOR_FLAG1_NO_SMART_OBJ", SEC_FLAGS1_NO_SMART_OBJ); + ScriptEnum("SECTOR_FLAG1_SMART_OBJ", SEC_FLAGS1_SMART_OBJ); + ScriptEnum("SECTOR_FLAG1_SAFE_SECTOR", SEC_FLAGS1_SAFESECTOR); + ScriptEnum("SECTOR_FLAG1_PLAYER", SEC_FLAGS1_PLAYER); + ScriptEnum("SECTOR_FLAG1_SECRET", SEC_FLAGS1_SECRET); + + ScriptEnumRegister("WallFlags1"); + ScriptEnum("WALL_FLAGS1_TRANSPARENT_MIDTEX", WF1_ADJ_MID_TEX); + ScriptEnum("WALL_FLAGS1_ILLUM_SIGN", WF1_ILLUM_SIGN); + ScriptEnum("WALL_FLAGS1_TEX_FLIP", WF1_FLIP_HORIZ); + ScriptEnum("WALL_FLAGS1_CHANGE_WALL_LIGHT", WF1_CHANGE_WALL_LIGHT); + ScriptEnum("WALL_FLAGS1_TEX_ANCHORED", WF1_TEX_ANCHORED); + ScriptEnum("WALL_FLAGS1_WALL_MORPHS", WF1_WALL_MORPHS); + ScriptEnum("WALL_FLAGS1_SCROLL_TOP_TEX", WF1_SCROLL_TOP_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_MID_TEX", WF1_SCROLL_MID_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_BOT_TEX", WF1_SCROLL_BOT_TEX); + ScriptEnum("WALL_FLAGS1_SCROLL_SIGN_TEX", WF1_SCROLL_SIGN_TEX); + ScriptEnum("WALL_FLAGS1_HIDE_ON_MAP", WF1_HIDE_ON_MAP); + ScriptEnum("WALL_FLAGS1_SHOW_NORMAL_ON_MAP", WF1_SHOW_NORMAL_ON_MAP); + ScriptEnum("WALL_FLAGS1_SIGN_ANCHORED", WF1_SIGN_ANCHORED); + ScriptEnum("WALL_FLAGS1_DAMAGE_WALL", WF1_DAMAGE_WALL); + ScriptEnum("WALL_FLAGS1_SHOW_AS_LEDGE_ON_MAP", WF1_SHOW_AS_LEDGE_ON_MAP); + ScriptEnum("WALL_FLAGS1_SHOW_AS_DOOR_ON_MAP", WF1_SHOW_AS_DOOR_ON_MAP); + + ScriptEnumRegister("WallFlags3"); + ScriptEnum("WALL_FLAGS3_ALWAYS_WALK", WF3_ALWAYS_WALK); + ScriptEnum("WALL_FLAGS3_SOLID", WF3_SOLID_WALL); + ScriptEnum("WALL_FLAGS3_PLAYER_WALK_ONLY", WF3_PLAYER_WALK_ONLY); + ScriptEnum("WALL_FLAGS3_CANNOT_FIRE_THRU", WF3_CANNOT_FIRE_THROUGH); + + // Functions + ScriptObjMethod("void findSector(const string &in)", findSector); + ScriptObjMethod("void findSectorById(int)", findSectorById); + // -- Getters -- + ScriptLambdaPropertyGet("string get_name()", std::string, { return s_level.name; }); + ScriptLambdaPropertyGet("string get_slot()", std::string, { return s_level.slot; }); + ScriptLambdaPropertyGet("string get_palette()", std::string, { return s_level.palette; }); + ScriptLambdaPropertyGet("int get_sectorCount()", s32, { return (s32)s_level.sectors.size(); }); + ScriptLambdaPropertyGet("int get_entityCount()", s32, { return (s32)s_level.entities.size(); }); + ScriptLambdaPropertyGet("int get_levelNoteCount()", s32, { return (s32)s_level.notes.size(); }); + ScriptLambdaPropertyGet("int get_guidelineCount()", s32, { return (s32)s_level.guidelines.size(); }); + ScriptLambdaPropertyGet("int get_minLayer()", s32, { return s_level.layerRange[0]; }); + ScriptLambdaPropertyGet("int get_maxLayer()", s32, { return s_level.layerRange[1]; }); + ScriptLambdaPropertyGet("float2 get_parallax()", TFE_ForceScript::float2, { return Vec2f_to_float2(s_level.parallax); }); + // TODO Vec3f s_level.bounds[2] + // Types: Sector, Entity, LevelNote, Guideline + // -- Setters -- + ScriptLambdaPropertySet("void set_name(const string &in)", (std::string& name), { s_level.name = name; }); + ScriptLambdaPropertySet("void set_palette(const string &in)", (std::string& palette), { s_level.palette = palette; }); + + // Setup a script variables. + ScriptMemberVariable("int index", m_index); + } + ScriptClassEnd(); } } #endif \ No newline at end of file diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.h b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.h index 2bf28c32c..65f8de84a 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_level.h @@ -9,34 +9,22 @@ #include #ifdef ENABLE_FORCE_SCRIPT #include -#include -#include "ls_api.h" +#include #include namespace LevelEditor { - class LS_Level : public LS_API + class LS_Level : public ScriptAPIClass { public: - // Properties - // Functions - std::string getName(); - std::string getSlot(); - std::string getPalette(); - s32 getSectorCount(); - s32 getEntityCount(); - s32 getLevelNoteCount(); - s32 getGuidelineCount(); - s32 getMinLayer(); - s32 getMaxLayer(); - TFE_ForceScript::float2 getParallax(); - - void setName(std::string& name); - void setPalette(std::string& name); void findSector(std::string& name); void findSectorById(s32 id); // System - bool scriptRegister(asIScriptEngine* engine) override; + bool scriptRegister(ScriptAPI api) override; + + public: + // Variables + s32 m_index; }; } #endif \ No newline at end of file diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.cpp b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.cpp index d7b1232f0..88b7cb43e 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -116,21 +116,13 @@ namespace LevelEditor return pos; } - bool LS_Selection::scriptRegister(asIScriptEngine* engine) + bool LS_Selection::scriptRegister(ScriptAPI api) { - s32 res = 0; - // Object Type - res = engine->RegisterObjectType("Selection", sizeof(LS_Selection), asOBJ_VALUE | asOBJ_POD); assert(res >= 0); - // Properties - - //------------------------------------ - // Functions - //------------------------------------ - res = engine->RegisterObjectMethod("Selection", "float2 getPositionXZ(int = 0)", asMETHOD(LS_Selection, getPositionXZ), asCALL_THISCALL); assert(res >= 0); - - // Script variable. - res = engine->RegisterGlobalProperty("Selection selection", this); assert(res >= 0); - return res >= 0; + ScriptClassBegin("Selection", "selection", api); + { + ScriptObjMethod("float2 getPositionXZ(int = 0)", getPositionXZ); + } + ScriptClassEnd(); } } diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.h b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.h index 099bbde9b..dafd9209e 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_selection.h @@ -9,19 +9,17 @@ #include #ifdef ENABLE_FORCE_SCRIPT #include -#include -#include "ls_api.h" -#include +#include #include namespace LevelEditor { - class LS_Selection : public LS_API + class LS_Selection : public ScriptAPIClass { public: TFE_ForceScript::float2 getPositionXZ(s32 index = 0); // System - bool scriptRegister(asIScriptEngine* engine) override; + bool scriptRegister(ScriptAPI api) override; }; } #endif \ No newline at end of file diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.cpp b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.cpp index 726284010..4f506b05e 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.cpp @@ -2,8 +2,10 @@ #include "levelEditorScripts.h" #include #include -#include +#include #include +#include +#include #ifdef ENABLE_FORCE_SCRIPT #include @@ -12,6 +14,154 @@ namespace LevelEditor { namespace // Static helper functions. { + std::string toString(CScriptArray* arr) + { + char output[4096] = ""; + s32 typeId = arr->GetElementTypeId(); + char tmp[256]; + + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_ARRAY)) + { + // Can't handle multi-dimensional arrays yet. + return "?"; + } + + const u32 len = arr->GetSize(); + for (u32 i = 0; i < len; i++) + { + const void* data = arr->At(i); + std::string value; + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_STRING)) + { + value = *(std::string*)data; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) + { + value = toString(*(TFE_ForceScript::float2*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + value = toStringF3(*(TFE_ForceScript::float3*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4)) + { + value = toStringF4(*(TFE_ForceScript::float4*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2x2)) + { + value = toStringF2x2(*(TFE_ForceScript::float2x2*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3x3)) + { + value = toStringF3x3(*(TFE_ForceScript::float3x3*)data); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4x4)) + { + value = toStringF4x4(*(TFE_ForceScript::float4x4*)data); + } + else + { + switch (typeId) + { + case 2: + { + char local = *(char*)(data); + sprintf(tmp, "%c", local); + value = tmp; + break; + } + case 3: + { + s16 local = *(s16*)(data); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 4: + { + s32 local = *(s32*)(data); + sprintf(tmp, "%d", local); + value = tmp; + break; + } + case 5: + { + s64 local = *(s64*)(data); + sprintf(tmp, "%lld", local); + value = tmp; + break; + } + case 6: + { + u8 local = *(u8*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 7: + { + u16 local = *(u16*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 8: + { + u32 local = *(u32*)(data); + sprintf(tmp, "%u", local); + value = tmp; + break; + } + case 9: + { + u64 local = *(u64*)(data); + sprintf(tmp, "%llu", local); + value = tmp; + break; + } + case 10: + { + f32 local = *(f32*)(data); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + case 11: + { + f64 local = *(f64*)(data); + sprintf(tmp, "%g", local); + value = tmp; + break; + } + default: + { + return "?"; + } + } + } + + char fmt[256]; + if (len == 1) + { + sprintf(fmt, "(%s)", value.c_str()); + } + else if (i == 0) + { + sprintf(fmt, "(%s, ", value.c_str()); + } + else if (i < len - 1) + { + sprintf(fmt, "%s, ", value.c_str()); + } + else + { + sprintf(fmt, "%s)", value.c_str()); + } + strcat(output, fmt); + } + return output; + } + std::string formatValue(s32 typeId, void* ref) { std::string value; @@ -21,10 +171,30 @@ namespace LevelEditor { value = *(std::string*)ref; } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_ARRAY)) + { + value = toString((CScriptArray*)ref); + } else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) { value = toString(*(TFE_ForceScript::float2*)ref); } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + value = toStringF3(*(TFE_ForceScript::float3*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2x2)) + { + value = toStringF2x2(*(TFE_ForceScript::float2x2*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3x3)) + { + value = toStringF3x3(*(TFE_ForceScript::float3x3*)ref); + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4x4)) + { + value = toStringF4x4(*(TFE_ForceScript::float4x4*)ref); + } else { switch (typeId) @@ -165,7 +335,7 @@ namespace LevelEditor void LS_System::runScript(std::string& scriptName) { - runLevelScript(scriptName.c_str()); + TFE_ScriptInterface::runScript(scriptName.c_str(), "main"); } void LS_System::showScript(std::string& scriptName) @@ -173,37 +343,32 @@ namespace LevelEditor showLevelScript(scriptName.c_str()); } - bool LS_System::scriptRegister(asIScriptEngine* engine) + bool LS_System::scriptRegister(ScriptAPI api) { - s32 res = 0; - // Object Type - res = engine->RegisterObjectType("System", sizeof(LS_System), asOBJ_VALUE | asOBJ_POD); assert(res >= 0); - // Properties - res = engine->RegisterObjectProperty("System", "int version", asOFFSET(LS_System, version)); assert(res >= 0); - - //------------------------------------ - // Functions - //------------------------------------ - // Currently support up to 10 arguments + format string. - // This has to be a static member of the class since it use the GENERIC calling convention, but it is still added as an object method - // so the script API follows the desired `system.func()` pattern. - res = engine->RegisterObjectMethod("System", - "void error(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", - asFUNCTION(error), asCALL_GENERIC); assert(res >= 0); - res = engine->RegisterObjectMethod("System", - "void warning(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", - asFUNCTION(warning), asCALL_GENERIC); assert(res >= 0); - res = engine->RegisterObjectMethod("System", - "void print(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", - asFUNCTION(print), asCALL_GENERIC); assert(res >= 0); - - res = engine->RegisterObjectMethod("System", "void clearOutput()", asMETHOD(LS_System, clearOutput), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("System", "void runScript(const string &in)", asMETHOD(LS_System, runScript), asCALL_THISCALL); assert(res >= 0); - res = engine->RegisterObjectMethod("System", "void showScript(const string &in)", asMETHOD(LS_System, showScript), asCALL_THISCALL); assert(res >= 0); - - // Script variable. - res = engine->RegisterGlobalProperty("System system", this); assert(res >= 0); - return res >= 0; + ScriptClassBegin("System", "system", api); + { + // Member Variables + ScriptMemberVariable("int version", version); + + // Functions + // Currently support up to 10 arguments + format string. + // This has to be a static member of the class since it use the GENERIC calling convention, but it is still added as an object method + // so the script API follows the desired `system.func()` pattern. + ScriptGenericMethod( + "void error(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + error); + ScriptGenericMethod( + "void warning(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + warning); + ScriptGenericMethod( + "void print(const string &in, ?&in arg0 = 0, ?&in arg1 = 0, ?&in arg2 = 0, ?&in arg3 = 0, ?&in arg4 = 0, ?&in arg5 = 0, ?&in arg6 = 0, ?&in arg7 = 0, ?&in arg8 = 0, ?&in arg9 = 0)", + print); + + ScriptObjMethod("void clearOutput()", clearOutput); + ScriptObjMethod("void runScript(const string &in)", runScript); + ScriptObjMethod("void showScript(const string &in)", showScript); + } + ScriptClassEnd(); } } diff --git a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.h b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.h index 1f03bb460..6e58c9ed4 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/Scripting/ls_system.h @@ -9,13 +9,12 @@ #include #ifdef ENABLE_FORCE_SCRIPT #include -#include "ls_api.h" -#include +#include #include namespace LevelEditor { - class LS_System : public LS_API + class LS_System : public ScriptAPIClass { public: // Properties @@ -28,7 +27,7 @@ namespace LevelEditor void runScript(std::string& scriptName); void showScript(std::string& scriptName); // System - bool scriptRegister(asIScriptEngine* engine) override; + bool scriptRegister(ScriptAPI api) override; }; } #endif \ No newline at end of file diff --git a/TheForceEngine/TFE_Editor/LevelEditor/infoPanel.cpp b/TheForceEngine/TFE_Editor/LevelEditor/infoPanel.cpp index 0ad4eba0f..fb96b305b 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/infoPanel.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/infoPanel.cpp @@ -971,7 +971,7 @@ namespace LevelEditor if (s_editMode == LEDIT_SECTOR) { s_sectorInfo.resize(count); - for (s32 s = 0; s < count; s++) + for (s32 s = 0; s < (s32)count; s++) { selection_getSector(s, sector); s_sectorInfo[s] = sector; @@ -984,7 +984,7 @@ namespace LevelEditor { s32 wallIndex; HitPart part; - for (s32 s = 0; s < count; s++) + for (s32 s = 0; s < (s32)count; s++) { selection_getSurface(s, sector, wallIndex, &part); if (part == HP_FLOOR || part == HP_CEIL) diff --git a/TheForceEngine/TFE_Editor/LevelEditor/levelEditor.cpp b/TheForceEngine/TFE_Editor/LevelEditor/levelEditor.cpp index 43e025a1c..0146b1286 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/levelEditor.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/levelEditor.cpp @@ -223,7 +223,8 @@ namespace LevelEditor bool init(Asset* asset) { - registerScriptFunctions(); + TFE_ScriptInterface::registerScriptInterface(API_LEVEL_EDITOR); + TFE_ScriptInterface::setAPI(API_LEVEL_EDITOR, "EditorDef/Scripts"); s_levelAsset = asset; // Initialize editors. @@ -2123,7 +2124,8 @@ namespace LevelEditor void update() { - levelScript_update(); + // levelScript_update(); + TFE_ScriptInterface::update(); updateViewportScroll(); handleHotkeys(); @@ -3444,7 +3446,7 @@ namespace LevelEditor }; const f32 c_scrollEps = 0.001f; - const f32 c_scrollMinScpeed = 128.0f; + const f32 c_scrollMinSpeed = 128.0f; const f32 c_scrollAngularSpd = 3.0f; static ViewportScrollMode s_viewScrollMode; @@ -3479,7 +3481,7 @@ namespace LevelEditor s_scrollLen = sqrtf(delta.x*delta.x + delta.z*delta.z); s_scrollDelta = delta; s_scrollPos = 0.0f; - s_scrollSpeed = speed == 0.0f ? max(c_scrollMinScpeed, s_scrollLen) : speed; + s_scrollSpeed = speed == 0.0f ? max(c_scrollMinSpeed, s_scrollLen) : speed; s_scrollView = true; s_viewScrollMode = VSCROLL_2D; } @@ -3500,7 +3502,7 @@ namespace LevelEditor s_scrollLen = sqrtf(delta.x*delta.x + delta.y*delta.y + delta.z*delta.z); s_scrollDelta3d = delta; s_scrollPos = 0.0f; - s_scrollSpeed = speed == 0.0f ? max(c_scrollMinScpeed, s_scrollLen) : speed; + s_scrollSpeed = speed == 0.0f ? max(c_scrollMinSpeed, s_scrollLen) : speed; s_scrollSrcAngles = { fmodf(s_camera.yaw + 2.0f*PI, 2.0f*PI), s_camera.pitch }; s_scrollDstAngles = { targetYaw, targetPitch }; @@ -3697,7 +3699,7 @@ namespace LevelEditor { EditorSector* hoveredSector = nullptr; selection_getSector(SEL_INDEX_HOVERED, hoveredSector); - if (!hoveredSector->name.empty()) + if (hoveredSector && !hoveredSector->name.empty()) { bool showInfo = true; Vec2i mapPos; diff --git a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorData.h b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorData.h index 06547bd29..e0a657856 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorData.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorData.h @@ -31,7 +31,8 @@ namespace LevelEditor LEF_Groups = 8, LEF_LevelNotes =10, LEF_Guidelines =11, - LEF_CurVersion =11, + LEF_ScriptCall1=12, + LEF_CurVersion =12, }; enum LevelEditMode diff --git a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.cpp b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.cpp index c927c1b8f..eaad3332e 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.cpp +++ b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.cpp @@ -141,7 +141,8 @@ namespace LevelEditor "Message", // ISC_MESSAGE "Adjoin", // ISC_ADJOIN "Texture", // ISC_TEXTURE - "Page" // ISC_PAGE + "Page", // ISC_PAGE + "ScriptCall", // ISC_SCRIPTCALL }; const char* c_infElevTypeName[] = @@ -276,7 +277,7 @@ namespace LevelEditor s_levelInf.elevator.clear(); s_levelInf.trigger.clear(); s_levelInf.teleport.clear(); - s_overlayList = {}; + s_overlayList = OverlayAssetList{}; } void editor_infDestroy() @@ -1626,6 +1627,7 @@ namespace LevelEditor const ImVec4 colorKeywordOuterSel = { 0.453f, 0.918f, 1.00f, 1.0f }; const ImVec4 colorKeywordOuter = { 0.302f, 0.612f, 0.84f, 1.0f }; const ImVec4 colorKeywordInner = { 0.306f, 0.788f, 0.69f, 1.0f }; + const ImVec4 colorKeywordArg = { 0.306f, 0.788f*0.5f, 0.69f, 1.0f }; const ImVec4 colorInnerHeaderBase = { 0.98f, 0.49f, 0.26f, 1.0f }; const ImVec4 colorInnerHeader = { colorInnerHeaderBase.x, colorInnerHeaderBase.y, colorInnerHeaderBase.z, 0.31f }; @@ -1730,6 +1732,7 @@ namespace LevelEditor for (s32 s = 0; s < stopCount; s++, stop++) { *contentHeight += elemHeight * f32(stop->msg.size()); + *contentHeight += elemHeight * f32(stop->scriptCall.size()); *contentHeight += elemHeight * f32(stop->adjoinCmd.size()); *contentHeight += elemHeight * f32(stop->textureCmd.size()); *contentHeight += (stop->overrideSet & ISO_PAGE) ? elemHeight : 0.0f; @@ -2463,6 +2466,10 @@ namespace LevelEditor stop->page = {}; stop->overrideSet |= ISO_PAGE; } break; + case ISC_SCRIPTCALL: + { + stop->scriptCall.push_back({}); + } break; } } setTooltip("Add a new command to the selected stop."); @@ -2472,6 +2479,7 @@ namespace LevelEditor const s32 msgCount = (s32)stop->msg.size(); const s32 adjoinCount = (s32)stop->adjoinCmd.size(); const s32 texCount = (s32)stop->textureCmd.size(); + const s32 scriptCallCount = (s32)stop->scriptCall.size(); s32 index = -1; s32 cmdIndexOffset = 0; @@ -2487,6 +2495,18 @@ namespace LevelEditor } cmdIndexOffset += msgCount; + if (index < 0 && s_infEditor.curStopCmdIndex < scriptCallCount + cmdIndexOffset) + { + index = s_infEditor.curStopCmdIndex - cmdIndexOffset; + for (s32 i = index; i < scriptCallCount - 1; i++) + { + stop->scriptCall[i] = stop->scriptCall[i + 1]; + } + stop->scriptCall.pop_back(); + s_infEditor.curStopCmdIndex = -1; + } + cmdIndexOffset += scriptCallCount; + if (index < 0 && s_infEditor.curStopCmdIndex < adjoinCount + cmdIndexOffset) { index = s_infEditor.curStopCmdIndex - cmdIndexOffset; @@ -2497,9 +2517,9 @@ namespace LevelEditor stop->adjoinCmd.pop_back(); s_infEditor.curStopCmdIndex = -1; } - cmdIndexOffset += (s32)stop->adjoinCmd.size(); + cmdIndexOffset += adjoinCount; - if (index < 0 && s_infEditor.curStopCmdIndex < (s32)stop->textureCmd.size() + cmdIndexOffset) + if (index < 0 && s_infEditor.curStopCmdIndex < texCount + cmdIndexOffset) { index = s_infEditor.curStopCmdIndex - cmdIndexOffset; for (s32 i = index; i < texCount - 1; i++) @@ -2509,7 +2529,7 @@ namespace LevelEditor stop->textureCmd.pop_back(); s_infEditor.curStopCmdIndex = -1; } - cmdIndexOffset += (s32)stop->textureCmd.size(); + cmdIndexOffset += texCount; if (index < 0 && s_infEditor.curStopCmdIndex <= cmdIndexOffset) { @@ -2517,6 +2537,7 @@ namespace LevelEditor stop->overrideSet &= ~ISO_PAGE; index = 0; } + cmdIndexOffset++; } setTooltip("Remove the selected command from the stop."); ImGui::SameLine(0.0f, 16.0f); @@ -2822,6 +2843,35 @@ namespace LevelEditor } cmdIndexOffset += msgCount; + const s32 scriptCallCount = (s32)stop->scriptCall.size(); + Editor_ScriptCall* call = stop->scriptCall.data(); + for (s32 c = 0; c < scriptCallCount; c++, call++) + { + bool cmdSelected = editor_stopCmdSelectable(elev, stop, itemClassIndex, c + cmdIndexOffset, "ScriptCall:"); + + ImGui::Text("Function"); ImGui::SameLine(0.0f, 8.0f); + strcpy(targetBuffer, call->funcName.c_str()); + ImGui::SetNextItemWidth(128.0f); + if (ImGui::InputText(editor_getUniqueLabel(""), targetBuffer, 256)) + { + call->funcName = targetBuffer; + } + ImGui::SameLine(0.0f, 16.0f); + + ImGui::Text("Arguments"); ImGui::SameLine(0.0f, 8.0f); + for (s32 a = 0; a < 4; a++) + { + strcpy(targetBuffer, call->arg[a].value.c_str()); + ImGui::SetNextItemWidth(96.0f); + if (ImGui::InputText(editor_getUniqueLabel(""), targetBuffer, 256)) + { + call->arg[a].value = targetBuffer; + } + if (a < 3) { ImGui::SameLine(0.0f, 8.0f); } + } + } + cmdIndexOffset += scriptCallCount; + const s32 adjoinCount = (s32)stop->adjoinCmd.size(); Editor_InfAdjoinCmd* cmd = stop->adjoinCmd.data(); for (s32 c = 0; c < adjoinCount; c++, cmd++) @@ -3621,6 +3671,22 @@ namespace LevelEditor appendToBuffer(outStr, buffer); } + const s32 scriptCallCount = (s32)stop->scriptCall.size(); + const Editor_ScriptCall* call = stop->scriptCall.data(); + for (s32 c = 0; c < scriptCallCount; c++, call++) + { + char argList[4096] = ""; + for (s32 a = 0; a < 4; a++) + { + if (call->arg[a].value.empty() || call->arg[a].value == "") { break; } + strcat(argList, call->arg[a].value.c_str()); + strcat(argList, " "); + } + + sprintf(buffer, "%s%s%sScriptCall: %d %s %s", curTab, tab, tab, s, call->funcName.c_str(), argList); + appendToBuffer(outStr, buffer); + } + const s32 adjoinCount = (s32)stop->adjoinCmd.size(); const Editor_InfAdjoinCmd* adjoinCmd = stop->adjoinCmd.data(); for (s32 a = 0; a < adjoinCount; a++, adjoinCmd++) @@ -4014,6 +4080,22 @@ namespace LevelEditor ImGui::Text("%d", s); ImGui::SameLine(0.0f, 8.0f); ImGui::Text("%s", stop->page.c_str()); } + + const s32 scriptCallCount = (s32)stop->scriptCall.size(); + const Editor_ScriptCall* call = stop->scriptCall.data(); + for (s32 c = 0; c < scriptCallCount; c++, call++) + { + ImGui::Text("%s%s", tab, tab); ImGui::SameLine(0.0f, 0.0f); + ImGui::TextColored(colorKeywordInner, "ScriptCall:"); ImGui::SameLine(0.0f, 8.0f); + ImGui::Text("%d", s); ImGui::SameLine(0.0f, 8.0f); + + ImGui::Text("%s", call->funcName.c_str()); ImGui::SameLine(0.0f, 8.0f); + for (s32 a = 0; a < 4; a++) + { + if (call->arg[a].value == "") { break; } + ImGui::TextColored(colorKeywordArg, "%s", call->arg[a].value.c_str()); ImGui::SameLine(0.0f, 8.0f); + } + } } const s32 slaveCount = (s32)elev->slaves.size(); @@ -4404,9 +4486,14 @@ namespace LevelEditor for (s32 s = 0; s < stopCount; s++, stop++) { s32 adjoinCount, texCount, msgCount; + s32 scriptCallCount = 0; file.read(&adjoinCount); file.read(&texCount); file.read(&msgCount); + if (version >= LEF_ScriptCall1) + { + file.read(&scriptCallCount); + } stop->adjoinCmd.resize(adjoinCount); stop->textureCmd.resize(texCount); stop->msg.resize(msgCount); @@ -4455,6 +4542,20 @@ namespace LevelEditor file.read(&msg->eventFlags); file.read(msg->arg, 2); } + + if (version >= LEF_ScriptCall1) + { + stop->scriptCall.resize(scriptCallCount); + Editor_ScriptCall* scriptCall = stop->scriptCall.data(); + for (s32 s = 0; s < scriptCallCount; s++, scriptCall++) + { + file.read(&scriptCall->funcName); + for (s32 a = 0; a < 4; a++) + { + file.read(&scriptCall->arg[a].value); + } + } + } } // Slaves @@ -4711,9 +4812,12 @@ namespace LevelEditor const s32 adjoinCount = (s32)stop->adjoinCmd.size(); const s32 texCount = (s32)stop->textureCmd.size(); const s32 msgCount = (s32)stop->msg.size(); + const s32 scriptCallCount = (s32)stop->scriptCall.size(); file.write(&adjoinCount); file.write(&texCount); file.write(&msgCount); + // version >= LEF_ScriptCall1 + file.write(&scriptCallCount); file.write(&stop->overrideSet); s32 rel = stop->relative ? 1 : 0; @@ -4753,6 +4857,17 @@ namespace LevelEditor file.write(&msg->eventFlags); file.write(msg->arg, 2); } + + // version >= LEF_ScriptCall1 + const Editor_ScriptCall* scriptCall = stop->scriptCall.data(); + for (s32 s = 0; s < scriptCallCount; s++, scriptCall++) + { + file.write(&scriptCall->funcName); + for (s32 a = 0; a < 4; a++) + { + file.write(&scriptCall->arg[a].value); + } + } } // Slaves diff --git a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.h b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.h index dc5fab6bd..b4dd22a48 100644 --- a/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.h +++ b/TheForceEngine/TFE_Editor/LevelEditor/levelEditorInf.h @@ -153,6 +153,7 @@ namespace LevelEditor ISC_ADJOIN, ISC_TEXTURE, ISC_PAGE, + ISC_SCRIPTCALL, ISC_COUNT }; @@ -198,6 +199,17 @@ namespace LevelEditor u32 arg[2] = { 0 }; }; + struct Editor_ScriptCallArg + { + std::string value = ""; + }; + + struct Editor_ScriptCall + { + std::string funcName; + Editor_ScriptCallArg arg[4]; + }; + struct Editor_InfStop { u32 overrideSet = ISO_NONE; // InfStopOverride - Which values were overriden from the defaults. @@ -212,6 +224,7 @@ namespace LevelEditor std::vector adjoinCmd; std::vector textureCmd; std::vector msg; + std::vector scriptCall; std::string page; }; diff --git a/TheForceEngine/TFE_Editor/editor.cpp b/TheForceEngine/TFE_Editor/editor.cpp index 3984a7feb..5dc3a9913 100644 --- a/TheForceEngine/TFE_Editor/editor.cpp +++ b/TheForceEngine/TFE_Editor/editor.cpp @@ -120,7 +120,7 @@ namespace TFE_Editor TFE_RenderShared::modelDraw_init(); thumbnail_init(64); TFE_Polygon::clipInit(); - s_msgBox = {}; + s_msgBox = MessageBox{}; s_gpuImages.clear(); } diff --git a/TheForceEngine/TFE_Editor/editorConfig.cpp b/TheForceEngine/TFE_Editor/editorConfig.cpp index 9044db5da..9d8856650 100644 --- a/TheForceEngine/TFE_Editor/editorConfig.cpp +++ b/TheForceEngine/TFE_Editor/editorConfig.cpp @@ -73,7 +73,7 @@ namespace TFE_Editor parser.addCommentString(";"); parser.addCommentString("#"); - s_editorConfig = {}; + s_editorConfig = EditorConfig{}; clearRecents(); size_t bufferPos = 0; diff --git a/TheForceEngine/TFE_Editor/editorLevel.cpp b/TheForceEngine/TFE_Editor/editorLevel.cpp index c44b5761d..21e31f7af 100644 --- a/TheForceEngine/TFE_Editor/editorLevel.cpp +++ b/TheForceEngine/TFE_Editor/editorLevel.cpp @@ -46,7 +46,7 @@ namespace TFE_Editor void level_prepareNew() { - s_newLevel = {}; + s_newLevel = NewLevel{}; } bool level_createEmpty(NewLevel& level) diff --git a/TheForceEngine/TFE_Editor/editorProject.cpp b/TheForceEngine/TFE_Editor/editorProject.cpp index 1bc382364..9d42f311c 100644 --- a/TheForceEngine/TFE_Editor/editorProject.cpp +++ b/TheForceEngine/TFE_Editor/editorProject.cpp @@ -48,14 +48,14 @@ namespace TFE_Editor void project_close() { - s_curProject = {}; + s_curProject = Project{}; resources_clear(); AssetBrowser::rebuildAssets(); } void project_prepareNew() { - s_newProject = {}; + s_newProject = Project{}; s_createDir = true; } @@ -117,7 +117,7 @@ namespace TFE_Editor parser.addCommentString(";"); parser.addCommentString("#"); - s_curProject = {}; + s_curProject = Project{}; s_curStringOut = nullptr; size_t bufferPos = 0; bool inResource = false; @@ -170,7 +170,7 @@ namespace TFE_Editor TFE_System::logWrite(LOG_ERROR, "Editor Project", "Editor Project Path '%s' does not exist.", s_curProject.path); removeFromRecents(s_curProject.path); - s_curProject = {}; + s_curProject = Project{}; return false; } diff --git a/TheForceEngine/TFE_ExternalData/logicTables.cpp b/TheForceEngine/TFE_ExternalData/logicTables.cpp index ee64fbdf4..ad8bced22 100644 --- a/TheForceEngine/TFE_ExternalData/logicTables.cpp +++ b/TheForceEngine/TFE_ExternalData/logicTables.cpp @@ -72,6 +72,7 @@ namespace TFE_ExternalData "LIFE", // 42 "MEDKIT", // 43 "PILE", // 44 + "ITEM10", // 45 - added for s_playerInfo.itemUnused }; const char* df_effectTable[] = @@ -95,4 +96,18 @@ namespace TFE_ExternalData "EXP_NO_DMG", // 16 // medium explosion, no damage. "EXP_25", // 17 // medium explosion, 25 damage. }; + + const char* df_weaponTable[] = + { + "FIST", // 0 + "PISTOL", // 1 + "RIFLE", // 2 + "THERMAL_DET", // 3 + "REPEATER", // 4 + "FUSION", // 5 + "MORTAR", // 6 + "MINE", // 7 + "CONCUSSION", // 8 + "CANNON", // 9 + }; } \ No newline at end of file diff --git a/TheForceEngine/TFE_ExternalData/logicTables.h b/TheForceEngine/TFE_ExternalData/logicTables.h index 93bbf7805..11215a1ee 100644 --- a/TheForceEngine/TFE_ExternalData/logicTables.h +++ b/TheForceEngine/TFE_ExternalData/logicTables.h @@ -5,5 +5,6 @@ namespace TFE_ExternalData extern const char* df_projectileTable[]; extern const char* df_dropItemTable[]; extern const char* df_effectTable[]; + extern const char* df_weaponTable[]; } diff --git a/TheForceEngine/TFE_ExternalData/pickupExternal.cpp b/TheForceEngine/TFE_ExternalData/pickupExternal.cpp new file mode 100644 index 000000000..d4c416c9c --- /dev/null +++ b/TheForceEngine/TFE_ExternalData/pickupExternal.cpp @@ -0,0 +1,581 @@ +#include +#include +#include +#include +#include +#include +#include +#include "pickupExternal.h" +#include "logicTables.h" + +namespace TFE_ExternalData +{ + static MaxAmounts s_externalMaxAmounts; + static ExternalPickup s_externalPickups[TFE_DarkForces::ITEM_COUNT]; + static bool s_externalPickupsFromMod = false; + + //////////////////////////////// + // Forward Declarations + //////////////////////////////// + int getPickupIndex(char* name); + bool tryAssignMaxAmount(cJSON* data); + bool tryAssignPickupProperty(cJSON* data, ExternalPickup& pickup); + + + MaxAmounts* getMaxAmounts() + { + return &s_externalMaxAmounts; + } + + ExternalPickup* getExternalPickups() + { + return s_externalPickups; + } + + void clearExternalPickups() + { + s_externalPickupsFromMod = false; + for (int i = 0; i < TFE_DarkForces::ITEM_COUNT; i++) + { + s_externalPickups[i].name = nullptr; + } + + // Restore defaults + s_externalMaxAmounts.ammoEnergyMax = 500; + s_externalMaxAmounts.ammoPowerMax = 500; + s_externalMaxAmounts.ammoShellMax = 50; + s_externalMaxAmounts.ammoPlasmaMax = 400; + s_externalMaxAmounts.ammoDetonatorMax = 50; + s_externalMaxAmounts.ammoMineMax = 30; + s_externalMaxAmounts.ammoMissileMax = 20; + s_externalMaxAmounts.shieldsMax = 200; + s_externalMaxAmounts.batteryPowerMax = 2 * ONE_16; + s_externalMaxAmounts.healthMax = 100; + } + + void loadExternalPickups() + { + const char* programDir = TFE_Paths::getPath(PATH_PROGRAM); + char extDataFile[TFE_MAX_PATH]; + sprintf(extDataFile, "%sExternalData/DarkForces/pickups.json", programDir); + + TFE_System::logWrite(LOG_MSG, "EXTERNAL_DATA", "Loading pickup data"); + FileStream file; + if (!file.open(extDataFile, FileStream::MODE_READ)) { return; } + + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + if (!data || size == 0) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Pickups.json is %u bytes in size and cannot be read.", size); + return; + } + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + + parseExternalPickups(data, false); + free(data); + } + + void parseExternalPickups(char* data, bool fromMod) + { + // If pickups have already been loaded from a mod, don't replace them + if (s_externalPickupsFromMod) + { + return; + } + + cJSON* root = cJSON_Parse(data); + if (root) + { + cJSON* section = root->child; + while (section) + { + if (cJSON_IsObject(section) && strcasecmp(section->string, "maxAmounts") == 0) + { + // parse the Max Amounts + cJSON* maxAmt = section->child; + while (maxAmt) + { + if (cJSON_IsNumber(maxAmt)) + { + tryAssignMaxAmount(maxAmt); + } + + maxAmt = maxAmt->next; + } + } + else if (cJSON_IsArray(section) && strcasecmp(section->string, "pickups") == 0) + { + cJSON* pickup = section->child; + while (pickup) + { + cJSON* pickupName = pickup->child; + + // get the pickup name + if (pickupName && cJSON_IsString(pickupName) && strcasecmp(pickupName->string, "name") == 0) + { + // For now stick with the hardcoded DF list + int index = getPickupIndex(pickupName->valuestring); + + if (index >= 0) + { + ExternalPickup extPickup = {}; + extPickup.name = pickupName->valuestring; + + cJSON* pickupData = pickupName->next; + if (pickupData && cJSON_IsObject(pickupData)) + { + cJSON* dataItem = pickupData->child; + + // iterate through the data and assign properties + while (dataItem) + { + tryAssignPickupProperty(dataItem, extPickup); + dataItem = dataItem->next; + } + } + + s_externalPickups[index] = extPickup; + } + } + + pickup = pickup->next; + } + } + + section = section->next; + } + + s_externalPickupsFromMod = fromMod && validateExternalPickups(); + } + else + { + const char* error = cJSON_GetErrorPtr(); + if (error) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json before\n%s", error); + } + else + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json"); + } + } + } + + bool validateExternalPickups() + { + // If the name field is null, we can assume the pickup's data hasn't loaded properly + for (int i = 0; i < TFE_DarkForces::ITEM_COUNT; i++) + { + if (!s_externalPickups[i].name) + { + return false; + } + } + + return true; + } + + int getPickupIndex(char* name) + { + for (int i = 0; i < TFE_DarkForces::ITEM_COUNT; i++) + { + if (strcasecmp(name, TFE_ExternalData::df_dropItemTable[i]) == 0) + { + return i; + } + } + + return -1; + } + + bool tryAssignMaxAmount(cJSON* data) + { + if (strcasecmp(data->string, "ammoEnergy") == 0) + { + s_externalMaxAmounts.ammoEnergyMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoPower") == 0) + { + s_externalMaxAmounts.ammoPowerMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoShell") == 0) + { + s_externalMaxAmounts.ammoShellMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoPlasma") == 0) + { + s_externalMaxAmounts.ammoPlasmaMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoDetonator") == 0) + { + s_externalMaxAmounts.ammoDetonatorMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoMine") == 0) + { + s_externalMaxAmounts.ammoMineMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "ammoMissile") == 0) + { + s_externalMaxAmounts.ammoMissileMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "shields") == 0) + { + s_externalMaxAmounts.shieldsMax = data->valueint; + return true; + } + + if (strcasecmp(data->string, "batteryPower") == 0) + { + // battery power is exposed as a percentage + float fraction = data->valueint / 100.0; + s_externalMaxAmounts.batteryPowerMax = s32(fraction * 2 * ONE_16); + return true; + } + + if (strcasecmp(data->string, "health") == 0) + { + s_externalMaxAmounts.healthMax = data->valueint; + return true; + } + + return false; + } + + bool tryAssignPickupProperty(cJSON* data, ExternalPickup& pickup) + { + if (cJSON_IsString(data) && strcasecmp(data->string, "type") == 0) + { + if (strcasecmp(data->valuestring, "objective") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_OBJECTIVE; + return true; + } + if (strcasecmp(data->valuestring, "weapon") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_WEAPON; + return true; + } + + if (strcasecmp(data->valuestring, "ammo") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_AMMO; + return true; + } + if (strcasecmp(data->valuestring, "usable") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_USABLE; + return true; + } + if (strcasecmp(data->valuestring, "codekey") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_CODEKEY; + return true; + } + if (strcasecmp(data->valuestring, "powerup") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_POWERUP; + return true; + } + if (strcasecmp(data->valuestring, "special") == 0) + { + pickup.type = TFE_DarkForces::ITYPE_SPECIAL; + return true; + } + + return false; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "weaponIndex") == 0) + { + pickup.weaponIndex = data->valueint; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "playerItem") == 0) + { + if (strcasecmp(data->valuestring, "itemPlans") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemPlans; + return true; + } + + if (strcasecmp(data->valuestring, "itemNava") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemNava; + return true; + } + + if (strcasecmp(data->valuestring, "itemPhrik") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemPhrik; + return true; + } + + if (strcasecmp(data->valuestring, "itemDtWeapon") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemDtWeapon; + return true; + } + + if (strcasecmp(data->valuestring, "itemDatatape") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemDatatape; + return true; + } + + if (strcasecmp(data->valuestring, "itemUnused") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemUnused; + return true; + } + + if (strcasecmp(data->valuestring, "itemRifle") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemRifle; + return true; + } + + if (strcasecmp(data->valuestring, "itemAutogun") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemAutogun; + return true; + } + + if (strcasecmp(data->valuestring, "itemMortar") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemMortar; + return true; + } + + if (strcasecmp(data->valuestring, "itemFusion") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemFusion; + return true; + } + + if (strcasecmp(data->valuestring, "itemConcussion") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemConcussion; + return true; + } + + if (strcasecmp(data->valuestring, "itemCannon") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCannon; + return true; + } + + if (strcasecmp(data->valuestring, "itemRedKey") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemRedKey; + return true; + } + + if (strcasecmp(data->valuestring, "itemYellowKey") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemYellowKey; + return true; + } + + if (strcasecmp(data->valuestring, "itemBlueKey") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemBlueKey; + return true; + } + + if (strcasecmp(data->valuestring, "itemGoggles") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemGoggles; + return true; + } + + if (strcasecmp(data->valuestring, "itemCleats") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCleats; + return true; + } + + if (strcasecmp(data->valuestring, "itemMask") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemMask; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode1") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode1; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode2") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode2; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode3") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode3; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode4") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode4; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode5") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode5; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode6") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode6; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode7") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode7; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode8") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode8; + return true; + } + + if (strcasecmp(data->valuestring, "itemCode9") == 0) + { + pickup.playerItem = &TFE_DarkForces::s_playerInfo.itemCode9; + return true; + } + + return false; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "playerAmmo") == 0) + { + if (strcasecmp(data->valuestring, "ammoEnergy") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoEnergy; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPower") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoPower; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPlasma") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoPlasma; + return true; + } + + if (strcasecmp(data->valuestring, "ammoDetonator") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoDetonator; + return true; + } + + if (strcasecmp(data->valuestring, "ammoShell") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoShell; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMine") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoMine; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMissile") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.ammoMissile; + return true; + } + + if (strcasecmp(data->valuestring, "shields") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.shields; + return true; + } + + if (strcasecmp(data->valuestring, "batteryPower") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_batteryPower; + return true; + } + + if (strcasecmp(data->valuestring, "health") == 0) + { + pickup.playerAmmo = &TFE_DarkForces::s_playerInfo.health; + return true; + } + + return false; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "amount") == 0) + { + pickup.amount = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "message1") == 0) + { + pickup.message1 = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "message2") == 0) + { + pickup.message2 = data->valueint; + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "fullBright") == 0) + { + pickup.fullBright = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "noRemove") == 0) + { + pickup.noRemove = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "asset") == 0) + { + pickup.asset = data->valuestring; + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/TheForceEngine/TFE_ExternalData/pickupExternal.h b/TheForceEngine/TFE_ExternalData/pickupExternal.h new file mode 100644 index 000000000..91c1fcf85 --- /dev/null +++ b/TheForceEngine/TFE_ExternalData/pickupExternal.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include + +/////////////////////////////////////////// +// TFE Externalised Pickup data +/////////////////////////////////////////// + +namespace TFE_ExternalData +{ + struct MaxAmounts + { + s32 ammoEnergyMax = 500; + s32 ammoPowerMax = 500; + s32 ammoShellMax = 50; + s32 ammoPlasmaMax = 400; + s32 ammoDetonatorMax = 50; + s32 ammoMineMax = 30; + s32 ammoMissileMax = 20; + s32 shieldsMax = 200; + s32 batteryPowerMax = 2 * ONE_16; + s32 healthMax = 100; + }; + + struct ExternalPickup + { + const char* name = ""; + s32 type; + s32 weaponIndex = -1; + JBool* playerItem = nullptr; + s32* playerAmmo = nullptr; + s32 amount = 0; + s32 message1 = -1; + s32 message2 = -1; + bool fullBright = false; + bool noRemove = false; + const char* asset = ""; + }; + + + MaxAmounts* getMaxAmounts(); + ExternalPickup* getExternalPickups(); + void clearExternalPickups(); + void loadExternalPickups(); + void parseExternalPickups(char* data, bool fromMod); + bool validateExternalPickups(); +} \ No newline at end of file diff --git a/TheForceEngine/TFE_ExternalData/weaponExternal.cpp b/TheForceEngine/TFE_ExternalData/weaponExternal.cpp new file mode 100644 index 000000000..207ca85d7 --- /dev/null +++ b/TheForceEngine/TFE_ExternalData/weaponExternal.cpp @@ -0,0 +1,955 @@ +#include +#include +#include +#include +#include +#include +#include +#include "weaponExternal.h" +#include "logicTables.h" + +namespace TFE_ExternalData +{ + static ExternalProjectile s_externalProjectiles[TFE_DarkForces::PROJ_COUNT]; + static ExternalEffect s_externalEffects[HEFFECT_COUNT]; + static ExternalWeapon s_externalWeapons[TFE_DarkForces::WPN_COUNT]; + static ExternalGasmask s_externalGasmask; + + static bool s_externalProjectilesFromMod = false; + static bool s_externalEffectsFromMod = false; + static bool s_externalWeaponsFromMod = false; + + ////////////////////////////// + // Forward Declarations + ////////////////////////////// + int getProjectileIndex(char* type); + bool tryAssignProjectileProperty(cJSON* data, ExternalProjectile& projectile); + int getEffectIndex(char* type); + bool tryAssignEffectProperty(cJSON* data, ExternalEffect& effect); + int getWeaponIndex(char* name); + bool tryAssignGasmaskProperty(cJSON* data); + bool tryAssignWeaponProperty(cJSON* data, ExternalWeapon& weapon); + void parseAnimationFrame(cJSON* element, WeaponAnimFrame& animFrame); + + + ExternalProjectile* getExternalProjectiles() + { + return s_externalProjectiles; + } + + ExternalEffect* getExternalEffects() + { + return s_externalEffects; + } + + ExternalWeapon* getExternalWeapons() + { + return s_externalWeapons; + } + + ExternalGasmask* getExternalGasmask() + { + return &s_externalGasmask; + } + + void clearExternalProjectiles() + { + s_externalProjectilesFromMod = false; + for (int i = 0; i < TFE_DarkForces::PROJ_COUNT; i++) + { + s_externalProjectiles[i].type = nullptr; + } + } + + void clearExternalEffects() + { + s_externalEffectsFromMod = false; + for (int i = 0; i < HEFFECT_COUNT; i++) + { + s_externalEffects[i].type = nullptr; + } + } + + void clearExternalWeapons() + { + s_externalWeaponsFromMod = false; + for (int i = 0; i < TFE_DarkForces::WPN_COUNT; i++) + { + s_externalWeapons[i].name = nullptr; + } + } + + void loadExternalProjectiles() + { + const char* programDir = TFE_Paths::getPath(PATH_PROGRAM); + char extDataFile[TFE_MAX_PATH]; + sprintf(extDataFile, "%sExternalData/DarkForces/projectiles.json", programDir); + + TFE_System::logWrite(LOG_MSG, "EXTERNAL_DATA", "Loading projectile data"); + FileStream file; + if (!file.open(extDataFile, FileStream::MODE_READ)) { return; } + + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + if (!data || size == 0) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Projectiles.json is %u bytes in size and cannot be read.", size); + return; + } + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + + parseExternalProjectiles(data, false); + free(data); + } + + void parseExternalProjectiles(char* data, bool fromMod) + { + // If projectiles have already been loaded from a mod, don't replace them + if (s_externalProjectilesFromMod) + { + return; + } + + cJSON* root = cJSON_Parse(data); + if (root) + { + cJSON* section = root->child; + if (section && cJSON_IsArray(section) && strcasecmp(section->string, "projectiles") == 0) + { + cJSON* projectile = section->child; + while (projectile) + { + cJSON* projectileType = projectile->child; + + // get the projectile type + if (projectileType && cJSON_IsString(projectileType) && strcasecmp(projectileType->string, "type") == 0) + { + // For now stick with the hardcoded DF list + int index = getProjectileIndex(projectileType->valuestring); + + if (index >= 0) + { + ExternalProjectile extProjectile = {}; + extProjectile.type = projectileType->valuestring; + + cJSON* projectileData = projectileType->next; + if (projectileData && cJSON_IsObject(projectileData)) + { + cJSON* dataItem = projectileData->child; + + // iterate through the data and assign properties + while (dataItem) + { + tryAssignProjectileProperty(dataItem, extProjectile); + dataItem = dataItem->next; + } + } + + s_externalProjectiles[index] = extProjectile; + } + } + + projectile = projectile->next; + } + } + + s_externalProjectilesFromMod = fromMod && validateExternalProjectiles(); + } + else + { + const char* error = cJSON_GetErrorPtr(); + if (error) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json before\n%s", error); + } + else + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json"); + } + } + } + + void loadExternalEffects() + { + const char* programDir = TFE_Paths::getPath(PATH_PROGRAM); + char extDataFile[TFE_MAX_PATH]; + sprintf(extDataFile, "%sExternalData/DarkForces/effects.json", programDir); + + TFE_System::logWrite(LOG_MSG, "EXTERNAL_DATA", "Loading effects data"); + FileStream file; + if (!file.open(extDataFile, FileStream::MODE_READ)) { return; } + + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + if (!data || size == 0) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Effects.json is %u bytes in size and cannot be read.", size); + return; + } + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + + parseExternalEffects(data, false); + free(data); + } + + void parseExternalEffects(char* data, bool fromMod) + { + // If effects have already been loaded from a mod, don't replace them + if (s_externalEffectsFromMod) + { + return; + } + + cJSON* root = cJSON_Parse(data); + if (root) + { + cJSON* section = root->child; + if (section && cJSON_IsArray(section) && strcasecmp(section->string, "effects") == 0) + { + cJSON* effect = section->child; + while (effect) + { + cJSON* effectType = effect->child; + + // get the effect type + if (effectType && cJSON_IsString(effectType) && strcasecmp(effectType->string, "type") == 0) + { + // For now stick with the hardcoded DF list + int index = getEffectIndex(effectType->valuestring); + + if (index >= 0) + { + ExternalEffect extEffect = {}; + extEffect.type = effectType->valuestring; + + cJSON* effectData = effectType->next; + if (effectData && cJSON_IsObject(effectData)) + { + cJSON* dataItem = effectData->child; + + // iterate through the data and assign properties + while (dataItem) + { + tryAssignEffectProperty(dataItem, extEffect); + dataItem = dataItem->next; + } + } + + s_externalEffects[index] = extEffect; + } + } + + effect = effect->next; + } + } + + s_externalEffectsFromMod = fromMod && validateExternalEffects(); + } + else + { + const char* error = cJSON_GetErrorPtr(); + if (error) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json before\n%s", error); + } + else + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json"); + } + } + } + + void loadExternalWeapons() + { + const char* programDir = TFE_Paths::getPath(PATH_PROGRAM); + char extDataFile[TFE_MAX_PATH]; + sprintf(extDataFile, "%sExternalData/DarkForces/weapons.json", programDir); + + TFE_System::logWrite(LOG_MSG, "EXTERNAL_DATA", "Loading weapon data"); + FileStream file; + if (!file.open(extDataFile, FileStream::MODE_READ)) { return; } + + const size_t size = file.getSize(); + char* data = (char*)malloc(size + 1); + if (!data || size == 0) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Weapons.json is %u bytes in size and cannot be read.", size); + return; + } + file.readBuffer(data, (u32)size); + data[size] = 0; + file.close(); + + parseExternalWeapons(data, false); + free(data); + } + + void parseExternalWeapons(char* data, bool fromMod) + { + // If weapons have already been loaded from a mod, don't replace them + if (s_externalWeaponsFromMod) + { + return; + } + + cJSON* root = cJSON_Parse(data); + if (root) + { + cJSON* section = root->child; + while (section) + { + if (cJSON_IsObject(section) && strcasecmp(section->string, "gasmask") == 0) + { + // parse the gas mask data + cJSON* dataItem = section->child; + while (dataItem) + { + tryAssignGasmaskProperty(dataItem); + dataItem = dataItem->next; + } + } + else if (cJSON_IsArray(section) && strcasecmp(section->string, "weapons") == 0) + { + cJSON* weapon = section->child; + while (weapon) + { + cJSON* weaponName = weapon->child; + + // get the weapon name + if (weaponName && cJSON_IsString(weaponName) && strcasecmp(weaponName->string, "name") == 0) + { + // For now stick with the hardcoded DF list + int index = getWeaponIndex(weaponName->valuestring); + + if (index >= 0) + { + ExternalWeapon extWeapon = {}; + extWeapon.name = weaponName->valuestring; + + cJSON* weaponData = weaponName->next; + if (weaponData && cJSON_IsObject(weaponData)) + { + cJSON* dataItem = weaponData->child; + + // iterate through the data and assign properties + while (dataItem) + { + tryAssignWeaponProperty(dataItem, extWeapon); + dataItem = dataItem->next; + } + } + + s_externalWeapons[index] = extWeapon; + } + } + + weapon = weapon->next; + } + } + + section = section->next; + } + + s_externalWeaponsFromMod = fromMod && validateExternalWeapons(); + } + else + { + const char* error = cJSON_GetErrorPtr(); + if (error) + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json before\n%s", error); + } + else + { + TFE_System::logWrite(LOG_ERROR, "EXTERNAL_DATA", "Failed to parse json"); + } + } + } + + bool validateExternalProjectiles() + { + // If the type field is null, we can assume the projectile's data hasn't loaded properly + for (int i = 0; i < TFE_DarkForces::PROJ_COUNT; i++) + { + if (!s_externalProjectiles[i].type) + { + return false; + } + } + + return true; + } + + bool validateExternalEffects() + { + // If the type field is null, we can assume the effect's data hasn't loaded properly + for (int i = 0; i < HEFFECT_COUNT; i++) + { + if (!s_externalEffects[i].type) + { + return false; + } + } + + return true; + } + + bool validateExternalWeapons() + { + // If the name field is null, we can assume the weapon's data hasn't loaded properly + for (int i = 0; i < TFE_DarkForces::WPN_COUNT; i++) + { + if (!s_externalWeapons[i].name) + { + return false; + } + } + + return true; + } + + int getProjectileIndex(char* type) + { + for (int i = 0; i < TFE_DarkForces::PROJ_COUNT; i++) + { + if (strcasecmp(type, TFE_ExternalData::df_projectileTable[i]) == 0) + { + return i; + } + } + + return -1; + } + + int getEffectIndex(char* type) + { + for (int i = 0; i < HEFFECT_COUNT; i++) + { + if (strcasecmp(type, TFE_ExternalData::df_effectTable[i]) == 0) + { + return i; + } + } + + return -1; + } + + int getWeaponIndex(char* name) + { + for (int i = 0; i < TFE_DarkForces::WPN_COUNT; i++) + { + if (strcasecmp(name, TFE_ExternalData::df_weaponTable[i]) == 0) + { + return i; + } + } + + return -1; + } + + bool tryAssignProjectileProperty(cJSON* data, ExternalProjectile &projectile) + { + if (!data) + { + return false; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "assetType") == 0) + { + projectile.assetType = data->valuestring; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "asset") == 0) + { + projectile.asset = data->valuestring; + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "fullBright") == 0) + { + projectile.fullBright = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "zeroWidth") == 0) + { + projectile.zeroWidth = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "autoAim") == 0) + { + projectile.autoAim = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "movable") == 0) + { + projectile.movable = cJSON_IsTrue(data); + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "updateFunc") == 0) + { + projectile.updateFunc = data->valuestring; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "damage") == 0) + { + projectile.damage = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "falloffAmount") == 0) + { + projectile.falloffAmount = u32(data->valuedouble * ONE_16); + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "nextFalloffTick") == 0) + { + projectile.nextFalloffTick = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "damageFalloffDelta") == 0) + { + projectile.damageFalloffDelta = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "minDamage") == 0) + { + projectile.minDamage = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "force") == 0) + { + projectile.force = u32(data->valuedouble * ONE_16); + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "speed") == 0) + { + projectile.speed = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "horzBounciness") == 0) + { + projectile.horzBounciness = u32(data->valuedouble * ONE_16); + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "vertBounciness") == 0) + { + projectile.vertBounciness = u32(data->valuedouble * ONE_16); + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "bounceCount") == 0) + { + projectile.bounceCount = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "reflectVariation") == 0) + { + projectile.reflectVariation = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "duration") == 0) + { + projectile.duration = u32(data->valuedouble * 145.65); // 145.65 ticks per second + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "angularSpeed") == 0) + { + projectile.homingAngularSpeed = floatToAngle(data->valueint); + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "flightSound") == 0) + { + projectile.flightSound = data->valuestring; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "reflectSound") == 0) + { + projectile.reflectSound = data->valuestring; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "cameraPassSound") == 0) + { + projectile.cameraPassSound = data->valuestring; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "reflectEffect") == 0) + { + for (int i = 0; i < HEFFECT_COUNT; i++) + { + if (strcasecmp(data->valuestring, TFE_ExternalData::df_effectTable[i]) == 0) + { + projectile.reflectEffectId = i; + return true; + } + } + + return false; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "hitEffect") == 0) + { + for (int i = 0; i < HEFFECT_COUNT; i++) + { + if (strcasecmp(data->valuestring, TFE_ExternalData::df_effectTable[i]) == 0) + { + projectile.hitEffectId = i; + return true; + } + } + + return false; + } + + if (cJSON_IsBool(data) && strcasecmp(data->string, "explodeOnTimeout") == 0) + { + projectile.explodeOnTimeout = cJSON_IsTrue(data); + return true; + } + + return false; + } + + bool tryAssignEffectProperty(cJSON* data, ExternalEffect& effect) + { + if (!data) + { + return false; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "wax") == 0) + { + effect.wax = data->valuestring; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "force") == 0) + { + effect.force = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "damage") == 0) + { + effect.damage = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "explosiveRange") == 0) + { + effect.explosiveRange = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "wakeupRange") == 0) + { + effect.wakeupRange = data->valueint; + return true; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "soundEffect") == 0) + { + effect.soundEffect = data->valuestring; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "soundPriority") == 0) + { + effect.soundPriority = data->valueint; + return true; + } + + return false; + } + + bool tryAssignGasmaskProperty(cJSON* data) + { + if (cJSON_IsString(data) && strcasecmp(data->string, "texture") == 0) + { + s_externalGasmask.texture = data->valuestring; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "xPos") == 0) + { + s_externalGasmask.xPos = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "yPos") == 0) + { + s_externalGasmask.yPos = data->valueint; + return true; + } + + return false; + } + + bool tryAssignWeaponProperty(cJSON* data, ExternalWeapon& weapon) + { + if (cJSON_IsNumber(data) && strcasecmp(data->string, "frameCount") == 0) + { + weapon.frameCount = data->valueint; + return true; + } + + if (cJSON_IsArray(data) && strcasecmp(data->string, "textures") == 0) + { + cJSON* element; + s32 index = 0; + cJSON_ArrayForEach(element, data) + { + if (cJSON_IsString(element)) + { + weapon.textures[index] = element->valuestring; + index++; + } + if (index >= WEAPON_NUM_TEXTURES) { break; } + } + } + + if (cJSON_IsArray(data) && strcasecmp(data->string, "xPos") == 0) + { + cJSON* element; + s32 index = 0; + cJSON_ArrayForEach(element, data) + { + if (cJSON_IsNumber(element)) + { + weapon.xPos[index] = element->valueint; + index++; + } + if (index >= WEAPON_NUM_TEXTURES) { break; } + } + } + + if (cJSON_IsArray(data) && strcasecmp(data->string, "yPos") == 0) + { + cJSON* element; + s32 index = 0; + cJSON_ArrayForEach(element, data) + { + if (cJSON_IsNumber(element)) + { + weapon.yPos[index] = element->valueint; + index++; + } + if (index >= WEAPON_NUM_TEXTURES) { break; } + } + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "ammo") == 0) + { + if (strcasecmp(data->valuestring, "") == 0) + { + weapon.ammo = nullptr; + return true; + } + + if (strcasecmp(data->valuestring, "ammoEnergy") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoEnergy; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPower") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoPower; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPlasma") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoPlasma; + return true; + } + + if (strcasecmp(data->valuestring, "ammoDetonator") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoDetonator; + return true; + } + + if (strcasecmp(data->valuestring, "ammoShell") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoShell; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMine") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoMine; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMissile") == 0) + { + weapon.ammo = &TFE_DarkForces::s_playerInfo.ammoMissile; + return true; + } + + return false; + } + + if (cJSON_IsString(data) && strcasecmp(data->string, "secondaryAmmo") == 0) + { + if (strcasecmp(data->valuestring, "ammoEnergy") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoEnergy; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPower") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoPower; + return true; + } + + if (strcasecmp(data->valuestring, "ammoPlasma") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoPlasma; + return true; + } + + if (strcasecmp(data->valuestring, "ammoDetonator") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoDetonator; + return true; + } + + if (strcasecmp(data->valuestring, "ammoShell") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoShell; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMine") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoMine; + return true; + } + + if (strcasecmp(data->valuestring, "ammoMissile") == 0) + { + weapon.secondaryAmmo = &TFE_DarkForces::s_playerInfo.ammoMissile; + return true; + } + + return false; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "wakeupRange") == 0) + { + weapon.wakeupRange = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "variation") == 0) + { + weapon.variation = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "primaryFireConsumption") == 0) + { + weapon.primaryFireConsumption = data->valueint; + return true; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "secondaryFireConsumption") == 0) + { + weapon.secondaryFireConsumption = data->valueint; + return true; + } + + if (cJSON_IsArray(data) && strcasecmp(data->string, "animFrames") == 0) + { + cJSON* element; + s32 index = 0; + cJSON_ArrayForEach(element, data) + { + if (cJSON_IsObject(element)) + { + parseAnimationFrame(element, weapon.animFrames[index]); + index++; + } + if (index >= WEAPON_NUM_ANIMFRAMES) { break; } + } + + weapon.numAnimFrames = index; + return true; + } + + if (cJSON_IsArray(data) && strcasecmp(data->string, "animFramesSecondary") == 0) + { + cJSON* element; + s32 index = 0; + cJSON_ArrayForEach(element, data) + { + if (cJSON_IsObject(element)) + { + parseAnimationFrame(element, weapon.animFramesSecondary[index]); + index++; + } + if (index >= WEAPON_NUM_ANIMFRAMES) { break; } + } + + weapon.numSecondaryAnimFrames = index; + return true; + } + + return false; + } + + void parseAnimationFrame(cJSON* element, WeaponAnimFrame& animFrame) + { + cJSON* data = element->child; + while (data) + { + if (cJSON_IsNumber(data) && strcasecmp(data->string, "texture") == 0) + { + animFrame.texture = data->valueint; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "light") == 0) + { + animFrame.light = data->valueint; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "durationNormal") == 0) + { + animFrame.durationNormal = data->valueint; + } + + if (cJSON_IsNumber(data) && strcasecmp(data->string, "durationSupercharge") == 0) + { + animFrame.durationSupercharge = data->valueint; + } + + data = data->next; + } + } +} \ No newline at end of file diff --git a/TheForceEngine/TFE_ExternalData/weaponExternal.h b/TheForceEngine/TFE_ExternalData/weaponExternal.h new file mode 100644 index 000000000..410255911 --- /dev/null +++ b/TheForceEngine/TFE_ExternalData/weaponExternal.h @@ -0,0 +1,117 @@ +#pragma once +#include +#include + +/////////////////////////////////////////// +// TFE Externalised Weapon data +/////////////////////////////////////////// + +namespace TFE_ExternalData +{ + enum + { + WEAPON_NUM_TEXTURES = 16, + WEAPON_NUM_ANIMFRAMES = 16, + }; + + struct ExternalProjectile + { + const char* type = nullptr; + + // Projectile object + const char* assetType = "spirit"; + const char* asset = ""; + bool fullBright = false; + bool zeroWidth = false; + bool autoAim = false; + bool movable = false; + + // Projectile logic + const char* updateFunc = ""; + u32 damage; + u32 falloffAmount; + u32 nextFalloffTick; + u32 damageFalloffDelta; + u32 minDamage; + u32 force; + u32 speed; + u32 horzBounciness; + u32 vertBounciness; + s32 bounceCount; + u32 reflectVariation; + u32 duration; + s32 homingAngularSpeed; + const char* flightSound = ""; + const char* reflectSound = ""; + const char* cameraPassSound = ""; + s32 reflectEffectId = -1; + s32 hitEffectId = -1; + bool explodeOnTimeout = false; + }; + + struct ExternalEffect + { + const char* type = nullptr; + const char* wax = ""; + s32 force; + s32 damage; + s32 explosiveRange; + s32 wakeupRange; + const char* soundEffect = ""; + s32 soundPriority; + }; + + // Maps to TFE_DarkForces::WeaponAnimFrame + struct WeaponAnimFrame + { + s32 texture = 0; + s32 light = 0; + u32 durationSupercharge = 0; + u32 durationNormal = 0; + }; + + struct ExternalWeapon + { + const char* name = nullptr; + s32 frameCount = 1; + const char* textures[WEAPON_NUM_TEXTURES] = { "default.bm" }; + s32 xPos[WEAPON_NUM_TEXTURES] = { 0 }; + s32 yPos[WEAPON_NUM_TEXTURES] = { 0 }; + s32* ammo = &TFE_DarkForces::s_playerInfo.ammoEnergy; + s32* secondaryAmmo = nullptr; + s32 wakeupRange = 0; + s32 variation = 0; + + s32 primaryFireConsumption = 1; + s32 secondaryFireConsumption = 1; + + s32 numAnimFrames = 1; + WeaponAnimFrame animFrames[WEAPON_NUM_ANIMFRAMES]; + s32 numSecondaryAnimFrames = 1; + WeaponAnimFrame animFramesSecondary[WEAPON_NUM_ANIMFRAMES]; + }; + + struct ExternalGasmask + { + const char* texture = "gmask.bm"; + s32 xPos = 105; + s32 yPos = 141; + }; + + ExternalProjectile* getExternalProjectiles(); + ExternalEffect* getExternalEffects(); + ExternalWeapon* getExternalWeapons(); + ExternalGasmask* getExternalGasmask(); + void clearExternalProjectiles(); + void clearExternalEffects(); + void clearExternalWeapons(); + void loadExternalProjectiles(); + void parseExternalProjectiles(char* data, bool fromMod); + bool validateExternalProjectiles(); + void loadExternalEffects(); + void parseExternalEffects(char* data, bool fromMod); + bool validateExternalEffects(); + void loadExternalWeapons(); + void parseExternalWeapons(char* data, bool fromMod); + bool validateExternalWeapons(); +} \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.cpp b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.cpp index ca3ec4771..03bc58713 100644 --- a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.cpp +++ b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.cpp @@ -252,13 +252,20 @@ void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray) else RegisterScriptArray_Generic(engine); + int r = 0; if( defaultArray ) { - int r = engine->RegisterDefaultArrayType("array"); assert( r >= 0 ); + r = engine->RegisterDefaultArrayType("array"); assert( r >= 0 ); UNUSED_VAR(r); } } +static int s_scriptArrayObjId = 268435480; +int GetScriptArrayObjectId() +{ + return s_scriptArrayObjId; +} + static void RegisterScriptArray_Native(asIScriptEngine *engine) { int r = 0; diff --git a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.h b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.h index f48aa39de..37b61f151 100644 --- a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.h +++ b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptarray/scriptarray.h @@ -19,7 +19,7 @@ // Some prefer to use property accessors to get/set the length of the array // This option registers the accessors instead of the method length() #ifndef AS_USE_ACCESSORS -#define AS_USE_ACCESSORS 0 +#define AS_USE_ACCESSORS 1 #endif BEGIN_AS_NAMESPACE @@ -137,6 +137,7 @@ class CScriptArray }; void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray); +int GetScriptArrayObjectId(); END_AS_NAMESPACE diff --git a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.cpp b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.cpp index 7dafbde94..1590ff0e4 100644 --- a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.cpp +++ b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.cpp @@ -1,3 +1,4 @@ +#include #include "scriptbuilder.h" #include #include @@ -32,6 +33,8 @@ CScriptBuilder::CScriptBuilder() pragmaCallback = 0; pragmaParam = 0; + + readStd = true; } void CScriptBuilder::SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam) @@ -60,6 +63,11 @@ int CScriptBuilder::StartNewModule(asIScriptEngine *inEngine, const char *module return 0; } +void CScriptBuilder::SetReadMode(bool _readStd) +{ + readStd = _readStd; +} + asIScriptEngine *CScriptBuilder::GetEngine() { return engine; @@ -95,7 +103,15 @@ int CScriptBuilder::AddSectionFromFile(const char *filename) { // The file name stored in the set should be the fully resolved name because // it is possible to name the same file in multiple ways using relative paths. - string fullpath = GetAbsolutePath(filename); + string fullpath; + if (readStd) + { + fullpath = GetAbsolutePath(filename); + } + else + { + fullpath = filename; + } if( IncludeIfNotAlreadyIncluded(fullpath.c_str()) ) { @@ -170,7 +186,29 @@ bool CScriptBuilder::IncludeIfNotAlreadyIncluded(const char *filename) return true; } -int CScriptBuilder::LoadScriptSection(const char *filename) +s32 readScriptFileTFE(const char* filename, string& code, asIScriptEngine* engine) +{ + FilePath path; + FileStream file; + if (!TFE_Paths::getFilePath(filename, &path) || !file.open(&path, FileStream::MODE_READ)) + { + // Write a message to the engine's message callback + string msg = "Failed to open script file '" + std::string(filename) + "'"; + engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str()); + // TODO: Write the file where this one was included from + return -1; + } + + if (file.getSize() > 0) + { + code.resize(file.getSize()); + file.readBuffer(&code[0], file.getSize()); + } + file.close(); + return 0; +} + +s32 readScriptFile(const char* filename, string& code, asIScriptEngine* engine) { // Open the script file string scriptFile = filename; @@ -180,14 +218,13 @@ int CScriptBuilder::LoadScriptSection(const char *filename) #else FILE *f = fopen(scriptFile.c_str(), "rb"); #endif - if( f == 0 ) + if (f == 0) { // Write a message to the engine's message callback string msg = "Failed to open script file '" + GetAbsolutePath(scriptFile) + "'"; engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str()); // TODO: Write the file where this one was included from - return -1; } @@ -200,9 +237,8 @@ int CScriptBuilder::LoadScriptSection(const char *filename) // int len = _filelength(_fileno(f)); // Read the entire file - string code; size_t c = 0; - if( len > 0 ) + if (len > 0) { code.resize(len); c = fread(&code[0], len, 1, f); @@ -210,13 +246,29 @@ int CScriptBuilder::LoadScriptSection(const char *filename) fclose(f); - if( c == 0 && len > 0 ) + if (c == 0 && len > 0) { // Write a message to the engine's message callback string msg = "Failed to load script file '" + GetAbsolutePath(scriptFile) + "'"; engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str()); return -1; } + return 0; +} + +int CScriptBuilder::LoadScriptSection(const char *filename) +{ + string code; + int c = 0; + if (readStd) + { + c = readScriptFile(filename, code, engine); + } + else + { + c = readScriptFileTFE(filename, code, engine); + } + if (c < 0) { return -1; } // Process the script section even if it is zero length so that the name is registered return ProcessScriptSection(code.c_str(), (unsigned int)(code.length()), filename, 0); diff --git a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.h b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.h index c11f1b925..214ddd550 100644 --- a/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.h +++ b/TheForceEngine/TFE_ForceScript/Angelscript/add_on/scriptbuilder/scriptbuilder.h @@ -62,6 +62,8 @@ class CScriptBuilder // Start a new module int StartNewModule(asIScriptEngine *engine, const char *moduleName); + void SetReadMode(bool readStd); + // Load a script section from a file on disk // Returns 1 if the file was included // 0 if the file had already been included before @@ -128,6 +130,7 @@ class CScriptBuilder int ExcludeCode(int start); void OverwriteCode(int start, int len); + bool readStd; asIScriptEngine *engine; asIScriptModule *module; std::string modifiedScript; diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/CMakeLists.txt b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/CMakeLists.txt new file mode 100644 index 000000000..cf0824deb --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB SOURCES "*.cpp") +target_sources(tfe PRIVATE ${SOURCES}) diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.cpp b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.cpp new file mode 100644 index 000000000..77fbf740b --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.cpp @@ -0,0 +1,413 @@ +#include "scriptMath.h" +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_ForceScript +{ + s32 countBits(u32 bits) + { + s32 count = 0; + while (bits) + { + if (bits & 1) { count++; } + bits >>= 1; + } + return count; + } + + s32 countBits(s32 bits) + { + s32 count = 0; + u32 ubits = *((u32*)&bits); + while (ubits) + { + if (ubits & 1) { count++; } + ubits >>= 1; + } + return count; + } + + s32 findLSB(u32 x) + { + s32 res = -1; + for (u32 i = 0; i < 32; i++) + { + const u32 mask = 1 << i; + if (x & mask) + { + res = i; + break; + } + } + return res; + } + + s32 findLSB(s32 x) + { + s32 res = -1; + for (s32 i = 0; i < 32; i++) + { + const s32 mask = 1 << i; + if (x & mask) + { + res = i; + break; + } + } + return res; + } + + s32 findMSB(u32 x) + { + s32 res = -1; + for (u32 i = 0; i < 32; i++) + { + const u32 mask = 0x80000000 >> i; + if (x & mask) + { + res = 31 - i; + break; + } + } + return res; + } + + s32 findMSB(s32 x) + { + s32 res = -1; + if (x < 0) { x = ~x; } + for (u32 i = 0; i < 32; i++) + { + const u32 mask = 0x80000000 >> i; + if (x & mask) + { + res = 31 - i; + break; + } + } + return res; + } + + f32 mix(f32 x, f32 y, f32 a) + { + return x * (1.0f - a) + y * a; + } + + f32 step(f32 edge, f32 x) + { + return edge < x ? 0.0f : 1.0f; + } + + s32 step(s32 edge, s32 x) + { + return edge < x ? 0 : 1; + } + + f32 smoothstep(f32 edge0, f32 edge1, f32 x) + { + x = clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + return x * x * (3.0f - 2.0f*x); + } + + f32 distance(float2 a, float2 b) + { + f32 dx = a.x - b.x, dy = a.y - b.y; + return sqrtf(dx*dx + dy*dy); + } + + f32 distance(float3 a, float3 b) + { + f32 dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z; + return sqrtf(dx*dx + dy*dy + dz*dz); + } + + f32 distance(float4 a, float4 b) + { + f32 dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z, dw = a.w - b.w; + return sqrtf(dx*dx + dy*dy + dz*dz + dw*dw); + } + + float2 normalize(float2 v) + { + const f32 lenSq = v.x*v.x + v.y*v.y; + if (fabsf(lenSq) > FLT_EPSILON) + { + const f32 scale = 1.0f / sqrtf(lenSq); + v.x *= scale; + v.y *= scale; + } + return v; + } + + float3 normalize(float3 v) + { + const f32 lenSq = v.x*v.x + v.y*v.y + v.z*v.z; + if (fabsf(lenSq) > FLT_EPSILON) + { + const f32 scale = 1.0f / sqrtf(lenSq); + v.x *= scale; + v.y *= scale; + v.z *= scale; + } + return v; + } + + float4 normalize(float4 v) + { + const f32 lenSq = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; + if (fabsf(lenSq) > FLT_EPSILON) + { + const f32 scale = 1.0f / sqrtf(lenSq); + v.x *= scale; + v.y *= scale; + v.z *= scale; + v.w *= scale; + } + return v; + } + + float3 cross(float3 s, float3 t) + { + return float3(s.y*t.z - s.z*t.y, s.z*t.x - s.x*t.z, s.x*t.y - s.y*t.x); + } + + float2x2 transpose2x2(float2x2 m2x2) + { + return float2x2(m2x2.m[0], m2x2.m[2], m2x2.m[1], m2x2.m[3]); + } + float3x3 transpose3x3(float3x3 m3x3) + { + return float3x3(m3x3.m[0], m3x3.m[3], m3x3.m[6], m3x3.m[1], m3x3.m[4], m3x3.m[7], m3x3.m[2], m3x3.m[5], m3x3.m[8]); + } + float4x4 transpose4x4(float4x4 m4x4) + { + return float4x4(m4x4.m[0], m4x4.m[4], m4x4.m[8], m4x4.m[12], m4x4.m[1], m4x4.m[5], m4x4.m[9], m4x4.m[13], m4x4.m[2], + m4x4.m[6], m4x4.m[10], m4x4.m[14], m4x4.m[3], m4x4.m[7], m4x4.m[11], m4x4.m[15]); + } + + float2x2 computeRotation2x2(f32 angle) + { + const f32 c = cosf(angle); + const f32 s = sinf(angle); + return float2x2(c, -s, s, c); + } + float3x3 computeRotation3x3(f32 pitch, f32 yaw, f32 roll) + { + Vec3f angles = { pitch, yaw, roll }; + Vec3f mat[3]; + TFE_Math::buildRotationMatrix(angles, mat); + return float3x3(mat[0].x, mat[0].y, mat[0].z, mat[1].x, mat[1].y, mat[1].z, mat[2].x, mat[2].y, mat[2].z); + } + + bool ScriptMath::scriptRegister(ScriptAPI api) + { + ScriptClassBegin("Math", "math", api); + { + // Constants + ScriptLambdaPropertyGet("float get_pi()", f32, { return PI; }); + ScriptLambdaPropertyGet("float get_twoPi()", f32, { return TWO_PI; }); + ScriptLambdaPropertyGet("float get_e()", f32, { return 2.718281828459045f; }); + + // Basic math functions - include overrides for different vector types. + ScriptLambdaMethod("int countBits(uint)", (u32 x), s32, { return countBits(x); }); + ScriptLambdaMethod("int countBits(int)", (s32 x), s32, { return countBits(x); }); + ScriptLambdaMethod("int findLSB(uint)", (u32 x), s32, { return findLSB(x); }); + ScriptLambdaMethod("int findLSB(int)", (s32 x), s32, { return findLSB(x); }); + ScriptLambdaMethod("int findMSB(uint)", (u32 x), s32, { return findMSB(x); }); + ScriptLambdaMethod("int findMSB(int)", (s32 x), s32, { return findMSB(x); }); + ScriptLambdaMethod("int abs(int)", (s32 x), s32, { return x < 0 ? -x : x; }); + ScriptLambdaMethod("float abs(float)", (f32 x), f32, { return fabsf(x); }); + ScriptLambdaMethod("float2 abs(float2)", (float2 v), float2, { return float2(fabsf(v.x), fabsf(v.y)); }); + ScriptLambdaMethod("float3 abs(float3)", (float3 v), float3, { return float3(fabsf(v.x), fabsf(v.y), fabsf(v.z)); }); + ScriptLambdaMethod("float4 abs(float4)", (float4 v), float4, { return float4(fabsf(v.x), fabsf(v.y), fabsf(v.z), fabsf(v.w)); }); + ScriptLambdaMethod("bool all(float)", (f32 x), bool, { return x != 0.0f; }); + ScriptLambdaMethod("bool all(float2)", (float2 v), bool, { return v.x != 0.0f && v.y != 0.0f; }); + ScriptLambdaMethod("bool all(float3)", (float3 v), bool, { return v.x != 0.0f && v.y != 0.0f && v.z != 0.0f; }); + ScriptLambdaMethod("bool all(float4)", (float4 v), bool, { return v.x != 0.0f && v.y != 0.0f && v.z != 0.0f && v.w != 0.0f; }); + ScriptLambdaMethod("bool any(float)", (f32 x), bool, { return x != 0.0f; }); + ScriptLambdaMethod("bool any(float2)", (float2 v), bool, { return v.x != 0.0f || v.y != 0.0f; }); + ScriptLambdaMethod("bool any(float3)", (float3 v), bool, { return v.x != 0.0f || v.y != 0.0f || v.z != 0.0f; }); + ScriptLambdaMethod("bool any(float4)", (float4 v), bool, { return v.x != 0.0f || v.y != 0.0f || v.z != 0.0f || v.w != 0.0f; }); + ScriptLambdaMethod("float acos(float)", (f32 x), f32, { return acosf(x); }); + ScriptLambdaMethod("float2 acos(float2)", (float2 v), float2, { return float2(acosf(v.x), acosf(v.y)); }); + ScriptLambdaMethod("float3 acos(float3)", (float3 v), float3, { return float3(acosf(v.x), acosf(v.y), acosf(v.z)); }); + ScriptLambdaMethod("float4 acos(float4)", (float4 v), float4, { return float4(acosf(v.x), acosf(v.y), acosf(v.z), acosf(v.w)); }); + ScriptLambdaMethod("float asin(float)", (f32 x), f32, { return asinf(x); }); + ScriptLambdaMethod("float2 asin(float2)", (float2 v), float2, { return float2(asinf(v.x), asinf(v.y)); }); + ScriptLambdaMethod("float3 asin(float3)", (float3 v), float3, { return float3(asinf(v.x), asinf(v.y), asinf(v.z)); }); + ScriptLambdaMethod("float4 asin(float4)", (float4 v), float4, { return float4(asinf(v.x), asinf(v.y), asinf(v.z), asinf(v.w)); }); + ScriptLambdaMethod("float atan(float)", (f32 x), f32, { return atanf(x); }); + ScriptLambdaMethod("float2 atan(float2)", (float2 v), float2, { return float2(atanf(v.x), atanf(v.y)); }); + ScriptLambdaMethod("float3 atan(float3)", (float3 v), float3, { return float3(atanf(v.x), atanf(v.y), atanf(v.z)); }); + ScriptLambdaMethod("float4 atan(float4)", (float4 v), float4, { return float4(atanf(v.x), atanf(v.y), atanf(v.z), atanf(v.w)); }); + ScriptLambdaMethod("float atan2(float, float)", (f32 y, f32 x), f32, { return atan2f(y, x); }); + ScriptLambdaMethod("float2 atan2(float2, float2)", (float2 t, float2 s), float2, { return float2(atan2f(t.x, s.x), atan2f(t.y, s.y)); }); + ScriptLambdaMethod("float3 atan2(float3, float3)", (float3 t, float3 s), float3, { return float3(atan2f(t.x, s.x), atan2f(t.y, s.y), atan2f(t.z, s.z)); }); + ScriptLambdaMethod("float4 atan2(float4, float4)", (float4 t, float4 s), float4, { return float4(atan2f(t.x, s.x), atan2f(t.y, s.y), atan2f(t.z, s.z), atan2f(t.w, s.w)); }); + ScriptLambdaMethod("float degrees(float)", (f32 r), f32, { return r * 180.0f / PI; }); + ScriptLambdaMethod("float2 degrees(float2)", (float2 r), float2, { return float2(r.x * 180.0f / PI, r.y * 180.0f / PI); }); + ScriptLambdaMethod("float3 degrees(float3)", (float3 r), float3, { return float3(r.x * 180.0f / PI, r.y * 180.0f / PI, r.z * 180.0f / PI); }); + ScriptLambdaMethod("float4 degrees(float4)", (float4 r), float4, { return float4(r.x * 180.0f / PI, r.y * 180.0f / PI, r.z * 180.0f / PI, r.w * 180.0f / PI); }); + ScriptLambdaMethod("float radians(float)", (f32 d), f32, { return d * PI / 180.0f; }); + ScriptLambdaMethod("float2 radians(float2)", (float2 d), float2, { return float2(d.x * PI / 180.0f, d.y * PI / 180.0f); }); + ScriptLambdaMethod("float3 radians(float3)", (float3 d), float3, { return float3(d.x * PI / 180.0f, d.y * PI / 180.0f, d.z * PI / 180.0f); }); + ScriptLambdaMethod("float4 radians(float4)", (float4 d), float4, { return float4(d.x * PI / 180.0f, d.y * PI / 180.0f, d.z * PI / 180.0f, d.w * PI / 180.0f); }); + ScriptLambdaMethod("float exp(float)", (f32 v), f32, { return expf(v); }); + ScriptLambdaMethod("float2 exp(float2)", (float2 v), float2, { return float2(expf(v.x), expf(v.y)); }); + ScriptLambdaMethod("float3 exp(float3)", (float3 v), float3, { return float3(expf(v.x), expf(v.y), expf(v.z)); }); + ScriptLambdaMethod("float4 exp(float4)", (float4 v), float4, { return float4(expf(v.x), expf(v.y), expf(v.z), expf(v.w)); }); + ScriptLambdaMethod("float exp2(float)", (f32 v), f32, { return exp2f(v); }); + ScriptLambdaMethod("float2 exp2(float2)", (float2 v), float2, { return float2(exp2f(v.x), exp2f(v.y)); }); + ScriptLambdaMethod("float3 exp2(float3)", (float3 v), float3, { return float3(exp2f(v.x), exp2f(v.y), exp2f(v.z)); }); + ScriptLambdaMethod("float4 exp2(float4)", (float4 v), float4, { return float4(exp2f(v.x), exp2f(v.y), exp2f(v.z), exp2f(v.w)); }); + ScriptLambdaMethod("float log(float)", (f32 v), f32, { return logf(v); }); + ScriptLambdaMethod("float2 log(float2)", (float2 v), float2, { return float2(logf(v.x), logf(v.y)); }); + ScriptLambdaMethod("float3 log(float3)", (float3 v), float3, { return float3(logf(v.x), logf(v.y), logf(v.z)); }); + ScriptLambdaMethod("float4 log(float4)", (float4 v), float4, { return float4(logf(v.x), logf(v.y), logf(v.z), logf(v.w)); }); + ScriptLambdaMethod("float log2(float)", (f32 v), f32, { return log2f(v); }); + ScriptLambdaMethod("float2 log2(float2)", (float2 v), float2, { return float2(log2f(v.x), log2f(v.y)); }); + ScriptLambdaMethod("float3 log2(float3)", (float3 v), float3, { return float3(log2f(v.x), log2f(v.y), log2f(v.z)); }); + ScriptLambdaMethod("float4 log2(float4)", (float4 v), float4, { return float4(log2f(v.x), log2f(v.y), log2f(v.z), log2f(v.w)); }); + ScriptLambdaMethod("float log10(float)", (f32 v), f32, { return log10f(v); }); + ScriptLambdaMethod("float2 log10(float2)", (float2 v), float2, { return float2(log10f(v.x), log10f(v.y)); }); + ScriptLambdaMethod("float3 log10(float3)", (float3 v), float3, { return float3(log10f(v.x), log10f(v.y), log10f(v.z)); }); + ScriptLambdaMethod("float4 log10(float4)", (float4 v), float4, { return float4(log10f(v.x), log10f(v.y), log10f(v.z), log10f(v.w)); }); + ScriptLambdaMethod("float pow(float, float)", (f32 s, f32 t), f32, { return powf(s, t); }); + ScriptLambdaMethod("float2 pow(float2, float)", (float2 s, f32 t), float2, { return float2(powf(s.x, t), powf(s.y, t)); }); + ScriptLambdaMethod("float2 pow(float2, float2)", (float2 s, float2 t), float2, { return float2(powf(s.x, t.x), powf(s.y, t.y)); }); + ScriptLambdaMethod("float3 pow(float3, float)", (float3 s, f32 t), float3, { return float3(powf(s.x, t), powf(s.y, t), powf(s.z, t)); }); + ScriptLambdaMethod("float3 pow(float3, float3)", (float3 s, float3 t), float3, { return float3(powf(s.x, t.x), powf(s.y, t.y), powf(s.z, t.z)); }); + ScriptLambdaMethod("float4 pow(float4, float)", (float4 s, f32 t), float4, { return float4(powf(s.x, t), powf(s.y, t), powf(s.z, t), powf(s.w, t)); }); + ScriptLambdaMethod("float4 pow(float4, float4)", (float4 s, float4 t), float4, { return float4(powf(s.x, t.x), powf(s.y, t.y), powf(s.z, t.z), powf(s.w, t.w)); }); + ScriptLambdaMethod("float cos(float)", (f32 x), f32, { return cosf(x); }); + ScriptLambdaMethod("float2 cos(float2)", (float2 v), float2, { return float2(cosf(v.x),cosf(v.y)); }); + ScriptLambdaMethod("float3 cos(float3)", (float3 v), float3, { return float3(cosf(v.x),cosf(v.y),cosf(v.z)); }); + ScriptLambdaMethod("float4 cos(float4)", (float4 v), float4, { return float4(cosf(v.x),cosf(v.y),cosf(v.z),cosf(v.w)); }); + ScriptLambdaMethod("float sin(float)", (f32 x), f32, { return sinf(x); }); + ScriptLambdaMethod("float2 sin(float2)", (float2 v), float2, { return float2(sinf(v.x),sinf(v.y)); }); + ScriptLambdaMethod("float3 sin(float3)", (float3 v), float3, { return float3(sinf(v.x),sinf(v.y),sinf(v.z)); }); + ScriptLambdaMethod("float4 sin(float4)", (float4 v), float4, { return float4(sinf(v.x),sinf(v.y),sinf(v.z),sinf(v.w)); }); + ScriptLambdaMethod("float tan(float)", (f32 x), f32, { return tanf(x); }); + ScriptLambdaMethod("float2 tan(float2)", (float2 v), float2, { return float2(tanf(v.x),tanf(v.y)); }); + ScriptLambdaMethod("float3 tan(float3)", (float3 v), float3, { return float3(tanf(v.x),tanf(v.y),tanf(v.z)); }); + ScriptLambdaMethod("float4 tan(float4)", (float4 v), float4, { return float4(tanf(v.x),tanf(v.y),tanf(v.z),tanf(v.w)); }); + ScriptLambdaMethod("float2 sincos(float)", (f32 v), float2, { return float2(sinf(v),cosf(v)); }); + ScriptLambdaMethod("float floor(float)", (f32 x), f32, { return floorf(x); }); + ScriptLambdaMethod("float2 floor(float2)", (float2 v), float2, { return float2(floorf(v.x),floorf(v.y)); }); + ScriptLambdaMethod("float3 floor(float3)", (float3 v), float3, { return float3(floorf(v.x),floorf(v.y),floorf(v.z)); }); + ScriptLambdaMethod("float4 floor(float4)", (float4 v), float4, { return float4(floorf(v.x),floorf(v.y),floorf(v.z),floorf(v.w)); }); + ScriptLambdaMethod("float ceil(float)", (f32 x), f32, { return ceilf(x); }); + ScriptLambdaMethod("float2 ceil(float2)", (float2 v), float2, { return float2(ceilf(v.x),ceilf(v.y)); }); + ScriptLambdaMethod("float3 ceil(float3)", (float3 v), float3, { return float3(ceilf(v.x),ceilf(v.y),ceilf(v.z)); }); + ScriptLambdaMethod("float4 ceil(float4)", (float4 v), float4, { return float4(ceilf(v.x),ceilf(v.y),ceilf(v.z),ceilf(v.w)); }); + ScriptLambdaMethod("float fract(float)", (f32 x), f32, { return x - floorf(x); }); + ScriptLambdaMethod("float2 fract(float2)", (float2 v), float2, { return float2(v.x - floorf(v.x), v.y - floorf(v.y)); }); + ScriptLambdaMethod("float3 fract(float3)", (float3 v), float3, { return float3(v.x - floorf(v.x), v.y - floorf(v.y), v.z - floorf(v.z)); }); + ScriptLambdaMethod("float4 fract(float4)", (float4 v), float4, { return float4(v.x - floorf(v.x), v.y - floorf(v.y), v.z - floorf(v.z), v.w - floorf(v.w)); }); + ScriptLambdaMethod("int max(int, int)", (s32 a, s32 b), s32, { return a > b ? a : b; }); + ScriptLambdaMethod("float max(float, float)", (f32 a, f32 b), f32, { return a > b ? a : b; }); + ScriptLambdaMethod("float2 max(float2, float2)", (float2 a, float2 b), float2, { return float2(a.x > b.x ? a.x : b.x,a.y > b.y ? a.y : b.y); }); + ScriptLambdaMethod("float2 max(float2, float)", (float2 a, f32 b), float2, { return float2(a.x > b ? a.x : b,a.y > b ? a.y : b); }); + ScriptLambdaMethod("float3 max(float3, float3)", (float3 a, float3 b), float3, { return float3(a.x > b.x ? a.x : b.x,a.y > b.y ? a.y : b.y,a.z > b.z ? a.z : b.z); }); + ScriptLambdaMethod("float3 max(float3, float)", (float3 a, f32 b), float3, { return float3(a.x > b ? a.x : b,a.y > b ? a.y : b,a.z > b ? a.z : b); }); + ScriptLambdaMethod("float4 max(float4, float4)", (float4 a, float4 b), float4, { return float4(a.x > b.x ? a.x : b.x,a.y > b.y ? a.y : b.y,a.z > b.z ? a.z : b.z,a.w > b.w ? a.w : b.w); }); + ScriptLambdaMethod("float4 max(float4, float)", (float4 a, f32 b), float4, { return float4(a.x > b ? a.x : b,a.y > b ? a.y : b,a.z > b ? a.z : b,a.w > b ? a.w : b); }); + ScriptLambdaMethod("int min(int, int)", (s32 a, s32 b), s32, { return a < b ? a : b; }); + ScriptLambdaMethod("float min(float, float)", (f32 a, f32 b), f32, { return a < b ? a : b; }); + ScriptLambdaMethod("float2 min(float2, float2)", (float2 a, float2 b), float2, { return float2(a.x < b.x ? a.x : b.x,a.y < b.y ? a.y : b.y); }); + ScriptLambdaMethod("float2 min(float2, float)", (float2 a, f32 b), float2, { return float2(a.x < b ? a.x : b,a.y < b ? a.y : b); }); + ScriptLambdaMethod("float3 min(float3, float3)", (float3 a, float3 b), float3, { return float3(a.x < b.x ? a.x : b.x,a.y < b.y ? a.y : b.y,a.z < b.z ? a.z : b.z); }); + ScriptLambdaMethod("float3 min(float3, float)", (float3 a, f32 b), float3, { return float3(a.x < b ? a.x : b,a.y < b ? a.y : b,a.z < b ? a.z : b); }); + ScriptLambdaMethod("float4 min(float4, float4)", (float4 a, float4 b), float4, { return float4(a.x < b.x ? a.x : b.x,a.y < b.y ? a.y : b.y,a.z < b.z ? a.z : b.z,a.w < b.w ? a.w : b.w); }); + ScriptLambdaMethod("float4 min(float4, float)", (float4 a, f32 b), float4, { return float4(a.x < b ? a.x : b,a.y < b ? a.y : b,a.z < b ? a.z : b,a.w < b ? a.w : b); }); + ScriptLambdaMethod("int clamp(int, int, int)", (s32 x, s32 a, s32 b), s32, { return clamp(x, a, b); }); + ScriptLambdaMethod("float clamp(float, float, float)", (f32 x, f32 a, f32 b), f32, { return clamp(x, a, b); }); + ScriptLambdaMethod("float2 clamp(float2, float2, float2)", (float2 v, float2 a, float2 b), float2, { return float2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); }); + ScriptLambdaMethod("float2 clamp(float2, float, float)", (float2 v, f32 a, f32 b), float2, { return float2(clamp(v.x, a, b), clamp(v.y, a, b)); }); + ScriptLambdaMethod("float3 clamp(float3, float3, float3)", (float3 v, float3 a, float3 b), float3, { return float3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); }); + ScriptLambdaMethod("float3 clamp(float3, float, float)", (float3 v, f32 a, f32 b), float3, { return float3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); }); + ScriptLambdaMethod("float4 clamp(float4, float4, float4)", (float4 v, float4 a, float4 b), float4, { return float4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); }); + ScriptLambdaMethod("float4 clamp(float4, float, float)", (float4 v, f32 a, f32 b), float4, { return float4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); }); + ScriptLambdaMethod("float saturate(float)", (f32 x), f32, { return clamp(x, 0.0f, 1.0f); }); + ScriptLambdaMethod("float2 saturate(float2)", (float2 v), float2, { return float2(clamp(v.x, 0.0f, 1.0f), clamp(v.y, 0.0f, 1.0f)); }); + ScriptLambdaMethod("float3 saturate(float3)", (float3 v), float3, { return float3(clamp(v.x, 0.0f, 1.0f), clamp(v.y, 0.0f, 1.0f), clamp(v.z, 0.0f, 1.0f)); }); + ScriptLambdaMethod("float4 saturate(float4)", (float4 v), float4, { return float4(clamp(v.x, 0.0f, 1.0f), clamp(v.y, 0.0f, 1.0f), clamp(v.z, 0.0f, 1.0f), clamp(v.w, 0.0f, 1.0f)); }); + ScriptLambdaMethod("float mix(float, float, float)", (f32 x, f32 y, f32 a), f32, { return mix(x, y, a); }); + ScriptLambdaMethod("float2 mix(float2, float2, float2)", (float2 s, float2 t, float2 a), float2, { return float2(mix(s.x, t.x, a.x), mix(s.y, t.y, a.y)); }); + ScriptLambdaMethod("float2 mix(float2, float2, float)", (float2 s, float2 t, f32 a), float2, { return float2(mix(s.x, t.x, a), mix(s.y, t.y, a)); }); + ScriptLambdaMethod("float3 mix(float3, float3, float3)", (float3 s, float3 t, float3 a), float3, { return float3(mix(s.x, t.x, a.x), mix(s.y, t.y, a.y), mix(s.z, t.z, a.z)); }); + ScriptLambdaMethod("float3 mix(float3, float3, float)", (float3 s, float3 t, f32 a), float3, { return float3(mix(s.x, t.x, a), mix(s.y, t.y, a), mix(s.z, t.z, a)); }); + ScriptLambdaMethod("float4 mix(float4, float4, float4)", (float4 s, float4 t, float4 a), float4, { return float4(mix(s.x, t.x, a.x), mix(s.y, t.y, a.y), mix(s.z, t.z, a.z), mix(s.w, t.w, a.w)); }); + ScriptLambdaMethod("float4 mix(float4, float4, float)", (float4 s, float4 t, f32 a), float4, { return float4(mix(s.x, t.x, a), mix(s.y, t.y, a), mix(s.z, t.z, a), mix(s.w, t.w, a)); }); + ScriptLambdaMethod("int step(int, int)", (s32 edge, s32 x), s32, { return step(edge, x); }); + ScriptLambdaMethod("float step(float, float)", (f32 edge, f32 x), f32, { return step(edge, x); }); + ScriptLambdaMethod("float2 step(float2, float2)", (float2 edge, float2 v), float2, { return float2(step(edge.x, v.x), step(edge.y, v.y)); }); + ScriptLambdaMethod("float2 step(float, float2)", (f32 edge, float2 v), float2, { return float2(step(edge, v.x), step(edge, v.y)); }); + ScriptLambdaMethod("float3 step(float3, float3)", (float3 edge, float3 v), float3, { return float3(step(edge.x, v.x), step(edge.y, v.y), step(edge.z, v.z)); }); + ScriptLambdaMethod("float3 step(float, float3)", (f32 edge, float3 v), float3, { return float3(step(edge, v.x), step(edge, v.y), step(edge, v.z)); }); + ScriptLambdaMethod("float4 step(float4, float4)", (float4 edge, float4 v), float4, { return float4(step(edge.x, v.x), step(edge.y, v.y), step(edge.z, v.z), step(edge.w, v.w)); }); + ScriptLambdaMethod("float4 step(float, float4)", (f32 edge, float4 v), float4, { return float4(step(edge, v.x), step(edge, v.y), step(edge, v.z), step(edge, v.w)); }); + ScriptLambdaMethod("int sign(int)", (s32 v), s32, { return (v == 0) ? 0 : (v < 0) ? -1 : 1; }); + ScriptLambdaMethod("float sign(float)", (f32 v), f32, { return (v == 0.0f) ? 0.0f : (v < 0.0f) ? -1.0f : 1.0f; }); + ScriptLambdaMethod("float2 sign(float2)", (float2 v), float2, { return float2((v.x == 0.0f) ? 0.0f : (v.x < 0.0f) ? -1.0f : 1.0f, (v.y == 0.0f) ? 0.0f : (v.y < 0.0f) ? -1.0f : 1.0f); }); + ScriptLambdaMethod("float3 sign(float3)", (float3 v), float3, { return float3((v.x == 0.0f) ? 0.0f : (v.x < 0.0f) ? -1.0f : 1.0f, (v.y == 0.0f) ? 0.0f : (v.y < 0.0f) ? -1.0f : 1.0f, (v.z == 0.0f) ? 0.0f : (v.z < 0.0f) ? -1.0f : 1.0f); }); + ScriptLambdaMethod("float4 sign(float4)", (float4 v), float4, { return float4((v.x == 0.0f) ? 0.0f : (v.x < 0.0f) ? -1.0f : 1.0f, (v.y == 0.0f) ? 0.0f : (v.y < 0.0f) ? -1.0f : 1.0f, (v.z == 0.0f) ? 0.0f : (v.z < 0.0f) ? -1.0f : 1.0f, (v.w == 0.0f) ? 0.0f : (v.w < 0.0f) ? -1.0f : 1.0f); }); + ScriptLambdaMethod("float sqrt(float)", (f32 v), f32, { return sqrtf(v); }); + ScriptLambdaMethod("float2 sqrt(float2)", (float2 v), float2, { return float2(sqrtf(v.x), sqrtf(v.y)); }); + ScriptLambdaMethod("float3 sqrt(float3)", (float3 v), float3, { return float3(sqrtf(v.x), sqrtf(v.y), sqrtf(v.z)); }); + ScriptLambdaMethod("float4 sqrt(float4)", (float4 v), float4, { return float4(sqrtf(v.x), sqrtf(v.y), sqrtf(v.z), sqrtf(v.w)); }); + ScriptLambdaMethod("float smoothstep(float, float, float)", (f32 edge0, f32 edge1, f32 x), f32, { return smoothstep(edge0, edge1, x); }); + ScriptLambdaMethod("float2 smoothstep(float2, float2, float2)", (float2 edge0, float2 edge1, float2 v), float2, { return float2(smoothstep(edge0.x, edge1.x, v.x), smoothstep(edge0.y, edge1.y, v.y)); }); + ScriptLambdaMethod("float2 smoothstep(float, float, float2)", (f32 edge0, f32 edge1, float2 v), float2, { return float2(smoothstep(edge0, edge1, v.x), smoothstep(edge0, edge1, v.y)); }); + ScriptLambdaMethod("float2 smoothstep(float2, float2, float)", (float2 edge0, float2 edge1, f32 v), float2, { return float2(smoothstep(edge0.x, edge1.x, v), smoothstep(edge0.y, edge1.y, v)); }); + ScriptLambdaMethod("float3 smoothstep(float3, float3, float3)", (float3 edge0, float3 edge1, float3 v), float3, { return float3(smoothstep(edge0.x, edge1.x, v.x), smoothstep(edge0.y, edge1.y, v.y), smoothstep(edge0.z, edge1.z, v.z)); }); + ScriptLambdaMethod("float3 smoothstep(float, float, float3)", (f32 edge0, f32 edge1, float3 v), float3, { return float3(smoothstep(edge0, edge1, v.x), smoothstep(edge0, edge1, v.y), smoothstep(edge0, edge1, v.z)); }); + ScriptLambdaMethod("float3 smoothstep(float3, float3, float)", (float3 edge0, float3 edge1, f32 v), float3, { return float3(smoothstep(edge0.x, edge1.x, v), smoothstep(edge0.y, edge1.y, v), smoothstep(edge0.z, edge1.z, v)); }); + ScriptLambdaMethod("float4 smoothstep(float4, float4, float4)", (float4 edge0, float4 edge1, float4 v), float4, { return float4(smoothstep(edge0.x, edge1.x, v.x), smoothstep(edge0.y, edge1.y, v.y), smoothstep(edge0.z, edge1.z, v.z), smoothstep(edge0.w, edge1.w, v.w)); }); + ScriptLambdaMethod("float4 smoothstep(float, float, float4)", (f32 edge0, f32 edge1, float4 v), float4, { return float4(smoothstep(edge0, edge1, v.x), smoothstep(edge0, edge1, v.y), smoothstep(edge0, edge1, v.z), smoothstep(edge0, edge1, v.w)); }); + ScriptLambdaMethod("float4 smoothstep(float4, float4, float)", (float4 edge0, float4 edge1, f32 v), float4, { return float4(smoothstep(edge0.x, edge1.x, v), smoothstep(edge0.y, edge1.y, v), smoothstep(edge0.z, edge1.z, v), smoothstep(edge0.w, edge1.w, v)); }); + ScriptLambdaMethod("float mod(float, float)", (f32 s, f32 t), f32, { return fmodf(s, t); }); + ScriptLambdaMethod("float2 mod(float2, float2)", (float2 s, float2 t), float2, { return float2(fmodf(s.x, t.x),fmodf(s.y, t.y)); }); + ScriptLambdaMethod("float2 mod(float2, float)", (float2 s, f32 t), float2, { return float2(fmodf(s.x, t),fmodf(s.y, t)); }); + ScriptLambdaMethod("float3 mod(float3, float3)", (float3 s, float3 t), float3, { return float3(fmodf(s.x, t.x),fmodf(s.y, t.y),fmodf(s.z, t.z)); }); + ScriptLambdaMethod("float3 mod(float3, float)", (float3 s, f32 t), float3, { return float3(fmodf(s.x, t),fmodf(s.y, t),fmodf(s.z, t)); }); + ScriptLambdaMethod("float4 mod(float4, float4)", (float4 s, float4 t), float4, { return float4(fmodf(s.x, t.x),fmodf(s.y, t.y),fmodf(s.z, t.z),fmodf(s.w, t.w)); }); + ScriptLambdaMethod("float4 mod(float4, float)", (float4 s, f32 t), float4, { return float4(fmodf(s.x, t),fmodf(s.y, t),fmodf(s.z, t),fmodf(s.w, t)); }); + ScriptLambdaMethod("float distance(float, float)", (f32 a, f32 b), f32, { return fabsf(a - b); }); + ScriptLambdaMethod("float distance(float2, float2)", (float2 a, float2 b), f32, { return distance(a, b); }); + ScriptLambdaMethod("float distance(float3, float3)", (float3 a, float3 b), f32, { return distance(a, b); }); + ScriptLambdaMethod("float distance(float4, float4)", (float4 a, float4 b), f32, { return distance(a, b); }); + ScriptLambdaMethod("float dot(float, float)", (f32 a, f32 b), f32, { return a*b; }); + ScriptLambdaMethod("float dot(float2, float2)", (float2 a, float2 b), f32, { return dot(a,b); }); + ScriptLambdaMethod("float dot(float3, float3)", (float3 a, float3 b), f32, { return dot(a,b); }); + ScriptLambdaMethod("float dot(float4, float4)", (float4 a, float4 b), f32, { return dot(a,b); }); + ScriptLambdaMethod("float length(float)", (f32 a), f32, { return fabsf(a); }); + ScriptLambdaMethod("float length(float2)", (float2 a), f32, { return sqrtf(a.x*a.x + a.y*a.y); }); + ScriptLambdaMethod("float length(float3)", (float3 a), f32, { return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z); }); + ScriptLambdaMethod("float length(float4)", (float4 a), f32, { return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w); }); + ScriptLambdaMethod("float normalize(float)", (f32 a), f32, { return a < 0.0f ? -1.0f : 1.0f; }); + ScriptLambdaMethod("float2 normalize(float2)", (float2 a), float2, { return normalize(a); }); + ScriptLambdaMethod("float3 normalize(float3)", (float3 a), float3, { return normalize(a); }); + ScriptLambdaMethod("float4 normalize(float4)", (float4 a), float4, { return normalize(a); }); + ScriptLambdaMethod("float perp(float2, float2)", (float2 a, float2 b), f32, { return a.x*b.y - a.y*b.x; }); + ScriptLambdaMethod("float3 cross(float3, float3)", (float3 a, float3 b), float3, { return cross(a, b); }); + + ScriptObjFunc("float2x2 transpose(float2x2)", transpose2x2); + ScriptObjFunc("float3x3 transpose(float3x3)", transpose3x3); + ScriptObjFunc("float4x4 transpose(float4x4)", transpose4x4); + + ScriptObjFunc("float2x2 rotationMatrix2x2(float)", computeRotation2x2); + ScriptObjFunc("float3x3 rotationMatrix3x3(float, float, float = 0.0)", computeRotation3x3); + } + ScriptClassEnd(); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.h b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.h new file mode 100644 index 000000000..f7c071f00 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptMath.h @@ -0,0 +1,27 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include +#include + +#define Vec2f_to_float2(v) TFE_ForceScript::float2(v.x, v.z) +#define float2_to_Vec2f(v) Vec2f{v.x, v.y}; + +namespace TFE_ForceScript +{ + class ScriptMath : public ScriptAPIClass + { + public: + // System + bool scriptRegister(ScriptAPI api) override; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.cpp b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.cpp new file mode 100644 index 000000000..c81786f28 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.cpp @@ -0,0 +1,654 @@ +#include "scriptTest.h" +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_ForceScript +{ + std::string ScriptTest::s_system; + std::vector ScriptTest::s_tests; + + #define WRITE_ERROR(err) TFE_System::logWrite(LOG_ERROR, "Force Script", "[%s] Error: %s", s_tests.back().name.c_str(), err) + + bool ScriptTest::checkInTest() + { + if (s_tests.empty()) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "No valid test in system %s", s_system.c_str()); + return false; + } + return true; + } + + bool ScriptTest::compareValues(s32 typeId, void* ref0, void* ref1, bool equal, std::string& errorMsg) + { + char errMsg[4096]; + + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_STRING)) + { + std::string v0 = *(std::string*)ref0; + std::string v1 = *(std::string*)ref1; + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "'%s' != '%s'", v0.c_str(), v1.c_str()); } + else { sprintf(errMsg, "'%s' == '%s'", v0.c_str(), v1.c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) + { + TFE_ForceScript::float2* v0 = (TFE_ForceScript::float2*)ref0; + TFE_ForceScript::float2* v1 = (TFE_ForceScript::float2*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toString(*v0).c_str(), toString(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toString(*v0).c_str(), toString(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + TFE_ForceScript::float3* v1 = (TFE_ForceScript::float3*)ref0; + TFE_ForceScript::float3* v0 = (TFE_ForceScript::float3*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toStringF3(*v0).c_str(), toStringF3(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toStringF3(*v0).c_str(), toStringF3(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4)) + { + TFE_ForceScript::float4* v1 = (TFE_ForceScript::float4*)ref0; + TFE_ForceScript::float4* v0 = (TFE_ForceScript::float4*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toStringF4(*v0).c_str(), toStringF4(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toStringF4(*v0).c_str(), toStringF4(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2x2)) + { + TFE_ForceScript::float2x2* v1 = (TFE_ForceScript::float2x2*)ref0; + TFE_ForceScript::float2x2* v0 = (TFE_ForceScript::float2x2*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toStringF2x2(*v0).c_str(), toStringF2x2(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toStringF2x2(*v0).c_str(), toStringF2x2(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3x3)) + { + TFE_ForceScript::float3x3* v1 = (TFE_ForceScript::float3x3*)ref0; + TFE_ForceScript::float3x3* v0 = (TFE_ForceScript::float3x3*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toStringF3x3(*v0).c_str(), toStringF3x3(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toStringF3x3(*v0).c_str(), toStringF3x3(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4x4)) + { + TFE_ForceScript::float4x4* v1 = (TFE_ForceScript::float4x4*)ref0; + TFE_ForceScript::float4x4* v0 = (TFE_ForceScript::float4x4*)ref1; + if ((equal && *v0 != *v1) || (!equal && *v0 == *v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", toStringF4x4(*v0).c_str(), toStringF4x4(*v1).c_str()); } + else { sprintf(errMsg, "%s == %s", toStringF4x4(*v0).c_str(), toStringF4x4(*v1).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else + { + switch (typeId) + { + case 1: + { + bool v0 = *(bool*)(ref0); + bool v1 = *(bool*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%s != %s", v0 ? "true" : "false", v1 ? "true" : "false"); } + else { sprintf(errMsg, "%s == %s", v0 ? "true" : "false", v1 ? "true" : "false"); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 2: + { + char v0 = *(char*)(ref0); + char v1 = *(char*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%c != %c", v0, v1); } + else { sprintf(errMsg, "%c == %c", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 3: + { + s16 v0 = *(s16*)(ref0); + s16 v1 = *(s16*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%d != %d", v0, v1); } + else { sprintf(errMsg, "%d == %d", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 4: + { + s32 v0 = *(s32*)(ref0); + s32 v1 = *(s32*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%d != %d", v0, v1); } + else { sprintf(errMsg, "%d == %d", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 5: + { + s64 v0 = *(s64*)(ref0); + s64 v1 = *(s64*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%lld != %lld", v0, v1); } + else { sprintf(errMsg, "%lld == %lld", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 6: + { + u8 v0 = *(u8*)(ref0); + u8 v1 = *(u8*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%u != %u", v0, v1); } + else { sprintf(errMsg, "%u == %u", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 7: + { + u16 v0 = *(u16*)(ref0); + u16 v1 = *(u16*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%u != %u", v0, v1); } + else { sprintf(errMsg, "%u == %u", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 8: + { + u32 v0 = *(u32*)(ref0); + u32 v1 = *(u32*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%u != %u", v0, v1); } + else { sprintf(errMsg, "%u == %u", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 9: + { + u64 v0 = *(u64*)(ref0); + u64 v1 = *(u64*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%llu != %llu", v0, v1); } + else { sprintf(errMsg, "%llu == %llu", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 10: + { + f32 v0 = *(f32*)(ref0); + f32 v1 = *(f32*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%f != %f", v0, v1); } + else { sprintf(errMsg, "%f == %f", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 11: + { + f64 v0 = *(f64*)(ref0); + f64 v1 = *(f64*)(ref1); + if ((equal && v0 != v1) || (!equal && v0 == v1)) + { + if (equal) { sprintf(errMsg, "%f != %f", v0, v1); } + else { sprintf(errMsg, "%f == %f", v0, v1); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + default: + { + WRITE_ERROR("Unsupported type used."); + } + } + } + return false; + } + + bool ScriptTest::checkBoolean(s32 typeId, void* ref, bool testForTrue, std::string& errorMsg) + { + char errMsg[4096]; + + if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_STRING)) + { + std::string v = *(std::string*)ref; + if ((testForTrue && v.empty()) || (!testForTrue && !v.empty())) + { + if (testForTrue) { sprintf(errMsg, "'%s' empty", v.c_str()); } + else { sprintf(errMsg, "'%s' is not empty", v.c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT2)) + { + TFE_ForceScript::float2* v = (TFE_ForceScript::float2*)ref; + TFE_ForceScript::float2 zero(0.0f, 0.0f); + if ((testForTrue && (*v) == zero) || (!testForTrue && (*v) != zero)) + { + if (testForTrue) { sprintf(errMsg, "%s is zero", toString(*v).c_str()); } + else { sprintf(errMsg, "%s is non-zero", toString(*v).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT3)) + { + TFE_ForceScript::float3* v = (TFE_ForceScript::float3*)ref; + TFE_ForceScript::float3 zero(0.0f, 0.0f, 0.0f); + if ((testForTrue && (*v) == zero) || (!testForTrue && (*v) != zero)) + { + if (testForTrue) { sprintf(errMsg, "%s is zero", toStringF3(*v).c_str()); } + else { sprintf(errMsg, "%s is non-zero", toStringF3(*v).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + else if (typeId == TFE_ForceScript::getObjectTypeId(TFE_ForceScript::FSTYPE_FLOAT4)) + { + TFE_ForceScript::float4* v = (TFE_ForceScript::float4*)ref; + TFE_ForceScript::float4 zero(0.0f, 0.0f, 0.0f, 0.0f); + if ((testForTrue && (*v) == zero) || (!testForTrue && (*v) != zero)) + { + if (testForTrue) { sprintf(errMsg, "%s is zero", toStringF4(*v).c_str()); } + else { sprintf(errMsg, "%s is non-zero", toStringF4(*v).c_str()); } + errorMsg = errMsg; + return false; + } + return true; + } + // Skip matrix types. + else + { + switch (typeId) + { + case 1: + { + bool v = *(bool*)(ref); + if ((testForTrue && !v) || (!testForTrue && v)) + { + if (testForTrue) { sprintf(errMsg, "%s is false, true expected", v ? "true" : "false"); } + else { sprintf(errMsg, "%s is true, false expected", v ? "true" : "false"); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 2: + { + char v = *(char*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%d is false, true expected", v); } + else { sprintf(errMsg, "%d is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 3: + { + s16 v = *(s16*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%d is false, true expected", v); } + else { sprintf(errMsg, "%d is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 4: + { + s32 v = *(s32*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%d is false, true expected", v); } + else { sprintf(errMsg, "%d is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 5: + { + s64 v = *(s64*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%lld is false, true expected", v); } + else { sprintf(errMsg, "%lld is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 6: + { + u8 v = *(u8*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%u is false, true expected", v); } + else { sprintf(errMsg, "%u is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 7: + { + u16 v = *(u16*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%u is false, true expected", v); } + else { sprintf(errMsg, "%u is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 8: + { + u32 v = *(u32*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%u is false, true expected", v); } + else { sprintf(errMsg, "%u is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 9: + { + u64 v = *(u64*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%llu is false, true expected", v); } + else { sprintf(errMsg, "%llu is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 10: + { + f32 v = *(f32*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%f is false, true expected", v); } + else { sprintf(errMsg, "%f is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + case 11: + { + f64 v = *(f64*)(ref); + if ((testForTrue && v == 0) || (!testForTrue && v != 0)) + { + if (testForTrue) { sprintf(errMsg, "%f is false, true expected", v); } + else { sprintf(errMsg, "%f is true, false expected", v); } + errorMsg = errMsg; + return false; + } + return true; + break; + } + default: + { + WRITE_ERROR("Unsupported type used."); + } + } + } + return false; + } + + bool ScriptTest::validateExpect(asIScriptGeneric* gen) + { + if (!checkInTest()) + { + return false; + } + + const s32 argCount = gen->GetArgCount(); + if (argCount != 2) + { + WRITE_ERROR("expectEq() - invalid number of arguments."); + s_tests.back().errorCount++; + return false; + } + + if (gen->GetArgTypeId(0) != gen->GetArgTypeId(1)) + { + WRITE_ERROR("expectEq() - arguments are not of the same type."); + s_tests.back().errorCount++; + return false; + } + return true; + } + + bool ScriptTest::validateBoolean(asIScriptGeneric* gen) + { + if (!checkInTest()) + { + return false; + } + + const s32 argCount = gen->GetArgCount(); + if (argCount != 1) + { + WRITE_ERROR("expectFalse() - invalid number of arguments."); + s_tests.back().errorCount++; + return false; + } + + return true; + } + + void ScriptTest::expectEq(asIScriptGeneric* gen) + { + if (!validateExpect(gen)) { return; } + + std::string errorMsg; + if (!compareValues(gen->GetArgTypeId(0), gen->GetArgAddress(0), gen->GetArgAddress(1), true/*values must be equal*/, errorMsg)) + { + WRITE_ERROR(errorMsg.c_str()); + s_tests.back().errorCount++; + } + } + + void ScriptTest::expectNotEq(asIScriptGeneric* gen) + { + if (!validateExpect(gen)) { return; } + + std::string errorMsg; + if (!compareValues(gen->GetArgTypeId(0), gen->GetArgAddress(0), gen->GetArgAddress(1), false/*values must be not equal*/, errorMsg)) + { + WRITE_ERROR(errorMsg.c_str()); + s_tests.back().errorCount++; + } + } + + void ScriptTest::expectTrue(asIScriptGeneric* gen) + { + if (!validateBoolean(gen)) { return; } + + std::string errorMsg; + if (!checkBoolean(gen->GetArgTypeId(0), gen->GetArgAddress(0), true/*value must be true*/, errorMsg)) + { + WRITE_ERROR(errorMsg.c_str()); + s_tests.back().errorCount++; + } + } + + void ScriptTest::expectFalse(asIScriptGeneric* gen) + { + if (!validateBoolean(gen)) { return; } + + std::string errorMsg; + if (!checkBoolean(gen->GetArgTypeId(0), gen->GetArgAddress(0), false/*value must be false*/, errorMsg)) + { + WRITE_ERROR(errorMsg.c_str()); + s_tests.back().errorCount++; + } + } + + void ScriptTest::setTestSystem(const std::string& name) + { + s_system = name; + s_tests.clear(); + TFE_System::logWrite(LOG_MSG, "Force Script", "----- Script Tests Begin %s -----", s_system.c_str()); + } + + void ScriptTest::report() + { + char results[4096]; + if (s_tests.empty()) + { + sprintf(results, "FAILURE! No tests ran."); + } + else + { + const s32 count = (s32)s_tests.size(); + s32 errorCount = 0; + for (s32 i = 0; i < count; i++) + { + errorCount += s_tests[i].errorCount; + } + if (errorCount == 0) + { + sprintf(results, "SUCCESS!\nTests Ran: %d", count); + } + else + { + sprintf(results, "FAILURE!\nTests Ran: %d, Error Count: %d", count, errorCount); + } + } + TFE_System::logWrite(LOG_MSG, "Force Script", "---Results---"); + TFE_System::logWrite(LOG_MSG, "Force Script", results); + TFE_System::logWrite(LOG_MSG, "Force Script", "-------------"); + } + + void ScriptTest::setTestName(const std::string& name) + { + Test newTest = {}; + newTest.name = name; + newTest.errorCount = 0; + s_tests.push_back(newTest); + } + + bool ScriptTest::scriptRegister(ScriptAPI api) + { + ScriptClassBegin("Test", "test", api); + { + ScriptGenericMethod("void expectEq(?&in, ?&in)", expectEq); + ScriptGenericMethod("void expectNotEq(?&in, ?&in)", expectNotEq); + ScriptGenericMethod("void expectTrue(?&in)", expectTrue); + ScriptGenericMethod("void expectFalse(?&in)", expectFalse); + + ScriptObjMethod("void beginSystem(const string &in)", setTestSystem); + ScriptObjMethod("void beginTest(const string &in)", setTestName); + ScriptObjMethod("void report()", report); + } + ScriptClassEnd(); + } +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.h b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.h new file mode 100644 index 000000000..7d0f44062 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/scriptTest.h @@ -0,0 +1,49 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#ifdef ENABLE_FORCE_SCRIPT +#include +#include +#include + +namespace TFE_ForceScript +{ + class ScriptTest : public ScriptAPIClass + { + public: + static void expectEq(asIScriptGeneric* gen); + static void expectNotEq(asIScriptGeneric* gen); + static void expectTrue(asIScriptGeneric* gen); + static void expectFalse(asIScriptGeneric* gen); + + void setTestSystem(const std::string& name); + void setTestName(const std::string& name); + void report(); + + static bool checkInTest(); + static bool compareValues(s32 typeId, void* ref0, void* ref1, bool equal, std::string& values); + static bool checkBoolean(s32 typeId, void* ref, bool testForTrue, std::string& errorMsg); + static bool validateExpect(asIScriptGeneric* gen); + static bool validateBoolean(asIScriptGeneric* gen); + + // System + bool scriptRegister(ScriptAPI api) override; + + private: + struct Test + { + std::string name; + s32 errorCount; + }; + + static std::string s_system; + static std::vector s_tests; + }; +} +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.cpp b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.cpp new file mode 100644 index 000000000..921ac5630 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.cpp @@ -0,0 +1,47 @@ +#include "sharedScriptAPI.h" +#include "scriptMath.h" +#include "scriptTest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT +#include + +using namespace TFE_Jedi; + +namespace TFE_ForceScript +{ + bool s_sharedScriptRegistered = false; + + ScriptMath s_math; + ScriptTest s_test; + + void registerSharedScriptAPI(ScriptAPI api) + { + if (s_sharedScriptRegistered) { return; } + s_sharedScriptRegistered = true; + + s_math.scriptRegister(api); + s_test.scriptRegister(api); + } +} +#else +namespace TFE_ForceScript +{ + void registerSharedScriptAPI(ScriptAPI api) {} +} +#endif diff --git a/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.h b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.h new file mode 100644 index 000000000..18bef5ac1 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/ScriptAPI-Shared/sharedScriptAPI.h @@ -0,0 +1,15 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#include + +namespace TFE_ForceScript +{ + void registerSharedScriptAPI(ScriptAPI api); +} diff --git a/TheForceEngine/TFE_ForceScript/float2.h b/TheForceEngine/TFE_ForceScript/float2.h index a433c14a0..5a3645f97 100644 --- a/TheForceEngine/TFE_ForceScript/float2.h +++ b/TheForceEngine/TFE_ForceScript/float2.h @@ -66,5 +66,7 @@ namespace TFE_ForceScript void registerScriptMath_float2(asIScriptEngine *engine); s32 getFloat2ObjectId(); std::string toString(const float2& v); + + inline f32 dot(float2 a, float2 b) { return a.x*b.x + a.y*b.y; } } // TFE_ForceScript #endif diff --git a/TheForceEngine/TFE_ForceScript/float2x2.cpp b/TheForceEngine/TFE_ForceScript/float2x2.cpp new file mode 100644 index 000000000..5f64f839f --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float2x2.cpp @@ -0,0 +1,161 @@ +#include "float2x2.h" +#include "forceScript.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT + +// TFE_ForceScript wraps Anglescript, so these includes should only exist here. +#include + +namespace TFE_ForceScript +{ + float2x2::float2x2() + { + m[0] = 1.0f; m[1] = 0.0f; + m[2] = 0.0f; m[3] = 1.0f; + } + + float2x2::float2x2(float2 _r0, float2 _r1) + { + r[0] = _r0; + r[1] = _r1; + } + + float2x2::float2x2(const float2x2& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + } + + float2x2::float2x2(f32 m00, f32 m01, f32 m10, f32 m11) + { + m[0] = m00; m[1] = m01; + m[2] = m10; m[3] = m11; + } + + bool float2x2::operator==(const float2x2& o) const + { + return (m[0] == o.m[0]) && (m[1] == o.m[1]) && (m[2] == o.m[2]) && (m[3] == o.m[3]); + } + bool float2x2::operator!=(const float2x2& o) const + { + return !(*this == o); + } + + float2x2& float2x2::operator=(const float2x2& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + return *this; + } + // Compound float2 + float2x2& float2x2::operator*=(const float2x2& other) + { + *this = *this * other; + return *this; + } + float2x2& float2x2::operator*=(const f32& other) + { + *this = *this * other; + return *this; + } + + float2x2 matMul(const float2x2& a, const float2x2& b) + { + float2 r0 = a.r[0]; + float2 r1 = a.r[1]; + float2 c0(b.r[0].x, b.r[1].x); + float2 c1(b.r[0].y, b.r[1].y); + + return float2x2(dot(r0, c0), dot(r0, c1), dot(r1, c0), dot(r1, c1)); + } + + float2 matVecMul(const float2x2& a, const float2& b) + { + return float2(dot(a.r[0], b), dot(a.r[1], b)); + } + + // float2x2 x float2x2 + float2x2 float2x2::operator*(const float2x2& other) const { return matMul(*this, other); } + // float2x2 x scalar + float2x2 float2x2::operator*(const f32& other) const { return float2x2(r[0]*other, r[1]*other); } + // float2x2 x float2 + float2 float2x2::operator*(const float2& other) const { return matVecMul(*this, other); } + + //----------------------- + // Array operator + //----------------------- + float2& float2x2::get_opIndex(s32 index) + { + if (index < 0 || index >= 2) + { + // TODO: WARNING + index = 0; + } + return r[index]; + } + + //----------------------- + // AngelScript functions + //----------------------- + static void float2x2DefaultConstructor(float2x2* self) { new(self) float2x2(); } + static void float2x2CopyConstructor(const float2x2& other, float2x2* self) { new(self) float2x2(other); } + static void float2x2ConvConstructor(const float2& m0, const float2& m1, float2x2* self) { new(self) float2x2(m0, m1); } + static void float2x2InitConstructor(f32 m0, f32 m1, f32 m2, f32 m3, float2* self) { new(self) float2x2(m0, m1, m2, m3); } + static void float2x2ListConstructor(f32* list, float2* self) { new(self) float2x2(list[0], list[1], list[2], list[3]); } + + //----------------------- + // Intrinsics + //----------------------- + std::string toStringF2x2(const float2x2& m) + { + char tmp[1024]; + sprintf(tmp, "[ (%g, %g), (%g, %g) ]", m.r[0].x, m.r[0].y, m.r[1].x, m.r[1].y); + return std::string(tmp); + } + + //------------------------------------- + // Registration + //------------------------------------- + static s32 s_float2x2ObjId = -1; + s32 getFloat2x2ObjectId() + { + return s_float2x2ObjId; + } + + void registerScriptMath_float2x2(asIScriptEngine *engine) + { + s32 r; + r = engine->RegisterObjectType("float2x2", sizeof(float2x2), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); assert(r >= 0); + s_float2x2ObjId = r; + + // Register the constructors + r = engine->RegisterObjectBehaviour("float2x2", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(float2x2DefaultConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float2x2", asBEHAVE_CONSTRUCT, "void f(const float2x2 &in)", asFUNCTION(float2x2CopyConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float2x2", asBEHAVE_CONSTRUCT, "void f(const float2 &in, const float2 &in)", asFUNCTION(float2x2ConvConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float2x2", asBEHAVE_CONSTRUCT, "void f(float, float, float, float)", asFUNCTION(float2x2InitConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float2x2", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float, float, float}", asFUNCTION(float2x2ListConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Register the operator overloads + r = engine->RegisterObjectMethod("float2x2", "float2x2 &opMulAssign(const float2x2 &in)", asMETHODPR(float2x2, operator*=, (const float2x2 &), float2x2&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float2x2", "float2x2 &opMulAssign(const float &in)", asMETHODPR(float2x2, operator*=, (const float &), float2x2&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float2x2", "bool opEquals(const float2x2 &in) const", asMETHODPR(float2x2, operator==, (const float2x2 &) const, bool), asCALL_THISCALL); assert(r >= 0); + + // Indexing operator. + r = engine->RegisterObjectMethod("float2x2", "float2& opIndex(int)", asMETHOD(float2x2, get_opIndex), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("float2x2", "float2x2 opMul(const float2x2 &in) const", asMETHODPR(float2x2, operator*, (const float2x2 &) const, float2x2), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float2x2", "float2x2 opMul(const float &in) const", asMETHODPR(float2x2, operator*, (const float &) const, float2x2), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float2x2", "float2 opMul(const float2 &in) const", asMETHODPR(float2x2, operator*, (const float2 &) const, float2), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string toString(float2x2 m)", asFUNCTION(toStringF2x2), asCALL_CDECL); assert(r >= 0); + } +} // TFE_ForceScript + +#endif diff --git a/TheForceEngine/TFE_ForceScript/float2x2.h b/TheForceEngine/TFE_ForceScript/float2x2.h new file mode 100644 index 000000000..869e510d6 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float2x2.h @@ -0,0 +1,52 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// Matrix types for scripts. +////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "float2.h" + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_ForceScript +{ + struct float2x2 + { + float2x2(); + float2x2(const float2x2& other); + float2x2(float2 r0, float2 r1); + float2x2(f32 m00, f32 m01, f32 m10, f32 m11); + + // Assignment operator + float2x2 &operator=(const float2x2& other); + + // Compound assigment operators + float2x2 &operator*=(const float2x2& other); + float2x2 &operator*=(const f32& other); + + // Indexing operator + float2& get_opIndex(s32 index); + + // Comparison + bool operator==(const float2x2& other) const; + bool operator!=(const float2x2& other) const; + + // Math operators + float2x2 operator*(const float2x2& other) const; + float2x2 operator*(const f32& other) const; + float2 operator*(const float2& other) const; + + union + { + float2 r[2]; + f32 m[4]; + }; + }; + + void registerScriptMath_float2x2(asIScriptEngine *engine); + s32 getFloat2x2ObjectId(); + std::string toStringF2x2(const float2x2& v); +} // TFE_ForceScript +#endif diff --git a/TheForceEngine/TFE_ForceScript/float3.cpp b/TheForceEngine/TFE_ForceScript/float3.cpp new file mode 100644 index 000000000..f13cd2e2f --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float3.cpp @@ -0,0 +1,301 @@ +#include "float3.h" +#include "forceScript.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT + +// TFE_ForceScript wraps Anglescript, so these includes should only exist here. +#include + +namespace TFE_ForceScript +{ + float3::float3() + { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + float3::float3(const float3& other) + { + x = other.x; + y = other.y; + z = other.z; + } + + float3::float3(f32 _x, f32 _y, f32 _z) + { + x = _x; + y = _y; + z = _z; + } + + bool float3::operator==(const float3& o) const + { + return (x == o.x) && (y == o.y) && (z == o.z); + } + bool float3::operator!=(const float3& o) const + { + return !(*this == o); + } + + float3& float3::operator=(const float3& other) + { + x = other.x; + y = other.y; + z = other.z; + return *this; + } + // Compound float3 + float3& float3::operator+=(const float3& other) + { + x += other.x; + y += other.y; + z += other.z; + return *this; + } + float3& float3::operator-=(const float3& other) + { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + float3& float3::operator*=(const float3& other) + { + *this = *this * other; + return *this; + } + float3& float3::operator/=(const float3& other) + { + *this = *this / other; + return *this; + } + // Compound Scalar + float3& float3::operator+=(const f32& other) + { + x += other; + y += other; + z += other; + return *this; + } + float3& float3::operator-=(const f32& other) + { + x -= other; + y -= other; + z -= other; + return *this; + } + float3& float3::operator*=(const f32& other) + { + *this = *this * other; + return *this; + } + float3& float3::operator/=(const f32& other) + { + *this = *this / other; + return *this; + } + + // float3 x float3 + float3 float3::operator+(const float3& other) const { return float3(x + other.x, y + other.y, z + other.z); } + float3 float3::operator-(const float3& other) const { return float3(x - other.x, y - other.y, z - other.z); } + float3 float3::operator*(const float3& other) const { return float3(x * other.x, y * other.y, z * other.z); } + float3 float3::operator/(const float3& other) const { return float3(x / other.x, y / other.y, z / other.z); } + // float3 x scalar + float3 float3::operator+(const f32& other) const { return float3(x + other, y + other, z + other); } + float3 float3::operator-(const f32& other) const { return float3(x - other, y - other, z - other); } + float3 float3::operator*(const f32& other) const { return float3(x * other, y * other, z * other); } + float3 float3::operator/(const f32& other) const { return float3(x / other, y / other, z / other); } + + //----------------------- + // Swizzle operators + //----------------------- + // float2 + float2 float3::get_xx() const { return float2(x, x); } + float2 float3::get_xy() const { return float2(x, y); } + float2 float3::get_xz() const { return float2(x, z); } + float2 float3::get_yx() const { return float2(y, x); } + float2 float3::get_yy() const { return float2(y, y); } + float2 float3::get_yz() const { return float2(y, z); } + float2 float3::get_zx() const { return float2(z, x); } + float2 float3::get_zy() const { return float2(z, y); } + float2 float3::get_zz() const { return float2(z, z); } + // float3 - x + float3 float3::get_xxx() const { return float3(x, x, x); } + float3 float3::get_xxy() const { return float3(x, x, y); } + float3 float3::get_xxz() const { return float3(x, x, z); } + float3 float3::get_xyx() const { return float3(x, y, x); } + float3 float3::get_xyy() const { return float3(x, y, y); } + float3 float3::get_xyz() const { return float3(x, y, z); } + float3 float3::get_xzx() const { return float3(x, z, x); } + float3 float3::get_xzy() const { return float3(x, z, y); } + float3 float3::get_xzz() const { return float3(x, z, z); } + void float3::set_xyz(const float3& o) { *this = o; } + void float3::set_xzy(const float3& o) { x = o.x; z = o.y; y = o.z; } + // float3 - y + float3 float3::get_yxx() const { return float3(y, x, x); } + float3 float3::get_yxy() const { return float3(y, x, y); } + float3 float3::get_yxz() const { return float3(y, x, z); } + float3 float3::get_yyx() const { return float3(y, y, x); } + float3 float3::get_yyy() const { return float3(y, y, y); } + float3 float3::get_yyz() const { return float3(y, y, z); } + float3 float3::get_yzx() const { return float3(y, z, x); } + float3 float3::get_yzy() const { return float3(y, z, y); } + float3 float3::get_yzz() const { return float3(y, z, z); } + void float3::set_yxz(const float3& o) { y = o.x; x = o.y; z = o.z; } + void float3::set_yzx(const float3& o) { y = o.x; z = o.y; x = o.z; } + // float3 - z + float3 float3::get_zxx() const { return float3(z, x, x); } + float3 float3::get_zxy() const { return float3(z, x, y); } + float3 float3::get_zxz() const { return float3(z, x, z); } + float3 float3::get_zyx() const { return float3(z, y, x); } + float3 float3::get_zyy() const { return float3(z, y, y); } + float3 float3::get_zyz() const { return float3(z, y, z); } + float3 float3::get_zzx() const { return float3(z, z, x); } + float3 float3::get_zzy() const { return float3(z, z, y); } + float3 float3::get_zzz() const { return float3(z, z, z); } + void float3::set_zxy(const float3& o) { z = o.x; x = o.y; y = o.z; } + void float3::set_zyx(const float3& o) { z = o.x; y = o.y; x = o.z; } + + //----------------------- + // Array operator + //----------------------- + f32& float3::get_opIndex(s32 index) + { + if (index < 0 || index >= 3) + { + // TODO: WARNING + index = 0; + } + return m[index]; + } + + //----------------------- + // AngelScript functions + //----------------------- + static void float3DefaultConstructor(float3* self) { new(self) float3(); } + static void float3CopyConstructor(const float3& other, float3* self) { new(self) float3(other); } + static void float3ConvConstructor(f32 x, float3* self) { new(self) float3(x, x, x); } + static void float3InitConstructor(f32 x, f32 y, f32 z, float3* self) { new(self) float3(x, y, z); } + static void float3ListConstructor(f32* list, float3* self) { new(self) float3(list[0], list[1], list[2]); } + + //----------------------- + // Intrinsics + //----------------------- + std::string toStringF3(const float3& v) + { + char tmp[1024]; + sprintf(tmp, "(%g, %g, %g)", v.x, v.y, v.z); + return std::string(tmp); + } + + //------------------------------------- + // Registration + //------------------------------------- + static s32 s_float3ObjId = -1; + s32 getFloat3ObjectId() + { + return s_float3ObjId; + } + + void registerScriptMath_float3(asIScriptEngine *engine) + { + s32 r; + r = engine->RegisterObjectType("float3", sizeof(float3), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); assert(r >= 0); + s_float3ObjId = r; + + // Register the object properties + r = engine->RegisterObjectProperty("float3", "float x", asOFFSET(float3, x)); assert(r >= 0); + r = engine->RegisterObjectProperty("float3", "float y", asOFFSET(float3, y)); assert(r >= 0); + r = engine->RegisterObjectProperty("float3", "float z", asOFFSET(float3, z)); assert(r >= 0); + + // Register the constructors + r = engine->RegisterObjectBehaviour("float3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(float3DefaultConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3", asBEHAVE_CONSTRUCT, "void f(const float3 &in)", asFUNCTION(float3CopyConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(float3ConvConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(float3InitConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float, float}", asFUNCTION(float3ListConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Register the operator overloads + r = engine->RegisterObjectMethod("float3", "float3 &opAddAssign(const float3 &in)", asMETHODPR(float3, operator+=, (const float3 &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opSubAssign(const float3 &in)", asMETHODPR(float3, operator-=, (const float3 &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opMulAssign(const float3 &in)", asMETHODPR(float3, operator*=, (const float3 &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opDivAssign(const float3 &in)", asMETHODPR(float3, operator/=, (const float3 &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opAddAssign(const float &in)", asMETHODPR(float3, operator+=, (const float &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opSubAssign(const float &in)", asMETHODPR(float3, operator-=, (const float &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opMulAssign(const float &in)", asMETHODPR(float3, operator*=, (const float &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 &opDivAssign(const float &in)", asMETHODPR(float3, operator/=, (const float &), float3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "bool opEquals(const float3 &in) const", asMETHODPR(float3, operator==, (const float3 &) const, bool), asCALL_THISCALL); assert(r >= 0); + + // Indexing operator. + r = engine->RegisterObjectMethod("float3", "float& opIndex(int)", asMETHOD(float3, get_opIndex), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("float3", "float3 opAdd(const float3 &in) const", asMETHODPR(float3, operator+, (const float3 &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opSub(const float3 &in) const", asMETHODPR(float3, operator-, (const float3 &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opMul(const float3 &in) const", asMETHODPR(float3, operator*, (const float3 &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opDiv(const float3 &in) const", asMETHODPR(float3, operator/, (const float3 &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opAdd(const float &in) const", asMETHODPR(float3, operator+, (const float &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opSub(const float &in) const", asMETHODPR(float3, operator-, (const float &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opMul(const float &in) const", asMETHODPR(float3, operator*, (const float &) const, float3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 opDiv(const float &in) const", asMETHODPR(float3, operator/, (const float &) const, float3), asCALL_THISCALL); assert(r >= 0); + + // Register the swizzle operators + r = engine->RegisterObjectMethod("float3", "float2 get_xx() const property", asMETHOD(float3, get_xx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_xy() const property", asMETHOD(float3, get_xy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_xz() const property", asMETHOD(float3, get_xz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_yx() const property", asMETHOD(float3, get_yx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_yy() const property", asMETHOD(float3, get_yy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_yz() const property", asMETHOD(float3, get_yz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_zx() const property", asMETHOD(float3, get_zx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_zy() const property", asMETHOD(float3, get_zy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float2 get_zz() const property", asMETHOD(float3, get_zz), asCALL_THISCALL); assert(r >= 0); + // Swizzle operators - float3 + // x + r = engine->RegisterObjectMethod("float3", "float3 get_xxx() const property", asMETHOD(float3, get_xxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xxy() const property", asMETHOD(float3, get_xxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xxz() const property", asMETHOD(float3, get_xxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xyx() const property", asMETHOD(float3, get_xyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xyy() const property", asMETHOD(float3, get_xyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xyz() const property", asMETHOD(float3, get_xyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xzx() const property", asMETHOD(float3, get_xzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xzy() const property", asMETHOD(float3, get_xzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_xzz() const property", asMETHOD(float3, get_xzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_xyz(const float3 &in) property", asMETHOD(float3, set_xyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_xzy(const float3 &in) property", asMETHOD(float3, set_xzy), asCALL_THISCALL); assert(r >= 0); + // y + r = engine->RegisterObjectMethod("float3", "float3 get_yxx() const property", asMETHOD(float3, get_yxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yxy() const property", asMETHOD(float3, get_yxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yxz() const property", asMETHOD(float3, get_yxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yyx() const property", asMETHOD(float3, get_yyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yyy() const property", asMETHOD(float3, get_yyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yyz() const property", asMETHOD(float3, get_yyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yzx() const property", asMETHOD(float3, get_yzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yzy() const property", asMETHOD(float3, get_yzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_yzz() const property", asMETHOD(float3, get_yzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_yxz(const float3 &in) property", asMETHOD(float3, set_yxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_yzx(const float3 &in) property", asMETHOD(float3, set_yzx), asCALL_THISCALL); assert(r >= 0); + // z + r = engine->RegisterObjectMethod("float3", "float3 get_zxx() const property", asMETHOD(float3, get_zxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zxy() const property", asMETHOD(float3, get_zxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zxz() const property", asMETHOD(float3, get_zxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zyx() const property", asMETHOD(float3, get_zyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zyy() const property", asMETHOD(float3, get_zyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zyz() const property", asMETHOD(float3, get_zyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zzx() const property", asMETHOD(float3, get_zzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zzy() const property", asMETHOD(float3, get_zzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "float3 get_zzz() const property", asMETHOD(float3, get_zzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_zxy(const float3 &in) property", asMETHOD(float3, set_zxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3", "void set_zyx(const float3 &in) property", asMETHOD(float3, set_zyx), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string toString(float3 v)", asFUNCTION(toStringF3), asCALL_CDECL); assert(r >= 0); + } +} // TFE_ForceScript + +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/float3.h b/TheForceEngine/TFE_ForceScript/float3.h new file mode 100644 index 000000000..5aec07dbf --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float3.h @@ -0,0 +1,115 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// Vector types for scripts. +////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "float2.h" + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_ForceScript +{ + struct float3 + { + float3(); + float3(const float3& other); + float3(f32 x, f32 y, f32 z); + + // Assignment operator + float3 &operator=(const float3& other); + + // Compound assigment operators + float3 &operator+=(const float3& other); + float3 &operator-=(const float3& other); + float3 &operator*=(const float3& other); + float3 &operator/=(const float3& other); + + float3 &operator+=(const f32& other); + float3 &operator-=(const f32& other); + float3 &operator*=(const f32& other); + float3 &operator/=(const f32& other); + + // Swizzle operators - float2 + float2 get_xx() const; + float2 get_xy() const; + float2 get_xz() const; + float2 get_yx() const; + float2 get_yy() const; + float2 get_yz() const; + float2 get_zx() const; + float2 get_zy() const; + float2 get_zz() const; + + // Swizzle operators - float3 + // x + float3 get_xxx() const; + float3 get_xxy() const; + float3 get_xxz() const; + float3 get_xyx() const; + float3 get_xyy() const; + float3 get_xyz() const; + float3 get_xzx() const; + float3 get_xzy() const; + float3 get_xzz() const; + void set_xyz(const float3& in); + void set_xzy(const float3& in); + // y + float3 get_yxx() const; + float3 get_yxy() const; + float3 get_yxz() const; + float3 get_yyx() const; + float3 get_yyy() const; + float3 get_yyz() const; + float3 get_yzx() const; + float3 get_yzy() const; + float3 get_yzz() const; + void set_yxz(const float3& in); + void set_yzx(const float3& in); + // z + float3 get_zxx() const; + float3 get_zxy() const; + float3 get_zxz() const; + float3 get_zyx() const; + float3 get_zyy() const; + float3 get_zyz() const; + float3 get_zzx() const; + float3 get_zzy() const; + float3 get_zzz() const; + void set_zxy(const float3& in); + void set_zyx(const float3& in); + + // Indexing operator + f32& get_opIndex(s32 index); + + // Comparison + bool operator==(const float3& other) const; + bool operator!=(const float3& other) const; + + // Math operators + float3 operator+(const float3& other) const; + float3 operator-(const float3& other) const; + float3 operator*(const float3& other) const; + float3 operator/(const float3& other) const; + + float3 operator+(const f32& other) const; + float3 operator-(const f32& other) const; + float3 operator*(const f32& other) const; + float3 operator/(const f32& other) const; + + union + { + struct { f32 x, y, z; }; + f32 m[3]; + }; + }; + + void registerScriptMath_float3(asIScriptEngine *engine); + s32 getFloat3ObjectId(); + std::string toStringF3(const float3& v); + + inline f32 dot(float3 a, float3 b) { return a.x*b.x + a.y*b.y + a.z*b.z; } +} // TFE_ForceScript +#endif diff --git a/TheForceEngine/TFE_ForceScript/float3x3.cpp b/TheForceEngine/TFE_ForceScript/float3x3.cpp new file mode 100644 index 000000000..f0029a03f --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float3x3.cpp @@ -0,0 +1,174 @@ +#include "float3x3.h" +#include "forceScript.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT + +// TFE_ForceScript wraps Anglescript, so these includes should only exist here. +#include + +namespace TFE_ForceScript +{ + float3x3::float3x3() + { + m[0] = 1.0f; m[1] = 0.0f; m[2] = 0.0f; + m[3] = 0.0f; m[4] = 1.0f; m[5] = 0.0f; + m[6] = 0.0f; m[7] = 0.0f; m[8] = 1.0f; + } + + float3x3::float3x3(float3 _r0, float3 _r1, float3 _r2) + { + r[0] = _r0; + r[1] = _r1; + r[2] = _r2; + } + + float3x3::float3x3(const float3x3& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + r[2] = other.r[2]; + } + + float3x3::float3x3(f32 m00, f32 m01, f32 m02, f32 m10, f32 m11, f32 m12, f32 m20, f32 m21, f32 m22) + { + m[0] = m00; m[1] = m01; m[2] = m02; + m[3] = m10; m[4] = m11; m[5] = m12; + m[6] = m20; m[7] = m21; m[8] = m22; + } + + bool float3x3::operator==(const float3x3& o) const + { + for (s32 i = 0; i < 9; i++) + { + if (m[i] != o.m[i]) { return false; } + } + return true; + } + bool float3x3::operator!=(const float3x3& o) const + { + return !(*this == o); + } + + float3x3& float3x3::operator=(const float3x3& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + r[2] = other.r[2]; + return *this; + } + // Compound float2 + float3x3& float3x3::operator*=(const float3x3& other) + { + *this = *this * other; + return *this; + } + float3x3& float3x3::operator*=(const f32& other) + { + *this = *this * other; + return *this; + } + + float3x3 matMul(const float3x3& a, const float3x3& b) + { + float3 r0 = a.r[0]; + float3 r1 = a.r[1]; + float3 r2 = a.r[2]; + float3 c0(b.r[0].x, b.r[1].x, b.r[2].x); + float3 c1(b.r[0].y, b.r[1].y, b.r[2].y); + float3 c2(b.r[0].z, b.r[1].z, b.r[2].z); + + return float3x3(dot(r0, c0), dot(r0, c1), dot(r0, c2), + dot(r1, c0), dot(r1, c1), dot(r1, c2), + dot(r2, c0), dot(r2, c1), dot(r2, c2)); + } + + float3 matVecMul(const float3x3& a, const float3& b) + { + return float3(dot(a.r[0], b), dot(a.r[1], b), dot(a.r[2], b)); + } + + // float3x3 x float3x3 + float3x3 float3x3::operator*(const float3x3& other) const { return matMul(*this, other); } + // float3x3 x scalar + float3x3 float3x3::operator*(const f32& other) const { return float3x3(r[0]*other, r[1]*other, r[2]*other); } + // float3x3 x float2 + float3 float3x3::operator*(const float3& other) const { return matVecMul(*this, other); } + + //----------------------- + // Array operator + //----------------------- + float3& float3x3::get_opIndex(s32 index) + { + if (index < 0 || index >= 3) + { + // TODO: WARNING + index = 0; + } + return r[index]; + } + + //----------------------- + // AngelScript functions + //----------------------- + static void float3x3DefaultConstructor(float3x3* self) { new(self) float3x3(); } + static void float3x3CopyConstructor(const float3x3& other, float3x3* self) { new(self) float3x3(other); } + static void float3x3ConvConstructor(const float3& m0, const float3& m1, const float3& m2, float3x3* self) { new(self) float3x3(m0, m1, m2); } + static void float3x3InitConstructor(f32 m0, f32 m1, f32 m2, f32 m3, f32 m4, f32 m5, f32 m6, f32 m7, f32 m8, float2* self) { new(self) float3x3(m0, m1, m2, m3, m4, m5, m6, m7, m8); } + static void float3x3ListConstructor(f32* list, float2* self) { new(self) float3x3(list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], list[8]); } + + //----------------------- + // Intrinsics + //----------------------- + std::string toStringF3x3(const float3x3& m) + { + char tmp[1024]; + sprintf(tmp, "[ (%g, %g, %g), (%g, %g, %g), (%g, %g, %g) ]", m.r[0].x, m.r[0].y, m.r[0].z, m.r[1].x, m.r[1].y, m.r[1].z, m.r[2].x, m.r[2].y, m.r[2].z); + return std::string(tmp); + } + + //------------------------------------- + // Registration + //------------------------------------- + static s32 s_float3x3ObjId = -1; + s32 getFloat3x3ObjectId() + { + return s_float3x3ObjId; + } + + void registerScriptMath_float3x3(asIScriptEngine *engine) + { + s32 r; + r = engine->RegisterObjectType("float3x3", sizeof(float3x3), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); assert(r >= 0); + s_float3x3ObjId = r; + + // Register the constructors + r = engine->RegisterObjectBehaviour("float3x3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(float3x3DefaultConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3x3", asBEHAVE_CONSTRUCT, "void f(const float3x3 &in)", asFUNCTION(float3x3CopyConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3x3", asBEHAVE_CONSTRUCT, "void f(const float3 &in, const float3 &in, const float3 &in)", asFUNCTION(float3x3ConvConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3x3", asBEHAVE_CONSTRUCT, "void f(float, float, float, float, float, float, float, float, float)", asFUNCTION(float3x3InitConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float3x3", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float, float, float, float, float, float, float, float}", asFUNCTION(float3x3ListConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Register the operator overloads + r = engine->RegisterObjectMethod("float3x3", "float3x3 &opMulAssign(const float3x3 &in)", asMETHODPR(float3x3, operator*=, (const float3x3 &), float3x3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3x3", "float3x3 &opMulAssign(const float &in)", asMETHODPR(float3x3, operator*=, (const float &), float3x3&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3x3", "bool opEquals(const float3x3 &in) const", asMETHODPR(float3x3, operator==, (const float3x3 &) const, bool), asCALL_THISCALL); assert(r >= 0); + + // Indexing operator. + r = engine->RegisterObjectMethod("float3x3", "float3& opIndex(int)", asMETHOD(float3x3, get_opIndex), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("float3x3", "float3x3 opMul(const float3x3 &in) const", asMETHODPR(float3x3, operator*, (const float3x3 &) const, float3x3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3x3", "float3x3 opMul(const float &in) const", asMETHODPR(float3x3, operator*, (const float &) const, float3x3), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float3x3", "float3 opMul(const float3 &in) const", asMETHODPR(float3x3, operator*, (const float3 &) const, float3), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string toString(float3x3 m)", asFUNCTION(toStringF3x3), asCALL_CDECL); assert(r >= 0); + } +} // TFE_ForceScript + +#endif diff --git a/TheForceEngine/TFE_ForceScript/float3x3.h b/TheForceEngine/TFE_ForceScript/float3x3.h new file mode 100644 index 000000000..786f2f891 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float3x3.h @@ -0,0 +1,52 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// Matrix types for scripts. +////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "float3.h" + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_ForceScript +{ + struct float3x3 + { + float3x3(); + float3x3(const float3x3& other); + float3x3(float3 r0, float3 r1, float3 r2); + float3x3(f32 m00, f32 m01, f32 m02, f32 m10, f32 m11, f32 m12, f32 m20, f32 m21, f32 m22); + + // Assignment operator + float3x3 &operator=(const float3x3& other); + + // Compound assigment operators + float3x3 &operator*=(const float3x3& other); + float3x3 &operator*=(const f32& other); + + // Indexing operator + float3& get_opIndex(s32 index); + + // Comparison + bool operator==(const float3x3& other) const; + bool operator!=(const float3x3& other) const; + + // Math operators + float3x3 operator*(const float3x3& other) const; + float3x3 operator*(const f32& other) const; + float3 operator*(const float3& other) const; + + union + { + float3 r[3]; + f32 m[9]; + }; + }; + + void registerScriptMath_float3x3(asIScriptEngine *engine); + s32 getFloat3x3ObjectId(); + std::string toStringF3x3(const float3x3& v); +} // TFE_ForceScript +#endif diff --git a/TheForceEngine/TFE_ForceScript/float4.cpp b/TheForceEngine/TFE_ForceScript/float4.cpp new file mode 100644 index 000000000..2072828bd --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float4.cpp @@ -0,0 +1,963 @@ +#include "float4.h" +#include "forceScript.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT + +// TFE_ForceScript wraps Anglescript, so these includes should only exist here. +#include + +namespace TFE_ForceScript +{ + float4::float4() + { + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 0.0f; + } + + float4::float4(const float4& other) + { + x = other.x; + y = other.y; + z = other.z; + w = other.w; + } + + float4::float4(f32 _x, f32 _y, f32 _z, f32 _w) + { + x = _x; + y = _y; + z = _z; + w = _w; + } + + bool float4::operator==(const float4& o) const + { + return (x == o.x) && (y == o.y) && (z == o.z) && (w == o.w); + } + bool float4::operator!=(const float4& o) const + { + return !(*this == o); + } + + float4& float4::operator=(const float4& other) + { + x = other.x; + y = other.y; + z = other.z; + w = other.w; + return *this; + } + // Compound float4 + float4& float4::operator+=(const float4& other) + { + x += other.x; + y += other.y; + z += other.z; + w += other.w; + return *this; + } + float4& float4::operator-=(const float4& other) + { + x -= other.x; + y -= other.y; + z -= other.z; + w -= other.w; + return *this; + } + float4& float4::operator*=(const float4& other) + { + *this = *this * other; + return *this; + } + float4& float4::operator/=(const float4& other) + { + *this = *this / other; + return *this; + } + // Compound Scalar + float4& float4::operator+=(const f32& other) + { + x += other; + y += other; + z += other; + w += other; + return *this; + } + float4& float4::operator-=(const f32& other) + { + x -= other; + y -= other; + z -= other; + w -= other; + return *this; + } + float4& float4::operator*=(const f32& other) + { + *this = *this * other; + return *this; + } + float4& float4::operator/=(const f32& other) + { + *this = *this / other; + return *this; + } + + // float4 x float4 + float4 float4::operator+(const float4& other) const { return float4(x + other.x, y + other.y, z + other.z, w + other.w); } + float4 float4::operator*(const float4& other) const { return float4(x * other.x, y * other.y, z * other.z, w * other.w); } + float4 float4::operator-(const float4& other) const { return float4(x - other.x, y - other.y, z - other.z, w - other.w); } + float4 float4::operator/(const float4& other) const { return float4(x / other.x, y / other.y, z / other.z, w / other.w); } + // float4 x scalar + float4 float4::operator+(const f32& other) const { return float4(x + other, y + other, z + other, w + other); } + float4 float4::operator-(const f32& other) const { return float4(x - other, y - other, z - other, w - other); } + float4 float4::operator*(const f32& other) const { return float4(x * other, y * other, z * other, w * other); } + float4 float4::operator/(const f32& other) const { return float4(x / other, y / other, z / other, w / other); } + + //----------------------- + // Swizzle operators + //----------------------- + // float2 + float2 float4::get_xx() const { return float2(x, x); } + float2 float4::get_xy() const { return float2(x, y); } + float2 float4::get_xz() const { return float2(x, z); } + float2 float4::get_xw() const { return float2(x, w); } + float2 float4::get_yx() const { return float2(y, x); } + float2 float4::get_yy() const { return float2(y, y); } + float2 float4::get_yz() const { return float2(y, z); } + float2 float4::get_yw() const { return float2(y, w); } + float2 float4::get_zx() const { return float2(z, x); } + float2 float4::get_zy() const { return float2(z, y); } + float2 float4::get_zz() const { return float2(z, z); } + float2 float4::get_zw() const { return float2(z, w); } + float2 float4::get_wx() const { return float2(w, x); } + float2 float4::get_wy() const { return float2(w, y); } + float2 float4::get_wz() const { return float2(w, z); } + float2 float4::get_ww() const { return float2(w, w); } + // float3 - x + float3 float4::get_xxx() const { return float3(x, x, x); } + float3 float4::get_xxy() const { return float3(x, x, y); } + float3 float4::get_xxz() const { return float3(x, x, z); } + float3 float4::get_xxw() const { return float3(x, x, w); } + float3 float4::get_xyx() const { return float3(x, y, x); } + float3 float4::get_xyy() const { return float3(x, y, y); } + float3 float4::get_xyz() const { return float3(x, y, z); } + float3 float4::get_xyw() const { return float3(x, y, w); } + float3 float4::get_xzx() const { return float3(x, z, x); } + float3 float4::get_xzy() const { return float3(x, z, y); } + float3 float4::get_xzz() const { return float3(x, z, z); } + float3 float4::get_xzw() const { return float3(x, z, w); } + float3 float4::get_xwx() const { return float3(x, w, x); } + float3 float4::get_xwy() const { return float3(x, w, y); } + float3 float4::get_xwz() const { return float3(x, w, z); } + float3 float4::get_xww() const { return float3(x, w, w); } + // float3 - y + float3 float4::get_yxx() const { return float3(y, x, x); } + float3 float4::get_yxy() const { return float3(y, x, y); } + float3 float4::get_yxz() const { return float3(y, x, z); } + float3 float4::get_yxw() const { return float3(y, x, w); } + float3 float4::get_yyx() const { return float3(y, y, x); } + float3 float4::get_yyy() const { return float3(y, y, y); } + float3 float4::get_yyz() const { return float3(y, y, z); } + float3 float4::get_yyw() const { return float3(y, y, w); } + float3 float4::get_yzx() const { return float3(y, z, x); } + float3 float4::get_yzy() const { return float3(y, z, y); } + float3 float4::get_yzz() const { return float3(y, z, z); } + float3 float4::get_yzw() const { return float3(y, z, w); } + float3 float4::get_ywx() const { return float3(y, w, x); } + float3 float4::get_ywy() const { return float3(y, w, y); } + float3 float4::get_ywz() const { return float3(y, w, z); } + float3 float4::get_yww() const { return float3(y, w, w); } + // float3 - z + float3 float4::get_zxx() const { return float3(z, x, x); } + float3 float4::get_zxy() const { return float3(z, x, y); } + float3 float4::get_zxz() const { return float3(z, x, z); } + float3 float4::get_zxw() const { return float3(z, x, w); } + float3 float4::get_zyx() const { return float3(z, y, x); } + float3 float4::get_zyy() const { return float3(z, y, y); } + float3 float4::get_zyz() const { return float3(z, y, z); } + float3 float4::get_zyw() const { return float3(z, y, w); } + float3 float4::get_zzx() const { return float3(z, z, x); } + float3 float4::get_zzy() const { return float3(z, z, y); } + float3 float4::get_zzz() const { return float3(z, z, z); } + float3 float4::get_zzw() const { return float3(z, z, w); } + float3 float4::get_zwx() const { return float3(z, w, x); } + float3 float4::get_zwy() const { return float3(z, w, y); } + float3 float4::get_zwz() const { return float3(z, w, z); } + float3 float4::get_zww() const { return float3(z, w, w); } + // float3 - w + float3 float4::get_wxx() const { return float3(w, x, x); } + float3 float4::get_wxy() const { return float3(w, x, y); } + float3 float4::get_wxz() const { return float3(w, x, z); } + float3 float4::get_wxw() const { return float3(w, x, w); } + float3 float4::get_wyx() const { return float3(w, y, x); } + float3 float4::get_wyy() const { return float3(w, y, y); } + float3 float4::get_wyz() const { return float3(w, y, z); } + float3 float4::get_wyw() const { return float3(w, y, w); } + float3 float4::get_wzx() const { return float3(w, z, x); } + float3 float4::get_wzy() const { return float3(w, z, y); } + float3 float4::get_wzz() const { return float3(w, z, z); } + float3 float4::get_wzw() const { return float3(w, z, w); } + float3 float4::get_wwx() const { return float3(w, w, x); } + float3 float4::get_wwy() const { return float3(w, w, y); } + float3 float4::get_wwz() const { return float3(w, w, z); } + float3 float4::get_www() const { return float3(w, w, w); } + // Swizzle operators - float4 + // x + float4 float4::get_xxxx() const { return float4(x, x, x, x); } + float4 float4::get_xxxy() const { return float4(x, x, x, y); } + float4 float4::get_xxxz() const { return float4(x, x, x, z); } + float4 float4::get_xxxw() const { return float4(x, x, x, w); } + float4 float4::get_xxyx() const { return float4(x, x, y, x); } + float4 float4::get_xxyy() const { return float4(x, x, y, y); } + float4 float4::get_xxyz() const { return float4(x, x, y, z); } + float4 float4::get_xxyw() const { return float4(x, x, y, w); } + float4 float4::get_xxzx() const { return float4(x, x, z, x); } + float4 float4::get_xxzy() const { return float4(x, x, z, y); } + float4 float4::get_xxzz() const { return float4(x, x, z, z); } + float4 float4::get_xxzw() const { return float4(x, x, z, w); } + float4 float4::get_xxwx() const { return float4(x, x, w, x); } + float4 float4::get_xxwy() const { return float4(x, x, w, y); } + float4 float4::get_xxwz() const { return float4(x, x, w, z); } + float4 float4::get_xxww() const { return float4(x, x, w, w); } + float4 float4::get_xyxx() const { return float4(x, y, x, x); } + float4 float4::get_xyxy() const { return float4(x, y, x, y); } + float4 float4::get_xyxz() const { return float4(x, y, x, z); } + float4 float4::get_xyxw() const { return float4(x, y, x, w); } + float4 float4::get_xyyx() const { return float4(x, y, y, x); } + float4 float4::get_xyyy() const { return float4(x, y, y, y); } + float4 float4::get_xyyz() const { return float4(x, y, y, z); } + float4 float4::get_xyyw() const { return float4(x, y, y, w); } + float4 float4::get_xyzx() const { return float4(x, y, z, x); } + float4 float4::get_xyzy() const { return float4(x, y, z, y); } + float4 float4::get_xyzz() const { return float4(x, y, z, z); } + float4 float4::get_xyzw() const { return float4(x, y, z, w); } + float4 float4::get_xywx() const { return float4(x, y, w, x); } + float4 float4::get_xywy() const { return float4(x, y, w, y); } + float4 float4::get_xywz() const { return float4(x, y, w, z); } + float4 float4::get_xyww() const { return float4(x, y, w, w); } + float4 float4::get_xzxx() const { return float4(x, z, x, x); } + float4 float4::get_xzxy() const { return float4(x, z, x, y); } + float4 float4::get_xzxz() const { return float4(x, z, x, z); } + float4 float4::get_xzxw() const { return float4(x, z, x, w); } + float4 float4::get_xzyx() const { return float4(x, z, y, x); } + float4 float4::get_xzyy() const { return float4(x, z, y, y); } + float4 float4::get_xzyz() const { return float4(x, z, y, z); } + float4 float4::get_xzyw() const { return float4(x, z, y, w); } + float4 float4::get_xzzx() const { return float4(x, z, z, x); } + float4 float4::get_xzzy() const { return float4(x, z, z, y); } + float4 float4::get_xzzz() const { return float4(x, z, z, z); } + float4 float4::get_xzzw() const { return float4(x, z, z, w); } + float4 float4::get_xzwx() const { return float4(x, z, w, x); } + float4 float4::get_xzwy() const { return float4(x, z, w, y); } + float4 float4::get_xzwz() const { return float4(x, z, w, z); } + float4 float4::get_xzww() const { return float4(x, z, w, w); } + float4 float4::get_xwxx() const { return float4(x, w, x, x); } + float4 float4::get_xwxy() const { return float4(x, w, x, y); } + float4 float4::get_xwxz() const { return float4(x, w, x, z); } + float4 float4::get_xwxw() const { return float4(x, w, x, w); } + float4 float4::get_xwyx() const { return float4(x, w, y, x); } + float4 float4::get_xwyy() const { return float4(x, w, y, y); } + float4 float4::get_xwyz() const { return float4(x, w, y, z); } + float4 float4::get_xwyw() const { return float4(x, w, y, w); } + float4 float4::get_xwzx() const { return float4(x, w, z, x); } + float4 float4::get_xwzy() const { return float4(x, w, z, y); } + float4 float4::get_xwzz() const { return float4(x, w, z, z); } + float4 float4::get_xwzw() const { return float4(x, w, z, w); } + float4 float4::get_xwwx() const { return float4(x, w, w, x); } + float4 float4::get_xwwy() const { return float4(x, w, w, y); } + float4 float4::get_xwwz() const { return float4(x, w, w, z); } + float4 float4::get_xwww() const { return float4(x, w, w, w); } + void float4::set_xyzw(const float4& o) { *this = o; } + void float4::set_xywz(const float4& o) { x = o.x; y = o.y; w = o.z; z = o.w; } + void float4::set_xzyw(const float4& o) { x = o.x; z = o.y; y = o.z; w = o.w; } + void float4::set_xzwy(const float4& o) { x = o.x; z = o.y; w = o.z; y = o.w; } + void float4::set_xwyz(const float4& o) { x = o.x; w = o.y; y = o.z; z = o.w; } + void float4::set_xwzy(const float4& o) { x = o.x; w = o.y; z = o.z; y = o.w; } + // y + float4 float4::get_yxxx() const { return float4(y, x, x, x); } + float4 float4::get_yxxy() const { return float4(y, x, x, y); } + float4 float4::get_yxxz() const { return float4(y, x, x, z); } + float4 float4::get_yxxw() const { return float4(y, x, x, w); } + float4 float4::get_yxyx() const { return float4(y, x, y, x); } + float4 float4::get_yxyy() const { return float4(y, x, y, y); } + float4 float4::get_yxyz() const { return float4(y, x, y, z); } + float4 float4::get_yxyw() const { return float4(y, x, y, w); } + float4 float4::get_yxzx() const { return float4(y, x, z, x); } + float4 float4::get_yxzy() const { return float4(y, x, z, y); } + float4 float4::get_yxzz() const { return float4(y, x, z, z); } + float4 float4::get_yxzw() const { return float4(y, x, z, w); } + float4 float4::get_yxwx() const { return float4(y, x, w, x); } + float4 float4::get_yxwy() const { return float4(y, x, w, y); } + float4 float4::get_yxwz() const { return float4(y, x, w, z); } + float4 float4::get_yxww() const { return float4(y, x, w, w); } + float4 float4::get_yyxx() const { return float4(y, y, x, x); } + float4 float4::get_yyxy() const { return float4(y, y, x, y); } + float4 float4::get_yyxz() const { return float4(y, y, x, z); } + float4 float4::get_yyxw() const { return float4(y, y, x, w); } + float4 float4::get_yyyx() const { return float4(y, y, y, x); } + float4 float4::get_yyyy() const { return float4(y, y, y, y); } + float4 float4::get_yyyz() const { return float4(y, y, y, z); } + float4 float4::get_yyyw() const { return float4(y, y, y, w); } + float4 float4::get_yyzx() const { return float4(y, y, z, x); } + float4 float4::get_yyzy() const { return float4(y, y, z, y); } + float4 float4::get_yyzz() const { return float4(y, y, z, z); } + float4 float4::get_yyzw() const { return float4(y, y, z, w); } + float4 float4::get_yywx() const { return float4(y, y, w, x); } + float4 float4::get_yywy() const { return float4(y, y, w, y); } + float4 float4::get_yywz() const { return float4(y, y, w, z); } + float4 float4::get_yyww() const { return float4(y, y, w, w); } + float4 float4::get_yzxx() const { return float4(y, z, x, x); } + float4 float4::get_yzxy() const { return float4(y, z, x, y); } + float4 float4::get_yzxz() const { return float4(y, z, x, z); } + float4 float4::get_yzxw() const { return float4(y, z, x, w); } + float4 float4::get_yzyx() const { return float4(y, z, y, x); } + float4 float4::get_yzyy() const { return float4(y, z, y, y); } + float4 float4::get_yzyz() const { return float4(y, z, y, z); } + float4 float4::get_yzyw() const { return float4(y, z, y, w); } + float4 float4::get_yzzx() const { return float4(y, z, z, x); } + float4 float4::get_yzzy() const { return float4(y, z, z, y); } + float4 float4::get_yzzz() const { return float4(y, z, z, z); } + float4 float4::get_yzzw() const { return float4(y, z, z, w); } + float4 float4::get_yzwx() const { return float4(y, z, w, x); } + float4 float4::get_yzwy() const { return float4(y, z, w, y); } + float4 float4::get_yzwz() const { return float4(y, z, w, z); } + float4 float4::get_yzww() const { return float4(y, z, w, w); } + float4 float4::get_ywxx() const { return float4(y, w, x, x); } + float4 float4::get_ywxy() const { return float4(y, w, x, y); } + float4 float4::get_ywxz() const { return float4(y, w, x, z); } + float4 float4::get_ywxw() const { return float4(y, w, x, w); } + float4 float4::get_ywyx() const { return float4(y, w, y, x); } + float4 float4::get_ywyy() const { return float4(y, w, y, y); } + float4 float4::get_ywyz() const { return float4(y, w, y, z); } + float4 float4::get_ywyw() const { return float4(y, w, y, w); } + float4 float4::get_ywzx() const { return float4(y, w, z, x); } + float4 float4::get_ywzy() const { return float4(y, w, z, y); } + float4 float4::get_ywzz() const { return float4(y, w, z, z); } + float4 float4::get_ywzw() const { return float4(y, w, z, w); } + float4 float4::get_ywwx() const { return float4(y, w, w, x); } + float4 float4::get_ywwy() const { return float4(y, w, w, y); } + float4 float4::get_ywwz() const { return float4(y, w, w, z); } + float4 float4::get_ywww() const { return float4(y, w, w, w); } + void float4::set_yxzw(const float4& o) { y = o.x; x = o.y; z = o.z; w = o.w; } + void float4::set_yxwz(const float4& o) { y = o.x; x = o.y; w = o.z; z = o.w; } + void float4::set_yzxw(const float4& o) { y = o.x; z = o.y; x = o.z; w = o.w; } + void float4::set_yzwx(const float4& o) { y = o.x; z = o.y; w = o.z; x = o.w; } + void float4::set_ywxz(const float4& o) { y = o.x; w = o.y; x = o.z; z = o.w; } + void float4::set_ywzx(const float4& o) { y = o.x; w = o.y; z = o.z; x = o.w; } + // z + float4 float4::get_zxxx() const { return float4(z, x, x, x); } + float4 float4::get_zxxy() const { return float4(z, x, x, y); } + float4 float4::get_zxxz() const { return float4(z, x, x, z); } + float4 float4::get_zxxw() const { return float4(z, x, x, w); } + float4 float4::get_zxyx() const { return float4(z, x, y, x); } + float4 float4::get_zxyy() const { return float4(z, x, y, y); } + float4 float4::get_zxyz() const { return float4(z, x, y, z); } + float4 float4::get_zxyw() const { return float4(z, x, y, w); } + float4 float4::get_zxzx() const { return float4(z, x, z, x); } + float4 float4::get_zxzy() const { return float4(z, x, z, y); } + float4 float4::get_zxzz() const { return float4(z, x, z, z); } + float4 float4::get_zxzw() const { return float4(z, x, z, w); } + float4 float4::get_zxwx() const { return float4(z, x, w, x); } + float4 float4::get_zxwy() const { return float4(z, x, w, y); } + float4 float4::get_zxwz() const { return float4(z, x, w, z); } + float4 float4::get_zxww() const { return float4(z, x, w, w); } + float4 float4::get_zyxx() const { return float4(z, y, x, x); } + float4 float4::get_zyxy() const { return float4(z, y, x, y); } + float4 float4::get_zyxz() const { return float4(z, y, x, z); } + float4 float4::get_zyxw() const { return float4(z, y, x, w); } + float4 float4::get_zyyx() const { return float4(z, y, y, x); } + float4 float4::get_zyyy() const { return float4(z, y, y, y); } + float4 float4::get_zyyz() const { return float4(z, y, y, z); } + float4 float4::get_zyyw() const { return float4(z, y, y, w); } + float4 float4::get_zyzx() const { return float4(z, y, z, x); } + float4 float4::get_zyzy() const { return float4(z, y, z, y); } + float4 float4::get_zyzz() const { return float4(z, y, z, z); } + float4 float4::get_zyzw() const { return float4(z, y, z, w); } + float4 float4::get_zywx() const { return float4(z, y, w, x); } + float4 float4::get_zywy() const { return float4(z, y, w, y); } + float4 float4::get_zywz() const { return float4(z, y, w, z); } + float4 float4::get_zyww() const { return float4(z, y, w, w); } + float4 float4::get_zzxx() const { return float4(z, z, x, x); } + float4 float4::get_zzxy() const { return float4(z, z, x, y); } + float4 float4::get_zzxz() const { return float4(z, z, x, z); } + float4 float4::get_zzxw() const { return float4(z, z, x, w); } + float4 float4::get_zzyx() const { return float4(z, z, y, x); } + float4 float4::get_zzyy() const { return float4(z, z, y, y); } + float4 float4::get_zzyz() const { return float4(z, z, y, z); } + float4 float4::get_zzyw() const { return float4(z, z, y, w); } + float4 float4::get_zzzx() const { return float4(z, z, z, x); } + float4 float4::get_zzzy() const { return float4(z, z, z, y); } + float4 float4::get_zzzz() const { return float4(z, z, z, z); } + float4 float4::get_zzzw() const { return float4(z, z, z, w); } + float4 float4::get_zzwx() const { return float4(z, z, w, x); } + float4 float4::get_zzwy() const { return float4(z, z, w, y); } + float4 float4::get_zzwz() const { return float4(z, z, w, z); } + float4 float4::get_zzww() const { return float4(z, z, w, w); } + float4 float4::get_zwxx() const { return float4(z, w, x, x); } + float4 float4::get_zwxy() const { return float4(z, w, x, y); } + float4 float4::get_zwxz() const { return float4(z, w, x, z); } + float4 float4::get_zwxw() const { return float4(z, w, x, w); } + float4 float4::get_zwyx() const { return float4(z, w, y, x); } + float4 float4::get_zwyy() const { return float4(z, w, y, y); } + float4 float4::get_zwyz() const { return float4(z, w, y, z); } + float4 float4::get_zwyw() const { return float4(z, w, y, w); } + float4 float4::get_zwzx() const { return float4(z, w, z, x); } + float4 float4::get_zwzy() const { return float4(z, w, z, y); } + float4 float4::get_zwzz() const { return float4(z, w, z, z); } + float4 float4::get_zwzw() const { return float4(z, w, z, w); } + float4 float4::get_zwwx() const { return float4(z, w, w, x); } + float4 float4::get_zwwy() const { return float4(z, w, w, y); } + float4 float4::get_zwwz() const { return float4(z, w, w, z); } + float4 float4::get_zwww() const { return float4(z, w, w, w); } + void float4::set_zyxw(const float4& o) { z = o.x; y = o.y; x = o.z; w = o.w; } + void float4::set_zywx(const float4& o) { z = o.x; y = o.y; w = o.z; x = o.w; } + void float4::set_zxyw(const float4& o) { z = o.x; x = o.y; y = o.z; w = o.w; } + void float4::set_zxwy(const float4& o) { z = o.x; x = o.y; w = o.z; y = o.w; } + void float4::set_zwyx(const float4& o) { z = o.x; w = o.y; y = o.z; x = o.w; } + void float4::set_zwxy(const float4& o) { z = o.x; w = o.y; x = o.z; y = o.w; } + // w + float4 float4::get_wxxx() const { return float4(w, x, x, x); } + float4 float4::get_wxxy() const { return float4(w, x, x, y); } + float4 float4::get_wxxz() const { return float4(w, x, x, z); } + float4 float4::get_wxxw() const { return float4(w, x, x, w); } + float4 float4::get_wxyx() const { return float4(w, x, y, x); } + float4 float4::get_wxyy() const { return float4(w, x, y, y); } + float4 float4::get_wxyz() const { return float4(w, x, y, z); } + float4 float4::get_wxyw() const { return float4(w, x, y, w); } + float4 float4::get_wxzx() const { return float4(w, x, z, x); } + float4 float4::get_wxzy() const { return float4(w, x, z, y); } + float4 float4::get_wxzz() const { return float4(w, x, z, z); } + float4 float4::get_wxzw() const { return float4(w, x, z, w); } + float4 float4::get_wxwx() const { return float4(w, x, w, x); } + float4 float4::get_wxwy() const { return float4(w, x, w, y); } + float4 float4::get_wxwz() const { return float4(w, x, w, z); } + float4 float4::get_wxww() const { return float4(w, x, w, w); } + float4 float4::get_wyxx() const { return float4(w, y, x, x); } + float4 float4::get_wyxy() const { return float4(w, y, x, y); } + float4 float4::get_wyxz() const { return float4(w, y, x, z); } + float4 float4::get_wyxw() const { return float4(w, y, x, w); } + float4 float4::get_wyyx() const { return float4(w, y, y, x); } + float4 float4::get_wyyy() const { return float4(w, y, y, y); } + float4 float4::get_wyyz() const { return float4(w, y, y, z); } + float4 float4::get_wyyw() const { return float4(w, y, y, w); } + float4 float4::get_wyzx() const { return float4(w, y, z, x); } + float4 float4::get_wyzy() const { return float4(w, y, z, y); } + float4 float4::get_wyzz() const { return float4(w, y, z, z); } + float4 float4::get_wyzw() const { return float4(w, y, z, w); } + float4 float4::get_wywx() const { return float4(w, y, w, x); } + float4 float4::get_wywy() const { return float4(w, y, w, y); } + float4 float4::get_wywz() const { return float4(w, y, w, z); } + float4 float4::get_wyww() const { return float4(w, y, w, w); } + float4 float4::get_wzxx() const { return float4(w, z, x, x); } + float4 float4::get_wzxy() const { return float4(w, z, x, y); } + float4 float4::get_wzxz() const { return float4(w, z, x, z); } + float4 float4::get_wzxw() const { return float4(w, z, x, w); } + float4 float4::get_wzyx() const { return float4(w, z, y, x); } + float4 float4::get_wzyy() const { return float4(w, z, y, y); } + float4 float4::get_wzyz() const { return float4(w, z, y, z); } + float4 float4::get_wzyw() const { return float4(w, z, y, w); } + float4 float4::get_wzzx() const { return float4(w, z, z, x); } + float4 float4::get_wzzy() const { return float4(w, z, z, y); } + float4 float4::get_wzzz() const { return float4(w, z, z, z); } + float4 float4::get_wzzw() const { return float4(w, z, z, w); } + float4 float4::get_wzwx() const { return float4(w, z, w, x); } + float4 float4::get_wzwy() const { return float4(w, z, w, y); } + float4 float4::get_wzwz() const { return float4(w, z, w, z); } + float4 float4::get_wzww() const { return float4(w, z, w, w); } + float4 float4::get_wwxx() const { return float4(w, w, x, x); } + float4 float4::get_wwxy() const { return float4(w, w, x, y); } + float4 float4::get_wwxz() const { return float4(w, w, x, z); } + float4 float4::get_wwxw() const { return float4(w, w, x, w); } + float4 float4::get_wwyx() const { return float4(w, w, y, x); } + float4 float4::get_wwyy() const { return float4(w, w, y, y); } + float4 float4::get_wwyz() const { return float4(w, w, y, z); } + float4 float4::get_wwyw() const { return float4(w, w, y, w); } + float4 float4::get_wwzx() const { return float4(w, w, z, x); } + float4 float4::get_wwzy() const { return float4(w, w, z, y); } + float4 float4::get_wwzz() const { return float4(w, w, z, z); } + float4 float4::get_wwzw() const { return float4(w, w, z, w); } + float4 float4::get_wwwx() const { return float4(w, w, w, x); } + float4 float4::get_wwwy() const { return float4(w, w, w, y); } + float4 float4::get_wwwz() const { return float4(w, w, w, z); } + float4 float4::get_wwww() const { return float4(w, w, w, w); } + void float4::set_wyzx(const float4& o) { w = o.x; y = o.y; z = o.z; x = o.w; } + void float4::set_wyxz(const float4& o) { w = o.x; y = o.y; x = o.z; z = o.w; } + void float4::set_wzyx(const float4& o) { w = o.x; z = o.y; y = o.z; x = o.w; } + void float4::set_wzxy(const float4& o) { w = o.x; z = o.y; x = o.z; y = o.w; } + void float4::set_wxyz(const float4& o) { w = o.x; x = o.y; y = o.z; z = o.w; } + void float4::set_wxzy(const float4& o) { w = o.x; x = o.y; z = o.z; y = o.w; } + + //----------------------- + // Array operator + //----------------------- + f32& float4::get_opIndex(s32 index) + { + if (index < 0 || index >= 4) + { + // TODO: WARNING + index = 0; + } + return m[index]; + } + + //----------------------- + // AngelScript functions + //----------------------- + static void float4DefaultConstructor(float4* self) { new(self) float4(); } + static void float4CopyConstructor(const float4& other, float4* self) { new(self) float4(other); } + static void float4ConvConstructor(f32 x, float4* self) { new(self) float4(x, x, x, x); } + static void float4InitConstructor(f32 x, f32 y, f32 z, f32 w, float4* self) { new(self) float4(x, y, z, w); } + static void float4ListConstructor(f32* list, float4* self) { new(self) float4(list[0], list[1], list[2], list[3]); } + + //----------------------- + // Intrinsics + //----------------------- + std::string toStringF4(const float4& v) + { + char tmp[1024]; + sprintf(tmp, "(%g, %g, %g, %g)", v.x, v.y, v.z, v.w); + return std::string(tmp); + } + + //------------------------------------- + // Registration + //------------------------------------- + static s32 s_float4ObjId = -1; + s32 getFloat4ObjectId() + { + return s_float4ObjId; + } + + void registerScriptMath_float4(asIScriptEngine *engine) + { + s32 r; + r = engine->RegisterObjectType("float4", sizeof(float4), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); assert(r >= 0); + s_float4ObjId = r; + + // Register the object properties + r = engine->RegisterObjectProperty("float4", "float x", asOFFSET(float4, x)); assert(r >= 0); + r = engine->RegisterObjectProperty("float4", "float y", asOFFSET(float4, y)); assert(r >= 0); + r = engine->RegisterObjectProperty("float4", "float z", asOFFSET(float4, z)); assert(r >= 0); + r = engine->RegisterObjectProperty("float4", "float w", asOFFSET(float4, w)); assert(r >= 0); + + // Register the constructors + r = engine->RegisterObjectBehaviour("float4", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(float4DefaultConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4", asBEHAVE_CONSTRUCT, "void f(const float4 &in)", asFUNCTION(float4CopyConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(float4ConvConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4", asBEHAVE_CONSTRUCT, "void f(float, float, float, float)", asFUNCTION(float4InitConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float, float, float}", asFUNCTION(float4ListConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Register the operator overloads + r = engine->RegisterObjectMethod("float4", "float4 &opAddAssign(const float4 &in)", asMETHODPR(float4, operator+=, (const float4 &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opSubAssign(const float4 &in)", asMETHODPR(float4, operator-=, (const float4 &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opMulAssign(const float4 &in)", asMETHODPR(float4, operator*=, (const float4 &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opDivAssign(const float4 &in)", asMETHODPR(float4, operator/=, (const float4 &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opAddAssign(const float &in)", asMETHODPR(float4, operator+=, (const float &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opSubAssign(const float &in)", asMETHODPR(float4, operator-=, (const float &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opMulAssign(const float &in)", asMETHODPR(float4, operator*=, (const float &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 &opDivAssign(const float &in)", asMETHODPR(float4, operator/=, (const float &), float4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "bool opEquals(const float4 &in) const", asMETHODPR(float4, operator==, (const float4 &) const, bool), asCALL_THISCALL); assert(r >= 0); + + // Indexing operator. + r = engine->RegisterObjectMethod("float4", "float& opIndex(int)", asMETHOD(float4, get_opIndex), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("float4", "float4 opAdd(const float4 &in) const", asMETHODPR(float4, operator+, (const float4 &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opSub(const float4 &in) const", asMETHODPR(float4, operator-, (const float4 &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opMul(const float4 &in) const", asMETHODPR(float4, operator*, (const float4 &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opDiv(const float4 &in) const", asMETHODPR(float4, operator/, (const float4 &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opAdd(const float &in) const", asMETHODPR(float4, operator+, (const float &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opSub(const float &in) const", asMETHODPR(float4, operator-, (const float &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opMul(const float &in) const", asMETHODPR(float4, operator*, (const float &) const, float4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 opDiv(const float &in) const", asMETHODPR(float4, operator/, (const float &) const, float4), asCALL_THISCALL); assert(r >= 0); + + // Register the swizzle operators + // float2 + // x + r = engine->RegisterObjectMethod("float4", "float2 get_xx() const property", asMETHOD(float4, get_xx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_xy() const property", asMETHOD(float4, get_xy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_xz() const property", asMETHOD(float4, get_xz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_xw() const property", asMETHOD(float4, get_xw), asCALL_THISCALL); assert(r >= 0); + // y + r = engine->RegisterObjectMethod("float4", "float2 get_yx() const property", asMETHOD(float4, get_yx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_yy() const property", asMETHOD(float4, get_yy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_yz() const property", asMETHOD(float4, get_yz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_yw() const property", asMETHOD(float4, get_yw), asCALL_THISCALL); assert(r >= 0); + // z + r = engine->RegisterObjectMethod("float4", "float2 get_zx() const property", asMETHOD(float4, get_zx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_zy() const property", asMETHOD(float4, get_zy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_zz() const property", asMETHOD(float4, get_zz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_zw() const property", asMETHOD(float4, get_zw), asCALL_THISCALL); assert(r >= 0); + // w + r = engine->RegisterObjectMethod("float4", "float2 get_wx() const property", asMETHOD(float4, get_wx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_wy() const property", asMETHOD(float4, get_wy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_wz() const property", asMETHOD(float4, get_wz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float2 get_ww() const property", asMETHOD(float4, get_ww), asCALL_THISCALL); assert(r >= 0); + // Swizzle operators - float3 + // x + r = engine->RegisterObjectMethod("float4", "float3 get_xxx() const property", asMETHOD(float4, get_xxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xxy() const property", asMETHOD(float4, get_xxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xxz() const property", asMETHOD(float4, get_xxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xxw() const property", asMETHOD(float4, get_xxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xyx() const property", asMETHOD(float4, get_xyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xyy() const property", asMETHOD(float4, get_xyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xyz() const property", asMETHOD(float4, get_xyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xyw() const property", asMETHOD(float4, get_xyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xzx() const property", asMETHOD(float4, get_xzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xzy() const property", asMETHOD(float4, get_xzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xzz() const property", asMETHOD(float4, get_xzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xzw() const property", asMETHOD(float4, get_xzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xwx() const property", asMETHOD(float4, get_xwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xwy() const property", asMETHOD(float4, get_xwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xwz() const property", asMETHOD(float4, get_xwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_xww() const property", asMETHOD(float4, get_xww), asCALL_THISCALL); assert(r >= 0); + // y + r = engine->RegisterObjectMethod("float4", "float3 get_yxx() const property", asMETHOD(float4, get_yxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yxy() const property", asMETHOD(float4, get_yxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yxz() const property", asMETHOD(float4, get_yxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yxw() const property", asMETHOD(float4, get_yxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yyx() const property", asMETHOD(float4, get_yyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yyy() const property", asMETHOD(float4, get_yyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yyz() const property", asMETHOD(float4, get_yyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yyw() const property", asMETHOD(float4, get_yyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yzx() const property", asMETHOD(float4, get_yzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yzy() const property", asMETHOD(float4, get_yzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yzz() const property", asMETHOD(float4, get_yzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yzw() const property", asMETHOD(float4, get_yzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_ywx() const property", asMETHOD(float4, get_ywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_ywy() const property", asMETHOD(float4, get_ywy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_ywz() const property", asMETHOD(float4, get_ywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_yww() const property", asMETHOD(float4, get_yww), asCALL_THISCALL); assert(r >= 0); + // z + r = engine->RegisterObjectMethod("float4", "float3 get_zxx() const property", asMETHOD(float4, get_zxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zxy() const property", asMETHOD(float4, get_zxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zxz() const property", asMETHOD(float4, get_zxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zxw() const property", asMETHOD(float4, get_zxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zyx() const property", asMETHOD(float4, get_zyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zyy() const property", asMETHOD(float4, get_zyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zyz() const property", asMETHOD(float4, get_zyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zyw() const property", asMETHOD(float4, get_zyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zzx() const property", asMETHOD(float4, get_zzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zzy() const property", asMETHOD(float4, get_zzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zzz() const property", asMETHOD(float4, get_zzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zzw() const property", asMETHOD(float4, get_zzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zwx() const property", asMETHOD(float4, get_zwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zwy() const property", asMETHOD(float4, get_zwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zwz() const property", asMETHOD(float4, get_zwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_zww() const property", asMETHOD(float4, get_zww), asCALL_THISCALL); assert(r >= 0); + // w + r = engine->RegisterObjectMethod("float4", "float3 get_wxx() const property", asMETHOD(float4, get_wxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wxy() const property", asMETHOD(float4, get_wxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wxz() const property", asMETHOD(float4, get_wxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wxw() const property", asMETHOD(float4, get_wxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wyx() const property", asMETHOD(float4, get_wyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wyy() const property", asMETHOD(float4, get_wyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wyz() const property", asMETHOD(float4, get_wyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wyw() const property", asMETHOD(float4, get_wyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wzx() const property", asMETHOD(float4, get_wzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wzy() const property", asMETHOD(float4, get_wzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wzz() const property", asMETHOD(float4, get_wzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wzw() const property", asMETHOD(float4, get_wzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wwx() const property", asMETHOD(float4, get_wwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wwy() const property", asMETHOD(float4, get_wwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_wwz() const property", asMETHOD(float4, get_wwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float3 get_www() const property", asMETHOD(float4, get_www), asCALL_THISCALL); assert(r >= 0); + // Swizzle operators - float4 + // x + r = engine->RegisterObjectMethod("float4", "float4 get_xxxx() const property", asMETHOD(float4, get_xxxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxxy() const property", asMETHOD(float4, get_xxxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxxz() const property", asMETHOD(float4, get_xxxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxxw() const property", asMETHOD(float4, get_xxxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxyx() const property", asMETHOD(float4, get_xxyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxyy() const property", asMETHOD(float4, get_xxyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxyz() const property", asMETHOD(float4, get_xxyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxyw() const property", asMETHOD(float4, get_xxyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxzx() const property", asMETHOD(float4, get_xxzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxzy() const property", asMETHOD(float4, get_xxzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxzz() const property", asMETHOD(float4, get_xxzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxzw() const property", asMETHOD(float4, get_xxzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxwx() const property", asMETHOD(float4, get_xxwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxwy() const property", asMETHOD(float4, get_xxwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxwz() const property", asMETHOD(float4, get_xxwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xxww() const property", asMETHOD(float4, get_xxww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyxx() const property", asMETHOD(float4, get_xyxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyxy() const property", asMETHOD(float4, get_xyxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyxz() const property", asMETHOD(float4, get_xyxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyxw() const property", asMETHOD(float4, get_xyxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyyx() const property", asMETHOD(float4, get_xyyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyyy() const property", asMETHOD(float4, get_xyyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyyz() const property", asMETHOD(float4, get_xyyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyyw() const property", asMETHOD(float4, get_xyyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyzx() const property", asMETHOD(float4, get_xyzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyzy() const property", asMETHOD(float4, get_xyzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyzz() const property", asMETHOD(float4, get_xyzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyzw() const property", asMETHOD(float4, get_xyzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xywx() const property", asMETHOD(float4, get_xywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xywy() const property", asMETHOD(float4, get_xywy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xywz() const property", asMETHOD(float4, get_xywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xyww() const property", asMETHOD(float4, get_xyww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzxx() const property", asMETHOD(float4, get_xzxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzxy() const property", asMETHOD(float4, get_xzxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzxz() const property", asMETHOD(float4, get_xzxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzxw() const property", asMETHOD(float4, get_xzxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzyx() const property", asMETHOD(float4, get_xzyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzyy() const property", asMETHOD(float4, get_xzyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzyz() const property", asMETHOD(float4, get_xzyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzyw() const property", asMETHOD(float4, get_xzyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzzx() const property", asMETHOD(float4, get_xzzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzzy() const property", asMETHOD(float4, get_xzzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzzz() const property", asMETHOD(float4, get_xzzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzzw() const property", asMETHOD(float4, get_xzzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzwx() const property", asMETHOD(float4, get_xzwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzwy() const property", asMETHOD(float4, get_xzwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzwz() const property", asMETHOD(float4, get_xzwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xzww() const property", asMETHOD(float4, get_xzww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwxx() const property", asMETHOD(float4, get_xwxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwxy() const property", asMETHOD(float4, get_xwxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwxz() const property", asMETHOD(float4, get_xwxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwxw() const property", asMETHOD(float4, get_xwxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwyx() const property", asMETHOD(float4, get_xwyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwyy() const property", asMETHOD(float4, get_xwyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwyz() const property", asMETHOD(float4, get_xwyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwyw() const property", asMETHOD(float4, get_xwyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwzx() const property", asMETHOD(float4, get_xwzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwzy() const property", asMETHOD(float4, get_xwzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwzz() const property", asMETHOD(float4, get_xwzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwzw() const property", asMETHOD(float4, get_xwzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwwx() const property", asMETHOD(float4, get_xwwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwwy() const property", asMETHOD(float4, get_xwwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwwz() const property", asMETHOD(float4, get_xwwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_xwww() const property", asMETHOD(float4, get_xwww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xyzw(const float4 &in) property", asMETHOD(float4, set_xyzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xywz(const float4 &in) property", asMETHOD(float4, set_xywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xzyw(const float4 &in) property", asMETHOD(float4, set_xzyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xzwy(const float4 &in) property", asMETHOD(float4, set_xzwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xwyz(const float4 &in) property", asMETHOD(float4, set_xwyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_xwzy(const float4 &in) property", asMETHOD(float4, set_xwzy), asCALL_THISCALL); assert(r >= 0); + // y + r = engine->RegisterObjectMethod("float4", "float4 get_yxxx() const property", asMETHOD(float4, get_yxxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxxy() const property", asMETHOD(float4, get_yxxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxxz() const property", asMETHOD(float4, get_yxxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxxw() const property", asMETHOD(float4, get_yxxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxyx() const property", asMETHOD(float4, get_yxyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxyy() const property", asMETHOD(float4, get_yxyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxyz() const property", asMETHOD(float4, get_yxyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxyw() const property", asMETHOD(float4, get_yxyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxzx() const property", asMETHOD(float4, get_yxzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxzy() const property", asMETHOD(float4, get_yxzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxzz() const property", asMETHOD(float4, get_yxzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxzw() const property", asMETHOD(float4, get_yxzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxwx() const property", asMETHOD(float4, get_yxwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxwy() const property", asMETHOD(float4, get_yxwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxwz() const property", asMETHOD(float4, get_yxwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yxww() const property", asMETHOD(float4, get_yxww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyxx() const property", asMETHOD(float4, get_yyxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyxy() const property", asMETHOD(float4, get_yyxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyxz() const property", asMETHOD(float4, get_yyxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyxw() const property", asMETHOD(float4, get_yyxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyyx() const property", asMETHOD(float4, get_yyyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyyy() const property", asMETHOD(float4, get_yyyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyyz() const property", asMETHOD(float4, get_yyyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyyw() const property", asMETHOD(float4, get_yyyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyzx() const property", asMETHOD(float4, get_yyzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyzy() const property", asMETHOD(float4, get_yyzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyzz() const property", asMETHOD(float4, get_yyzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyzw() const property", asMETHOD(float4, get_yyzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yywx() const property", asMETHOD(float4, get_yywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yywy() const property", asMETHOD(float4, get_yywy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yywz() const property", asMETHOD(float4, get_yywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yyww() const property", asMETHOD(float4, get_yyww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzxx() const property", asMETHOD(float4, get_yzxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzxy() const property", asMETHOD(float4, get_yzxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzxz() const property", asMETHOD(float4, get_yzxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzxw() const property", asMETHOD(float4, get_yzxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzyx() const property", asMETHOD(float4, get_yzyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzyy() const property", asMETHOD(float4, get_yzyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzyz() const property", asMETHOD(float4, get_yzyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzyw() const property", asMETHOD(float4, get_yzyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzzx() const property", asMETHOD(float4, get_yzzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzzy() const property", asMETHOD(float4, get_yzzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzzz() const property", asMETHOD(float4, get_yzzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzzw() const property", asMETHOD(float4, get_yzzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzwx() const property", asMETHOD(float4, get_yzwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzwy() const property", asMETHOD(float4, get_yzwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzwz() const property", asMETHOD(float4, get_yzwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_yzww() const property", asMETHOD(float4, get_yzww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywxx() const property", asMETHOD(float4, get_ywxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywxy() const property", asMETHOD(float4, get_ywxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywxz() const property", asMETHOD(float4, get_ywxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywxw() const property", asMETHOD(float4, get_ywxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywyx() const property", asMETHOD(float4, get_ywyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywyy() const property", asMETHOD(float4, get_ywyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywyz() const property", asMETHOD(float4, get_ywyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywyw() const property", asMETHOD(float4, get_ywyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywzx() const property", asMETHOD(float4, get_ywzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywzy() const property", asMETHOD(float4, get_ywzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywzz() const property", asMETHOD(float4, get_ywzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywzw() const property", asMETHOD(float4, get_ywzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywwx() const property", asMETHOD(float4, get_ywwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywwy() const property", asMETHOD(float4, get_ywwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywwz() const property", asMETHOD(float4, get_ywwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_ywww() const property", asMETHOD(float4, get_ywww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_yxzw(const float4 &in) property", asMETHOD(float4, set_yxzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_yxwz(const float4 &in) property", asMETHOD(float4, set_yxwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_yzxw(const float4 &in) property", asMETHOD(float4, set_yzxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_yzwx(const float4 &in) property", asMETHOD(float4, set_yzwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_ywxz(const float4 &in) property", asMETHOD(float4, set_ywxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_ywzx(const float4 &in) property", asMETHOD(float4, set_ywzx), asCALL_THISCALL); assert(r >= 0); + // z + r = engine->RegisterObjectMethod("float4", "float4 get_zxxx() const property", asMETHOD(float4, get_zxxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxxy() const property", asMETHOD(float4, get_zxxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxxz() const property", asMETHOD(float4, get_zxxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxxw() const property", asMETHOD(float4, get_zxxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxyx() const property", asMETHOD(float4, get_zxyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxyy() const property", asMETHOD(float4, get_zxyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxyz() const property", asMETHOD(float4, get_zxyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxyw() const property", asMETHOD(float4, get_zxyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxzx() const property", asMETHOD(float4, get_zxzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxzy() const property", asMETHOD(float4, get_zxzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxzz() const property", asMETHOD(float4, get_zxzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxzw() const property", asMETHOD(float4, get_zxzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxwx() const property", asMETHOD(float4, get_zxwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxwy() const property", asMETHOD(float4, get_zxwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxwz() const property", asMETHOD(float4, get_zxwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zxww() const property", asMETHOD(float4, get_zxww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyxx() const property", asMETHOD(float4, get_zyxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyxy() const property", asMETHOD(float4, get_zyxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyxz() const property", asMETHOD(float4, get_zyxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyxw() const property", asMETHOD(float4, get_zyxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyyx() const property", asMETHOD(float4, get_zyyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyyy() const property", asMETHOD(float4, get_zyyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyyz() const property", asMETHOD(float4, get_zyyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyyw() const property", asMETHOD(float4, get_zyyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyzx() const property", asMETHOD(float4, get_zyzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyzy() const property", asMETHOD(float4, get_zyzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyzz() const property", asMETHOD(float4, get_zyzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyzw() const property", asMETHOD(float4, get_zyzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zywx() const property", asMETHOD(float4, get_zywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zywy() const property", asMETHOD(float4, get_zywy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zywz() const property", asMETHOD(float4, get_zywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zyww() const property", asMETHOD(float4, get_zyww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzxx() const property", asMETHOD(float4, get_zzxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzxy() const property", asMETHOD(float4, get_zzxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzxz() const property", asMETHOD(float4, get_zzxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzxw() const property", asMETHOD(float4, get_zzxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzyx() const property", asMETHOD(float4, get_zzyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzyy() const property", asMETHOD(float4, get_zzyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzyz() const property", asMETHOD(float4, get_zzyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzyw() const property", asMETHOD(float4, get_zzyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzzx() const property", asMETHOD(float4, get_zzzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzzy() const property", asMETHOD(float4, get_zzzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzzz() const property", asMETHOD(float4, get_zzzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzzw() const property", asMETHOD(float4, get_zzzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzwx() const property", asMETHOD(float4, get_zzwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzwy() const property", asMETHOD(float4, get_zzwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzwz() const property", asMETHOD(float4, get_zzwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zzww() const property", asMETHOD(float4, get_zzww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwxx() const property", asMETHOD(float4, get_zwxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwxy() const property", asMETHOD(float4, get_zwxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwxz() const property", asMETHOD(float4, get_zwxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwxw() const property", asMETHOD(float4, get_zwxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwyx() const property", asMETHOD(float4, get_zwyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwyy() const property", asMETHOD(float4, get_zwyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwyz() const property", asMETHOD(float4, get_zwyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwyw() const property", asMETHOD(float4, get_zwyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwzx() const property", asMETHOD(float4, get_zwzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwzy() const property", asMETHOD(float4, get_zwzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwzz() const property", asMETHOD(float4, get_zwzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwzw() const property", asMETHOD(float4, get_zwzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwwx() const property", asMETHOD(float4, get_zwwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwwy() const property", asMETHOD(float4, get_zwwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwwz() const property", asMETHOD(float4, get_zwwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_zwww() const property", asMETHOD(float4, get_zwww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zyxw(const float4 &in) property", asMETHOD(float4, set_zyxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zywx(const float4 &in) property", asMETHOD(float4, set_zywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zxyw(const float4 &in) property", asMETHOD(float4, set_zxyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zxwy(const float4 &in) property", asMETHOD(float4, set_zxwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zwyx(const float4 &in) property", asMETHOD(float4, set_zwyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_zwxy(const float4 &in) property", asMETHOD(float4, set_zwxy), asCALL_THISCALL); assert(r >= 0); + // w + r = engine->RegisterObjectMethod("float4", "float4 get_wxxx() const property", asMETHOD(float4, get_wxxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxxy() const property", asMETHOD(float4, get_wxxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxxz() const property", asMETHOD(float4, get_wxxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxxw() const property", asMETHOD(float4, get_wxxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxyx() const property", asMETHOD(float4, get_wxyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxyy() const property", asMETHOD(float4, get_wxyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxyz() const property", asMETHOD(float4, get_wxyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxyw() const property", asMETHOD(float4, get_wxyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxzx() const property", asMETHOD(float4, get_wxzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxzy() const property", asMETHOD(float4, get_wxzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxzz() const property", asMETHOD(float4, get_wxzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxzw() const property", asMETHOD(float4, get_wxzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxwx() const property", asMETHOD(float4, get_wxwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxwy() const property", asMETHOD(float4, get_wxwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxwz() const property", asMETHOD(float4, get_wxwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wxww() const property", asMETHOD(float4, get_wxww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyxx() const property", asMETHOD(float4, get_wyxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyxy() const property", asMETHOD(float4, get_wyxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyxz() const property", asMETHOD(float4, get_wyxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyxw() const property", asMETHOD(float4, get_wyxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyyx() const property", asMETHOD(float4, get_wyyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyyy() const property", asMETHOD(float4, get_wyyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyyz() const property", asMETHOD(float4, get_wyyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyyw() const property", asMETHOD(float4, get_wyyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyzx() const property", asMETHOD(float4, get_wyzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyzy() const property", asMETHOD(float4, get_wyzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyzz() const property", asMETHOD(float4, get_wyzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyzw() const property", asMETHOD(float4, get_wyzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wywx() const property", asMETHOD(float4, get_wywx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wywy() const property", asMETHOD(float4, get_wywy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wywz() const property", asMETHOD(float4, get_wywz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wyww() const property", asMETHOD(float4, get_wyww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzxx() const property", asMETHOD(float4, get_wzxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzxy() const property", asMETHOD(float4, get_wzxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzxz() const property", asMETHOD(float4, get_wzxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzxw() const property", asMETHOD(float4, get_wzxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzyx() const property", asMETHOD(float4, get_wzyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzyy() const property", asMETHOD(float4, get_wzyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzyz() const property", asMETHOD(float4, get_wzyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzyw() const property", asMETHOD(float4, get_wzyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzzx() const property", asMETHOD(float4, get_wzzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzzy() const property", asMETHOD(float4, get_wzzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzzz() const property", asMETHOD(float4, get_wzzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzzw() const property", asMETHOD(float4, get_wzzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzwx() const property", asMETHOD(float4, get_wzwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzwy() const property", asMETHOD(float4, get_wzwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzwz() const property", asMETHOD(float4, get_wzwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wzww() const property", asMETHOD(float4, get_wzww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwxx() const property", asMETHOD(float4, get_wwxx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwxy() const property", asMETHOD(float4, get_wwxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwxz() const property", asMETHOD(float4, get_wwxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwxw() const property", asMETHOD(float4, get_wwxw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwyx() const property", asMETHOD(float4, get_wwyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwyy() const property", asMETHOD(float4, get_wwyy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwyz() const property", asMETHOD(float4, get_wwyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwyw() const property", asMETHOD(float4, get_wwyw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwzx() const property", asMETHOD(float4, get_wwzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwzy() const property", asMETHOD(float4, get_wwzy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwzz() const property", asMETHOD(float4, get_wwzz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwzw() const property", asMETHOD(float4, get_wwzw), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwwx() const property", asMETHOD(float4, get_wwwx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwwy() const property", asMETHOD(float4, get_wwwy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwwz() const property", asMETHOD(float4, get_wwwz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "float4 get_wwww() const property", asMETHOD(float4, get_wwww), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wyzx(const float4 &in) property", asMETHOD(float4, set_wyzx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wyxz(const float4 &in) property", asMETHOD(float4, set_wyxz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wzyx(const float4 &in) property", asMETHOD(float4, set_wzyx), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wzxy(const float4 &in) property", asMETHOD(float4, set_wzxy), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wxyz(const float4 &in) property", asMETHOD(float4, set_wxyz), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4", "void set_wxzy(const float4 &in) property", asMETHOD(float4, set_wxzy), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string toString(float4 v)", asFUNCTION(toStringF4), asCALL_CDECL); assert(r >= 0); + } +} // TFE_ForceScript + +#endif \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/float4.h b/TheForceEngine/TFE_ForceScript/float4.h new file mode 100644 index 000000000..9300ba7cd --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float4.h @@ -0,0 +1,445 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// Vector types for scripts. +////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "float2.h" +#include "float3.h" + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_ForceScript +{ + struct float4 + { + float4(); + float4(const float4& other); + float4(f32 x, f32 y, f32 z, f32 w); + + // Assignment operator + float4 &operator=(const float4& other); + + // Compound assigment operators + float4 &operator+=(const float4& other); + float4 &operator-=(const float4& other); + float4 &operator*=(const float4& other); + float4 &operator/=(const float4& other); + + float4 &operator+=(const f32& other); + float4 &operator-=(const f32& other); + float4 &operator*=(const f32& other); + float4 &operator/=(const f32& other); + + // Swizzle operators - float2 + // x + float2 get_xx() const; + float2 get_xy() const; + float2 get_xz() const; + float2 get_xw() const; + // y + float2 get_yx() const; + float2 get_yy() const; + float2 get_yz() const; + float2 get_yw() const; + // z + float2 get_zx() const; + float2 get_zy() const; + float2 get_zz() const; + float2 get_zw() const; + // w + float2 get_wx() const; + float2 get_wy() const; + float2 get_wz() const; + float2 get_ww() const; + + // Swizzle operators - float3 + // x + float3 get_xxx() const; + float3 get_xxy() const; + float3 get_xxz() const; + float3 get_xxw() const; + float3 get_xyx() const; + float3 get_xyy() const; + float3 get_xyz() const; + float3 get_xyw() const; + float3 get_xzx() const; + float3 get_xzy() const; + float3 get_xzz() const; + float3 get_xzw() const; + float3 get_xwx() const; + float3 get_xwy() const; + float3 get_xwz() const; + float3 get_xww() const; + // y + float3 get_yxx() const; + float3 get_yxy() const; + float3 get_yxz() const; + float3 get_yxw() const; + float3 get_yyx() const; + float3 get_yyy() const; + float3 get_yyz() const; + float3 get_yyw() const; + float3 get_yzx() const; + float3 get_yzy() const; + float3 get_yzz() const; + float3 get_yzw() const; + float3 get_ywx() const; + float3 get_ywy() const; + float3 get_ywz() const; + float3 get_yww() const; + // z + float3 get_zxx() const; + float3 get_zxy() const; + float3 get_zxz() const; + float3 get_zxw() const; + float3 get_zyx() const; + float3 get_zyy() const; + float3 get_zyz() const; + float3 get_zyw() const; + float3 get_zzx() const; + float3 get_zzy() const; + float3 get_zzz() const; + float3 get_zzw() const; + float3 get_zwx() const; + float3 get_zwy() const; + float3 get_zwz() const; + float3 get_zww() const; + // w + float3 get_wxx() const; + float3 get_wxy() const; + float3 get_wxz() const; + float3 get_wxw() const; + float3 get_wyx() const; + float3 get_wyy() const; + float3 get_wyz() const; + float3 get_wyw() const; + float3 get_wzx() const; + float3 get_wzy() const; + float3 get_wzz() const; + float3 get_wzw() const; + float3 get_wwx() const; + float3 get_wwy() const; + float3 get_wwz() const; + float3 get_www() const; + + // Swizzle operators - float4 + // x + float4 get_xxxx() const; + float4 get_xxxy() const; + float4 get_xxxz() const; + float4 get_xxxw() const; + float4 get_xxyx() const; + float4 get_xxyy() const; + float4 get_xxyz() const; + float4 get_xxyw() const; + float4 get_xxzx() const; + float4 get_xxzy() const; + float4 get_xxzz() const; + float4 get_xxzw() const; + float4 get_xxwx() const; + float4 get_xxwy() const; + float4 get_xxwz() const; + float4 get_xxww() const; + float4 get_xyxx() const; + float4 get_xyxy() const; + float4 get_xyxz() const; + float4 get_xyxw() const; + float4 get_xyyx() const; + float4 get_xyyy() const; + float4 get_xyyz() const; + float4 get_xyyw() const; + float4 get_xyzx() const; + float4 get_xyzy() const; + float4 get_xyzz() const; + float4 get_xyzw() const; + float4 get_xywx() const; + float4 get_xywy() const; + float4 get_xywz() const; + float4 get_xyww() const; + float4 get_xzxx() const; + float4 get_xzxy() const; + float4 get_xzxz() const; + float4 get_xzxw() const; + float4 get_xzyx() const; + float4 get_xzyy() const; + float4 get_xzyz() const; + float4 get_xzyw() const; + float4 get_xzzx() const; + float4 get_xzzy() const; + float4 get_xzzz() const; + float4 get_xzzw() const; + float4 get_xzwx() const; + float4 get_xzwy() const; + float4 get_xzwz() const; + float4 get_xzww() const; + float4 get_xwxx() const; + float4 get_xwxy() const; + float4 get_xwxz() const; + float4 get_xwxw() const; + float4 get_xwyx() const; + float4 get_xwyy() const; + float4 get_xwyz() const; + float4 get_xwyw() const; + float4 get_xwzx() const; + float4 get_xwzy() const; + float4 get_xwzz() const; + float4 get_xwzw() const; + float4 get_xwwx() const; + float4 get_xwwy() const; + float4 get_xwwz() const; + float4 get_xwww() const; + void set_xyzw(const float4& in); + void set_xywz(const float4& in); + void set_xzyw(const float4& in); + void set_xzwy(const float4& in); + void set_xwyz(const float4& in); + void set_xwzy(const float4& in); + // y + float4 get_yxxx() const; + float4 get_yxxy() const; + float4 get_yxxz() const; + float4 get_yxxw() const; + float4 get_yxyx() const; + float4 get_yxyy() const; + float4 get_yxyz() const; + float4 get_yxyw() const; + float4 get_yxzx() const; + float4 get_yxzy() const; + float4 get_yxzz() const; + float4 get_yxzw() const; + float4 get_yxwx() const; + float4 get_yxwy() const; + float4 get_yxwz() const; + float4 get_yxww() const; + float4 get_yyxx() const; + float4 get_yyxy() const; + float4 get_yyxz() const; + float4 get_yyxw() const; + float4 get_yyyx() const; + float4 get_yyyy() const; + float4 get_yyyz() const; + float4 get_yyyw() const; + float4 get_yyzx() const; + float4 get_yyzy() const; + float4 get_yyzz() const; + float4 get_yyzw() const; + float4 get_yywx() const; + float4 get_yywy() const; + float4 get_yywz() const; + float4 get_yyww() const; + float4 get_yzxx() const; + float4 get_yzxy() const; + float4 get_yzxz() const; + float4 get_yzxw() const; + float4 get_yzyx() const; + float4 get_yzyy() const; + float4 get_yzyz() const; + float4 get_yzyw() const; + float4 get_yzzx() const; + float4 get_yzzy() const; + float4 get_yzzz() const; + float4 get_yzzw() const; + float4 get_yzwx() const; + float4 get_yzwy() const; + float4 get_yzwz() const; + float4 get_yzww() const; + float4 get_ywxx() const; + float4 get_ywxy() const; + float4 get_ywxz() const; + float4 get_ywxw() const; + float4 get_ywyx() const; + float4 get_ywyy() const; + float4 get_ywyz() const; + float4 get_ywyw() const; + float4 get_ywzx() const; + float4 get_ywzy() const; + float4 get_ywzz() const; + float4 get_ywzw() const; + float4 get_ywwx() const; + float4 get_ywwy() const; + float4 get_ywwz() const; + float4 get_ywww() const; + void set_yxzw(const float4& in); + void set_yxwz(const float4& in); + void set_yzxw(const float4& in); + void set_yzwx(const float4& in); + void set_ywxz(const float4& in); + void set_ywzx(const float4& in); + // z + float4 get_zxxx() const; + float4 get_zxxy() const; + float4 get_zxxz() const; + float4 get_zxxw() const; + float4 get_zxyx() const; + float4 get_zxyy() const; + float4 get_zxyz() const; + float4 get_zxyw() const; + float4 get_zxzx() const; + float4 get_zxzy() const; + float4 get_zxzz() const; + float4 get_zxzw() const; + float4 get_zxwx() const; + float4 get_zxwy() const; + float4 get_zxwz() const; + float4 get_zxww() const; + float4 get_zyxx() const; + float4 get_zyxy() const; + float4 get_zyxz() const; + float4 get_zyxw() const; + float4 get_zyyx() const; + float4 get_zyyy() const; + float4 get_zyyz() const; + float4 get_zyyw() const; + float4 get_zyzx() const; + float4 get_zyzy() const; + float4 get_zyzz() const; + float4 get_zyzw() const; + float4 get_zywx() const; + float4 get_zywy() const; + float4 get_zywz() const; + float4 get_zyww() const; + float4 get_zzxx() const; + float4 get_zzxy() const; + float4 get_zzxz() const; + float4 get_zzxw() const; + float4 get_zzyx() const; + float4 get_zzyy() const; + float4 get_zzyz() const; + float4 get_zzyw() const; + float4 get_zzzx() const; + float4 get_zzzy() const; + float4 get_zzzz() const; + float4 get_zzzw() const; + float4 get_zzwx() const; + float4 get_zzwy() const; + float4 get_zzwz() const; + float4 get_zzww() const; + float4 get_zwxx() const; + float4 get_zwxy() const; + float4 get_zwxz() const; + float4 get_zwxw() const; + float4 get_zwyx() const; + float4 get_zwyy() const; + float4 get_zwyz() const; + float4 get_zwyw() const; + float4 get_zwzx() const; + float4 get_zwzy() const; + float4 get_zwzz() const; + float4 get_zwzw() const; + float4 get_zwwx() const; + float4 get_zwwy() const; + float4 get_zwwz() const; + float4 get_zwww() const; + void set_zyxw(const float4& in); + void set_zywx(const float4& in); + void set_zxyw(const float4& in); + void set_zxwy(const float4& in); + void set_zwyx(const float4& in); + void set_zwxy(const float4& in); + // w + float4 get_wxxx() const; + float4 get_wxxy() const; + float4 get_wxxz() const; + float4 get_wxxw() const; + float4 get_wxyx() const; + float4 get_wxyy() const; + float4 get_wxyz() const; + float4 get_wxyw() const; + float4 get_wxzx() const; + float4 get_wxzy() const; + float4 get_wxzz() const; + float4 get_wxzw() const; + float4 get_wxwx() const; + float4 get_wxwy() const; + float4 get_wxwz() const; + float4 get_wxww() const; + float4 get_wyxx() const; + float4 get_wyxy() const; + float4 get_wyxz() const; + float4 get_wyxw() const; + float4 get_wyyx() const; + float4 get_wyyy() const; + float4 get_wyyz() const; + float4 get_wyyw() const; + float4 get_wyzx() const; + float4 get_wyzy() const; + float4 get_wyzz() const; + float4 get_wyzw() const; + float4 get_wywx() const; + float4 get_wywy() const; + float4 get_wywz() const; + float4 get_wyww() const; + float4 get_wzxx() const; + float4 get_wzxy() const; + float4 get_wzxz() const; + float4 get_wzxw() const; + float4 get_wzyx() const; + float4 get_wzyy() const; + float4 get_wzyz() const; + float4 get_wzyw() const; + float4 get_wzzx() const; + float4 get_wzzy() const; + float4 get_wzzz() const; + float4 get_wzzw() const; + float4 get_wzwx() const; + float4 get_wzwy() const; + float4 get_wzwz() const; + float4 get_wzww() const; + float4 get_wwxx() const; + float4 get_wwxy() const; + float4 get_wwxz() const; + float4 get_wwxw() const; + float4 get_wwyx() const; + float4 get_wwyy() const; + float4 get_wwyz() const; + float4 get_wwyw() const; + float4 get_wwzx() const; + float4 get_wwzy() const; + float4 get_wwzz() const; + float4 get_wwzw() const; + float4 get_wwwx() const; + float4 get_wwwy() const; + float4 get_wwwz() const; + float4 get_wwww() const; + void set_wyzx(const float4& in); + void set_wyxz(const float4& in); + void set_wzyx(const float4& in); + void set_wzxy(const float4& in); + void set_wxyz(const float4& in); + void set_wxzy(const float4& in); + + // Indexing operator + f32& get_opIndex(s32 index); + + // Comparison + bool operator==(const float4& other) const; + bool operator!=(const float4& other) const; + + // Math operators + float4 operator+(const float4& other) const; + float4 operator-(const float4& other) const; + float4 operator*(const float4& other) const; + float4 operator/(const float4& other) const; + + float4 operator+(const f32& other) const; + float4 operator-(const f32& other) const; + float4 operator*(const f32& other) const; + float4 operator/(const f32& other) const; + + union + { + struct { f32 x, y, z, w; }; + f32 m[4]; + }; + }; + + void registerScriptMath_float4(asIScriptEngine *engine); + s32 getFloat4ObjectId(); + std::string toStringF4(const float4& v); + + inline f32 dot(float4 a, float4 b) { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; } +} // TFE_ForceScript +#endif diff --git a/TheForceEngine/TFE_ForceScript/float4x4.cpp b/TheForceEngine/TFE_ForceScript/float4x4.cpp new file mode 100644 index 000000000..b5ad57ec5 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float4x4.cpp @@ -0,0 +1,186 @@ +#include "float4x4.h" +#include "forceScript.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_FORCE_SCRIPT + +// TFE_ForceScript wraps Anglescript, so these includes should only exist here. +#include + +namespace TFE_ForceScript +{ + float4x4::float4x4() + { + m[0] = 1.0f; m[1] = 0.0f; m[2] = 0.0f; m[3] = 0.0f; + m[4] = 0.0f; m[5] = 1.0f; m[6] = 0.0f; m[7] = 0.0f; + m[8] = 0.0f; m[9] = 0.0f; m[10] = 1.0f; m[11] = 0.0f; + m[12] = 0.0f; m[13] = 0.0f; m[14] = 0.0f; m[15] = 1.0f; + } + + float4x4::float4x4(float4 _r0, float4 _r1, float4 _r2, float4 _r3) + { + r[0] = _r0; + r[1] = _r1; + r[2] = _r2; + r[3] = _r3; + } + + float4x4::float4x4(const float4x4& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + r[2] = other.r[2]; + r[3] = other.r[3]; + } + + float4x4::float4x4(f32 m00, f32 m01, f32 m02, f32 m03, f32 m10, f32 m11, f32 m12, f32 m13, f32 m20, f32 m21, f32 m22, f32 m23, f32 m30, f32 m31, f32 m32, f32 m33) + { + m[0] = m00; m[1] = m01; m[2] = m02; m[3] = m03; + m[4] = m10; m[5] = m11; m[6] = m12; m[7] = m13; + m[8] = m20; m[9] = m21; m[10] = m22; m[11] = m23; + m[12] = m30; m[13] = m31; m[14] = m32; m[15] = m33; + } + + bool float4x4::operator==(const float4x4& o) const + { + for (s32 i = 0; i < 16; i++) + { + if (m[i] != o.m[i]) { return false; } + } + return true; + } + bool float4x4::operator!=(const float4x4& o) const + { + return !(*this == o); + } + + float4x4& float4x4::operator=(const float4x4& other) + { + r[0] = other.r[0]; + r[1] = other.r[1]; + r[2] = other.r[2]; + r[3] = other.r[3]; + return *this; + } + // Compound float2 + float4x4& float4x4::operator*=(const float4x4& other) + { + *this = *this * other; + return *this; + } + float4x4& float4x4::operator*=(const f32& other) + { + *this = *this * other; + return *this; + } + + float4x4 matMul(const float4x4& a, const float4x4& b) + { + float4 r0 = a.r[0]; + float4 r1 = a.r[1]; + float4 r2 = a.r[2]; + float4 r3 = a.r[3]; + float4 c0(b.r[0].x, b.r[1].x, b.r[2].x, b.r[3].x); + float4 c1(b.r[0].y, b.r[1].y, b.r[2].y, b.r[3].y); + float4 c2(b.r[0].z, b.r[1].z, b.r[2].z, b.r[3].z); + float4 c3(b.r[0].w, b.r[1].w, b.r[2].w, b.r[3].w); + + return float4x4(dot(r0, c0), dot(r0, c1), dot(r0, c2), dot(r0, c3), + dot(r1, c0), dot(r1, c1), dot(r1, c2), dot(r1, c3), + dot(r2, c0), dot(r2, c1), dot(r2, c2), dot(r2, c3), + dot(r3, c0), dot(r3, c1), dot(r3, c2), dot(r3, c3)); + } + + float4 matVecMul(const float4x4& a, const float4& b) + { + return float4(dot(a.r[0], b), dot(a.r[1], b), dot(a.r[2], b), dot(a.r[3], b)); + } + + // float4x4 x float4x4 + float4x4 float4x4::operator*(const float4x4& other) const { return matMul(*this, other); } + // float4x4 x scalar + float4x4 float4x4::operator*(const f32& other) const { return float4x4(r[0]*other, r[1]*other, r[2]*other, r[3]*other); } + // float4x4 x float2 + float4 float4x4::operator*(const float4& other) const { return matVecMul(*this, other); } + + //----------------------- + // Array operator + //----------------------- + float4& float4x4::get_opIndex(s32 index) + { + if (index < 0 || index >= 4) + { + // TODO: WARNING + index = 0; + } + return r[index]; + } + + //----------------------- + // AngelScript functions + //----------------------- + static void float4x4DefaultConstructor(float4x4* self) { new(self) float4x4(); } + static void float4x4CopyConstructor(const float4x4& other, float4x4* self) { new(self) float4x4(other); } + static void float4x4ConvConstructor(const float4& m0, const float4& m1, const float4& m2, const float4& m3, float4x4* self) { new(self) float4x4(m0, m1, m2, m3); } + static void float4x4InitConstructor(f32 m0, f32 m1, f32 m2, f32 m3, f32 m4, f32 m5, f32 m6, f32 m7, f32 m8, f32 m9, f32 m10, f32 m11, + f32 m12, f32 m13, f32 m14, f32 m15, float2* self) { new(self) float4x4(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15); } + static void float4x4ListConstructor(f32* list, float2* self) { new(self) float4x4(list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], list[8], + list[9], list[10], list[11], list[12], list[13], list[14], list[15]); } + + //----------------------- + // Intrinsics + //----------------------- + std::string toStringF4x4(const float4x4& m) + { + char tmp[1024]; + sprintf(tmp, "[ (%g, %g, %g, %g), (%g, %g, %g, %g), (%g, %g, %g, %g), (%g, %g, %g, %g) ]", + m.r[0].x, m.r[0].y, m.r[0].z, m.r[0].w, m.r[1].x, m.r[1].y, m.r[1].z, m.r[1].w, + m.r[2].x, m.r[2].y, m.r[2].z, m.r[2].w, m.r[3].x, m.r[3].y, m.r[3].z, m.r[3].w); + return std::string(tmp); + } + + //------------------------------------- + // Registration + //------------------------------------- + static s32 s_float4x4ObjId = -1; + s32 getFloat4x4ObjectId() + { + return s_float4x4ObjId; + } + + void registerScriptMath_float4x4(asIScriptEngine *engine) + { + s32 r; + r = engine->RegisterObjectType("float4x4", sizeof(float4x4), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits() | asOBJ_APP_CLASS_ALLFLOATS); assert(r >= 0); + s_float4x4ObjId = r; + + // Register the constructors + r = engine->RegisterObjectBehaviour("float4x4", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(float4x4DefaultConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4x4", asBEHAVE_CONSTRUCT, "void f(const float4x4 &in)", asFUNCTION(float4x4CopyConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4x4", asBEHAVE_CONSTRUCT, "void f(const float4 &in, const float4 &in, const float4 &in, const float4 &in)", asFUNCTION(float4x4ConvConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4x4", asBEHAVE_CONSTRUCT, "void f(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float)", asFUNCTION(float4x4InitConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectBehaviour("float4x4", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float}", asFUNCTION(float4x4ListConstructor), asCALL_CDECL_OBJLAST); assert(r >= 0); + + // Register the operator overloads + r = engine->RegisterObjectMethod("float4x4", "float4x4 &opMulAssign(const float4x4 &in)", asMETHODPR(float4x4, operator*=, (const float4x4 &), float4x4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4x4", "float4x4 &opMulAssign(const float &in)", asMETHODPR(float4x4, operator*=, (const float &), float4x4&), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4x4", "bool opEquals(const float4x4 &in) const", asMETHODPR(float4x4, operator==, (const float4x4 &) const, bool), asCALL_THISCALL); assert(r >= 0); + + // Indexing operator. + r = engine->RegisterObjectMethod("float4x4", "float4& opIndex(int)", asMETHOD(float4x4, get_opIndex), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("float4x4", "float4x4 opMul(const float4x4 &in) const", asMETHODPR(float4x4, operator*, (const float4x4 &) const, float4x4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4x4", "float4x4 opMul(const float &in) const", asMETHODPR(float4x4, operator*, (const float &) const, float4x4), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("float4x4", "float4 opMul(const float4 &in) const", asMETHODPR(float4x4, operator*, (const float4 &) const, float4), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterGlobalFunction("string toString(float4x4 m)", asFUNCTION(toStringF4x4), asCALL_CDECL); assert(r >= 0); + } +} // TFE_ForceScript + +#endif diff --git a/TheForceEngine/TFE_ForceScript/float4x4.h b/TheForceEngine/TFE_ForceScript/float4x4.h new file mode 100644 index 000000000..e49dfc96d --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/float4x4.h @@ -0,0 +1,52 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// Matrix types for scripts. +////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "float4.h" + +#ifdef ENABLE_FORCE_SCRIPT +#include + +namespace TFE_ForceScript +{ + struct float4x4 + { + float4x4(); + float4x4(const float4x4& other); + float4x4(float4 r0, float4 r1, float4 r2, float4 r3); + float4x4(f32 m00, f32 m01, f32 m02, f32 m03, f32 m10, f32 m11, f32 m12, f32 m13, f32 m20, f32 m21, f32 m22, f32 m23, f32 m30, f32 m31, f32 m32, f32 m33); + + // Assignment operator + float4x4 &operator=(const float4x4& other); + + // Compound assigment operators + float4x4 &operator*=(const float4x4& other); + float4x4 &operator*=(const f32& other); + + // Indexing operator + float4& get_opIndex(s32 index); + + // Comparison + bool operator==(const float4x4& other) const; + bool operator!=(const float4x4& other) const; + + // Math operators + float4x4 operator*(const float4x4& other) const; + float4x4 operator*(const f32& other) const; + float4 operator*(const float4& other) const; + + union + { + float4 r[4]; + f32 m[16]; + }; + }; + + void registerScriptMath_float4x4(asIScriptEngine *engine); + s32 getFloat4x4ObjectId(); + std::string toStringF4x4(const float4x4& v); +} // TFE_ForceScript +#endif diff --git a/TheForceEngine/TFE_ForceScript/forceScript.cpp b/TheForceEngine/TFE_ForceScript/forceScript.cpp index 115b63bb6..fd31052e3 100644 --- a/TheForceEngine/TFE_ForceScript/forceScript.cpp +++ b/TheForceEngine/TFE_ForceScript/forceScript.cpp @@ -1,7 +1,13 @@ #include "forceScript.h" #include "float2.h" +#include "float3.h" +#include "float4.h" +#include "float2x2.h" +#include "float3x3.h" +#include "float4x4.h" #include #include +#include #include #include #include @@ -12,6 +18,7 @@ // TFE_ForceScript wraps Anglescript, so these includes should only exist here. #include #include +#include #include namespace TFE_ForceScript @@ -21,9 +28,22 @@ namespace TFE_ForceScript struct ScriptThread { asIScriptContext* asContext; + std::string name; + std::string funcName; f32 delay; }; + struct ModuleDef + { + std::string name; + std::string path; + bool allowReadFromArchive; + u32 accessMask; + + asIScriptModule* mod; + }; + std::vector s_modules; + static asIScriptEngine* s_engine = nullptr; static std::vector s_scriptThreads; static std::vector s_freeThreads; @@ -31,6 +51,8 @@ namespace TFE_ForceScript static s32 s_typeId[FSTYPE_COUNT] = { 0 }; + void serializeVariable(Stream* stream, s32 typeId, void*& varAddr, const char* name, bool allocateObjects = false); + // Script message callback. void messageCallback(const asSMessageInfo* msg, void* param) { @@ -58,6 +80,19 @@ namespace TFE_ForceScript } } + void abort(const std::string& msg) + { + asIScriptContext* context = asGetActiveContext(); + if (context) + { + const s32 id = (s32)((intptr_t)context->GetUserData(ThreadId)); + assert(id >= 0 && id < (s32)s_scriptThreads.size()); + TFE_System::logWrite(LOG_ERROR, "Force Script", "Script '%s', Entry Point '%s' Aborted! - '%s'.", + s_scriptThreads[id].name.c_str(), s_scriptThreads[id].funcName.c_str(), msg.c_str()); + context->Abort(); + } + } + void resume(s32 id) { s_scriptThreads[id].delay = 0.0f; @@ -81,17 +116,35 @@ namespace TFE_ForceScript RegisterStdString(s_engine); s_typeId[FSTYPE_STRING] = GetStdStringObjectId(); + // Register the default array type. + RegisterScriptArray(s_engine, true); + s_typeId[FSTYPE_ARRAY] = GetScriptArrayObjectId(); + // Language features. - res = s_engine->RegisterGlobalFunction("void yield(float)", asFUNCTION(yield), asCALL_CDECL); assert(res >= 0); + res = s_engine->RegisterGlobalFunction("void yield(float = 0.0)", asFUNCTION(yield), asCALL_CDECL); assert(res >= 0); + res = s_engine->RegisterGlobalFunction("void abort(const string &in)", asFUNCTION(abort), asCALL_CDECL); assert(res >= 0); registerScriptMath_float2(s_engine); + registerScriptMath_float3(s_engine); + registerScriptMath_float4(s_engine); + registerScriptMath_float2x2(s_engine); + registerScriptMath_float3x3(s_engine); + registerScriptMath_float4x4(s_engine); s_typeId[FSTYPE_FLOAT2] = getFloat2ObjectId(); + s_typeId[FSTYPE_FLOAT3] = getFloat3ObjectId(); + s_typeId[FSTYPE_FLOAT4] = getFloat4ObjectId(); + s_typeId[FSTYPE_FLOAT2x2] = getFloat2x2ObjectId(); + s_typeId[FSTYPE_FLOAT3x3] = getFloat3x3ObjectId(); + s_typeId[FSTYPE_FLOAT4x4] = getFloat4x4ObjectId(); + + s_modules.clear(); } void destroy() { // Clean up stopAllFunc(); + s_modules.clear(); if (s_engine) { s_engine->ShutDownAndRelease(); @@ -104,9 +157,502 @@ namespace TFE_ForceScript s_msgCallback = callback; } - void update() + typedef void(*SerializeTypeFunc)(Stream*, void** ptr, const asITypeInfo*, bool); + + std::map s_serializeTypeMap; + SerializeTypeFunc getSerializeTypeFunc(const char* name); + + void serializeArray(Stream* stream, CScriptArray** array, const asITypeInfo* typeInfo, bool allocate) + { + if (allocate && array && serialization_getMode() == SMODE_READ) + { + *array = CScriptArray::Create((asITypeInfo*)typeInfo); + } + + // Array size. + u32 arrayLen = serialization_getMode() == SMODE_WRITE && array ? (*array)->GetSize() : 0; + SERIALIZE(SaveVersionLevelScriptV1, arrayLen, 0); + if (serialization_getMode() == SMODE_READ) { (*array)->Resize(arrayLen); } + if (arrayLen == 0) { return; } + + // Array data. + s32 elementTypeId = (*array)->GetElementTypeId(); + s32 size = s_engine->GetSizeOfPrimitiveType(elementTypeId); + asITypeInfo* elementType = s_engine->GetTypeInfoById(elementTypeId); + assert(elementType); + SerializeTypeFunc elemFunc = getSerializeTypeFunc(elementType->GetName()); + + if (elemFunc) + { + for (u32 i = 0; i < arrayLen; i++) + { + void* ptrAt = (*array)->At(i); + elemFunc(stream, &ptrAt, elementType, allocate); + } + } + else if (size > 0 || (elementType->GetFlags() & asOBJ_POD)) + { + size = size ? size : elementType->GetSize(); + for (u32 i = 0; i < arrayLen; i++) + { + SERIALIZE_BUF(SaveVersionLevelScriptV1, (*array)->At(i), size); + } + } + else + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "No serialization function for type: %s", elementType->GetName()); + assert(0); + } + } + + void serializeString(Stream* stream, std::string** str, const asITypeInfo* typeInfo, bool allocate) + { + if (str) + { + SERIALIZE_STRING(SaveVersionLevelScriptV1, *(*str)); + } + } + + void registerSerializeTypeFunc(const char* name, SerializeTypeFunc func) + { + s_serializeTypeMap[name] = func; + } + + SerializeTypeFunc getSerializeTypeFunc(const char* name) + { + SerializeTypeFunc func = s_serializeTypeMap[name]; + if (!func) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "No serialization function for type: %s", name); + } + return func; + } + + void initSerliazableTypes() + { + if (!s_serializeTypeMap.empty()) { return; } + registerSerializeTypeFunc("array", (SerializeTypeFunc)serializeArray); + registerSerializeTypeFunc("string", (SerializeTypeFunc)serializeString); + } + + void serializeVariable(Stream* stream, s32 typeId, void*& varAddr, const char* name, bool allocateObjects) + { + const s32 size = s_engine->GetSizeOfPrimitiveType(typeId); + const asITypeInfo* type = s_engine->GetTypeInfoById(typeId); + if (typeId & asTYPEID_OBJHANDLE) + { + TFE_System::logWrite(LOG_WARNING, "Force Script", "Serializing object handles is not supported."); + return; + } + else if (typeId & asTYPEID_SCRIPTOBJECT) + { + TFE_System::logWrite(LOG_WARNING, "Force Script", "Serializing script objects is not supported."); + return; + } + else if (size > 0) + { + // Primitive type. + if (varAddr) { SERIALIZE_BUF(SaveVersionLevelScriptV1, varAddr, size); } + } + else if (type && type->GetFlags() & asOBJ_POD) + { + // POD object type (float2, Sector, etc.). + const u32 typeSize = type->GetSize(); + if (allocateObjects && serialization_getMode() == SMODE_READ) + { + void** varHandle = (void**)varAddr; + *varHandle = asAllocMem(typeSize); + varAddr = *varHandle; + } + else if (allocateObjects && !varAddr) + { + // We need to write something here... + u8 empty[256]; + memset(empty, 0, typeSize); + SERIALIZE_BUF(SaveVersionLevelScriptV1, empty, typeSize); + } + + if (varAddr) { SERIALIZE_BUF(SaveVersionLevelScriptV1, varAddr, typeSize); } + } + else if (type) + { + // Complex script type (array, string, dictionary, etc.). + const char* typeName = type->GetName(); + assert(typeName); + + SerializeTypeFunc func = getSerializeTypeFunc(typeName); + if (func) + { + void** addr = nullptr; + if (allocateObjects && varAddr && serialization_getMode() == SMODE_READ) + { + addr = (void**)varAddr; + } + else if (varAddr) + { + addr = &varAddr; + } + + func(stream, addr, type, allocateObjects); + } + else + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "No serialization function for type: %s", typeName); + assert(0); + } + } + else + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Type of variable \"%s\" has no type info, cannot serialize.", name); + assert(0); + } + } + + enum ScriptSerializeOptFlags { - const f32 dt = (f32)TFE_System::getDeltaTime(); + OPT_NONE = 0, + OPT_SYS_FUNC = FLAG_BIT(0), + OPT_INIT_FUNC = FLAG_BIT(1), + OPT_OBJ_REG = FLAG_BIT(2) + }; + + void serialize(Stream* stream) + { + if (serialization_getVersion() < SaveVersionLevelScriptV1) + { + return; + } + initSerliazableTypes(); + + s32 moduleCount = serialization_getMode() == SMODE_WRITE ? (s32)s_modules.size() : 0; + s32 scriptThreadCount = serialization_getMode() == SMODE_WRITE ? (s32)s_scriptThreads.size() : 0; + s32 freeThreadCount = serialization_getMode() == SMODE_WRITE ? (s32)s_freeThreads.size() : 0; + SERIALIZE(SaveVersionLevelScriptV1, moduleCount, 0); + SERIALIZE(SaveVersionLevelScriptV1, scriptThreadCount, 0); + SERIALIZE(SaveVersionLevelScriptV1, freeThreadCount, 0); + + ///////////////////////////////////////////////////////// + // Serialize Script Modules + ///////////////////////////////////////////////////////// + std::vector modDef; + if (serialization_getMode() == SMODE_READ) + { + modDef.resize(moduleCount); + } + else + { + modDef = s_modules; + } + + std::string nameStr, nameSpaceStr; + for (s32 m = 0; m < moduleCount; m++) + { + ModuleDef* curModDef = &modDef[m]; + + SERIALIZE_STRING(SaveVersionLevelScriptV1, curModDef->name); + SERIALIZE_STRING(SaveVersionLevelScriptV1, curModDef->path); + SERIALIZE(SaveVersionLevelScriptV1, curModDef->allowReadFromArchive, false); + SERIALIZE(SaveVersionLevelScriptV1, curModDef->accessMask, 0); + + u32 globalCount = 0; + if (serialization_getMode() == SMODE_READ) + { + curModDef->mod = (asIScriptModule*)createModule(curModDef->name.c_str(), curModDef->path.c_str(), curModDef->allowReadFromArchive, curModDef->accessMask); + } + else + { + globalCount = curModDef->mod->GetGlobalVarCount(); + } + asIScriptModule* mod = curModDef->mod; + SERIALIZE(SaveVersionLevelScriptV1, globalCount, 0); + + for (u32 g = 0; g < globalCount; g++) + { + s32 typeId = 0; + if (serialization_getMode() == SMODE_WRITE) + { + const char* name; + const char* nameSpace; + mod->GetGlobalVar(g, &name, &nameSpace, &typeId); + nameStr = name; + nameSpaceStr = nameSpace; + } + SERIALIZE_STRING(SaveVersionLevelScriptV1, nameStr); + SERIALIZE_STRING(SaveVersionLevelScriptV1, nameSpaceStr); + SERIALIZE(SaveVersionLevelScriptV1, typeId, 0); + + if (serialization_getMode() == SMODE_READ) + { + const char* readName; + const char* readNameSpace; + s32 readTypeId = 0; + // This needs to be called to initialize the type info. + mod->GetGlobalVar(g, &readName, &readNameSpace, &readTypeId); + assert(readTypeId == typeId); + } + + s32 varIndex = g; + if (serialization_getMode() == SMODE_READ) + { + varIndex = mod->GetGlobalVarIndexByName(nameStr.c_str()); + assert(varIndex >= 0); + } + void* varAddr = mod->GetAddressOfGlobalVar(varIndex); + serializeVariable(stream, typeId, varAddr, nameStr.c_str()); + } + } + + ///////////////////////////////////////////////////////// + // Serialize Script Threads + ///////////////////////////////////////////////////////// + if (serialization_getMode() == SMODE_READ) + { + s_freeThreads.resize(freeThreadCount); + s_scriptThreads.resize(scriptThreadCount); + } + SERIALIZE_BUF(SaveVersionLevelScriptV1, s_freeThreads.data(), sizeof(s32) * freeThreadCount); + + ScriptThread* thread = s_scriptThreads.data(); + for (s32 i = 0; i < scriptThreadCount; i++, thread++) + { + SERIALIZE(SaveVersionLevelScriptV1, thread->delay, 0.0f); + if (serialization_getMode() == SMODE_WRITE) + { + asIScriptContext* context = thread->asContext; + u32 callstackSize = context ? context->GetCallstackSize() : 0; + SERIALIZE(SaveVersionLevelScriptV1, callstackSize, 0); + if (!callstackSize) + { + assert(!context); + continue; + } + + for (u32 c = 0; c < callstackSize; c++) + { + u32 stackFramePtr = 0, programPtr = 0, stackPtr = 0, stackIndex = 0; + asIScriptFunction* scriptFunc = nullptr; + // Serialize the stack data. + s32 res = context->GetCallStateRegisters(c, (asDWORD*)&stackFramePtr, &scriptFunc, (asDWORD*)&programPtr, (asDWORD*)&stackPtr, (asDWORD*)&stackIndex); + if (res != asSUCCESS) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Cannot serialize script context, error = %x.", res); + } + SERIALIZE(SaveVersionLevelScriptV1, stackFramePtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, programPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, stackPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, stackIndex, 0); + // Serialize the function/module. + const char* modName = scriptFunc->GetModuleName(); + const char* funcName = scriptFunc->GetName(); + SERIALIZE_CSTRING_WRITE(SaveVersionLevelScriptV1, modName); + SERIALIZE_CSTRING_WRITE(SaveVersionLevelScriptV1, funcName); + // Serialize local variables. + u32 localCount = context->GetVarCount(); + SERIALIZE(SaveVersionLevelScriptV1, localCount, 0); + for (u32 v = 0; v < localCount; v++) + { + const char* varName = nullptr; + s32 typeId = 0, stackOffset = 0; + u32 typeMod = 0; + bool isOnHeap = false; + res = context->GetVar(v, c, &varName, &typeId, (asETypeModifiers*)&typeMod, &isOnHeap, &stackOffset); + if (res != asSUCCESS) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Cannot serialize script context local variable, error = %x.", res); + } + + void* varPtr = context->GetAddressOfVar(v, c); + serializeVariable(stream, typeId, varPtr, varName, isOnHeap); + } + // Serialize stack arguments. + u32 argCount = context->GetArgsOnStackCount(c); + SERIALIZE(SaveVersionLevelScriptV1, argCount, 0); + for (u32 a = 0; a < argCount; a++) + { + s32 typeId = 0; + u32 flags = 0; + void* addr = nullptr; + context->GetArgOnStack(c, a, &typeId, &flags, &addr); + serializeVariable(stream, typeId, addr, ""); + } + + // Additional context registers. + asIScriptFunction* callingSysFunc = nullptr; + asIScriptFunction* initFunc = nullptr; + u32 origStackPtr = 0, argSize = 0; + u64 valueReg = 0; + void* objReg = nullptr; + asITypeInfo* objRegType = nullptr; + context->GetStateRegisters(c, &callingSysFunc, &initFunc, (asDWORD*)&origStackPtr, (asDWORD*)&argSize, (asQWORD*)&valueReg, &objReg, &objRegType); + + u32 optFlags = OPT_NONE; + if (callingSysFunc) { optFlags |= OPT_SYS_FUNC; } + if (initFunc) { optFlags |= OPT_INIT_FUNC; } + if (objReg && objRegType) { optFlags |= OPT_OBJ_REG; } + SERIALIZE(SaveVersionLevelScriptV1, optFlags, 0); + + if (optFlags & OPT_SYS_FUNC) + { + SERIALIZE_CSTRING_WRITE(SaveVersionLevelScriptV1, callingSysFunc->GetName()); + } + if (optFlags & OPT_INIT_FUNC) + { + SERIALIZE_CSTRING_WRITE(SaveVersionLevelScriptV1, initFunc->GetName()); + } + + SERIALIZE(SaveVersionLevelScriptV1, origStackPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, argSize, 0); + SERIALIZE(SaveVersionLevelScriptV1, valueReg, 0); + + if (optFlags & OPT_OBJ_REG) + { + s32 typeId = objRegType->GetTypeId(); + serializeVariable(stream, typeId, objReg, ""); + } + } + } + else + { + u32 callstackSize = 0; + SERIALIZE(SaveVersionLevelScriptV1, callstackSize, 0); + if (callstackSize == 0) + { + thread->asContext = nullptr; + continue; + } + + thread->asContext = s_engine->RequestContext(); + asIScriptContext* context = thread->asContext; + assert(context); + + if (context->StartDeserialization() == asSUCCESS) + { + asIScriptModule* curMod = nullptr; + s32 res = asSUCCESS; + for (u32 c = 0; c < callstackSize; c++) + { + u32 stackFramePtr = 0, programPtr = 0, stackPtr = 0, stackIndex = 0; + SERIALIZE(SaveVersionLevelScriptV1, stackFramePtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, programPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, stackPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, stackIndex, 0); + // Serialize the function/module. + char modName[256], funcName[256]; + SERIALIZE_CSTRING(SaveVersionLevelScriptV1, modName); + SERIALIZE_CSTRING(SaveVersionLevelScriptV1, funcName); + thread->name = modName; + thread->funcName = funcName; + + asIScriptModule* mod = s_engine->GetModule(modName); + asIScriptFunction* scriptFunc = mod->GetFunctionByName(funcName); + assert(mod == curMod || !curMod); + curMod = mod; + + res = context->PushFunction(scriptFunc, nullptr); assert(res >= 0); + res = context->SetCallStateRegisters(c, stackFramePtr, scriptFunc, programPtr, stackPtr, stackIndex); assert(res >= 0); + + // Local Variables. + u32 localCount = 0; + SERIALIZE(SaveVersionLevelScriptV1, localCount, 0); + if (localCount != context->GetVarCount(c)) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Failed to load saved game, script code no longer matches."); + assert(0); + return; + } + + for (u32 v = 0; v < localCount; v++) + { + const char* varName = nullptr; + s32 typeId = 0, stackOffset = 0; + u32 typeMod = 0; + bool isOnHeap = false; + res = context->GetVar(v, c, &varName, &typeId, (asETypeModifiers*)&typeMod, &isOnHeap, &stackOffset); + if (res != asSUCCESS) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Cannot serialize script context local variable, error = %x.", res); + } + + void* varPtr = context->GetAddressOfVar(v, c, isOnHeap); + serializeVariable(stream, typeId, varPtr, varName, isOnHeap); + } + + // Serialize stack arguments. + u32 argCount = 0; + SERIALIZE(SaveVersionLevelScriptV1, argCount, 0); + assert(argCount == (u32)context->GetArgsOnStackCount(c)); + for (u32 a = 0; a < argCount; a++) + { + s32 typeId = 0; + u32 flags = 0; + void* addr = nullptr; + + res = context->GetArgOnStack(c, a, &typeId, &flags, &addr); assert(res >= 0); + serializeVariable(stream, typeId, addr, ""); + } + + // Additional context registers. + asIScriptFunction* callingSysFunc = nullptr; + asIScriptFunction* initFunc = nullptr; + u32 origStackPtr = 0, argSize = 0; + u64 valueReg = 0; + + u32 optFlags = OPT_NONE; + SERIALIZE(SaveVersionLevelScriptV1, optFlags, OPT_NONE); + + if (optFlags & OPT_SYS_FUNC) + { + char name[256]; + SERIALIZE_CSTRING(SaveVersionLevelScriptV1, name); + callingSysFunc = curMod->GetFunctionByName(name); + } + if (optFlags & OPT_INIT_FUNC) + { + char name[256]; + SERIALIZE_CSTRING(SaveVersionLevelScriptV1, name); + initFunc = curMod->GetFunctionByName(name); + } + + SERIALIZE(SaveVersionLevelScriptV1, origStackPtr, 0); + SERIALIZE(SaveVersionLevelScriptV1, argSize, 0); + SERIALIZE(SaveVersionLevelScriptV1, valueReg, 0); + + void* objReg = nullptr; + asITypeInfo* objRegType = nullptr; + if (optFlags & OPT_OBJ_REG) + { + // TODO + assert(0); + } + res = context->SetStateRegisters(c, callingSysFunc, initFunc, origStackPtr, argSize, valueReg, objReg, objRegType); + assert(res >= 0); + } + + // Finish + const s32 serializationResult = context->FinishDeserialization(); + if (serializationResult != asSUCCESS) + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Failed to load saved game, script code no longer matches."); + assert(0); + } + } + else + { + TFE_System::logWrite(LOG_ERROR, "Force Script", "Failed to load saved game, script serialization failed to start."); + assert(0); + } + } + } + + // Temp: Not necessary, this is here to catch memory issues. + nameStr.clear(); + nameSpaceStr.clear(); + modDef.clear(); + } + + void update(f32 dt) + { + if (dt == 0.0f) { dt = (f32)TFE_System::getDeltaTime(); } const s32 count = (s32)s_scriptThreads.size(); ScriptThread* thread = s_scriptThreads.data(); for (s32 i = 0; i < count; i++) @@ -119,7 +665,7 @@ namespace TFE_ForceScript if (thread[i].delay == 0.0f) { asIScriptContext* context = thread[i].asContext; - s32 res = context->Execute(); + const s32 res = context->Execute(); if (res != asEXECUTION_SUSPENDED) { // Finally done! @@ -134,14 +680,13 @@ namespace TFE_ForceScript void stopAllFunc() { - const s32 count = (s32)s_scriptThreads.size(); - ScriptThread* thread = s_scriptThreads.data(); if (s_engine) { + const s32 count = (s32)s_scriptThreads.size(); + ScriptThread* thread = s_scriptThreads.data(); + for (s32 i = 0; i < count; i++) { - // Allow for holes to keep IDs consistent. - // Fill holes with new threads. if (thread[i].asContext == nullptr) { continue; } asIScriptContext* context = thread[i].asContext; @@ -158,15 +703,46 @@ namespace TFE_ForceScript { return s_engine; } - - ModuleHandle createModule(const char* moduleName, const char* filePath) + + ModuleHandle getModule(const char* moduleName) + { + return s_engine->GetModule(moduleName); + } + + void deleteModule(const char* moduleName) + { + asIScriptModule* mod = s_engine->GetModule(moduleName); + if (!mod) { return; } + mod->Discard(); + + s32 count = (s32)s_modules.size(); + for (s32 i = 0; i < count; i++) + { + if (mod == s_modules[i].mod) + { + s_modules.erase(s_modules.begin() + i); + break; + } + } + } + + ModuleHandle createModule(const char* moduleName, const char* filePath, bool allowReadFromArchive, u32 accessMask) { CScriptBuilder builder; + builder.SetReadMode(!allowReadFromArchive); // true to read from disk, false to read from the TFE filesystem. s32 res = builder.StartNewModule(s_engine, moduleName); if (res < 0) { return nullptr; } + // Set Access Mask + asIScriptModule* mod = builder.GetModule(); + if (!mod) + { + return nullptr; + } + mod->SetAccessMask(accessMask); + // Load the source and build. res = builder.AddSectionFromFile(filePath); if (res < 0) { @@ -177,10 +753,15 @@ namespace TFE_ForceScript { return nullptr; } - return builder.GetModule(); + mod = builder.GetModule(); + if (mod) + { + s_modules.push_back({ moduleName, filePath, allowReadFromArchive, accessMask, mod }); + } + return mod; } - ModuleHandle createModule(const char* moduleName, const char* sectionName, const char* srcCode) + ModuleHandle createModule(const char* moduleName, const char* sectionName, const char* srcCode, u32 accessMask) { CScriptBuilder builder; s32 res = builder.StartNewModule(s_engine, moduleName); @@ -188,6 +769,14 @@ namespace TFE_ForceScript { return nullptr; } + // Set Access Mask + asIScriptModule* mod = builder.GetModule(); + if (!mod) + { + return nullptr; + } + mod->SetAccessMask(accessMask); + // Add the source and build. res = builder.AddSectionFromMemory(sectionName, srcCode); if (res < 0) { @@ -201,7 +790,7 @@ namespace TFE_ForceScript return builder.GetModule(); } - FunctionHandle findScriptFunc(ModuleHandle modHandle, const char* funcName) + FunctionHandle findScriptFuncByDecl(ModuleHandle modHandle, const char* funcDecl) { if (!modHandle) { return nullptr; } @@ -215,19 +804,76 @@ namespace TFE_ForceScript return nullptr; } - asIScriptFunction* func = mod->GetFunctionByDecl(funcName); + asIScriptFunction* func = mod->GetFunctionByDecl(funcDecl); if (!func) { // The function couldn't be found. Instruct the script writer // to include the expected function in the script. - TFE_System::logWrite(LOG_ERROR, "Script", "The script must have the function '%s'. Please add it and try again.\n", funcName); + TFE_System::logWrite(LOG_ERROR, "Script", "The script must have the function with declaration '%s'. Please add it and try again.\n", funcDecl); return nullptr; } return func; } - + + FunctionHandle findScriptFuncByName(ModuleHandle modHandle, const char* funcName) + { + if (!modHandle) { return nullptr; } + + // Find the function that is to be called. + asIScriptModule* mod = (asIScriptModule*)modHandle; + if (!mod) + { + // The function couldn't be found. Instruct the script writer + // to include the expected function in the script. + TFE_System::logWrite(LOG_ERROR, "Script", "Cannot find module '%s'.\n", mod->GetName()); + return nullptr; + } + + asIScriptFunction* func = mod->GetFunctionByName(funcName); + if (!func) + { + // The function couldn't be found. Instruct the script writer + // to include the expected function in the script. + TFE_System::logWrite(LOG_ERROR, "Script", "The script must have the function with name '%s'. Please add it and try again.\n", funcName); + return nullptr; + } + return func; + } + + FunctionHandle findScriptFuncByNameNoCase(ModuleHandle modHandle, const char* funcName) + { + if (!modHandle) { return nullptr; } + + // Find the function that is to be called. + asIScriptModule* mod = (asIScriptModule*)modHandle; + if (!mod) + { + // The function couldn't be found. Instruct the script writer + // to include the expected function in the script. + TFE_System::logWrite(LOG_ERROR, "Script", "Cannot find module '%s'.\n", mod->GetName()); + return nullptr; + } + + // Manually search. + u32 funcCount = mod->GetFunctionCount(); + for (u32 f = 0; f < funcCount; f++) + { + asIScriptFunction* func = mod->GetFunctionByIndex(f); + const char* name = func->GetName(); + if (strcasecmp(name, funcName) == 0) + { + return func; + } + } + + // The function couldn't be found. Instruct the script writer + // to include the expected function in the script. + TFE_System::logWrite(LOG_ERROR, "Script", "The script must have the function with name '%s'. Please add it and try again.\n", funcName); + return nullptr; + } + // Add a script function to be executed during the update. - s32 execFunc(FunctionHandle funcHandle) + s32 execFunc(FunctionHandle funcHandle, s32 argCount, const ScriptArg* arg) { s32 id = -1; if (!funcHandle) { return id; } @@ -247,6 +893,53 @@ namespace TFE_ForceScript s_engine->ReturnContext(context); return id; } + // Set arguments. + argCount = std::min(argCount, (s32)func->GetParamCount()); + for (s32 i = 0; i < argCount; i++) + { + switch (arg[i].type) + { + case ARG_S32: + { + context->SetArgDWord(i, *((asDWORD*)&arg[i].iValue)); + } break; + case ARG_U32: + { + context->SetArgDWord(i, arg[i].uValue); + } break; + case ARG_F32: + { + context->SetArgFloat(i, arg[i].fValue); + } break; + case ARG_BOOL: + { + context->SetArgByte(i, arg[i].bValue ? 1 : 0); + } break; + case ARG_OBJECT: + { + context->SetArgObject(i, arg[i].objPtr); + } break; + case ARG_STRING: + { + context->SetArgObject(i, (void*)&arg[i].stdStr); + } break; + case ARG_FLOAT2: + { + float2 f2(arg[i].float2Value.x, arg[i].float2Value.z); + context->SetArgObject(i, (void*)&f2); + } break; + case ARG_FLOAT3: + { + float3 f3(arg[i].float3Value.x, arg[i].float3Value.y, arg[i].float3Value.z); + context->SetArgObject(i, (void*)&f3); + } break; + case ARG_FLOAT4: + { + float4 f4(arg[i].float4Value.x, arg[i].float4Value.y, arg[i].float4Value.z, arg[i].float4Value.w); + context->SetArgObject(i, (void*)&f4); + } break; + }; + } // Get the new thread ID and prepare it. // The function will be executed during the next update. if (!s_freeThreads.empty()) @@ -261,6 +954,8 @@ namespace TFE_ForceScript } s_scriptThreads[id].asContext = context; s_scriptThreads[id].delay = 0.0f; + s_scriptThreads[id].name = func->GetModuleName(); + s_scriptThreads[id].funcName = func->GetName(); context->SetUserData((void*)((intptr_t)id), ThreadId); return id; @@ -276,7 +971,7 @@ namespace TFE_ForceScript void destroy() {} void overrideCallback(ScriptMessageCallback callback) {} // Run any active script functions. - void update() {} + void update(f32 dt) {} // Stop all running script functions. void stopAllFunc() {} @@ -287,7 +982,8 @@ namespace TFE_ForceScript ModuleHandle createModule(const char* moduleName, const char* sectionName, const char* srcCode) { return nullptr; } ModuleHandle createModule(const char* moduleName, const char* filePath) { return nullptr; } // Find a specific script function in a module. - FunctionHandle findScriptFunc(ModuleHandle modHandle, const char* funcName) { return nullptr; } + FunctionHandle findScriptFuncByDecl(ModuleHandle modHandle, const char* funcDecl) { return nullptr; } + FunctionHandle findScriptFuncByName(ModuleHandle modHandle, const char* funcName) { return nullptr; } // Types s32 getObjectTypeId(FS_BuiltInType type) { return 0; } @@ -297,7 +993,7 @@ namespace TFE_ForceScript // --------------------------------------- // Returns -1 on failure or id on success. // Note id is only valid as long as the function is running. - s32 execFunc(FunctionHandle funcHandle) { return -1; } + s32 execFunc(FunctionHandle funcHandle, s32 argCount, const ScriptArg* arg) { return -1; } // Resume a suspended script function given by id. void resume(s32 id) { return; } } diff --git a/TheForceEngine/TFE_ForceScript/forceScript.h b/TheForceEngine/TFE_ForceScript/forceScript.h index 2b989cc8b..34a2bbfd3 100644 --- a/TheForceEngine/TFE_ForceScript/forceScript.h +++ b/TheForceEngine/TFE_ForceScript/forceScript.h @@ -5,16 +5,59 @@ ////////////////////////////////////////////////////////////////////// #include #include +#include +#include namespace TFE_ForceScript { + enum ScriptConst + { + TFE_MAX_SCRIPT_ARG = 16, + }; + enum FS_BuiltInType { FSTYPE_STRING = 0, + FSTYPE_ARRAY, FSTYPE_FLOAT2, + FSTYPE_FLOAT3, + FSTYPE_FLOAT4, + FSTYPE_FLOAT2x2, + FSTYPE_FLOAT3x3, + FSTYPE_FLOAT4x4, FSTYPE_COUNT }; + enum ScriptArgType + { + ARG_S32 = 0, + ARG_U32, + ARG_F32, + ARG_BOOL, + ARG_OBJECT, + ARG_STRING, + ARG_FLOAT2, + ARG_FLOAT3, + ARG_FLOAT4, + }; + + struct ScriptArg + { + ScriptArgType type; + union + { + s32 iValue; + u32 uValue; + f32 fValue; + bool bValue; + void* objPtr; + Vec2f float2Value; + Vec3f float3Value; + Vec4f float4Value; + }; + std::string stdStr; + }; + // Opaque Handles. typedef void* ModuleHandle; typedef void* FunctionHandle; @@ -23,9 +66,10 @@ namespace TFE_ForceScript // Initialize and destroy script system. void init(); void destroy(); + void stopAllFunc(); void overrideCallback(ScriptMessageCallback callback = nullptr); // Run any active script functions. - void update(); + void update(f32 dt = 0.0f); // Stop all running script functions. void stopAllFunc(); @@ -33,10 +77,15 @@ namespace TFE_ForceScript void* getEngine(); // Compile module. - ModuleHandle createModule(const char* moduleName, const char* sectionName, const char* srcCode); - ModuleHandle createModule(const char* moduleName, const char* filePath); + ModuleHandle getModule(const char* moduleName); + ModuleHandle createModule(const char* moduleName, const char* sectionName, const char* srcCode, u32 accessMask); + ModuleHandle createModule(const char* moduleName, const char* filePath, bool allowReadFromArchive, u32 accessMask); + void deleteModule(const char* moduleName); // Find a specific script function in a module. - FunctionHandle findScriptFunc(ModuleHandle modHandle, const char* funcName); + FunctionHandle findScriptFuncByDecl(ModuleHandle modHandle, const char* funcDecl); + FunctionHandle findScriptFuncByName(ModuleHandle modHandle, const char* funcName); + // Normally function names are case-sensitive, this function ignores case. + FunctionHandle findScriptFuncByNameNoCase(ModuleHandle modHandle, const char* funcName); // Types s32 getObjectTypeId(FS_BuiltInType type); @@ -46,7 +95,43 @@ namespace TFE_ForceScript // --------------------------------------- // Returns -1 on failure or id on success. // Note id is only valid as long as the function is running. - s32 execFunc(FunctionHandle funcHandle); + s32 execFunc(FunctionHandle funcHandle, s32 argCount = 0, const ScriptArg* arg = nullptr); // Resume a suspended script function given by id. void resume(s32 id); + + void serialize(Stream* stream); + + /////////////////////////////////////////////////// + // Helpers for handle variable number of arguments + // of different types for calling script functions. + /////////////////////////////////////////////////// + inline ScriptArg scriptArg(s32 value) { ScriptArg arg; arg.type = ARG_S32; arg.iValue = value; return arg; } + inline ScriptArg scriptArg(u32 value) { ScriptArg arg; arg.type = ARG_U32; arg.uValue = value; return arg; } + inline ScriptArg scriptArg(f32 value) { ScriptArg arg; arg.type = ARG_F32; arg.fValue = value; return arg; } + inline ScriptArg scriptArg(bool value) { ScriptArg arg; arg.type = ARG_BOOL; arg.bValue = value; return arg; } + inline ScriptArg scriptArg(const std::string& value) { ScriptArg arg; arg.type = ARG_STRING; arg.stdStr = value; return arg; } + inline ScriptArg scriptArg(const char* value) { ScriptArg arg; arg.type = ARG_STRING; arg.stdStr = value; return arg; } + inline ScriptArg scriptArg(Vec2f value) { ScriptArg arg; arg.type = ARG_FLOAT2; arg.float2Value = value; return arg; } + inline ScriptArg scriptArg(Vec3f value) { ScriptArg arg; arg.type = ARG_FLOAT3; arg.float3Value = value; return arg; } + inline ScriptArg scriptArg(Vec4f value) { ScriptArg arg; arg.type = ARG_FLOAT4; arg.float4Value = value; return arg; } + inline ScriptArg scriptArg(void* value) { ScriptArg arg; arg.type = ARG_OBJECT; arg.objPtr = value; return arg; } + + template + inline void scriptArgIterator(s32& index, ScriptArg* outArg, T v) + { + if (index < TFE_MAX_SCRIPT_ARG) + { + outArg[index++] = scriptArg(v); + } + } + + template + inline void scriptArgIterator(s32& index, ScriptArg* outArg, T first, Args... args) + { + if (index < TFE_MAX_SCRIPT_ARG) + { + outArg[index++] = scriptArg(first); + } + scriptArgIterator(index, outArg, args...); + } } // TFE_ForceScript \ No newline at end of file diff --git a/TheForceEngine/TFE_ForceScript/scriptAPI.h b/TheForceEngine/TFE_ForceScript/scriptAPI.h new file mode 100644 index 000000000..9b64ff862 --- /dev/null +++ b/TheForceEngine/TFE_ForceScript/scriptAPI.h @@ -0,0 +1,84 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// The Force Engine Editor +// A system built to view and edit Dark Forces data files. +// The viewing aspect needs to be put in place at the beginning +// in order to properly test elements in isolation without having +// to "play" the game as intended. +////////////////////////////////////////////////////////////////////// +#include +#include + +class asIScriptEngine; + +enum ScriptAPI +{ + API_SHARED = 1, + API_LEVEL_EDITOR = 2, + API_SYSTEM_UI = 4, + API_GAME = 8, + API_UNKNOWN = 65536, +}; + +extern const char* s_enumType; +extern const char* s_typeName; +extern const char* s_objVar; + +//Helpers +#define xstr(s) str(s) +#define str(s) #s + +#define ScriptClassBegin(typeName, typeInst, api) \ +s32 res = 0; \ +asIScriptEngine* engine = (asIScriptEngine*)TFE_ForceScript::getEngine(); \ +ScriptSetAPI(api); \ +ScriptObjectType(typeName); \ +ScriptObjectVariable(typeInst) + +#define ScriptClassEnd() ScriptClass(std::string(s_typeName + std::string(" ") + s_objVar).c_str()); return res >= 0 + +#define ScriptSetAPI(api) engine->SetDefaultAccessMask(api); +#define ScriptCurType std::remove_reference::type +#define ScriptEnumRegister(type) s_enumType = type; res = engine->RegisterEnum(type); assert(res >= 0) +#define ScriptEnum(name, value) res = engine->RegisterEnumValue(s_enumType, name, value); assert(res >= 0) +#define ScriptEnumStr(value) res = engine->RegisterEnumValue(s_enumType, xstr(value), value); assert(res >= 0) + +// objType in C++ +// objName in script +// function declaration in script +// function name +// Example: ScriptObjMethod(LS_Level, "Level", "string getSlot()", getSlot); +// +#define ScriptObjMethod(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, decl, asMETHOD(ScriptCurType, funcName), asCALL_THISCALL); assert(res >= 0); +#define ScriptObjFunc(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, decl, asFUNCTION(funcName), asCALL_CDECL_OBJLAST); assert(res >= 0); + +#define ScriptGenericMethod(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, decl, asFUNCTION(funcName), asCALL_GENERIC); assert(res >= 0) + +#define ScriptPropertyGet(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" const property")).c_str(), asMETHOD(ScriptCurType, funcName), asCALL_THISCALL); assert(r >= 0); +#define ScriptPropertySet(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" property")).c_str(), asMETHOD(ScriptCurType, funcName), asCALL_THISCALL); assert(r >= 0); + +#define ScriptPropertyGetFunc(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" const property")).c_str(), asFUNCTION(funcName), asCALL_CDECL_OBJLAST); assert(res >= 0); +#define ScriptPropertySetFunc(decl, funcName) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" property")).c_str(), asFUNCTION(funcName), asCALL_CDECL_OBJLAST); assert(res >= 0); + +#define ScriptLambdaPropertyGet(decl, ret, lambda) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" const property")).c_str(), asFUNCTIONPR([]() lambda, (), ret), asCALL_CDECL_OBJLAST); assert(res >= 0) +#define ScriptLambdaPropertySet(decl, args, lambda) res = engine->RegisterObjectMethod(s_typeName, std::string(decl + std::string(" property")).c_str(), asFUNCTIONPR([]args lambda, args, void), asCALL_CDECL_OBJLAST); assert(res >= 0) + +#define ScriptMemberVariable(decl, var) res = engine->RegisterObjectProperty(s_typeName, decl, asOFFSET(ScriptCurType, var)); assert(res >= 0) + +// objName in script +// function declaration in script +// args = named arguments, example: (std::string& name, f32 value) +// returnType = return type, example: void +// lambda = {funcBody;} +// Example, setup in the class "Level" with variable "level": +// ScriptLambdaMethod("void setName(const string &in)", (std::string& name), void, { s_level.name = name; }); +//