2020-04-17 21:36:25 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
|
|
|
*
|
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
|
|
*
|
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2019-05-11 21:31:34 +02:00
|
|
|
#include "GameStateSnapshots.h"
|
|
|
|
|
|
|
|
#include "core/CircularBuffer.h"
|
|
|
|
#include "peep/Peep.h"
|
2021-05-29 07:01:32 +02:00
|
|
|
#include "ride/Vehicle.h"
|
|
|
|
#include "world/Balloon.h"
|
|
|
|
#include "world/Duck.h"
|
2021-02-23 10:49:41 +01:00
|
|
|
#include "world/EntityList.h"
|
2021-05-29 07:01:32 +02:00
|
|
|
#include "world/Fountain.h"
|
|
|
|
#include "world/Litter.h"
|
|
|
|
#include "world/MoneyEffect.h"
|
|
|
|
#include "world/Particle.h"
|
2019-05-11 21:31:34 +02:00
|
|
|
#include "world/Sprite.h"
|
|
|
|
|
|
|
|
static constexpr size_t MaximumGameStateSnapshots = 32;
|
|
|
|
static constexpr uint32_t InvalidTick = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
struct GameStateSnapshot_t
|
|
|
|
{
|
2019-10-21 05:20:25 +02:00
|
|
|
GameStateSnapshot_t& operator=(GameStateSnapshot_t&& mv) noexcept
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
tick = mv.tick;
|
|
|
|
storedSprites = std::move(mv.storedSprites);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t tick = InvalidTick;
|
|
|
|
uint32_t srand0 = 0;
|
|
|
|
|
2020-07-30 23:01:22 +02:00
|
|
|
OpenRCT2::MemoryStream storedSprites;
|
|
|
|
OpenRCT2::MemoryStream parkParameters;
|
2019-05-11 21:31:34 +02:00
|
|
|
|
2020-07-25 19:03:14 +02:00
|
|
|
// Must pass a function that can access the sprite.
|
|
|
|
void SerialiseSprites(std::function<rct_sprite*(const size_t)> getEntity, const size_t numSprites, bool saving)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
const bool loading = !saving;
|
|
|
|
|
|
|
|
storedSprites.SetPosition(0);
|
|
|
|
DataSerialiser ds(saving, storedSprites);
|
|
|
|
|
|
|
|
std::vector<uint32_t> indexTable;
|
|
|
|
indexTable.reserve(numSprites);
|
|
|
|
|
|
|
|
uint32_t numSavedSprites = 0;
|
|
|
|
|
|
|
|
if (saving)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < numSprites; i++)
|
|
|
|
{
|
2020-07-25 19:03:14 +02:00
|
|
|
auto entity = getEntity(i);
|
2021-05-29 07:01:32 +02:00
|
|
|
if (entity == nullptr || entity->base.Type == EntityType::Null)
|
2019-05-11 21:31:34 +02:00
|
|
|
continue;
|
2020-04-17 21:36:25 +02:00
|
|
|
indexTable.push_back(static_cast<uint32_t>(i));
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2020-04-17 21:36:25 +02:00
|
|
|
numSavedSprites = static_cast<uint32_t>(indexTable.size());
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ds << numSavedSprites;
|
|
|
|
|
|
|
|
if (loading)
|
|
|
|
{
|
|
|
|
indexTable.resize(numSavedSprites);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < numSavedSprites; i++)
|
|
|
|
{
|
|
|
|
ds << indexTable[i];
|
|
|
|
|
|
|
|
const uint32_t spriteIdx = indexTable[i];
|
2020-07-25 19:03:14 +02:00
|
|
|
rct_sprite* entity = getEntity(spriteIdx);
|
|
|
|
if (entity == nullptr)
|
|
|
|
{
|
|
|
|
log_error("Entity index corrupted!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto& sprite = *entity;
|
2019-05-11 21:31:34 +02:00
|
|
|
|
2021-05-29 07:01:32 +02:00
|
|
|
ds << sprite.base.Type;
|
2019-05-11 21:31:34 +02:00
|
|
|
|
2021-05-29 07:01:32 +02:00
|
|
|
switch (sprite.base.Type)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Vehicle:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Vehicle&>(sprite).Serialise(ds);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Guest:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Guest&>(sprite).Serialise(ds);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Staff:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Staff&>(sprite).Serialise(ds);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Litter:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Litter&>(sprite).Serialise(ds);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::MoneyEffect:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<MoneyEffect&>(sprite).Serialise(ds);
|
2020-10-27 22:52:56 +01:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Balloon:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Balloon&>(sprite).Serialise(ds);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Duck:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<Duck&>(sprite).Serialise(ds);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::JumpingFountain:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<JumpingFountain&>(sprite).Serialise(ds);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::SteamParticle:
|
2021-05-09 20:12:44 +02:00
|
|
|
reinterpret_cast<SteamParticle&>(sprite).Serialise(ds);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Null:
|
|
|
|
break;
|
|
|
|
default:
|
2020-10-27 22:52:56 +01:00
|
|
|
break;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-19 22:34:03 +02:00
|
|
|
struct GameStateSnapshots final : public IGameStateSnapshots
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
virtual void Reset() override final
|
|
|
|
{
|
|
|
|
_snapshots.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual GameStateSnapshot_t& CreateSnapshot() override final
|
|
|
|
{
|
|
|
|
auto snapshot = std::make_unique<GameStateSnapshot_t>();
|
|
|
|
_snapshots.push_back(std::move(snapshot));
|
|
|
|
|
|
|
|
return *_snapshots.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void LinkSnapshot(GameStateSnapshot_t& snapshot, uint32_t tick, uint32_t srand0) override final
|
|
|
|
{
|
|
|
|
snapshot.tick = tick;
|
|
|
|
snapshot.srand0 = srand0;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Capture(GameStateSnapshot_t& snapshot) override final
|
|
|
|
{
|
2020-07-25 19:03:14 +02:00
|
|
|
snapshot.SerialiseSprites(
|
2021-02-24 23:34:34 +01:00
|
|
|
[](const size_t index) { return reinterpret_cast<rct_sprite*>(GetEntity(index)); }, MAX_ENTITIES, true);
|
2019-05-11 21:31:34 +02:00
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
// log_info("Snapshot size: %u bytes", static_cast<uint32_t>(snapshot.storedSprites.GetLength()));
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual const GameStateSnapshot_t* GetLinkedSnapshot(uint32_t tick) const override final
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < _snapshots.size(); i++)
|
|
|
|
{
|
|
|
|
if (_snapshots[i]->tick == tick)
|
|
|
|
return _snapshots[i].get();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SerialiseSnapshot(GameStateSnapshot_t& snapshot, DataSerialiser& ds) const override final
|
|
|
|
{
|
|
|
|
ds << snapshot.tick;
|
|
|
|
ds << snapshot.srand0;
|
|
|
|
ds << snapshot.storedSprites;
|
|
|
|
ds << snapshot.parkParameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<rct_sprite> BuildSpriteList(GameStateSnapshot_t& snapshot) const
|
|
|
|
{
|
|
|
|
std::vector<rct_sprite> spriteList;
|
2021-02-24 23:34:34 +01:00
|
|
|
spriteList.resize(MAX_ENTITIES);
|
2019-05-11 21:31:34 +02:00
|
|
|
|
|
|
|
for (auto& sprite : spriteList)
|
|
|
|
{
|
|
|
|
// By default they don't exist.
|
2021-05-29 07:01:32 +02:00
|
|
|
sprite.base.Type = EntityType::Null;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-02-24 23:34:34 +01:00
|
|
|
snapshot.SerialiseSprites([&spriteList](const size_t index) { return &spriteList[index]; }, MAX_ENTITIES, false);
|
2019-05-11 21:31:34 +02:00
|
|
|
|
|
|
|
return spriteList;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define COMPARE_FIELD(struc, field) \
|
|
|
|
if (std::memcmp(&spriteBase.field, &spriteCmp.field, sizeof(struc::field)) != 0) \
|
|
|
|
{ \
|
|
|
|
uint64_t valA = 0; \
|
|
|
|
uint64_t valB = 0; \
|
|
|
|
std::memcpy(&valA, &spriteBase.field, sizeof(struc::field)); \
|
|
|
|
std::memcpy(&valB, &spriteCmp.field, sizeof(struc::field)); \
|
|
|
|
uintptr_t offset = reinterpret_cast<uintptr_t>(&spriteBase.field) - reinterpret_cast<uintptr_t>(&spriteBase); \
|
|
|
|
changeData.diffs.push_back( \
|
2020-04-17 21:36:25 +02:00
|
|
|
GameStateSpriteChange_t::Diff_t{ static_cast<size_t>(offset), sizeof(struc::field), #struc, #field, valA, valB }); \
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataCommon(
|
2020-01-19 16:50:01 +01:00
|
|
|
const SpriteBase& spriteBase, const SpriteBase& spriteCmp, GameStateSpriteChange_t& changeData) const
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
COMPARE_FIELD(SpriteBase, Type);
|
2020-01-19 16:50:01 +01:00
|
|
|
COMPARE_FIELD(SpriteBase, sprite_index);
|
|
|
|
COMPARE_FIELD(SpriteBase, x);
|
|
|
|
COMPARE_FIELD(SpriteBase, y);
|
|
|
|
COMPARE_FIELD(SpriteBase, z);
|
2019-05-11 21:31:34 +02:00
|
|
|
/* Only relevant for rendering, does not affect game state.
|
2020-01-19 16:50:01 +01:00
|
|
|
COMPARE_FIELD(SpriteBase, sprite_width);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_height_negative);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_height_positive);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_left);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_top);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_right);
|
|
|
|
COMPARE_FIELD(SpriteBase, sprite_bottom);
|
2019-05-11 21:31:34 +02:00
|
|
|
*/
|
2020-01-19 16:50:01 +01:00
|
|
|
COMPARE_FIELD(SpriteBase, sprite_direction);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataPeep(const Peep& spriteBase, const Peep& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2020-02-11 23:01:14 +01:00
|
|
|
COMPARE_FIELD(Peep, NextLoc.x);
|
|
|
|
COMPARE_FIELD(Peep, NextLoc.y);
|
|
|
|
COMPARE_FIELD(Peep, NextLoc.z);
|
2020-06-14 07:31:08 +02:00
|
|
|
COMPARE_FIELD(Peep, NextFlags);
|
|
|
|
COMPARE_FIELD(Peep, State);
|
|
|
|
COMPARE_FIELD(Peep, SubState);
|
|
|
|
COMPARE_FIELD(Peep, SpriteType);
|
|
|
|
COMPARE_FIELD(Peep, TshirtColour);
|
|
|
|
COMPARE_FIELD(Peep, TrousersColour);
|
|
|
|
COMPARE_FIELD(Peep, DestinationX);
|
|
|
|
COMPARE_FIELD(Peep, DestinationY);
|
|
|
|
COMPARE_FIELD(Peep, DestinationTolerance);
|
2020-06-09 04:47:45 +02:00
|
|
|
COMPARE_FIELD(Peep, Var37);
|
2020-06-09 04:34:35 +02:00
|
|
|
COMPARE_FIELD(Peep, Energy);
|
2020-06-09 04:32:22 +02:00
|
|
|
COMPARE_FIELD(Peep, EnergyTarget);
|
2020-06-09 04:07:31 +02:00
|
|
|
COMPARE_FIELD(Peep, Mass);
|
2020-06-09 03:25:54 +02:00
|
|
|
COMPARE_FIELD(Peep, WindowInvalidateFlags);
|
2020-06-08 03:21:53 +02:00
|
|
|
COMPARE_FIELD(Peep, CurrentRide);
|
2020-06-08 03:18:17 +02:00
|
|
|
COMPARE_FIELD(Peep, CurrentRideStation);
|
2020-06-08 03:10:09 +02:00
|
|
|
COMPARE_FIELD(Peep, CurrentTrain);
|
2020-06-08 02:45:12 +02:00
|
|
|
COMPARE_FIELD(Peep, TimeToSitdown);
|
2020-06-08 02:23:59 +02:00
|
|
|
COMPARE_FIELD(Peep, SpecialSprite);
|
2020-06-08 02:22:13 +02:00
|
|
|
COMPARE_FIELD(Peep, ActionSpriteType);
|
2020-06-08 02:19:44 +02:00
|
|
|
COMPARE_FIELD(Peep, NextActionSpriteType);
|
2020-06-08 01:23:21 +02:00
|
|
|
COMPARE_FIELD(Peep, ActionSpriteImageOffset);
|
2020-06-08 01:18:42 +02:00
|
|
|
COMPARE_FIELD(Peep, Action);
|
|
|
|
COMPARE_FIELD(Peep, ActionFrame);
|
2020-06-08 01:00:07 +02:00
|
|
|
COMPARE_FIELD(Peep, StepProgress);
|
2020-06-06 16:18:58 +02:00
|
|
|
COMPARE_FIELD(Peep, MazeLastEdge);
|
2020-06-06 15:57:14 +02:00
|
|
|
COMPARE_FIELD(Peep, InteractionRideIndex);
|
2020-06-06 15:31:19 +02:00
|
|
|
COMPARE_FIELD(Peep, Id);
|
2020-05-31 23:28:20 +02:00
|
|
|
COMPARE_FIELD(Peep, PathCheckOptimisation);
|
2020-05-31 22:42:49 +02:00
|
|
|
COMPARE_FIELD(Peep, PathfindGoal);
|
2019-05-11 21:31:34 +02:00
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
2020-05-31 22:38:18 +02:00
|
|
|
COMPARE_FIELD(Peep, PathfindHistory[i]);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2020-06-04 03:45:15 +02:00
|
|
|
COMPARE_FIELD(Peep, WalkingFrameNum);
|
2021-04-18 18:48:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataStaff(const Staff& spriteBase, const Staff& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
|
|
|
CompareSpriteDataPeep(spriteBase, spriteCmp, changeData);
|
|
|
|
|
|
|
|
COMPARE_FIELD(Staff, AssignedStaffType);
|
|
|
|
COMPARE_FIELD(Staff, MechanicTimeSinceCall);
|
|
|
|
COMPARE_FIELD(Staff, HireDate);
|
|
|
|
COMPARE_FIELD(Staff, StaffId);
|
|
|
|
COMPARE_FIELD(Staff, StaffOrders);
|
|
|
|
COMPARE_FIELD(Staff, StaffMowingTimeout);
|
|
|
|
COMPARE_FIELD(Staff, StaffRidesFixed);
|
|
|
|
COMPARE_FIELD(Staff, StaffRidesInspected);
|
|
|
|
COMPARE_FIELD(Staff, StaffLitterSwept);
|
|
|
|
COMPARE_FIELD(Staff, StaffBinsEmptied);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataGuest(const Guest& spriteBase, const Guest& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
|
|
|
CompareSpriteDataPeep(spriteBase, spriteCmp, changeData);
|
|
|
|
|
|
|
|
COMPARE_FIELD(Guest, OutsideOfPark);
|
|
|
|
COMPARE_FIELD(Guest, GuestNumRides);
|
|
|
|
COMPARE_FIELD(Guest, Happiness);
|
|
|
|
COMPARE_FIELD(Guest, HappinessTarget);
|
|
|
|
COMPARE_FIELD(Guest, Nausea);
|
|
|
|
COMPARE_FIELD(Guest, NauseaTarget);
|
|
|
|
COMPARE_FIELD(Guest, Hunger);
|
|
|
|
COMPARE_FIELD(Guest, Thirst);
|
|
|
|
COMPARE_FIELD(Guest, Toilet);
|
|
|
|
COMPARE_FIELD(Guest, TimeToConsume);
|
|
|
|
COMPARE_FIELD(Guest, Intensity);
|
|
|
|
COMPARE_FIELD(Guest, NauseaTolerance);
|
|
|
|
COMPARE_FIELD(Guest, PaidOnDrink);
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
|
|
{
|
|
|
|
COMPARE_FIELD(Guest, RideTypesBeenOn[i]);
|
|
|
|
}
|
|
|
|
COMPARE_FIELD(Guest, ItemFlags);
|
|
|
|
COMPARE_FIELD(Guest, Photo2RideRef);
|
|
|
|
COMPARE_FIELD(Guest, Photo3RideRef);
|
|
|
|
COMPARE_FIELD(Guest, Photo4RideRef);
|
|
|
|
COMPARE_FIELD(Guest, GuestNextInQueue);
|
|
|
|
COMPARE_FIELD(Guest, TimeInQueue);
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
COMPARE_FIELD(Guest, RidesBeenOn[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
COMPARE_FIELD(Guest, CashInPocket);
|
|
|
|
COMPARE_FIELD(Guest, CashSpent);
|
|
|
|
COMPARE_FIELD(Guest, ParkEntryTime);
|
|
|
|
COMPARE_FIELD(Guest, RejoinQueueTimeout);
|
|
|
|
COMPARE_FIELD(Guest, PreviousRide);
|
|
|
|
COMPARE_FIELD(Guest, PreviousRideTimeOut);
|
|
|
|
for (int i = 0; i < PEEP_MAX_THOUGHTS; i++)
|
|
|
|
{
|
|
|
|
COMPARE_FIELD(Guest, Thoughts[i]);
|
|
|
|
}
|
|
|
|
COMPARE_FIELD(Guest, GuestHeadingToRideId);
|
|
|
|
COMPARE_FIELD(Guest, GuestIsLostCountdown);
|
|
|
|
COMPARE_FIELD(Guest, Photo1RideRef);
|
|
|
|
COMPARE_FIELD(Guest, PeepFlags);
|
|
|
|
COMPARE_FIELD(Guest, LitterCount);
|
|
|
|
COMPARE_FIELD(Guest, GuestTimeOnRide);
|
|
|
|
COMPARE_FIELD(Guest, DisgustingCount);
|
|
|
|
COMPARE_FIELD(Guest, PaidToEnter);
|
|
|
|
COMPARE_FIELD(Guest, PaidOnRides);
|
|
|
|
COMPARE_FIELD(Guest, PaidOnFood);
|
|
|
|
COMPARE_FIELD(Guest, PaidOnSouvenirs);
|
|
|
|
COMPARE_FIELD(Guest, AmountOfFood);
|
|
|
|
COMPARE_FIELD(Guest, AmountOfDrinks);
|
|
|
|
COMPARE_FIELD(Guest, AmountOfSouvenirs);
|
|
|
|
COMPARE_FIELD(Guest, VandalismSeen);
|
|
|
|
COMPARE_FIELD(Guest, VoucherType);
|
|
|
|
COMPARE_FIELD(Guest, VoucherRideId);
|
|
|
|
COMPARE_FIELD(Guest, SurroundingsThoughtTimeout);
|
|
|
|
COMPARE_FIELD(Guest, Angriness);
|
|
|
|
COMPARE_FIELD(Guest, TimeLost);
|
|
|
|
COMPARE_FIELD(Guest, DaysInQueue);
|
|
|
|
COMPARE_FIELD(Guest, BalloonColour);
|
|
|
|
COMPARE_FIELD(Guest, UmbrellaColour);
|
|
|
|
COMPARE_FIELD(Guest, HatColour);
|
|
|
|
COMPARE_FIELD(Guest, FavouriteRide);
|
|
|
|
COMPARE_FIELD(Guest, FavouriteRideRating);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataVehicle(
|
2020-01-19 16:53:17 +01:00
|
|
|
const Vehicle& spriteBase, const Vehicle& spriteCmp, GameStateSpriteChange_t& changeData) const
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-05-12 13:38:46 +02:00
|
|
|
COMPARE_FIELD(Vehicle, Pitch);
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, bank_rotation);
|
|
|
|
COMPARE_FIELD(Vehicle, remaining_distance);
|
|
|
|
COMPARE_FIELD(Vehicle, velocity);
|
|
|
|
COMPARE_FIELD(Vehicle, acceleration);
|
|
|
|
COMPARE_FIELD(Vehicle, ride);
|
|
|
|
COMPARE_FIELD(Vehicle, vehicle_type);
|
|
|
|
COMPARE_FIELD(Vehicle, colours);
|
|
|
|
COMPARE_FIELD(Vehicle, track_progress);
|
2021-01-30 15:16:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, TrackTypeAndDirection);
|
2020-03-01 19:45:51 +01:00
|
|
|
COMPARE_FIELD(Vehicle, TrackLocation.x);
|
|
|
|
COMPARE_FIELD(Vehicle, TrackLocation.y);
|
|
|
|
COMPARE_FIELD(Vehicle, TrackLocation.z);
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, next_vehicle_on_train);
|
|
|
|
COMPARE_FIELD(Vehicle, prev_vehicle_on_ride);
|
|
|
|
COMPARE_FIELD(Vehicle, next_vehicle_on_ride);
|
|
|
|
COMPARE_FIELD(Vehicle, var_44);
|
|
|
|
COMPARE_FIELD(Vehicle, mass);
|
|
|
|
COMPARE_FIELD(Vehicle, update_flags);
|
2020-05-30 23:02:54 +02:00
|
|
|
COMPARE_FIELD(Vehicle, SwingSprite);
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, current_station);
|
2020-05-30 23:02:54 +02:00
|
|
|
COMPARE_FIELD(Vehicle, SwingPosition);
|
|
|
|
COMPARE_FIELD(Vehicle, SwingSpeed);
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, status);
|
|
|
|
COMPARE_FIELD(Vehicle, sub_state);
|
2019-05-11 21:31:34 +02:00
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
{
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, peep[i]);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
{
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, peep_tshirt_colours[i]);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, num_seats);
|
|
|
|
COMPARE_FIELD(Vehicle, num_peeps);
|
|
|
|
COMPARE_FIELD(Vehicle, next_free_seat);
|
|
|
|
COMPARE_FIELD(Vehicle, restraints_position);
|
|
|
|
COMPARE_FIELD(Vehicle, spin_speed);
|
|
|
|
COMPARE_FIELD(Vehicle, sound2_flags);
|
|
|
|
COMPARE_FIELD(Vehicle, spin_sprite);
|
|
|
|
COMPARE_FIELD(Vehicle, sound1_id);
|
|
|
|
COMPARE_FIELD(Vehicle, sound1_volume);
|
|
|
|
COMPARE_FIELD(Vehicle, sound2_id);
|
|
|
|
COMPARE_FIELD(Vehicle, sound2_volume);
|
|
|
|
COMPARE_FIELD(Vehicle, sound_vector_factor);
|
|
|
|
COMPARE_FIELD(Vehicle, cable_lift_target);
|
|
|
|
COMPARE_FIELD(Vehicle, speed);
|
|
|
|
COMPARE_FIELD(Vehicle, powered_acceleration);
|
|
|
|
COMPARE_FIELD(Vehicle, var_C4);
|
|
|
|
COMPARE_FIELD(Vehicle, animation_frame);
|
2019-05-11 21:31:34 +02:00
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
{
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, pad_C6[i]);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2020-01-19 16:53:17 +01:00
|
|
|
COMPARE_FIELD(Vehicle, var_C8);
|
|
|
|
COMPARE_FIELD(Vehicle, var_CA);
|
|
|
|
COMPARE_FIELD(Vehicle, scream_sound_id);
|
|
|
|
COMPARE_FIELD(Vehicle, TrackSubposition);
|
|
|
|
COMPARE_FIELD(Vehicle, num_laps);
|
|
|
|
COMPARE_FIELD(Vehicle, brake_speed);
|
|
|
|
COMPARE_FIELD(Vehicle, lost_time_out);
|
|
|
|
COMPARE_FIELD(Vehicle, vertical_drop_countdown);
|
|
|
|
COMPARE_FIELD(Vehicle, var_D3);
|
|
|
|
COMPARE_FIELD(Vehicle, mini_golf_current_animation);
|
|
|
|
COMPARE_FIELD(Vehicle, mini_golf_flags);
|
|
|
|
COMPARE_FIELD(Vehicle, ride_subtype);
|
|
|
|
COMPARE_FIELD(Vehicle, colours_extended);
|
|
|
|
COMPARE_FIELD(Vehicle, seat_rotation);
|
|
|
|
COMPARE_FIELD(Vehicle, target_seat_rotation);
|
2020-04-06 23:08:23 +02:00
|
|
|
COMPARE_FIELD(Vehicle, BoatLocation.x);
|
|
|
|
COMPARE_FIELD(Vehicle, BoatLocation.y);
|
2021-04-11 19:02:19 +02:00
|
|
|
COMPARE_FIELD(Vehicle, IsCrashedVehicle);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
2020-01-19 18:19:45 +01:00
|
|
|
void CompareSpriteDataLitter(const Litter& spriteBase, const Litter& spriteCmp, GameStateSpriteChange_t& changeData) const
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2020-01-19 16:50:01 +01:00
|
|
|
COMPARE_FIELD(Litter, creationTick);
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
2020-04-06 23:32:41 +02:00
|
|
|
void CompareSpriteDataMoneyEffect(
|
|
|
|
const MoneyEffect& spriteBase, const MoneyEffect& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-05-16 22:04:31 +02:00
|
|
|
COMPARE_FIELD(MoneyEffect, MoveDelay);
|
|
|
|
COMPARE_FIELD(MoneyEffect, NumMovements);
|
|
|
|
COMPARE_FIELD(MoneyEffect, Vertical);
|
|
|
|
COMPARE_FIELD(MoneyEffect, Value);
|
|
|
|
COMPARE_FIELD(MoneyEffect, OffsetX);
|
|
|
|
COMPARE_FIELD(MoneyEffect, Wiggle);
|
2020-04-06 23:32:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataSteamParticle(
|
|
|
|
const SteamParticle& spriteBase, const SteamParticle& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
COMPARE_FIELD(SteamParticle, time_to_move);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataVehicleCrashParticle(
|
|
|
|
const VehicleCrashParticle& spriteBase, const VehicleCrashParticle& spriteCmp,
|
|
|
|
GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
COMPARE_FIELD(VehicleCrashParticle, time_to_live);
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, colour[i]);
|
|
|
|
}
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, crashed_sprite_base);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, velocity_x);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, velocity_y);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, velocity_z);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, acceleration_x);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, acceleration_y);
|
|
|
|
COMPARE_FIELD(VehicleCrashParticle, acceleration_z);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataDuck(const Duck& spriteBase, const Duck& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
COMPARE_FIELD(Duck, target_x);
|
|
|
|
COMPARE_FIELD(Duck, target_y);
|
|
|
|
COMPARE_FIELD(Duck, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataBalloon(
|
|
|
|
const Balloon& spriteBase, const Balloon& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
COMPARE_FIELD(Balloon, popped);
|
|
|
|
COMPARE_FIELD(Balloon, time_to_move);
|
|
|
|
COMPARE_FIELD(Balloon, colour);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompareSpriteDataJumpingFountain(
|
|
|
|
const JumpingFountain& spriteBase, const JumpingFountain& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
CompareSpriteDataMisc(spriteBase, spriteCmp, changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
COMPARE_FIELD(JumpingFountain, NumTicksAlive);
|
|
|
|
COMPARE_FIELD(JumpingFountain, FountainFlags);
|
|
|
|
COMPARE_FIELD(JumpingFountain, TargetX);
|
|
|
|
COMPARE_FIELD(JumpingFountain, TargetY);
|
|
|
|
COMPARE_FIELD(JumpingFountain, Iteration);
|
2021-03-22 22:24:50 +01:00
|
|
|
COMPARE_FIELD(JumpingFountain, FountainType);
|
2020-04-06 23:32:41 +02:00
|
|
|
}
|
|
|
|
|
2021-01-10 16:14:34 +01:00
|
|
|
void CompareSpriteDataMisc(
|
|
|
|
const MiscEntity& spriteBase, const MiscEntity& spriteCmp, GameStateSpriteChange_t& changeData) const
|
2020-04-06 23:32:41 +02:00
|
|
|
{
|
2021-01-10 16:14:34 +01:00
|
|
|
COMPARE_FIELD(MiscEntity, frame);
|
2020-04-06 23:32:41 +02:00
|
|
|
}
|
|
|
|
|
2019-05-11 21:31:34 +02:00
|
|
|
void CompareSpriteData(const rct_sprite& spriteBase, const rct_sprite& spriteCmp, GameStateSpriteChange_t& changeData) const
|
|
|
|
{
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataCommon(spriteBase.base, spriteCmp.base, changeData);
|
|
|
|
if (spriteBase.base.Type == spriteCmp.base.Type)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-05-29 07:01:32 +02:00
|
|
|
switch (spriteBase.base.Type)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Guest:
|
2021-04-18 18:48:21 +02:00
|
|
|
CompareSpriteDataGuest(
|
2021-05-29 07:01:32 +02:00
|
|
|
static_cast<const Guest&>(spriteBase.base), static_cast<const Guest&>(spriteCmp.base), changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Staff:
|
2021-04-18 18:48:21 +02:00
|
|
|
CompareSpriteDataStaff(
|
2021-05-29 07:01:32 +02:00
|
|
|
static_cast<const Staff&>(spriteBase.base), static_cast<const Staff&>(spriteCmp.base), changeData);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Vehicle:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataVehicle(
|
|
|
|
static_cast<const Vehicle&>(spriteBase.base), static_cast<const Vehicle&>(spriteCmp.base), changeData);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Litter:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataLitter(
|
|
|
|
static_cast<const Litter&>(spriteBase.base), static_cast<const Litter&>(spriteCmp.base), changeData);
|
2019-05-11 21:31:34 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::SteamParticle:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataSteamParticle(
|
|
|
|
static_cast<const SteamParticle&>(spriteBase.base), static_cast<const SteamParticle&>(spriteCmp.base),
|
|
|
|
changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::MoneyEffect:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataMoneyEffect(
|
|
|
|
static_cast<const MoneyEffect&>(spriteBase.base), static_cast<const MoneyEffect&>(spriteCmp.base),
|
|
|
|
changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::CrashedVehicleParticle:
|
|
|
|
CompareSpriteDataVehicleCrashParticle(
|
2021-05-29 07:01:32 +02:00
|
|
|
static_cast<const VehicleCrashParticle&>(spriteBase.base),
|
|
|
|
static_cast<const VehicleCrashParticle&>(spriteCmp.base), changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::ExplosionCloud:
|
|
|
|
case EntityType::CrashSplash:
|
|
|
|
case EntityType::ExplosionFlare:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataMisc(
|
|
|
|
static_cast<const MiscEntity&>(spriteBase.base), static_cast<const MiscEntity&>(spriteCmp.base),
|
|
|
|
changeData);
|
2020-04-06 23:32:41 +02:00
|
|
|
break;
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::JumpingFountain:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataJumpingFountain(
|
|
|
|
static_cast<const JumpingFountain&>(spriteBase.base),
|
|
|
|
static_cast<const JumpingFountain&>(spriteCmp.base), changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Balloon:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataBalloon(
|
|
|
|
static_cast<const Balloon&>(spriteBase.base), static_cast<const Balloon&>(spriteCmp.base), changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Duck:
|
2021-05-29 07:01:32 +02:00
|
|
|
CompareSpriteDataDuck(
|
|
|
|
static_cast<const Duck&>(spriteBase.base), static_cast<const Duck&>(spriteCmp.base), changeData);
|
2021-03-17 09:04:41 +01:00
|
|
|
break;
|
|
|
|
case EntityType::Null:
|
|
|
|
break;
|
|
|
|
default:
|
2020-10-27 22:52:56 +01:00
|
|
|
break;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual GameStateCompareData_t Compare(const GameStateSnapshot_t& base, const GameStateSnapshot_t& cmp) const override final
|
|
|
|
{
|
|
|
|
GameStateCompareData_t res;
|
2021-01-22 14:35:16 +01:00
|
|
|
res.tickLeft = base.tick;
|
|
|
|
res.tickRight = cmp.tick;
|
2019-05-11 21:31:34 +02:00
|
|
|
res.srand0Left = base.srand0;
|
|
|
|
res.srand0Right = cmp.srand0;
|
|
|
|
|
|
|
|
std::vector<rct_sprite> spritesBase = BuildSpriteList(const_cast<GameStateSnapshot_t&>(base));
|
|
|
|
std::vector<rct_sprite> spritesCmp = BuildSpriteList(const_cast<GameStateSnapshot_t&>(cmp));
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
for (uint32_t i = 0; i < static_cast<uint32_t>(spritesBase.size()); i++)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
GameStateSpriteChange_t changeData;
|
|
|
|
changeData.spriteIndex = i;
|
|
|
|
|
|
|
|
const rct_sprite& spriteBase = spritesBase[i];
|
|
|
|
const rct_sprite& spriteCmp = spritesCmp[i];
|
|
|
|
|
2021-05-29 07:01:32 +02:00
|
|
|
changeData.entityType = spriteBase.base.Type;
|
2019-05-11 21:31:34 +02:00
|
|
|
|
2021-05-29 07:01:32 +02:00
|
|
|
if (spriteBase.base.Type == EntityType::Null && spriteCmp.base.Type != EntityType::Null)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
// Sprite was added.
|
|
|
|
changeData.changeType = GameStateSpriteChange_t::ADDED;
|
2021-05-29 07:01:32 +02:00
|
|
|
changeData.entityType = spriteCmp.base.Type;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2021-05-29 07:01:32 +02:00
|
|
|
else if (spriteBase.base.Type != EntityType::Null && spriteCmp.base.Type == EntityType::Null)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
// Sprite was removed.
|
|
|
|
changeData.changeType = GameStateSpriteChange_t::REMOVED;
|
2021-05-29 07:01:32 +02:00
|
|
|
changeData.entityType = spriteBase.base.Type;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
2021-05-29 07:01:32 +02:00
|
|
|
else if (spriteBase.base.Type == EntityType::Null && spriteCmp.base.Type == EntityType::Null)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
// Do nothing.
|
|
|
|
changeData.changeType = GameStateSpriteChange_t::EQUAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CompareSpriteData(spriteBase, spriteCmp, changeData);
|
|
|
|
if (changeData.diffs.size() == 0)
|
|
|
|
{
|
|
|
|
changeData.changeType = GameStateSpriteChange_t::EQUAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
changeData.changeType = GameStateSpriteChange_t::MODIFIED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 21:59:55 +01:00
|
|
|
res.spriteChanges.push_back(std::move(changeData));
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-03-17 09:04:41 +01:00
|
|
|
static const char* GetEntityTypeName(EntityType type)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
switch (type)
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Null:
|
2019-05-11 21:31:34 +02:00
|
|
|
return "Null";
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Guest:
|
|
|
|
return "Guest";
|
|
|
|
case EntityType::Staff:
|
|
|
|
return "Staff";
|
|
|
|
case EntityType::Vehicle:
|
2019-05-11 21:31:34 +02:00
|
|
|
return "Vehicle";
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::Litter:
|
2019-05-11 21:31:34 +02:00
|
|
|
return "Litter";
|
2021-03-17 09:04:41 +01:00
|
|
|
case EntityType::SteamParticle:
|
|
|
|
return "Misc: Steam Particle";
|
|
|
|
case EntityType::MoneyEffect:
|
|
|
|
return "Misc: Money effect";
|
|
|
|
case EntityType::CrashedVehicleParticle:
|
|
|
|
return "Misc: Crash Vehicle Particle";
|
|
|
|
case EntityType::ExplosionCloud:
|
|
|
|
return "Misc: Explosion Cloud";
|
|
|
|
case EntityType::CrashSplash:
|
|
|
|
return "Misc: Crash Splash";
|
|
|
|
case EntityType::ExplosionFlare:
|
|
|
|
return "Misc: Explosion Flare";
|
|
|
|
case EntityType::JumpingFountain:
|
|
|
|
return "Misc: Jumping fountain";
|
|
|
|
case EntityType::Balloon:
|
|
|
|
return "Misc: Balloon";
|
|
|
|
case EntityType::Duck:
|
|
|
|
return "Misc: Duck";
|
|
|
|
default:
|
|
|
|
break;
|
2019-05-11 21:31:34 +02:00
|
|
|
}
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
|
2021-06-08 09:35:31 +02:00
|
|
|
virtual std::string GetCompareDataText(const GameStateCompareData_t& cmpData) const override
|
2019-05-11 21:31:34 +02:00
|
|
|
{
|
|
|
|
std::string outputBuffer;
|
|
|
|
char tempBuffer[1024] = {};
|
|
|
|
|
2021-01-22 14:35:16 +01:00
|
|
|
if (cmpData.tickLeft != cmpData.tickRight)
|
|
|
|
{
|
|
|
|
outputBuffer += "WARNING: Comparing two snapshots with different ticks, this will very likely result in false "
|
|
|
|
"positives\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(tempBuffer, sizeof(tempBuffer), "tick left = %08X, tick right = %08X\n", cmpData.tickLeft, cmpData.tickRight);
|
2019-05-11 21:31:34 +02:00
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
|
|
|
|
snprintf(
|
|
|
|
tempBuffer, sizeof(tempBuffer), "srand0 left = %08X, srand0 right = %08X\n", cmpData.srand0Left,
|
|
|
|
cmpData.srand0Right);
|
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
|
|
|
|
for (auto& change : cmpData.spriteChanges)
|
|
|
|
{
|
|
|
|
if (change.changeType == GameStateSpriteChange_t::EQUAL)
|
|
|
|
continue;
|
|
|
|
|
2021-03-17 09:04:41 +01:00
|
|
|
const char* typeName = GetEntityTypeName(change.entityType);
|
2019-05-11 21:31:34 +02:00
|
|
|
|
|
|
|
if (change.changeType == GameStateSpriteChange_t::ADDED)
|
|
|
|
{
|
|
|
|
snprintf(tempBuffer, sizeof(tempBuffer), "Sprite added (%s), index: %u\n", typeName, change.spriteIndex);
|
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
}
|
|
|
|
else if (change.changeType == GameStateSpriteChange_t::REMOVED)
|
|
|
|
{
|
|
|
|
snprintf(tempBuffer, sizeof(tempBuffer), "Sprite removed (%s), index: %u\n", typeName, change.spriteIndex);
|
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
}
|
|
|
|
else if (change.changeType == GameStateSpriteChange_t::MODIFIED)
|
|
|
|
{
|
|
|
|
snprintf(
|
|
|
|
tempBuffer, sizeof(tempBuffer), "Sprite modifications (%s), index: %u\n", typeName, change.spriteIndex);
|
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
for (auto& diff : change.diffs)
|
|
|
|
{
|
|
|
|
snprintf(
|
|
|
|
tempBuffer, sizeof(tempBuffer),
|
|
|
|
" %s::%s, len = %u, offset = %u, left = 0x%.16llX, right = 0x%.16llX\n", diff.structname,
|
2020-04-17 21:36:25 +02:00
|
|
|
diff.fieldname, static_cast<uint32_t>(diff.length), static_cast<uint32_t>(diff.offset),
|
|
|
|
static_cast<unsigned long long>(diff.valueA), static_cast<unsigned long long>(diff.valueB));
|
2019-05-11 21:31:34 +02:00
|
|
|
outputBuffer += tempBuffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 09:35:31 +02:00
|
|
|
return outputBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool LogCompareDataToFile(const std::string& fileName, const GameStateCompareData_t& cmpData) const override
|
|
|
|
{
|
|
|
|
auto outputBuffer = GetCompareDataText(cmpData);
|
2019-05-11 21:31:34 +02:00
|
|
|
|
|
|
|
FILE* fp = fopen(fileName.c_str(), "wt");
|
|
|
|
if (!fp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
fputs(outputBuffer.c_str(), fp);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
CircularBuffer<std::unique_ptr<GameStateSnapshot_t>, MaximumGameStateSnapshots> _snapshots;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<IGameStateSnapshots> CreateGameStateSnapshots()
|
|
|
|
{
|
|
|
|
return std::make_unique<GameStateSnapshots>();
|
|
|
|
}
|