diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index 7fa0d1ba0..b10a91473 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -952,6 +952,7 @@ DOPPELGANGER_ORIGIN CORPSE WRAITH_TRAP WATERFALL_EMITTER +FIREFLY_EMITTER MESHSWAP1 MESHSWAP2 MESHSWAP3 @@ -1132,6 +1133,7 @@ AIR_BAR_TEXTURE DASH_BAR_TEXTURE SFX_BAR_TEXTURE WATERFALL_SPRITES +FIREFLY_SPRITES CROSSHAIR_GRAPHICS SPEEDOMETER_GRAPHICS CUSTOM_BAR_GRAPHICS @@ -1410,6 +1412,7 @@ AIR_BAR_TEXTURE DASH_BAR_TEXTURE SFX_BAR_TEXTURE WATERFALL_SPRITES +FIREFLY_SPRITES CROSSHAIR_GRAPHICS SPEEDOMETER_GRAPHICS CUSTOM_BAR_GRAPHICS diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index 42bfb08df..22ab28749 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -27,6 +27,7 @@ #include "Objects/TR4/Entity/tr4_beetle_swarm.h" #include "Objects/Utils/object_helper.h" #include "Specific/level.h" +#include "Objects/Effects/Fireflies.h" using namespace TEN::Effects::Hair; using namespace TEN::Entities; @@ -200,6 +201,7 @@ void InitializeSpecialEffects() TEN::Entities::TR4::ClearBeetleSwarm(); TEN::Entities::Creatures::TR3::ClearFishSwarm(); + TEN::Effects::Fireflies::ClearFireflySwarm(); } void CustomObjects() diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 412c974dd..d5171cbae 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -59,6 +59,7 @@ #include "Specific/Input/Input.h" #include "Specific/level.h" #include "Specific/winmain.h" +#include "Objects/Effects/Fireflies.h" using namespace std::chrono; using namespace TEN::Effects; @@ -89,6 +90,7 @@ using namespace TEN::Math; using namespace TEN::Renderer; using namespace TEN::Entities::Creatures::TR3; using namespace TEN::Entities::Effects; +using namespace TEN::Effects::Fireflies; constexpr auto DEATH_NO_INPUT_TIMEOUT = 10 * FPS; constexpr auto DEATH_INPUT_TIMEOUT = 3 * FPS; @@ -211,6 +213,7 @@ GameStatus GamePhase(bool insideMenu) UpdateLocusts(); UpdateUnderwaterBloodParticles(); UpdateFishSwarm(); + UpdateFireflySwarm(); UpdateGlobalLensFlare(); // Update HUD. diff --git a/TombEngine/Game/control/flipeffect.cpp b/TombEngine/Game/control/flipeffect.cpp index e1b3af5d4..c473386d0 100644 --- a/TombEngine/Game/control/flipeffect.cpp +++ b/TombEngine/Game/control/flipeffect.cpp @@ -17,6 +17,7 @@ #include "Game/Setup.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Objects/Effects/Fireflies.h" #include "Objects/Generic/puzzles_keys.h" #include "Objects/TR3/Entity/FishSwarm.h" #include "Objects/TR4/Entity/tr4_beetle_swarm.h" @@ -29,6 +30,7 @@ using namespace TEN::Effects::Environment; using namespace TEN::Effects::Footprint; using namespace TEN::Effects::Hair; using namespace TEN::Entities::Creatures::TR3; +using namespace TEN::Effects::Fireflies; int FlipEffect; @@ -90,6 +92,7 @@ void ClearSwarmEnemies(ItemInfo* item) ClearBeetleSwarm(); ClearLocusts(); ClearFishSwarm(); + ClearFireflySwarm(); } void FlashOrange(ItemInfo* item) diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index b2ae7ec0c..8f3f1394c 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -10,6 +10,7 @@ #include "Game/control/flipeffect.h" #include "Game/control/lot.h" #include "Game/control/volume.h" +#include "Objects/Effects/Fireflies.h" #include "Game/effects/item_fx.h" #include "Game/effects/effects.h" #include "Game/effects/weather.h" @@ -43,8 +44,9 @@ using namespace flatbuffers; using namespace TEN::Collision::Floordata; using namespace TEN::Control::Volumes; -using namespace TEN::Effects::Items; using namespace TEN::Effects::Environment; +using namespace TEN::Effects::Fireflies; +using namespace TEN::Effects::Items; using namespace TEN::Entities::Creatures::TR3; using namespace TEN::Entities::Generic; using namespace TEN::Entities::Switches; @@ -924,6 +926,38 @@ const std::vector SaveGame::Build() } auto fishSwarmOffset = fbb.CreateVector(fishSwarm); + std::vector> fireflySwarm; + for (const auto& firefly : FireflySwarm) + { + Save::FireflyDataBuilder fireflySave{ fbb }; + fireflySave.add_sprite_index(firefly.SpriteSeqID); + fireflySave.add_sprite_id(firefly.SpriteID); + fireflySave.add_blend_mode((int)firefly.blendMode); + fireflySave.add_scalar(firefly.scalar); + fireflySave.add_position(&FromVector3(firefly.Position)); + fireflySave.add_room_number(firefly.RoomNumber); + fireflySave.add_position_target(&FromVector3(firefly.PositionTarget)); + fireflySave.add_orientation(&FromEulerAngles(firefly.Orientation)); + fireflySave.add_velocity(firefly.Velocity); + fireflySave.add_target_item_number((firefly.TargetItemPtr == nullptr) ? -1 : firefly.TargetItemPtr->Index); + fireflySave.add_z_vel(firefly.zVel); + fireflySave.add_life(firefly.Life); + fireflySave.add_number(firefly.Number); + fireflySave.add_d_r(firefly.rB); + fireflySave.add_d_g(firefly.gB); + fireflySave.add_d_b(firefly.bB); + fireflySave.add_r(firefly.r); + fireflySave.add_g(firefly.g); + fireflySave.add_b(firefly.b); + fireflySave.add_on(firefly.on); + fireflySave.add_size(firefly.size); + fireflySave.add_rot_Ang(firefly.rotAng); + + auto fireflySaveOffset = fireflySave.Finish(); + fireflySwarm.push_back(fireflySaveOffset); + } + auto fireflySwarmOffset = fbb.CreateVector(fireflySwarm); + // TODO: In future, we should save only active FX, not whole array. // This may come together with Monty's branch merge -- Lwmte, 10.07.22 @@ -1545,6 +1579,7 @@ const std::vector SaveGame::Build() sgb.add_next_item_active(NextItemActive); sgb.add_items(serializedItemsOffset); sgb.add_fish_swarm(fishSwarmOffset); + sgb.add_firefly_swarm(fireflySwarmOffset); sgb.add_fxinfos(serializedEffectsOffset); sgb.add_next_fx_free(NextFxFree); sgb.add_next_fx_active(NextFxActive); @@ -2289,6 +2324,38 @@ static void ParseEffects(const Save::SaveGame* s) FishSwarm.push_back(fish); } + // Load firefly swarm. + for (int i = 0; i < s->firefly_swarm()->size(); i++) + { + const auto& fireflySave = s->firefly_swarm()->Get(i); + auto firefly = FireflyData{}; + + firefly.SpriteSeqID = fireflySave->sprite_index(); + firefly.SpriteID = fireflySave->sprite_id(); + firefly.blendMode = (BlendMode)fireflySave->blend_mode(); + firefly.scalar = fireflySave->scalar(); + firefly.Position = ToVector3(fireflySave->position()); + firefly.RoomNumber = fireflySave->room_number(); + firefly.PositionTarget = ToVector3(fireflySave->position_target()); + firefly.Orientation = ToEulerAngles(fireflySave->orientation()); + firefly.Velocity = fireflySave->velocity(); + firefly.TargetItemPtr = (fireflySave->target_item_number() == -1) ? nullptr : &g_Level.Items[fireflySave->target_item_number()]; + firefly.zVel = fireflySave->z_vel(); + firefly.Life = fireflySave->life(); + firefly.Number = fireflySave->number(); + firefly.rB = fireflySave->d_r(); + firefly.gB = fireflySave->d_g(); + firefly.bB = fireflySave->d_b(); + firefly.r = fireflySave->r(); + firefly.g = fireflySave->g(); + firefly.b = fireflySave->b(); + firefly.on = fireflySave->on(); + firefly.size = fireflySave->size(); + firefly.rotAng = fireflySave->rot_Ang(); + + FireflySwarm.push_back(firefly); + } + // Load particles. for (int i = 0; i < s->particles()->size(); i++) { diff --git a/TombEngine/Objects/Effects/Fireflies.cpp b/TombEngine/Objects/Effects/Fireflies.cpp new file mode 100644 index 000000000..0510283f9 --- /dev/null +++ b/TombEngine/Objects/Effects/Fireflies.cpp @@ -0,0 +1,445 @@ +#include "framework.h" +#include "Objects/Effects/Fireflies.h" + +#include "Game/collision/collide_item.h" +#include "Game/collision/collide_room.h" +#include "Game/collision/Point.h" +#include "Game/control/box.h" +#include "Game/control/flipeffect.h" +#include "Game/effects/effects.h" +#include "Game/effects/Streamer.h" +#include "Game/effects/tomb4fx.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/Lara/lara_helpers.h" +#include "Game/misc.h" +#include "Game/Setup.h" +#include "Math/Math.h" +#include "Renderer/Renderer.h" +#include "Specific/clock.h" +#include "Specific/level.h" + +using namespace TEN::Collision::Point; +using namespace TEN::Math; +using namespace TEN::Renderer; +using namespace TEN::Effects::Streamer; + +namespace TEN::Effects::Fireflies +{ + constexpr auto FIREFLY_COHESION_FACTOR = 600.1f; + constexpr auto FIREFLY_SPACING_FACTOR = 600.0f; + constexpr auto FIREFLY_CATCH_UP_FACTOR = 0.2f; + constexpr auto FIREFLY_TARGET_DISTANCE_MAX = SQUARE(BLOCK(1.0f)); + constexpr auto FIREFLY_BASE_SEPARATION_DISTANCE = 10.0f; + constexpr auto FIREFLY_FLEE_DISTANCE = BLOCK(0.7); + constexpr auto MAX_FIREFLIES = 92; + constexpr auto DEFAULT_FIREFLY_COUNT = 20; + constexpr auto FIREFLY_RISE_UP_FACTOR = 200; + constexpr auto MAX_AREA_RANGE = 8; + constexpr auto LIGHT_ALPHA_CYCLE_DURATION = 120.0f; + + std::vector FireflySwarm = {}; + std::unordered_map nextFireflyNumberMap; // Numbering the Fireflies for Streamer effect. + + void InitializeFireflySwarm(short itemNumber) + { + auto& item = g_Level.Items[itemNumber]; + + item.Animation.Velocity.z = Random::GenerateFloat(32.0f, 160.0f); + + item.HitPoints = DEFAULT_FIREFLY_COUNT; + item.ItemFlags[FirefliesItemFlags::TargetItemPtr] = item.Index; + item.ItemFlags[FirefliesItemFlags::Light] = 1; // 0 = Turn off light effect, 1 = turn on light (DEFAULT). + + item.ItemFlags[FirefliesItemFlags::TriggerFlags] = std::clamp((int)item.TriggerFlags, -MAX_AREA_RANGE, MAX_AREA_RANGE); + + item.ItemFlags[FirefliesItemFlags::Spawncounter] = 0; + item.ItemFlags[FirefliesItemFlags::RemoveFliesEffect] = 0; + + // Firefly numbers that has the light. + item.ItemFlags[FirefliesItemFlags::LightIndex1] = NO_VALUE; + item.ItemFlags[FirefliesItemFlags::LightIndex2] = NO_VALUE; + } + + void SpawnFireflySwarm(ItemInfo& item, int triggerFlags) + { + constexpr auto VEL_MAX = 34.0f; + constexpr auto VEL_MIN = 6.0f; + + // Create new firefly. + auto& firefly = GetNewEffect(FireflySwarm, MAX_FIREFLIES); + + unsigned char r = 255; + unsigned char g = 255; + unsigned char b = 255; + + if (triggerFlags >= 0) + { + float brightnessShift = Random::GenerateFloat(-0.1f, 0.1f); + r = std::clamp(item.Model.Color.x / 2.0f + brightnessShift, 0.0f, 1.0f) * UCHAR_MAX; + g = std::clamp(item.Model.Color.y / 2.0f + brightnessShift, 0.0f, 1.0f) * UCHAR_MAX; + b = std::clamp(item.Model.Color.z / 2.0f + brightnessShift, 0.0f, 1.0f) * UCHAR_MAX; + + firefly.SpriteSeqID = ID_FIREFLY_SPRITES; + firefly.SpriteID = 0; + firefly.blendMode = BlendMode::Additive; + firefly.scalar = 3.0f; + firefly.size = 1.0f; + } + else + { + firefly.SpriteSeqID = ID_FIREFLY_SPRITES; + firefly.SpriteID = 1; + firefly.blendMode = BlendMode::Subtractive; + firefly.scalar = 1.2f; + firefly.size = 1.2f; + } + + firefly.r = firefly.rB = r; + firefly.g = firefly.gB = g; + firefly.b = firefly.bB = b; + + firefly.rotAng = ANGLE(0.0f); + + if (item.TriggerFlags > 8) + firefly.rotAng = ANGLE(90.0f); + + firefly.on = true; + + firefly.Position = item.Pose.Position.ToVector3(); + firefly.RoomNumber = item.RoomNumber; + firefly.Orientation = item.Pose.Orientation; + firefly.Velocity = Random::GenerateFloat(VEL_MIN, VEL_MAX); + firefly.zVel = 0.3f; + + firefly.Life = Random::GenerateInt(1, 400); + firefly.TargetItemPtr = &g_Level.Items[item.ItemFlags[FirefliesItemFlags::TargetItemPtr]]; + + int& nextFireflyNumber = nextFireflyNumberMap[item.Index]; + firefly.Number = nextFireflyNumber++; + } + + void ControlFireflySwarm(short itemNumber) + { + auto& item = g_Level.Items[itemNumber]; + + if (!TriggerActive(&item)) + { + // Remove all fireflies associated with this item. + RemoveFireflies(item); + + // Reset ItemFlags. + if (item.HitPoints == NOT_TARGETABLE) + item.HitPoints = item.ItemFlags[FirefliesItemFlags::Spawncounter]; + + item.ItemFlags[FirefliesItemFlags::Spawncounter] = 0; + item.ItemFlags[FirefliesItemFlags::LightIndex1] = NO_VALUE; + item.ItemFlags[FirefliesItemFlags::LightIndex2] = NO_VALUE; + + return; + } + + constexpr auto ALPHA_PAUSE_DURATION = 2.0f; + static float frameCounter = 0.0f; + + // Increment the counter variable in each frame. + frameCounter += 1.0f; + + if (item.HitPoints != NOT_TARGETABLE) + { + int fireflyCount = item.HitPoints - item.ItemFlags[FirefliesItemFlags::Spawncounter]; + + if (fireflyCount < 0) + { + int firefliesToTurnOff = -fireflyCount; + for (auto& firefly : FireflySwarm) + { + if (firefly.TargetItemPtr == &item && firefly.Life > 0.0f) + { + firefly.Life = 0.0f; + firefly.on = false; + firefliesToTurnOff--; + + if (firefliesToTurnOff == 0) + break; + } + } + } + else if (fireflyCount > 0) + { + for (int i = 0; i < fireflyCount; i++) + { + SpawnFireflySwarm(item, item.TriggerFlags); + } + } + + item.ItemFlags[FirefliesItemFlags::Spawncounter] = item.HitPoints; + item.HitPoints = NOT_TARGETABLE; + } + + // Update color values for blinking effect. + float alphaFactor; + + for (auto& firefly : FireflySwarm) + { + auto targetItem = firefly.TargetItemPtr; + + if (targetItem == &item) + { + // Choose one of the available firefly number that has the light. + if (targetItem->ItemFlags[FirefliesItemFlags::LightIndex1] == NO_VALUE && targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] >= 0) + { + targetItem->ItemFlags[FirefliesItemFlags::LightIndex1] = Random::GenerateInt(0, targetItem->TriggerFlags); + } + // Two lights max for each cluster. + if (targetItem->ItemFlags[FirefliesItemFlags::LightIndex2] == NO_VALUE && targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] >= 0) + { + targetItem->ItemFlags[FirefliesItemFlags::LightIndex2] = Random::GenerateInt(0, targetItem->TriggerFlags); + } + + auto posBase = firefly.Position; + auto rotMatrix = firefly.Orientation.ToRotationMatrix(); + auto pos = posBase + Vector3::Transform(Vector3(0, 0, 30), rotMatrix); + auto direction0 = Geometry::RotatePoint(posBase, EulerAngles::Identity); + short orient2D = firefly.Orientation.z; + + if (targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] >= 0) + { + StreamerEffect.Spawn(targetItem->Index, firefly.Number, pos, direction0, orient2D, + Vector4(firefly.r / (float)UCHAR_MAX, firefly.g / (float)UCHAR_MAX, firefly.b / (float)UCHAR_MAX, 1.0f), + Vector4::Zero, + 6.3f - (firefly.zVel / 12), ((firefly.Velocity / 8) + firefly.zVel * 3) / (float)UCHAR_MAX, 0.0f, -0.1f, 90.0f, StreamerFeatherMode::None, BlendMode::Additive); + } + else if (targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] < 0) + { + StreamerEffect.Spawn(targetItem->Index, firefly.Number, pos, direction0, orient2D, + Vector4((firefly.r / 2) / (float)UCHAR_MAX, (firefly.g / 2) / (float)UCHAR_MAX, (firefly.b / 2) / (float)UCHAR_MAX, 0.2f), + Vector4((firefly.r / 3) / (float)UCHAR_MAX, (firefly.g / 3) / (float)UCHAR_MAX, (firefly.b / 3) / (float)UCHAR_MAX, 0.2f), + 0.0f, 0.4f, 0.0f, 0.2f, 0.0f, StreamerFeatherMode::None, BlendMode::Subtractive); + } + + if ((targetItem->ItemFlags[FirefliesItemFlags::LightIndex1] == firefly.Number || targetItem->ItemFlags[FirefliesItemFlags::LightIndex2] == firefly.Number) && + targetItem->ItemFlags[FirefliesItemFlags::Light] == 1) + { + float totalCycleDuration = 2 * (LIGHT_ALPHA_CYCLE_DURATION + ALPHA_PAUSE_DURATION); + float alphaTime = fmod(frameCounter, totalCycleDuration); + + if (alphaTime < ALPHA_PAUSE_DURATION) + { + alphaFactor = 1.0f; // Pause on Alpha 1. + } + else if (alphaTime < ALPHA_PAUSE_DURATION + LIGHT_ALPHA_CYCLE_DURATION) + { + alphaFactor = 1.0f - ((alphaTime - ALPHA_PAUSE_DURATION) / LIGHT_ALPHA_CYCLE_DURATION); + } + else if (alphaTime < 2 * ALPHA_PAUSE_DURATION + LIGHT_ALPHA_CYCLE_DURATION) + { + alphaFactor = 0.0f; // Pause on Alpha 0. + targetItem->ItemFlags[FirefliesItemFlags::LightIndex1] = NO_VALUE; + targetItem->ItemFlags[FirefliesItemFlags::LightIndex2] = NO_VALUE; + } + else + { + alphaFactor = (alphaTime - 2 * ALPHA_PAUSE_DURATION - LIGHT_ALPHA_CYCLE_DURATION) / LIGHT_ALPHA_CYCLE_DURATION; + } + + SpawnDynamicLight(firefly.Position.x, firefly.Position.y, firefly.Position.z, 3, + static_cast(std::clamp(firefly.r * alphaFactor, 0.0f, (float)firefly.r)), + static_cast(std::clamp(firefly.g * alphaFactor, 0.0f, (float)firefly.g)), + static_cast(std::clamp(firefly.b * alphaFactor, 0.0f, (float)firefly.b))); + } + } + } + } + + void UpdateFireflySwarm() + { + constexpr auto FLEE_VEL = 1.5f; + constexpr auto ALPHA_PAUSE_DURATION = 100.0f; + + static const auto SPHERE = BoundingSphere(Vector3::Zero, BLOCK(1 / 8.0f)); + + if (FireflySwarm.empty()) + return; + + const auto& playerItem = *LaraItem; + static float frameCounter = 0.0f; + + // Increment the counter variable in each frame. + frameCounter += 1.0f; + + for (auto& firefly : FireflySwarm) + { + if (firefly.Life <= 0.0f || !firefly.on) + continue; + + auto targetItem = firefly.TargetItemPtr; + + if (targetItem->ItemFlags[FirefliesItemFlags::RemoveFliesEffect]) + { + firefly.r = 0; + firefly.g = 0; + firefly.b = 0; + continue; + } + + firefly.StoreInterpolationData(); + + firefly.PositionTarget = Random::GeneratePointInSphere(SPHERE); + + int multiplierX = CLICK(targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] * 2); + int multiplierY = CLICK(targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] * 4); + int multiplierZ = CLICK(targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] * 2); + + auto spheroidAxis = Vector3(multiplierX, multiplierY, multiplierZ); + auto itemPos = Vector3i(targetItem->Pose.Position.x, targetItem->Pose.Position.y - FIREFLY_RISE_UP_FACTOR, targetItem->Pose.Position.z); + + // Calculate desired position based on target object and random offsets. + auto desiredPos = itemPos + Random::GeneratePointInSpheroid(firefly.PositionTarget, EulerAngles::Identity, spheroidAxis); + auto dir = desiredPos - firefly.Position; + + auto dirs = dir.ToVector3(); + dirs.Normalize(); + auto dirNorm = dirs; + + // Define cohesion factor to keep fireflies close together. + float distToTarget = dirs.Length(); + + float targetVel = (distToTarget * FIREFLY_COHESION_FACTOR) + Random::GenerateFloat(3.0f, 5.0f); + firefly.Velocity = std::min(targetVel, targetItem->Animation.Velocity.z - 21.0f); + + // If firefly is too far from target, increase velocity to catch up. + if (distToTarget > FIREFLY_TARGET_DISTANCE_MAX) + firefly.Velocity += FIREFLY_CATCH_UP_FACTOR; + + // Translate. + auto moveDir = firefly.Orientation.ToDirection(); + moveDir.Normalize(); + firefly.Position += (moveDir * firefly.Velocity) / 26.0f; + firefly.Position += (moveDir * FIREFLY_SPACING_FACTOR) / 26.0f; + + auto orientTo = Geometry::GetOrientToPoint(firefly.Position, desiredPos.ToVector3()); + firefly.Orientation.Lerp(orientTo, 0.1f); + + // Update color values for blinking effect. + float totalCycleDuration = 2 * (LIGHT_ALPHA_CYCLE_DURATION + ALPHA_PAUSE_DURATION); + float alphaTime = fmod(frameCounter + firefly.Life, totalCycleDuration); + float alphaFactor; + + if (alphaTime < ALPHA_PAUSE_DURATION) + { + alphaFactor = 1.0f; + } + else if (alphaTime < ALPHA_PAUSE_DURATION + LIGHT_ALPHA_CYCLE_DURATION) + { + alphaFactor = 1.0f - ((alphaTime - ALPHA_PAUSE_DURATION) / LIGHT_ALPHA_CYCLE_DURATION); + } + else if (alphaTime < 2 * ALPHA_PAUSE_DURATION + LIGHT_ALPHA_CYCLE_DURATION) + { + alphaFactor = 0.0f; + } + else + { + alphaFactor = (alphaTime - 2 * ALPHA_PAUSE_DURATION - LIGHT_ALPHA_CYCLE_DURATION) / LIGHT_ALPHA_CYCLE_DURATION; + } + + firefly.r = static_cast(firefly.rB * alphaFactor); + firefly.g = static_cast(firefly.gB * alphaFactor); + firefly.b = static_cast(firefly.bB * alphaFactor); + + for (const auto& otherFirefly : FireflySwarm) + { + if (&firefly == &otherFirefly) + continue; + + float distToOtherFirefly = Vector3i::Distance(firefly.Position, otherFirefly.Position); + float distToPlayer = Vector3i::Distance(firefly.Position, playerItem.Pose.Position); + + // If player is too close, flee. + if (distToPlayer < FIREFLY_FLEE_DISTANCE && playerItem.Animation.ActiveState != 2) + { + auto separationDir = firefly.Position - playerItem.Pose.Position.ToVector3(); + separationDir.Normalize(); + + // Reduce the Y component of the escape direction. + separationDir.y *= Random::GenerateFloat(0.0f, 0.4f); + + // Normalize the direction again to get the length of the vector. + separationDir.Normalize(); + + firefly.Position += separationDir * FLEE_VEL; + + auto orientTo = Geometry::GetOrientToPoint(firefly.Position, separationDir); + firefly.Orientation.Lerp(orientTo, 0.05f); + + firefly.Velocity -= std::min(FLEE_VEL, firefly.TargetItemPtr->Animation.Velocity.z - 1.0f); + + if (Random::TestProbability(1.0f / 700.0f) && + targetItem->ItemFlags[FirefliesItemFlags::Light] == 1 && + targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] >= 0) + { + if (firefly.zVel == 0.3f) + { + firefly.zVel = 50.0f; + } + } + + if (firefly.zVel > 50.0f) + firefly.zVel = 0.3f; + } + + if (distToOtherFirefly < FIREFLY_BASE_SEPARATION_DISTANCE) + { + auto separationDir = firefly.Position - otherFirefly.Position; + separationDir.Normalize(); + + firefly.Position += separationDir * (FIREFLY_BASE_SEPARATION_DISTANCE - distToOtherFirefly); + } + } + + auto pointColl = GetPointCollision(firefly.Position, firefly.RoomNumber); + + // Update firefly room number. + if (pointColl.GetRoomNumber() != firefly.RoomNumber && + pointColl.GetRoomNumber() != NO_VALUE) + { + firefly.RoomNumber = pointColl.GetRoomNumber(); + } + + if (targetItem->ItemFlags[FirefliesItemFlags::Light] == 1 && targetItem->ItemFlags[FirefliesItemFlags::TriggerFlags] >= 0) + { + if (Random::TestProbability(1.0f / (700.0f - (float)(targetItem->ItemFlags[FirefliesItemFlags::Spawncounter] * 2)))) + firefly.zVel = 100.0f; + + if (firefly.zVel > 1.0f) + firefly.zVel -= 2.0f; + if (firefly.zVel <= 1.0f) + firefly.zVel = 0.3f; + } + } + } + + void RemoveFireflies(ItemInfo& item) + { + FireflySwarm.erase(std::remove_if(FireflySwarm.begin(), FireflySwarm.end(), + [&item](FireflyData& firefly) + { + if (firefly.TargetItemPtr == &item) + { + firefly.Life = 0.0f; + firefly.on = false; + return true; + } + return false; + }), FireflySwarm.end()); + nextFireflyNumberMap.erase(item.Index); + } + + void ClearFireflySwarm() + { + FireflySwarm.clear(); + nextFireflyNumberMap.clear(); + } +} + + diff --git a/TombEngine/Objects/Effects/Fireflies.h b/TombEngine/Objects/Effects/Fireflies.h new file mode 100644 index 000000000..807869b81 --- /dev/null +++ b/TombEngine/Objects/Effects/Fireflies.h @@ -0,0 +1,76 @@ +#pragma once +#include "Game/items.h" +#include "Math/Math.h" + +using namespace TEN::Math; + +namespace TEN::Effects::Fireflies +{ + enum FirefliesItemFlags + { + TargetItemPtr, + Light, + TriggerFlags, + Spawncounter, + RemoveFliesEffect, + LightIndex1, + LightIndex2 + }; + + struct FireflyData + { + int SpriteSeqID = ID_DEFAULT_SPRITES; + int SpriteID = SPR_UNDERWATERDUST; + BlendMode blendMode; + unsigned int scalar; + + Vector3 Position = Vector3::Zero; + int RoomNumber = 0; + Vector3 PositionTarget = Vector3::Zero; + EulerAngles Orientation = EulerAngles::Identity; + float Velocity = 0.0f; + + ItemInfo* TargetItemPtr = nullptr; + + float zVel; + float Life = 0.0f; + int Number = 0; + + unsigned char rB; + unsigned char gB; + unsigned char bB; + unsigned char r; + unsigned char g; + unsigned char b; + + bool on; + float size; + short rotAng; + + int PrevX; + int PrevY; + int PrevZ; + byte PrevR; + byte PrevG; + byte PrevB; + + void StoreInterpolationData() + { + PrevX = Position.x; + PrevY = Position.y; + PrevZ = Position.z; + PrevR = r; + PrevG = g; + PrevB = b; + } + }; + + extern std::vector FireflySwarm; + + void InitializeFireflySwarm(short itemNumber); + void ControlFireflySwarm(short itemNumber); + void UpdateFireflySwarm(); + void ClearFireflySwarm(); + void SpawnFireflySwarm(ItemInfo& item, int triggerFlags); + void RemoveFireflies(ItemInfo& item); +} diff --git a/TombEngine/Objects/TR3/Object/corpse.cpp b/TombEngine/Objects/TR3/Object/corpse.cpp index 527f12f1c..3b3da8a3b 100644 --- a/TombEngine/Objects/TR3/Object/corpse.cpp +++ b/TombEngine/Objects/TR3/Object/corpse.cpp @@ -17,6 +17,7 @@ #include "Game/Lara/lara_helpers.h" #include "Game/Setup.h" #include "Math/Math.h" +#include "Objects/Effects/Fireflies.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Sound/sound.h" #include "Specific/level.h" @@ -25,9 +26,13 @@ using namespace TEN::Collision::Point; using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Splash; using namespace TEN::Math; +using namespace TEN::Effects::Fireflies; namespace TEN::Entities::TR3 { + constexpr auto FLY_EFFECT_MAX_WIDTH = -1; + constexpr auto FLY_AMOUNT = 16; + enum CorpseState { CORPSE_STATE_GROUNDED = 0, @@ -51,19 +56,25 @@ namespace TEN::Entities::TR3 if (item.TriggerFlags == 1) { - item.ItemFlags[1] = (int)CorpseFlag::Hang; + item.ItemFlags[7] = (int)CorpseFlag::Hang; item.Animation.AnimNumber = object.animIndex + CORPSE_ANIM_HANG; item.Animation.ActiveState = CORPSE_STATE_HANG; } else { - item.ItemFlags[1] = (int)CorpseFlag::Grounded; + item.ItemFlags[7] = (int)CorpseFlag::Grounded; item.Animation.AnimNumber = object.animIndex + CORPSE_ANIM_GROUNDED; item.Animation.ActiveState = CORPSE_STATE_GROUNDED; } + item.ItemFlags[FirefliesItemFlags::RemoveFliesEffect] = 0; + AddActiveItem(itemNumber); item.Status = ITEM_ACTIVE; + + item.ItemFlags[FirefliesItemFlags::TargetItemPtr] = item.Index; + item.ItemFlags[FirefliesItemFlags::TriggerFlags] = -1; + item.HitPoints = FLY_AMOUNT; } void ControlCorpse(short itemNumber) @@ -71,9 +82,11 @@ namespace TEN::Entities::TR3 auto& item = g_Level.Items[itemNumber]; const auto& object = Objects[item.ObjectNumber]; - if (item.ItemFlags[1] == (int)CorpseFlag::Fall) + if (item.ItemFlags[7] == (int)CorpseFlag::Fall) { bool isWater = TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item.RoomNumber); + bool isSwamp = TestEnvironment(RoomEnvFlags::ENV_FLAG_SWAMP, item.RoomNumber); + float verticalVelCoeff = isWater ? 81.0f : 1.0f; auto pointColl = GetPointCollision(item); @@ -94,31 +107,37 @@ namespace TEN::Entities::TR3 ItemNewRoom(itemNumber, pointColl.GetRoomNumber()); } - pointColl = GetPointCollision(item); + // Remove fly effect when in water. + if (isWater || isSwamp) + { + item.ItemFlags[FirefliesItemFlags::RemoveFliesEffect] = 1; + } + + auto bounds = GameBoundingBox(&item); + item.Animation.IsAirborne = true; - if (pointColl.GetFloorHeight() < item.Pose.Position.y) + if (pointColl.GetFloorHeight() <= item.Pose.Position.y - bounds.Y2) { if (!isWater) { - item.Pose.Position.y = item.Pose.Position.y - item.Animation.Velocity.y; + item.Pose.Position.y = pointColl.GetFloorHeight(); SoundEffect(SFX_TR4_CROCGOD_LAND, &item.Pose); } - else + else { - item.Pose.Position.y = item.Pose.Position.y; + item.Pose.Position.y = pointColl.GetFloorHeight(); } item.Animation.IsAirborne = false; item.Animation.Velocity = Vector3::Zero; item.Animation.TargetState = CORPSE_STATE_LAND; item.Animation.AnimNumber = object.animIndex + CORPSE_ANIM_LAND; - AlignEntityToSurface(&item, Vector2(object.radius)); - item.ItemFlags[1] = (int)CorpseFlag::Grounded; + item.ItemFlags[7] = (int)CorpseFlag::Grounded; return; } - else + else if (item.Animation.ActiveState == CORPSE_STATE_FALL) { if (isWater) { @@ -133,17 +152,56 @@ namespace TEN::Entities::TR3 AnimateItem(&item); - if (!TriggerActive(&item)) - return; - - int meshCount = object.nmeshes; - for (int i = 0; i < meshCount; i++) + if (!TriggerActive(&item) || item.ItemFlags[FirefliesItemFlags::RemoveFliesEffect] == 1) { - if (Random::TestProbability(1 / 72.0f)) + // Remove all fireflies associated with this item. + RemoveFireflies(item); + + // Reset ItemFlags. + if (item.HitPoints == NOT_TARGETABLE) + item.HitPoints = FLY_AMOUNT; + + item.ItemFlags[FirefliesItemFlags::Spawncounter] = 0; + + return; + } + else + { + AddActiveItem(itemNumber); + item.Status = ITEM_ACTIVE; + } + + // Spawn fly effect. + if (item.HitPoints != NOT_TARGETABLE) + { + int fireflyCount = item.HitPoints - item.ItemFlags[FirefliesItemFlags::Spawncounter]; + + if (fireflyCount < 0) { - auto pos = GetJointPosition(&item, i).ToVector3(); - SpawnCorpseEffect(pos); + int firefliesToTurnOff = -fireflyCount; + for (auto& firefly : FireflySwarm) + { + if (firefly.TargetItemPtr == &item && firefly.Life > 0.0f) + { + firefly.Life = 0.0f; + firefly.on = false; + firefliesToTurnOff--; + + if (firefliesToTurnOff == 0) + break; + } + } } + else if (fireflyCount > 0) + { + for (int i = 0; i < fireflyCount; i++) + { + SpawnFireflySwarm(item, FLY_EFFECT_MAX_WIDTH); + } + } + + item.ItemFlags[FirefliesItemFlags::Spawncounter] = item.HitPoints; + item.HitPoints = NOT_TARGETABLE; } } @@ -160,9 +218,9 @@ namespace TEN::Entities::TR3 { DoBloodSplat(pos->x, pos->y, pos->z, Random::GenerateInt(4, 8), source.Pose.Orientation.y, pos->RoomNumber); - if (target.ItemFlags[1] == (int)CorpseFlag::Hang) + if (target.ItemFlags[7] == (int)CorpseFlag::Hang) { - target.ItemFlags[1] = (int)CorpseFlag::Fall; + target.ItemFlags[7] = (int)CorpseFlag::Fall; target.Animation.AnimNumber = object.animIndex + CORPSE_ANIM_FALL; target.Animation.ActiveState = CORPSE_STATE_FALL; } diff --git a/TombEngine/Objects/TR5/tr5_objects.cpp b/TombEngine/Objects/TR5/tr5_objects.cpp index 9b5b878b6..130d7405f 100644 --- a/TombEngine/Objects/TR5/tr5_objects.cpp +++ b/TombEngine/Objects/TR5/tr5_objects.cpp @@ -42,6 +42,7 @@ #include "Objects/TR5/Emitter/tr5_spider_emitter.h" #include "Objects/TR5/Emitter/tr5_smoke_emitter.h" #include "Objects/TR5/Emitter/Waterfall.h" +#include "Objects/Effects/Fireflies.h" // Objects #include "Objects/TR5/Light/tr5_light.h" @@ -81,6 +82,7 @@ using namespace TEN::Effects::WaterfallEmitter; using namespace TEN::Entities::Creatures::TR5; using namespace TEN::Entities::Switches; using namespace TEN::Entities::Traps; +using namespace TEN::Effects::Fireflies; static void StartEntity(ObjectInfo *obj) { @@ -796,6 +798,14 @@ static void StartObject(ObjectInfo *obj) obj->control = ControlEmberEmitter; } + obj = &Objects[ID_FIREFLY_EMITTER]; + if (obj->loaded) + { + obj->Initialize = InitializeFireflySwarm; + obj->control = ControlFireflySwarm; + obj->drawRoutine = NULL; + } + obj = &Objects[ID_GEN_SLOT1]; if (obj->loaded) { diff --git a/TombEngine/Objects/game_object_ids.h b/TombEngine/Objects/game_object_ids.h index 26138ce91..18c64a25b 100644 --- a/TombEngine/Objects/game_object_ids.h +++ b/TombEngine/Objects/game_object_ids.h @@ -817,6 +817,7 @@ enum GAME_OBJECT_ID : short ID_CORPSE, ID_WRAITH_TRAP, ID_WATERFALL_EMITTER, + ID_FIREFLY_EMITTER, ID_MESHSWAP1 = 1100, ID_MESHSWAP2, @@ -1006,8 +1007,8 @@ enum GAME_OBJECT_ID : short ID_DASH_BAR_TEXTURE, ID_SFX_BAR_TEXTURE, ID_WATERFALL_SPRITES, - // 1379 - ID_CROSSHAIR_GRAPHICS = 1380, + ID_FIREFLY_SPRITES, + ID_CROSSHAIR_GRAPHICS, ID_SPEEDOMETER_GRAPHICS, ID_CUSTOM_BAR_GRAPHICS, ID_CUSTOM_AMMO_GRAPHICS, diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index cfe315b4c..4e643388d 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -409,6 +409,7 @@ namespace TEN::Renderer void PrepareFires(RenderView& view); void PrepareParticles(RenderView& view); void PrepareSmokes(RenderView& view); + void PrepareFireflies(RenderView& view); void PrepareElectricity(RenderView& view); void PrepareHelicalLasers(RenderView& view); void PrepareBlood(RenderView& view); diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index a79e06298..1c63b24d8 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -1759,6 +1759,7 @@ namespace TEN::Renderer PrepareStreamers(view); PrepareLaserBarriers(view); PrepareSingleLaserBeam(view); + PrepareFireflies(view); // Sprites grouped in buckets for instancing. Non-commutative sprites are collected at a later stage. SortAndPrepareSprites(view); diff --git a/TombEngine/Renderer/RendererDrawEffect.cpp b/TombEngine/Renderer/RendererDrawEffect.cpp index 3b00f6c5f..e647f86b1 100644 --- a/TombEngine/Renderer/RendererDrawEffect.cpp +++ b/TombEngine/Renderer/RendererDrawEffect.cpp @@ -35,6 +35,7 @@ #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Specific/level.h" #include "Structures/RendererSpriteBucket.h" +#include "Objects/Effects/Fireflies.h" using namespace TEN::Effects::Blood; using namespace TEN::Effects::Bubble; @@ -48,6 +49,7 @@ using namespace TEN::Effects::Streamer; using namespace TEN::Entities::Creatures::TR5; using namespace TEN::Entities::Traps; using namespace TEN::Math; +using namespace TEN::Effects::Fireflies; extern BLOOD_STRUCT Blood[MAX_SPARKS_BLOOD]; extern FIRE_SPARKS FireSparks[MAX_SPARKS_FIRE]; @@ -330,6 +332,49 @@ namespace TEN::Renderer } } + void Renderer::PrepareFireflies(RenderView& view) + { + if (!Objects[ID_FIREFLY_EMITTER].loaded) + return; + + for (auto& firefly : FireflySwarm) + { + if (!firefly.on) + continue; + + + if (!CheckIfSlotExists(ID_SPARK_SPRITE, "Particle rendering")) + continue; + + auto axis = Vector3(0,0,0); + axis.Normalize(); + + + firefly.scalar = 3; + firefly.size = 3; + + auto pos = Vector3::Lerp( + Vector3(firefly.PrevX, firefly.PrevY, firefly.PrevZ), + Vector3(firefly.Position.x, firefly.Position.y, firefly.Position.z), + GetInterpolationFactor()); + + pos = Vector3(firefly.Position.x, firefly.Position.y, firefly.Position.z); + + // Disallow sprites out of bounds. + int spriteIndex = Objects[firefly.SpriteSeqID].meshIndex + firefly.SpriteID; + spriteIndex = std::clamp(spriteIndex, 0, (int)_sprites.size()); + + AddSpriteBillboard( + &_sprites[spriteIndex], + pos, + Color(firefly.r / (float)UCHAR_MAX, firefly.g / (float)UCHAR_MAX, firefly.b / (float)UCHAR_MAX, 1.0f), + TO_RAD(firefly.rotAng << 4), firefly.scalar, + Vector2(firefly.size, firefly.size), + firefly.blendMode, true, view); + + } + } + void Renderer::PrepareSmokes(RenderView& view) { for (const auto& smoke : SmokeSparks) diff --git a/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h b/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h index 41dba67cb..04e5732a8 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/ObjectIDs.h @@ -1,7 +1,7 @@ #pragma once // This file is generated automatically, do not edit it. -// Last generated on 13/03/2025. +// Last generated on 17/03/2025. #include #include @@ -815,6 +815,7 @@ The following constants are inside ObjID. CORPSE WRAITH_TRAP WATERFALL_EMITTER + FIREFLY_EMITTER MESHSWAP1 MESHSWAP2 MESHSWAP3 @@ -995,6 +996,7 @@ The following constants are inside ObjID. DASH_BAR_TEXTURE SFX_BAR_TEXTURE WATERFALL_SPRITES + FIREFLY_SPRITES CROSSHAIR_GRAPHICS SPEEDOMETER_GRAPHICS CUSTOM_BAR_GRAPHICS @@ -1245,6 +1247,7 @@ The following ObjID members refer to sprites. DASH_BAR_TEXTURE SFX_BAR_TEXTURE WATERFALL_SPRITES + FIREFLY_SPRITES CROSSHAIR_GRAPHICS SPEEDOMETER_GRAPHICS CUSTOM_BAR_GRAPHICS @@ -2052,6 +2055,7 @@ static const std::unordered_map GAME_OBJECT_IDS { { "CORPSE", ID_CORPSE }, { "WRAITH_TRAP", ID_WRAITH_TRAP }, { "WATERFALL_EMITTER", ID_WATERFALL_EMITTER }, + { "FIREFLY_EMITTER", ID_FIREFLY_EMITTER }, { "MESHSWAP1", ID_MESHSWAP1 }, { "MESHSWAP2", ID_MESHSWAP2 }, { "MESHSWAP3", ID_MESHSWAP3 }, @@ -2232,6 +2236,7 @@ static const std::unordered_map GAME_OBJECT_IDS { { "DASH_BAR_TEXTURE", ID_DASH_BAR_TEXTURE }, { "SFX_BAR_TEXTURE", ID_SFX_BAR_TEXTURE }, { "WATERFALL_SPRITES", ID_WATERFALL_SPRITES }, + { "FIREFLY_SPRITES", ID_FIREFLY_SPRITES }, { "CROSSHAIR_GRAPHICS", ID_CROSSHAIR_GRAPHICS }, { "SPEEDOMETER_GRAPHICS", ID_SPEEDOMETER_GRAPHICS }, { "CUSTOM_BAR_GRAPHICS", ID_CUSTOM_BAR_GRAPHICS }, diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index 5c8333d07..a99d79882 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -169,6 +169,10 @@ struct FishData; struct FishDataBuilder; struct FishDataT; +struct FireflyData; +struct FireflyDataBuilder; +struct FireflyDataT; + struct KeyValPair; struct ScriptTable; @@ -6861,6 +6865,295 @@ struct FishData::Traits { flatbuffers::Offset CreateFishData(flatbuffers::FlatBufferBuilder &_fbb, const FishDataT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct FireflyDataT : public flatbuffers::NativeTable { + typedef FireflyData TableType; + int32_t sprite_index = 0; + int32_t sprite_id = 0; + int32_t blend_mode = 0; + int32_t scalar = 0; + std::unique_ptr position{}; + int32_t room_number = 0; + std::unique_ptr position_target{}; + std::unique_ptr orientation{}; + float velocity = 0.0f; + int32_t target_item_number = 0; + float z_vel = 0.0f; + float life = 0.0f; + int32_t number = 0; + int32_t d_r = 0; + int32_t d_g = 0; + int32_t d_b = 0; + int32_t r = 0; + int32_t g = 0; + int32_t b = 0; + bool on = false; + float size = 0.0f; + int32_t rot_Ang = 0; +}; + +struct FireflyData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef FireflyDataT NativeTableType; + typedef FireflyDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SPRITE_INDEX = 4, + VT_SPRITE_ID = 6, + VT_BLEND_MODE = 8, + VT_SCALAR = 10, + VT_POSITION = 12, + VT_ROOM_NUMBER = 14, + VT_POSITION_TARGET = 16, + VT_ORIENTATION = 18, + VT_VELOCITY = 20, + VT_TARGET_ITEM_NUMBER = 22, + VT_Z_VEL = 24, + VT_LIFE = 26, + VT_NUMBER = 28, + VT_D_R = 30, + VT_D_G = 32, + VT_D_B = 34, + VT_R = 36, + VT_G = 38, + VT_B = 40, + VT_ON = 42, + VT_SIZE = 44, + VT_ROT_ANG = 46 + }; + int32_t sprite_index() const { + return GetField(VT_SPRITE_INDEX, 0); + } + int32_t sprite_id() const { + return GetField(VT_SPRITE_ID, 0); + } + int32_t blend_mode() const { + return GetField(VT_BLEND_MODE, 0); + } + int32_t scalar() const { + return GetField(VT_SCALAR, 0); + } + const TEN::Save::Vector3 *position() const { + return GetStruct(VT_POSITION); + } + int32_t room_number() const { + return GetField(VT_ROOM_NUMBER, 0); + } + const TEN::Save::Vector3 *position_target() const { + return GetStruct(VT_POSITION_TARGET); + } + const TEN::Save::EulerAngles *orientation() const { + return GetStruct(VT_ORIENTATION); + } + float velocity() const { + return GetField(VT_VELOCITY, 0.0f); + } + int32_t target_item_number() const { + return GetField(VT_TARGET_ITEM_NUMBER, 0); + } + float z_vel() const { + return GetField(VT_Z_VEL, 0.0f); + } + float life() const { + return GetField(VT_LIFE, 0.0f); + } + int32_t number() const { + return GetField(VT_NUMBER, 0); + } + int32_t d_r() const { + return GetField(VT_D_R, 0); + } + int32_t d_g() const { + return GetField(VT_D_G, 0); + } + int32_t d_b() const { + return GetField(VT_D_B, 0); + } + int32_t r() const { + return GetField(VT_R, 0); + } + int32_t g() const { + return GetField(VT_G, 0); + } + int32_t b() const { + return GetField(VT_B, 0); + } + bool on() const { + return GetField(VT_ON, 0) != 0; + } + float size() const { + return GetField(VT_SIZE, 0.0f); + } + int32_t rot_Ang() const { + return GetField(VT_ROT_ANG, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SPRITE_INDEX) && + VerifyField(verifier, VT_SPRITE_ID) && + VerifyField(verifier, VT_BLEND_MODE) && + VerifyField(verifier, VT_SCALAR) && + VerifyField(verifier, VT_POSITION) && + VerifyField(verifier, VT_ROOM_NUMBER) && + VerifyField(verifier, VT_POSITION_TARGET) && + VerifyField(verifier, VT_ORIENTATION) && + VerifyField(verifier, VT_VELOCITY) && + VerifyField(verifier, VT_TARGET_ITEM_NUMBER) && + VerifyField(verifier, VT_Z_VEL) && + VerifyField(verifier, VT_LIFE) && + VerifyField(verifier, VT_NUMBER) && + VerifyField(verifier, VT_D_R) && + VerifyField(verifier, VT_D_G) && + VerifyField(verifier, VT_D_B) && + VerifyField(verifier, VT_R) && + VerifyField(verifier, VT_G) && + VerifyField(verifier, VT_B) && + VerifyField(verifier, VT_ON) && + VerifyField(verifier, VT_SIZE) && + VerifyField(verifier, VT_ROT_ANG) && + verifier.EndTable(); + } + FireflyDataT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FireflyDataT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const FireflyDataT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FireflyDataBuilder { + typedef FireflyData Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_sprite_index(int32_t sprite_index) { + fbb_.AddElement(FireflyData::VT_SPRITE_INDEX, sprite_index, 0); + } + void add_sprite_id(int32_t sprite_id) { + fbb_.AddElement(FireflyData::VT_SPRITE_ID, sprite_id, 0); + } + void add_blend_mode(int32_t blend_mode) { + fbb_.AddElement(FireflyData::VT_BLEND_MODE, blend_mode, 0); + } + void add_scalar(int32_t scalar) { + fbb_.AddElement(FireflyData::VT_SCALAR, scalar, 0); + } + void add_position(const TEN::Save::Vector3 *position) { + fbb_.AddStruct(FireflyData::VT_POSITION, position); + } + void add_room_number(int32_t room_number) { + fbb_.AddElement(FireflyData::VT_ROOM_NUMBER, room_number, 0); + } + void add_position_target(const TEN::Save::Vector3 *position_target) { + fbb_.AddStruct(FireflyData::VT_POSITION_TARGET, position_target); + } + void add_orientation(const TEN::Save::EulerAngles *orientation) { + fbb_.AddStruct(FireflyData::VT_ORIENTATION, orientation); + } + void add_velocity(float velocity) { + fbb_.AddElement(FireflyData::VT_VELOCITY, velocity, 0.0f); + } + void add_target_item_number(int32_t target_item_number) { + fbb_.AddElement(FireflyData::VT_TARGET_ITEM_NUMBER, target_item_number, 0); + } + void add_z_vel(float z_vel) { + fbb_.AddElement(FireflyData::VT_Z_VEL, z_vel, 0.0f); + } + void add_life(float life) { + fbb_.AddElement(FireflyData::VT_LIFE, life, 0.0f); + } + void add_number(int32_t number) { + fbb_.AddElement(FireflyData::VT_NUMBER, number, 0); + } + void add_d_r(int32_t d_r) { + fbb_.AddElement(FireflyData::VT_D_R, d_r, 0); + } + void add_d_g(int32_t d_g) { + fbb_.AddElement(FireflyData::VT_D_G, d_g, 0); + } + void add_d_b(int32_t d_b) { + fbb_.AddElement(FireflyData::VT_D_B, d_b, 0); + } + void add_r(int32_t r) { + fbb_.AddElement(FireflyData::VT_R, r, 0); + } + void add_g(int32_t g) { + fbb_.AddElement(FireflyData::VT_G, g, 0); + } + void add_b(int32_t b) { + fbb_.AddElement(FireflyData::VT_B, b, 0); + } + void add_on(bool on) { + fbb_.AddElement(FireflyData::VT_ON, static_cast(on), 0); + } + void add_size(float size) { + fbb_.AddElement(FireflyData::VT_SIZE, size, 0.0f); + } + void add_rot_Ang(int32_t rot_Ang) { + fbb_.AddElement(FireflyData::VT_ROT_ANG, rot_Ang, 0); + } + explicit FireflyDataBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateFireflyData( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t sprite_index = 0, + int32_t sprite_id = 0, + int32_t blend_mode = 0, + int32_t scalar = 0, + const TEN::Save::Vector3 *position = 0, + int32_t room_number = 0, + const TEN::Save::Vector3 *position_target = 0, + const TEN::Save::EulerAngles *orientation = 0, + float velocity = 0.0f, + int32_t target_item_number = 0, + float z_vel = 0.0f, + float life = 0.0f, + int32_t number = 0, + int32_t d_r = 0, + int32_t d_g = 0, + int32_t d_b = 0, + int32_t r = 0, + int32_t g = 0, + int32_t b = 0, + bool on = false, + float size = 0.0f, + int32_t rot_Ang = 0) { + FireflyDataBuilder builder_(_fbb); + builder_.add_rot_Ang(rot_Ang); + builder_.add_size(size); + builder_.add_b(b); + builder_.add_g(g); + builder_.add_r(r); + builder_.add_d_b(d_b); + builder_.add_d_g(d_g); + builder_.add_d_r(d_r); + builder_.add_number(number); + builder_.add_life(life); + builder_.add_z_vel(z_vel); + builder_.add_target_item_number(target_item_number); + builder_.add_velocity(velocity); + builder_.add_orientation(orientation); + builder_.add_position_target(position_target); + builder_.add_room_number(room_number); + builder_.add_position(position); + builder_.add_scalar(scalar); + builder_.add_blend_mode(blend_mode); + builder_.add_sprite_id(sprite_id); + builder_.add_sprite_index(sprite_index); + builder_.add_on(on); + return builder_.Finish(); +} + +struct FireflyData::Traits { + using type = FireflyData; + static auto constexpr Create = CreateFireflyData; +}; + +flatbuffers::Offset CreateFireflyData(flatbuffers::FlatBufferBuilder &_fbb, const FireflyDataT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct ScriptTableT : public flatbuffers::NativeTable { typedef ScriptTable TableType; std::vector keys_vals{}; @@ -8000,6 +8293,7 @@ struct SaveGameT : public flatbuffers::NativeTable { int32_t next_item_active = 0; std::vector room_items{}; std::vector> fish_swarm{}; + std::vector> firefly_swarm{}; std::vector> fxinfos{}; int32_t next_fx_free = 0; int32_t next_fx_active = 0; @@ -8066,52 +8360,53 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_NEXT_ITEM_ACTIVE = 24, VT_ROOM_ITEMS = 26, VT_FISH_SWARM = 28, - VT_FXINFOS = 30, - VT_NEXT_FX_FREE = 32, - VT_NEXT_FX_ACTIVE = 34, - VT_FIXED_CAMERAS = 36, - VT_SINKS = 38, - VT_STATIC_MESHES = 40, - VT_FLYBY_CAMERAS = 42, - VT_PARTICLES = 44, - VT_RATS = 46, - VT_SPIDERS = 48, - VT_SCARABS = 50, - VT_BATS = 52, - VT_FLIP_MAPS = 54, - VT_FLIP_STATS = 56, - VT_FLIP_EFFECT = 58, - VT_FLIP_TIMER = 60, - VT_FLIP_STATUS = 62, - VT_CURRENT_FOV = 64, - VT_LAST_INV_ITEM = 66, - VT_ACTION_QUEUE = 68, - VT_SOUNDTRACKS = 70, - VT_CD_FLAGS = 72, - VT_POSTPROCESS_MODE = 74, - VT_POSTPROCESS_STRENGTH = 76, - VT_POSTPROCESS_TINT = 78, - VT_ROPE = 80, - VT_PENDULUM = 82, - VT_ALTERNATE_PENDULUM = 84, - VT_VOLUMES = 86, - VT_GLOBAL_EVENT_SETS = 88, - VT_VOLUME_EVENT_SETS = 90, - VT_SCRIPT_VARS = 92, - VT_CALLBACKS_PRE_START = 94, - VT_CALLBACKS_POST_START = 96, - VT_CALLBACKS_PRE_END = 98, - VT_CALLBACKS_POST_END = 100, - VT_CALLBACKS_PRE_SAVE = 102, - VT_CALLBACKS_POST_SAVE = 104, - VT_CALLBACKS_PRE_LOAD = 106, - VT_CALLBACKS_POST_LOAD = 108, - VT_CALLBACKS_PRE_LOOP = 110, - VT_CALLBACKS_POST_LOOP = 112, - VT_CALLBACKS_PRE_USEITEM = 114, - VT_CALLBACKS_POST_USEITEM = 116, - VT_CALLBACKS_PRE_FREEZE = 118, - VT_CALLBACKS_POST_FREEZE = 120 + VT_FIREFLY_SWARM = 30, + VT_FXINFOS = 32, + VT_NEXT_FX_FREE = 34, + VT_NEXT_FX_ACTIVE = 36, + VT_FIXED_CAMERAS = 38, + VT_SINKS = 40, + VT_STATIC_MESHES = 42, + VT_FLYBY_CAMERAS = 44, + VT_PARTICLES = 46, + VT_RATS = 48, + VT_SPIDERS = 50, + VT_SCARABS = 52, + VT_BATS = 54, + VT_FLIP_MAPS = 56, + VT_FLIP_STATS = 58, + VT_FLIP_EFFECT = 60, + VT_FLIP_TIMER = 62, + VT_FLIP_STATUS = 64, + VT_CURRENT_FOV = 66, + VT_LAST_INV_ITEM = 68, + VT_ACTION_QUEUE = 70, + VT_SOUNDTRACKS = 72, + VT_CD_FLAGS = 74, + VT_POSTPROCESS_MODE = 76, + VT_POSTPROCESS_STRENGTH = 78, + VT_POSTPROCESS_TINT = 80, + VT_ROPE = 82, + VT_PENDULUM = 84, + VT_ALTERNATE_PENDULUM = 86, + VT_VOLUMES = 88, + VT_GLOBAL_EVENT_SETS = 90, + VT_VOLUME_EVENT_SETS = 92, + VT_SCRIPT_VARS = 94, + VT_CALLBACKS_PRE_START = 96, + VT_CALLBACKS_POST_START = 98, + VT_CALLBACKS_PRE_END = 100, + VT_CALLBACKS_POST_END = 102, + VT_CALLBACKS_PRE_SAVE = 104, + VT_CALLBACKS_POST_SAVE = 106, + VT_CALLBACKS_PRE_LOAD = 108, + VT_CALLBACKS_POST_LOAD = 110, + VT_CALLBACKS_PRE_LOOP = 112, + VT_CALLBACKS_POST_LOOP = 114, + VT_CALLBACKS_PRE_USEITEM = 116, + VT_CALLBACKS_POST_USEITEM = 118, + VT_CALLBACKS_PRE_FREEZE = 120, + VT_CALLBACKS_POST_FREEZE = 122 }; const TEN::Save::SaveGameHeader *header() const { return GetPointer(VT_HEADER); @@ -8152,6 +8447,9 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *fish_swarm() const { return GetPointer> *>(VT_FISH_SWARM); } + const flatbuffers::Vector> *firefly_swarm() const { + return GetPointer> *>(VT_FIREFLY_SWARM); + } const flatbuffers::Vector> *fxinfos() const { return GetPointer> *>(VT_FXINFOS); } @@ -8318,6 +8616,9 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyOffset(verifier, VT_FISH_SWARM) && verifier.VerifyVector(fish_swarm()) && verifier.VerifyVectorOfTables(fish_swarm()) && + VerifyOffset(verifier, VT_FIREFLY_SWARM) && + verifier.VerifyVector(firefly_swarm()) && + verifier.VerifyVectorOfTables(firefly_swarm()) && VerifyOffset(verifier, VT_FXINFOS) && verifier.VerifyVector(fxinfos()) && verifier.VerifyVectorOfTables(fxinfos()) && @@ -8478,6 +8779,9 @@ struct SaveGameBuilder { void add_fish_swarm(flatbuffers::Offset>> fish_swarm) { fbb_.AddOffset(SaveGame::VT_FISH_SWARM, fish_swarm); } + void add_firefly_swarm(flatbuffers::Offset>> firefly_swarm) { + fbb_.AddOffset(SaveGame::VT_FIREFLY_SWARM, firefly_swarm); + } void add_fxinfos(flatbuffers::Offset>> fxinfos) { fbb_.AddOffset(SaveGame::VT_FXINFOS, fxinfos); } @@ -8642,6 +8946,7 @@ inline flatbuffers::Offset CreateSaveGame( int32_t next_item_active = 0, flatbuffers::Offset> room_items = 0, flatbuffers::Offset>> fish_swarm = 0, + flatbuffers::Offset>> firefly_swarm = 0, flatbuffers::Offset>> fxinfos = 0, int32_t next_fx_free = 0, int32_t next_fx_active = 0, @@ -8734,6 +9039,7 @@ inline flatbuffers::Offset CreateSaveGame( builder_.add_next_fx_active(next_fx_active); builder_.add_next_fx_free(next_fx_free); builder_.add_fxinfos(fxinfos); + builder_.add_firefly_swarm(firefly_swarm); builder_.add_fish_swarm(fish_swarm); builder_.add_room_items(room_items); builder_.add_next_item_active(next_item_active); @@ -8771,6 +9077,7 @@ inline flatbuffers::Offset CreateSaveGameDirect( int32_t next_item_active = 0, const std::vector *room_items = nullptr, const std::vector> *fish_swarm = nullptr, + const std::vector> *firefly_swarm = nullptr, const std::vector> *fxinfos = nullptr, int32_t next_fx_free = 0, int32_t next_fx_active = 0, @@ -8821,6 +9128,7 @@ inline flatbuffers::Offset CreateSaveGameDirect( auto items__ = items ? _fbb.CreateVector>(*items) : 0; auto room_items__ = room_items ? _fbb.CreateVector(*room_items) : 0; auto fish_swarm__ = fish_swarm ? _fbb.CreateVector>(*fish_swarm) : 0; + auto firefly_swarm__ = firefly_swarm ? _fbb.CreateVector>(*firefly_swarm) : 0; auto fxinfos__ = fxinfos ? _fbb.CreateVector>(*fxinfos) : 0; auto fixed_cameras__ = fixed_cameras ? _fbb.CreateVector>(*fixed_cameras) : 0; auto sinks__ = sinks ? _fbb.CreateVector>(*sinks) : 0; @@ -8868,6 +9176,7 @@ inline flatbuffers::Offset CreateSaveGameDirect( next_item_active, room_items__, fish_swarm__, + firefly_swarm__, fxinfos__, next_fx_free, next_fx_active, @@ -10889,6 +11198,95 @@ inline flatbuffers::Offset CreateFishData(flatbuffers::FlatBufferBuild _velocity); } +inline FireflyDataT *FireflyData::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::make_unique(); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FireflyData::UnPackTo(FireflyDataT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = sprite_index(); _o->sprite_index = _e; } + { auto _e = sprite_id(); _o->sprite_id = _e; } + { auto _e = blend_mode(); _o->blend_mode = _e; } + { auto _e = scalar(); _o->scalar = _e; } + { auto _e = position(); if (_e) _o->position = std::unique_ptr(new TEN::Save::Vector3(*_e)); } + { auto _e = room_number(); _o->room_number = _e; } + { auto _e = position_target(); if (_e) _o->position_target = std::unique_ptr(new TEN::Save::Vector3(*_e)); } + { auto _e = orientation(); if (_e) _o->orientation = std::unique_ptr(new TEN::Save::EulerAngles(*_e)); } + { auto _e = velocity(); _o->velocity = _e; } + { auto _e = target_item_number(); _o->target_item_number = _e; } + { auto _e = z_vel(); _o->z_vel = _e; } + { auto _e = life(); _o->life = _e; } + { auto _e = number(); _o->number = _e; } + { auto _e = d_r(); _o->d_r = _e; } + { auto _e = d_g(); _o->d_g = _e; } + { auto _e = d_b(); _o->d_b = _e; } + { auto _e = r(); _o->r = _e; } + { auto _e = g(); _o->g = _e; } + { auto _e = b(); _o->b = _e; } + { auto _e = on(); _o->on = _e; } + { auto _e = size(); _o->size = _e; } + { auto _e = rot_Ang(); _o->rot_Ang = _e; } +} + +inline flatbuffers::Offset FireflyData::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FireflyDataT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateFireflyData(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateFireflyData(flatbuffers::FlatBufferBuilder &_fbb, const FireflyDataT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const FireflyDataT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _sprite_index = _o->sprite_index; + auto _sprite_id = _o->sprite_id; + auto _blend_mode = _o->blend_mode; + auto _scalar = _o->scalar; + auto _position = _o->position ? _o->position.get() : 0; + auto _room_number = _o->room_number; + auto _position_target = _o->position_target ? _o->position_target.get() : 0; + auto _orientation = _o->orientation ? _o->orientation.get() : 0; + auto _velocity = _o->velocity; + auto _target_item_number = _o->target_item_number; + auto _z_vel = _o->z_vel; + auto _life = _o->life; + auto _number = _o->number; + auto _d_r = _o->d_r; + auto _d_g = _o->d_g; + auto _d_b = _o->d_b; + auto _r = _o->r; + auto _g = _o->g; + auto _b = _o->b; + auto _on = _o->on; + auto _size = _o->size; + auto _rot_Ang = _o->rot_Ang; + return TEN::Save::CreateFireflyData( + _fbb, + _sprite_index, + _sprite_id, + _blend_mode, + _scalar, + _position, + _room_number, + _position_target, + _orientation, + _velocity, + _target_item_number, + _z_vel, + _life, + _number, + _d_r, + _d_g, + _d_b, + _r, + _g, + _b, + _on, + _size, + _rot_Ang); +} + inline ScriptTableT *ScriptTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::make_unique(); UnPackTo(_o.get(), _resolver); @@ -11323,6 +11721,7 @@ inline void SaveGame::UnPackTo(SaveGameT *_o, const flatbuffers::resolver_functi { auto _e = next_item_active(); _o->next_item_active = _e; } { auto _e = room_items(); if (_e) { _o->room_items.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->room_items[_i] = _e->Get(_i); } } } { auto _e = fish_swarm(); if (_e) { _o->fish_swarm.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->fish_swarm[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } + { auto _e = firefly_swarm(); if (_e) { _o->firefly_swarm.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->firefly_swarm[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } { auto _e = fxinfos(); if (_e) { _o->fxinfos.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->fxinfos[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } { auto _e = next_fx_free(); _o->next_fx_free = _e; } { auto _e = next_fx_active(); _o->next_fx_active = _e; } @@ -11392,6 +11791,7 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild auto _next_item_active = _o->next_item_active; auto _room_items = _fbb.CreateVector(_o->room_items); auto _fish_swarm = _fbb.CreateVector> (_o->fish_swarm.size(), [](size_t i, _VectorArgs *__va) { return CreateFishData(*__va->__fbb, __va->__o->fish_swarm[i].get(), __va->__rehasher); }, &_va ); + auto _firefly_swarm = _fbb.CreateVector> (_o->firefly_swarm.size(), [](size_t i, _VectorArgs *__va) { return CreateFireflyData(*__va->__fbb, __va->__o->firefly_swarm[i].get(), __va->__rehasher); }, &_va ); auto _fxinfos = _fbb.CreateVector> (_o->fxinfos.size(), [](size_t i, _VectorArgs *__va) { return CreateFXInfo(*__va->__fbb, __va->__o->fxinfos[i].get(), __va->__rehasher); }, &_va ); auto _next_fx_free = _o->next_fx_free; auto _next_fx_active = _o->next_fx_active; @@ -11453,6 +11853,7 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild _next_item_active, _room_items, _fish_swarm, + _firefly_swarm, _fxinfos, _next_fx_free, _next_fx_active, diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index b4e852148..bc41d27be 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -499,6 +499,31 @@ table FishData { velocity: float; } +table FireflyData { + sprite_index: int32; + sprite_id: int32; + blend_mode: int32; + scalar: int32; + position: Vector3; + room_number: int32; + position_target: Vector3; + orientation: EulerAngles; + velocity: float; + target_item_number: int32; + z_vel: float; + life: float; + number: int32; + d_r: int32; + d_g: int32; + d_b: int32; + r: int32; + g: int32; + b: int32; + on: bool; + size: float; + rot_Ang: int32; +} + struct KeyValPair { key: uint32; val: uint32; @@ -602,6 +627,7 @@ table SaveGame { next_item_active: int32; room_items: [int32]; fish_swarm: [FishData]; + firefly_swarm: [FireflyData]; fxinfos: [FXInfo]; next_fx_free: int32; next_fx_active: int32; diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index 19ff7ecfb..64841e2e1 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -506,6 +506,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + @@ -1044,6 +1045,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. +