mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-01 18:13:57 +00:00
* Add multiplayer extension * Fix animation system to work when host is outside ISLE world - Move TickHostSessions outside m_inIsleWorld gate so the host can coordinate animations from any world - Load animation catalog early in HandleCreate so the host can coordinate before entering the ISLE world - Use network-reported positions for remote player location detection instead of requiring spawned ROIs - Always erase sessions at launch — the host's job ends when the animation starts; clients play and complete independently - Replace BroadcastAnimComplete with locally-driven completion callbacks: host generates eventId at launch, clients cache completion JSON at start time, fire it when ScenePlayer finishes - Make StopAnimation only do local cleanup (stop playback, cancel own interest, reset coordinator) without destroying the session host, so other players' sessions survive world transitions - Broadcast state=0 in ResetAnimationState for full teardown paths (shutdown, reconnect, host migration) so clients aren't left with stale session state * Fix use-after-free crash in ScenePlayer when remote player disconnects mid-animation When a remote player's ROI is destroyed (disconnect, timeout, or respawn), notify all active ScenePlayer instances to null out dangling references before the ROI is freed. The animation engine already handles null ROI map entries gracefully, so playback continues for remaining participants. * Fix crash when performer's child ROIs are left dangling in ScenePlayer NotifyROIDestroyed now walks the parent chain to also invalidate child ROIs of the destroyed performer (head, limbs, etc.) that were placed into the roiMap by BuildROIMap. The ancestor walk happens once; all other fields are cleaned with simple pointer equality. * Allow spectator to play click animation during scene playback * Make PTATCAM track spectator ROI instead of camera in ScenePlayer * Only regenerate emscripten version files when git state changes Replace add_custom_target(ALL) with add_custom_command(OUTPUT) so the version script only runs when .git/HEAD or the current branch ref file changes, instead of on every build. * Fix ROI name collision causing dangling pointers in NPC locomotion roiMaps When ScenePlayer created cloned NPC ROIs for cooperative animations, it renamed them to match the original character name and added them to the ViewManager. This created a name collision: two ROIs with the same name. The original game's AppendROIToScene searches by name and stops at the first match, so if a locomotion BuildROIMap ran while the clone existed, it could capture pointers to the clone's child ROIs. When the clone was later destroyed (CleanupProps), those roiMap entries became dangling pointers, crashing in AnimateWithTransform at roi.h:151 (SetVisibility). Fix: use the alias mechanism (already supported by AnimUtils::BuildROIMap) instead of renaming clones. Also unify all ROI name generation behind a shared counter to prevent character manager key collisions.
315 lines
9.4 KiB
C++
315 lines
9.4 KiB
C++
#ifndef LEGOANIMATIONMANAGER_H
|
|
#define LEGOANIMATIONMANAGER_H
|
|
|
|
#include "decomp.h"
|
|
#include "extensions/fwd.h"
|
|
#include "lego1_export.h"
|
|
#include "legolocations.h"
|
|
#include "legomain.h"
|
|
#include "legostate.h"
|
|
#include "legotraninfolist.h"
|
|
#include "mxcore.h"
|
|
#include "mxgeometry/mxquaternion.h"
|
|
|
|
class LegoAnimPresenter;
|
|
class LegoEntity;
|
|
class LegoExtraActor;
|
|
class LegoFile;
|
|
class LegoPathActor;
|
|
class LegoPathBoundary;
|
|
class LegoROIList;
|
|
struct LegoOrientedEdge;
|
|
class LegoWorld;
|
|
class MxDSAction;
|
|
|
|
// SIZE 0x30
|
|
struct ModelInfo {
|
|
char* m_name; // 0x00
|
|
MxU8 m_unk0x04; // 0x04
|
|
float m_location[3]; // 0x08
|
|
float m_direction[3]; // 0x14
|
|
float m_up[3]; // 0x20
|
|
MxU8 m_unk0x2c; // 0x2c
|
|
};
|
|
|
|
// SIZE 0x30
|
|
struct AnimInfo {
|
|
char* m_name; // 0x00
|
|
MxU32 m_objectId; // 0x04
|
|
MxS16 m_location; // 0x08
|
|
MxBool m_unk0x0a; // 0x0a
|
|
MxU8 m_unk0x0b; // 0x0b
|
|
MxU8 m_unk0x0c; // 0x0c
|
|
MxU8 m_unk0x0d; // 0x0d
|
|
float m_unk0x10[4]; // 0x10
|
|
MxU8 m_modelCount; // 0x20
|
|
MxU16 m_unk0x22; // 0x22
|
|
ModelInfo* m_models; // 0x24
|
|
MxS8 m_characterIndex; // 0x28
|
|
MxBool m_unk0x29; // 0x29
|
|
MxS8 m_unk0x2a[3]; // 0x2a
|
|
};
|
|
|
|
// VTABLE: LEGO1 0x100d8d80
|
|
// VTABLE: BETA10 0x101bae58
|
|
// SIZE 0x1c
|
|
class AnimState : public LegoState {
|
|
public:
|
|
AnimState();
|
|
~AnimState() override; // vtable+0x00
|
|
|
|
// FUNCTION: LEGO1 0x10065070
|
|
// FUNCTION: BETA10 0x1004afe0
|
|
const char* ClassName() const override // vtable+0x0c
|
|
{
|
|
// STRING: LEGO1 0x100f0460
|
|
return "AnimState";
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10065080
|
|
MxBool IsA(const char* p_name) const override // vtable+0x10
|
|
{
|
|
return !strcmp(p_name, AnimState::ClassName()) || LegoState::IsA(p_name);
|
|
}
|
|
|
|
MxBool Reset() override; // vtable+0x18
|
|
MxResult Serialize(LegoStorage* p_storage) override; // vtable+0x1c
|
|
|
|
void CopyToAnims(MxU32, AnimInfo* p_anims, MxU32& p_outExtraCharacterId);
|
|
void InitFromAnims(MxU32 p_animsLength, AnimInfo* p_anims, MxU32 p_extraCharacterId);
|
|
|
|
// SYNTHETIC: LEGO1 0x10065130
|
|
// AnimState::`scalar deleting destructor'
|
|
|
|
private:
|
|
MxU32 m_extraCharacterId; // 0x08
|
|
|
|
// appears to store the length of m_unk0x10
|
|
MxU32 m_unk0x0c; // 0x0c
|
|
// dynamically sized array of MxU16, corresponding to AnimInfo::m_unk0x22
|
|
MxU16* m_unk0x10; // 0x10
|
|
|
|
MxU32 m_locationsFlagsLength; // 0x14
|
|
// dynamically sized array of bools, corresponding to LegoLocation.m_unk0x5c
|
|
MxBool* m_locationsFlags; // 0x18
|
|
};
|
|
|
|
// VTABLE: LEGO1 0x100d8c18
|
|
// VTABLE: BETA10 0x101bab60
|
|
// SIZE 0x500
|
|
class LegoAnimationManager : public MxCore {
|
|
public:
|
|
// SIZE 0x18
|
|
struct Character {
|
|
const char* m_name; // 0x00
|
|
MxBool m_inExtras; // 0x04
|
|
MxS8 m_vehicleId; // 0x05
|
|
undefined m_unk0x06; // 0x06 (unused?)
|
|
MxBool m_unk0x07; // 0x07
|
|
MxBool m_unk0x08; // 0x08
|
|
MxBool m_unk0x09; // 0x09
|
|
MxS32 m_unk0x0c; // 0x0c
|
|
MxS32 m_unk0x10; // 0x10
|
|
MxBool m_active; // 0x14
|
|
MxU8 m_unk0x15; // 0x15
|
|
MxS8 m_unk0x16; // 0x16
|
|
};
|
|
|
|
// SIZE 0x08
|
|
struct Vehicle {
|
|
const char* m_name; // 0x00
|
|
MxBool m_unk0x04; // 0x04
|
|
MxBool m_unk0x05; // 0x05
|
|
};
|
|
|
|
// SIZE 0x18
|
|
struct Extra {
|
|
LegoROI* m_roi; // 0x00
|
|
MxS32 m_characterId; // 0x04
|
|
MxLong m_unk0x08; // 0x08
|
|
MxBool m_unk0x0c; // 0x0c
|
|
MxBool m_unk0x0d; // 0x0d
|
|
float m_speed; // 0x10
|
|
MxBool m_unk0x14; // 0x14
|
|
};
|
|
|
|
enum PlayMode {
|
|
e_unk0 = 0,
|
|
e_unk1,
|
|
e_unk2
|
|
};
|
|
|
|
LegoAnimationManager();
|
|
~LegoAnimationManager() override;
|
|
|
|
MxLong Notify(MxParam& p_param) override; // vtable+0x04
|
|
MxResult Tickle() override; // vtable+0x08
|
|
|
|
// FUNCTION: LEGO1 0x1005ec80
|
|
// FUNCTION: BETA10 0x100483d0
|
|
const char* ClassName() const override // vtable+0x0c
|
|
{
|
|
// STRING: LEGO1 0x100f7508
|
|
return "LegoAnimationManager";
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1005ec90
|
|
MxBool IsA(const char* p_name) const override // vtable+0x10
|
|
{
|
|
return !strcmp(p_name, ClassName()) || MxCore::IsA(p_name);
|
|
}
|
|
|
|
void Reset(MxBool p_und);
|
|
void Suspend();
|
|
void Resume();
|
|
void FUN_1005f6d0(MxBool p_unk0x400);
|
|
void EnableCamAnims(MxBool p_enableCamAnims);
|
|
MxResult LoadWorldInfo(LegoOmni::World p_worldId);
|
|
MxBool FindVehicle(const char* p_name, MxU32& p_index);
|
|
MxResult ReadAnimInfo(LegoStorage* p_storage, AnimInfo* p_info);
|
|
MxResult ReadModelInfo(LegoStorage* p_storage, ModelInfo* p_info);
|
|
void FUN_10060480(const LegoChar* p_characterNames[], MxU32 p_numCharacterNames);
|
|
void FUN_100604d0(MxBool p_unk0x08);
|
|
void FUN_100604f0(MxS32 p_objectIds[], MxU32 p_numObjectIds);
|
|
void FUN_10060540(MxBool p_unk0x29);
|
|
void FUN_10060570(MxBool p_unk0x1a);
|
|
MxResult StartEntityAction(MxDSAction& p_dsAction, LegoEntity* p_entity);
|
|
MxResult FUN_10060dc0(
|
|
MxU32 p_objectId,
|
|
MxMatrix* p_matrix,
|
|
MxBool p_param3,
|
|
MxU8 p_param4,
|
|
LegoROI* p_roi,
|
|
MxBool p_param6,
|
|
MxBool p_param7,
|
|
MxBool p_param8,
|
|
MxBool p_param9
|
|
);
|
|
void CameraTriggerFire(LegoPathActor* p_actor, MxBool, MxU32 p_location, MxBool p_bool);
|
|
void FUN_10061010(MxBool p_und);
|
|
LegoTranInfo* GetTranInfo(MxU32 p_index);
|
|
void FUN_10062770();
|
|
void PurgeExtra(MxBool p_und);
|
|
void AddExtra(MxS32 p_location, MxBool p_und);
|
|
void FUN_10063270(LegoROIList* p_list, LegoAnimPresenter* p_presenter);
|
|
void FUN_10063780(LegoROIList* p_list);
|
|
MxResult FUN_10064670(Vector3* p_position);
|
|
MxResult FUN_10064740(Vector3* p_position);
|
|
MxResult FUN_10064880(const char* p_name, MxS32 p_unk0x0c, MxS32 p_unk0x10);
|
|
MxBool FUN_10064ee0(MxU32 p_objectId);
|
|
|
|
LEGO1_EXPORT static void configureLegoAnimationManager(MxS32 p_legoAnimationManagerConfig);
|
|
|
|
// SYNTHETIC: LEGO1 0x1005ed10
|
|
// LegoAnimationManager::`scalar deleting destructor'
|
|
|
|
private:
|
|
friend class Multiplayer::NetworkManager;
|
|
friend class Multiplayer::Animation::Catalog;
|
|
|
|
void Init();
|
|
MxResult FUN_100605e0(
|
|
MxU32 p_index,
|
|
MxBool p_unk0x0a,
|
|
MxMatrix* p_matrix,
|
|
MxBool p_bool1,
|
|
LegoROI* p_roi,
|
|
MxBool p_bool2,
|
|
MxBool p_bool3,
|
|
MxBool p_bool4,
|
|
MxBool p_bool5
|
|
);
|
|
MxResult FUN_100609f0(MxU32 p_objectId, MxMatrix* p_matrix, MxBool p_und1, MxBool p_und2);
|
|
void DeleteAnimations();
|
|
void FUN_10061530();
|
|
MxResult FUN_100617c0(MxS32 p_unk0x08, MxU16& p_unk0x0e, MxU16& p_unk0x10);
|
|
MxU16 FUN_10062110(
|
|
LegoROI* p_roi,
|
|
Vector3& p_direction,
|
|
Vector3& p_position,
|
|
LegoPathBoundary* p_boundary,
|
|
float p_speed,
|
|
MxU8 p_unk0x0c,
|
|
MxBool p_unk0x14
|
|
);
|
|
MxS8 GetCharacterIndex(const char* p_name);
|
|
MxBool FUN_100623a0(AnimInfo& p_info);
|
|
MxBool ModelExists(AnimInfo& p_info, const char* p_name);
|
|
void FUN_10062580(AnimInfo& p_info);
|
|
MxBool FUN_10062650(Mx3DPointFloat& p_position, float p_und, LegoROI* p_roi);
|
|
MxBool FUN_10062710(AnimInfo& p_info);
|
|
MxBool FUN_10062e20(LegoROI* p_roi, LegoAnimPresenter* p_presenter);
|
|
void FUN_10063950(LegoROI* p_roi);
|
|
void FUN_10063aa0();
|
|
MxBool FUN_10063b90(LegoWorld* p_world, LegoExtraActor* p_actor, MxU8 p_mood, MxU32 p_characterId);
|
|
void FUN_10063d10();
|
|
void FUN_10063e40(LegoAnimPresenter* p_presenter);
|
|
MxBool FUN_10063fb0(LegoLocation::Boundary* p_boundary, LegoWorld* p_world);
|
|
MxBool FUN_10064010(LegoPathBoundary* p_boundary, LegoOrientedEdge* p_edge, float p_destScale);
|
|
MxBool FUN_10064120(LegoLocation::Boundary* p_boundary, MxBool p_bool1, MxBool p_bool2);
|
|
MxResult FUN_10064380(
|
|
const char* p_name,
|
|
const char* p_boundaryName,
|
|
MxS32 p_src,
|
|
float p_srcScale,
|
|
MxS32 p_dest,
|
|
float p_destScale,
|
|
MxU32 p_undIdx1,
|
|
MxS32 p_unk0x0c,
|
|
MxU32 p_undIdx2,
|
|
MxS32 p_unk0x10,
|
|
float p_speed
|
|
);
|
|
void FUN_100648f0(LegoTranInfo* p_tranInfo, MxLong p_unk0x404);
|
|
void FUN_10064b50(MxLong p_time);
|
|
|
|
LegoOmni::World m_worldId; // 0x08
|
|
MxU16 m_animCount; // 0x0c
|
|
MxU16 m_unk0x0e; // 0x0e
|
|
MxU16 m_unk0x10; // 0x10
|
|
AnimInfo* m_anims; // 0x14
|
|
undefined2 m_unk0x18; // 0x18
|
|
MxBool m_unk0x1a; // 0x1a
|
|
MxU32 m_unk0x1c; // 0x1c
|
|
LegoTranInfoList* m_tranInfoList; // 0x20
|
|
LegoTranInfoList* m_tranInfoList2; // 0x24
|
|
MxPresenter* m_unk0x28[2]; // 0x28
|
|
MxLong m_unk0x30[2]; // 0x30
|
|
MxBool m_unk0x38; // 0x38
|
|
MxBool m_animRunning; // 0x39
|
|
MxBool m_enableCamAnims; // 0x3a
|
|
Extra m_extras[40]; // 0x3c
|
|
MxU32 m_lastExtraCharacterId; // 0x3fc
|
|
MxBool m_unk0x400; // 0x400
|
|
MxBool m_unk0x401; // 0x401
|
|
MxBool m_unk0x402; // 0x402
|
|
MxLong m_unk0x404; // 0x404
|
|
MxLong m_unk0x408; // 0x408
|
|
MxLong m_unk0x40c; // 0x40c
|
|
MxLong m_unk0x410; // 0x410
|
|
MxU32 m_unk0x414; // 0x414
|
|
MxU32 m_numAllowedExtras; // 0x418
|
|
MxU32 m_maxAllowedExtras; // 0x41c
|
|
AnimState* m_animState; // 0x420
|
|
LegoROIList* m_unk0x424; // 0x424
|
|
MxBool m_suspendedEnableCamAnims; // 0x428
|
|
MxBool m_unk0x429; // 0x429
|
|
MxBool m_unk0x42a; // 0x42a
|
|
MxBool m_suspended; // 0x42b
|
|
LegoTranInfo* m_unk0x42c; // 0x42c
|
|
MxBool m_unk0x430; // 0x430
|
|
MxLong m_unk0x434; // 0x434
|
|
MxLong m_unk0x438; // 0x438
|
|
MxMatrix m_unk0x43c; // 0x43c
|
|
MxMatrix m_unk0x484; // 0x484
|
|
MxQuaternionTransformer m_unk0x4cc; // 0x4cc
|
|
};
|
|
|
|
// TEMPLATE: LEGO1 0x10061750
|
|
// MxListCursor<LegoTranInfo *>::MxListCursor<LegoTranInfo *>
|
|
|
|
// TEMPLATE: BETA10 0x1004b5d0
|
|
// MxListCursor<LegoTranInfo *>::Next
|
|
|
|
#endif // LEGOANIMATIONMANAGER_H
|