isle-portable/LEGO1/lego/legoomni/src/entity/legoworld.cpp
Nathan M Gilbert d07d7edc81
Implement methods to load dta files. (#673)
* Implement methods to load dta files.

* fix style issues

* fix vtables

* fix more style issues

* fix ddtor names

* Remove explicit padding, add annotations, asserts

* Use List template

* trigger CI

* Fix class refactor

* Match code, fix annotations

* Fix

* 98% match

* Fix

---------

Co-authored-by: Christian Semmler <mail@csemmler.com>
2024-03-16 01:43:45 +01:00

719 lines
16 KiB
C++

#include "legoworld.h"
#include "legoanimationmanager.h"
#include "legoanimpresenter.h"
#include "legobuildingmanager.h"
#include "legocontrolmanager.h"
#include "legogamestate.h"
#include "legoinputmanager.h"
#include "legolocomotionanimpresenter.h"
#include "legonavcontroller.h"
#include "legoomni.h"
#include "legoplantmanager.h"
#include "legosoundmanager.h"
#include "legoutils.h"
#include "legovideomanager.h"
#include "misc.h"
#include "mxactionnotificationparam.h"
#include "mxcontrolpresenter.h"
#include "mxmisc.h"
#include "mxnotificationmanager.h"
#include "mxnotificationparam.h"
#include "mxticklemanager.h"
DECOMP_SIZE_ASSERT(LegoWorld, 0xf8)
DECOMP_SIZE_ASSERT(LegoEntityList, 0x18)
DECOMP_SIZE_ASSERT(LegoEntityListCursor, 0x10)
DECOMP_SIZE_ASSERT(LegoCacheSoundList, 0x18)
DECOMP_SIZE_ASSERT(LegoCacheSoundListCursor, 0x10)
// FUNCTION: LEGO1 0x1001ca40
LegoWorld::LegoWorld() : m_list0x68(TRUE)
{
m_startupTicks = e_four;
m_cameraController = NULL;
m_entityList = NULL;
m_cacheSoundList = NULL;
m_destroyed = FALSE;
m_hideAnimPresenter = NULL;
m_worldStarted = FALSE;
NotificationManager()->Register(this);
}
// FUNCTION: LEGO1 0x1001d670
MxBool LegoWorld::VTable0x5c()
{
return FALSE;
}
// FUNCTION: LEGO1 0x1001d680
MxBool LegoWorld::VTable0x64()
{
return FALSE;
}
// FUNCTION: LEGO1 0x1001dfa0
LegoWorld::~LegoWorld()
{
Destroy(TRUE);
TickleManager()->UnregisterClient(this);
NotificationManager()->Unregister(this);
}
// FUNCTION: LEGO1 0x1001e0b0
MxResult LegoWorld::Create(MxDSAction& p_dsAction)
{
MxEntity::Create(p_dsAction);
m_entityList = new LegoEntityList(TRUE);
if (!m_entityList) {
return FAILURE;
}
m_cacheSoundList = new LegoCacheSoundList(TRUE);
if (!m_cacheSoundList) {
return FAILURE;
}
if (!VTable0x54()) {
return FAILURE;
}
if (p_dsAction.GetFlags() & MxDSAction::c_enabled) {
if (CurrentWorld()) {
CurrentWorld()->Enable(FALSE);
}
SetCurrentWorld(this);
ControlManager()->FUN_10028df0(&m_controlPresenters);
}
SetIsWorldActive(TRUE);
m_scriptIndex = -1;
return SUCCESS;
}
// FUNCTION: LEGO1 0x1001e9d0
void LegoWorld::Destroy(MxBool p_fromDestructor)
{
m_destroyed = TRUE;
if (CurrentWorld() == this) {
ControlManager()->FUN_10028df0(NULL);
SetCurrentWorld(NULL);
}
m_list0x68.DeleteAll();
if (m_cameraController) {
delete m_cameraController;
m_cameraController = NULL;
}
MxPresenterListCursor animPresenterCursor(&m_animPresenters);
MxPresenter* presenter;
while (animPresenterCursor.First(presenter)) {
animPresenterCursor.Detach();
MxDSAction* action = presenter->GetAction();
if (action) {
if (presenter->IsA("LegoLocomotionAnimPresenter")) {
LegoLocomotionAnimPresenter* animPresenter = (LegoLocomotionAnimPresenter*) presenter;
animPresenter->DecrementUnknown0xd4();
if (animPresenter->GetUnknown0xd4() == 0) {
FUN_100b7220(action, MxDSAction::c_world, FALSE);
presenter->EndAction();
}
}
else {
FUN_100b7220(action, MxDSAction::c_world, FALSE);
presenter->EndAction();
}
}
}
while (!m_set0xa8.empty()) {
MxCoreSet::iterator it = m_set0xa8.begin();
MxCore* object = *it;
m_set0xa8.erase(it);
if (object->IsA("MxPresenter")) {
MxPresenter* presenter = (MxPresenter*) object;
MxDSAction* action = presenter->GetAction();
if (action) {
FUN_100b7220(action, MxDSAction::c_world, FALSE);
presenter->EndAction();
}
}
else {
delete object;
}
}
MxPresenterListCursor controlPresenterCursor(&m_controlPresenters);
while (controlPresenterCursor.First(presenter)) {
controlPresenterCursor.Detach();
MxDSAction* action = presenter->GetAction();
if (action) {
FUN_100b7220(action, MxDSAction::c_world, FALSE);
presenter->EndAction();
}
}
if (m_scriptIndex != -1 && m_set0xd0.empty()) {
PlantManager()->FUN_100263a0(m_scriptIndex);
BuildingManager()->FUN_1002fb30();
}
if (m_entityList) {
LegoEntityListCursor cursor(m_entityList);
LegoEntity* entity;
while (cursor.First(entity)) {
cursor.Detach();
if (!(entity->GetFlags() & LegoEntity::c_bit2)) {
delete entity;
}
}
delete m_entityList;
m_entityList = NULL;
}
if (m_cacheSoundList) {
LegoCacheSoundListCursor cursor(m_cacheSoundList);
LegoCacheSound* sound;
while (cursor.First(sound)) {
cursor.Detach();
SoundManager()->GetUnknown0x40()->FUN_1003dc40(&sound);
}
delete m_cacheSoundList;
m_cacheSoundList = NULL;
}
while (!m_roiList.empty()) {
LegoROI* roi = m_roiList.front();
m_roiList.pop_front();
delete roi;
}
if (!p_fromDestructor) {
LegoEntity::Destroy(FALSE);
}
}
// FUNCTION: LEGO1 0x1001f5e0
MxLong LegoWorld::Notify(MxParam& p_param)
{
MxLong ret = 0;
switch (((MxNotificationParam&) p_param).GetNotification()) {
case c_notificationEndAction: {
MxPresenter* presenter = (MxPresenter*) ((MxEndActionNotificationParam&) p_param).GetSender();
Remove(presenter);
ret = 1;
break;
}
case c_notificationNewPresenter:
TickleManager()->RegisterClient(this, 100);
break;
}
return ret;
}
// FUNCTION: LEGO1 0x1001f630
LegoCameraController* LegoWorld::VTable0x54()
{
MxBool success = FALSE;
if (!VideoManager()) {
goto done;
}
if (!(m_cameraController = new LegoCameraController())) {
goto done;
}
if (m_cameraController->Create() != SUCCESS) {
goto done;
}
m_cameraController->OnViewSize(
VideoManager()->GetVideoParam().GetRect().GetWidth(),
VideoManager()->GetVideoParam().GetRect().GetHeight()
);
success = TRUE;
done:
if (!success) {
if (m_cameraController) {
delete m_cameraController;
m_cameraController = NULL;
}
}
return m_cameraController;
}
// FUNCTION: LEGO1 0x1001fc80
void LegoWorld::FUN_1001fc80(IslePathActor* p_actor)
{
LegoPathControllerListCursor cursor(&m_list0x68);
LegoPathController* controller;
while (cursor.Next(controller)) {
if (!controller->FUN_10046770(p_actor)) {
break;
}
}
}
// FUNCTION: LEGO1 0x10020120
MxResult LegoWorld::GetCurrPathInfo(LegoPathBoundary** p_path, MxS32& p_value)
{
LegoPathControllerListCursor cursor(&m_list0x68);
LegoPathController* controller;
cursor.Next(controller);
if (!controller) {
return FAILURE;
}
return controller->FUN_10046b30(p_path, p_value);
}
// FUNCTION: LEGO1 0x10020220
void LegoWorld::Add(MxCore* p_object)
{
if (p_object && !p_object->IsA("LegoWorld") && !p_object->IsA("LegoWorldPresenter")) {
if (p_object->IsA("LegoAnimPresenter")) {
LegoAnimPresenter* animPresenter = (LegoAnimPresenter*) p_object;
if (!strcmpi(animPresenter->GetAction()->GetObjectName(), "ConfigAnimation")) {
FUN_1003e050(animPresenter);
animPresenter->GetAction()->SetDuration(animPresenter->GetAnimation()->GetDuration());
}
}
if (p_object->IsA("MxControlPresenter")) {
MxPresenterListCursor cursor(&m_controlPresenters);
if (cursor.Find((MxPresenter*) p_object)) {
return;
}
m_controlPresenters.Append((MxPresenter*) p_object);
}
else if (p_object->IsA("MxEntity")) {
LegoEntityListCursor cursor(m_entityList);
if (cursor.Find((LegoEntity*) p_object)) {
return;
}
m_entityList->Append((LegoEntity*) p_object);
}
else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || p_object->IsA("LegoLoopingAnimPresenter")) {
MxPresenterListCursor cursor(&m_animPresenters);
if (cursor.Find((MxPresenter*) p_object)) {
return;
}
((MxPresenter*) p_object)->SendToCompositePresenter(Lego());
m_animPresenters.Append(((MxPresenter*) p_object));
if (p_object->IsA("LegoHideAnimPresenter")) {
m_hideAnimPresenter = (LegoHideAnimPresenter*) p_object;
}
}
else if (p_object->IsA("LegoCacheSound")) {
LegoCacheSoundListCursor cursor(m_cacheSoundList);
if (cursor.Find((LegoCacheSound*) p_object)) {
return;
}
m_cacheSoundList->Append((LegoCacheSound*) p_object);
}
else {
if (m_set0xa8.find(p_object) == m_set0xa8.end()) {
m_set0xa8.insert(p_object);
}
}
if (!m_set0xd0.empty() && p_object->IsA("MxPresenter")) {
if (((MxPresenter*) p_object)->IsEnabled()) {
((MxPresenter*) p_object)->Enable(FALSE);
m_set0xd0.insert(p_object);
}
}
}
}
// FUNCTION: LEGO1 0x10020f10
void LegoWorld::Remove(MxCore* p_object)
{
if (p_object) {
MxCoreSet::iterator it;
if (p_object->IsA("MxControlPresenter")) {
MxPresenterListCursor cursor(&m_controlPresenters);
if (cursor.Find((MxControlPresenter*) p_object)) {
cursor.Detach();
((MxControlPresenter*) p_object)->GetAction()->SetOrigin(Lego());
((MxControlPresenter*) p_object)->VTable0x68(TRUE);
}
}
else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || p_object->IsA("LegoLoopingAnimPresenter")) {
MxPresenterListCursor cursor(&m_animPresenters);
if (cursor.Find((MxPresenter*) p_object)) {
cursor.Detach();
}
if (p_object->IsA("LegoHideAnimPresenter")) {
m_hideAnimPresenter = NULL;
}
}
else if (p_object->IsA("MxEntity")) {
if (p_object->IsA("LegoPathActor")) {
FUN_1001fc80((IslePathActor*) p_object);
}
if (m_entityList) {
LegoEntityListCursor cursor(m_entityList);
if (cursor.Find((LegoEntity*) p_object)) {
cursor.Detach();
}
}
}
else if (p_object->IsA("LegoCacheSound")) {
LegoCacheSoundListCursor cursor(m_cacheSoundList);
if (cursor.Find((LegoCacheSound*) p_object)) {
cursor.Detach();
}
}
else {
it = m_set0xa8.find(p_object);
if (it != m_set0xa8.end()) {
m_set0xa8.erase(it);
}
}
it = m_set0xd0.find(p_object);
if (it != m_set0xd0.end()) {
m_set0xd0.erase(it);
}
}
}
// FUNCTION: LEGO1 0x100213a0
MxCore* LegoWorld::Find(const char* p_class, const char* p_name)
{
if (!strcmp(p_class, "MxControlPresenter")) {
MxPresenterListCursor cursor(&m_controlPresenters);
MxPresenter* presenter;
while (cursor.Next(presenter)) {
MxDSAction* action = presenter->GetAction();
if (!strcmp(action->GetObjectName(), p_name)) {
return presenter;
}
}
return NULL;
}
else if (!strcmp(p_class, "MxEntity")) {
LegoEntityListCursor cursor(m_entityList);
LegoEntity* entity;
while (cursor.Next(entity)) {
if (!p_name) {
return entity;
}
LegoROI* roi = entity->GetROI();
if (roi && !strcmpi(roi->GetName(), p_name)) {
return entity;
}
}
return NULL;
}
else if (!strcmp(p_class, "LegoAnimPresenter")) {
MxPresenterListCursor cursor(&m_animPresenters);
MxPresenter* presenter;
while (cursor.Next(presenter)) {
if (!strcmpi(((LegoAnimPresenter*) presenter)->GetActionObjectName(), p_name)) {
return presenter;
}
}
return NULL;
}
else {
for (MxCoreSet::iterator it = m_set0xa8.begin(); it != m_set0xa8.end(); it++) {
if ((*it)->IsA(p_class) && (*it)->IsA("MxPresenter")) {
MxPresenter* presenter = (MxPresenter*) *it;
MxDSAction* action = presenter->GetAction();
if (!strcmp(action->GetObjectName(), p_name)) {
return *it;
}
}
}
return NULL;
}
}
// FUNCTION: LEGO1 0x10021790
MxCore* LegoWorld::Find(const MxAtomId& p_atom, MxS32 p_entityId)
{
LegoEntityListCursor entityCursor(m_entityList);
LegoEntity* entity;
while (entityCursor.Next(entity)) {
if (entity->GetAtom() == p_atom && entity->GetEntityId() == p_entityId) {
return entity;
}
}
MxPresenterListCursor controlPresenterCursor(&m_controlPresenters);
MxPresenter* presenter;
while (controlPresenterCursor.Next(presenter)) {
MxDSAction* action = presenter->GetAction();
if (action->GetAtomId() == p_atom && action->GetObjectId() == p_entityId) {
return presenter;
}
}
MxPresenterListCursor animPresenterCursor(&m_animPresenters);
while (animPresenterCursor.Next(presenter)) {
MxDSAction* action = presenter->GetAction();
if (action && action->GetAtomId() == p_atom && action->GetObjectId() == p_entityId) {
return presenter;
}
}
for (MxCoreSet::iterator it = m_set0xa8.begin(); it != m_set0xa8.end(); it++) {
MxCore* core = *it;
if (core->IsA("MxPresenter")) {
MxPresenter* presenter = (MxPresenter*) *it;
MxDSAction* action = presenter->GetAction();
if (action->GetAtomId() == p_atom && action->GetObjectId() == p_entityId) {
return *it;
}
}
}
return NULL;
}
// FUNCTION: LEGO1 0x10021a70
void LegoWorld::Enable(MxBool p_enable)
{
if (p_enable && !m_set0xd0.empty()) {
if (CurrentWorld() != this) {
if (CurrentWorld()) {
AnimationManager()->FUN_10061010(0);
CurrentWorld()->Enable(FALSE);
LegoEntityListCursor cursor(m_entityList);
LegoEntity* entity;
while (cursor.Next(entity)) {
if (entity->GetROI()) {
entity->GetROI()->SetEntity(entity);
GetViewManager()->Add(entity->GetROI());
}
}
}
while (!m_set0xd0.empty()) {
MxCoreSet::iterator it = m_set0xd0.begin();
if ((*it)->IsA("MxPresenter")) {
((MxPresenter*) *it)->Enable(TRUE);
}
else if ((*it)->IsA("LegoPathController")) {
((LegoPathController*) *it)->Enable(TRUE);
}
m_set0xd0.erase(it);
}
SetCurrentWorld(this);
ControlManager()->FUN_10028df0(&m_controlPresenters);
InputManager()->SetCamera(m_cameraController);
if (m_cameraController) {
InputManager()->Register(m_cameraController->GetNavController());
Lego()->SetNavController(m_cameraController->GetNavController());
}
if (m_scriptIndex != -1) {
PlantManager()->FUN_10026360(m_scriptIndex);
AnimationManager()->LoadScriptInfo(m_scriptIndex);
BuildingManager()->FUN_1002fa00();
AnimationManager()->FUN_1005f0b0();
}
GameState()->ResetROI();
SetIsWorldActive(TRUE);
}
}
else if (!p_enable && m_set0xd0.empty()) {
MxPresenter* presenter;
LegoPathController* controller;
IslePathActor* actor = CurrentActor();
if (actor) {
FUN_1001fc80(actor);
}
AnimationManager()->FUN_1005ee80(FALSE);
m_set0xd0.insert(this);
if (m_scriptIndex != -1) {
PlantManager()->FUN_100263a0(m_scriptIndex);
BuildingManager()->FUN_1002fb30();
}
MxPresenterListCursor controlPresenterCursor(&m_controlPresenters);
while (controlPresenterCursor.Next(presenter)) {
if (presenter->IsEnabled()) {
m_set0xd0.insert(presenter);
presenter->Enable(FALSE);
}
}
for (MxCoreSet::iterator it = m_set0xa8.begin(); it != m_set0xa8.end(); it++) {
if ((*it)->IsA("LegoActionControlPresenter") ||
((*it)->IsA("MxPresenter") && ((MxPresenter*) *it)->IsEnabled())) {
m_set0xd0.insert(*it);
((MxPresenter*) *it)->Enable(FALSE);
}
}
if (CurrentWorld() && CurrentWorld() == this) {
ControlManager()->FUN_10028df0(NULL);
Lego()->SetCurrentWorld(NULL);
}
if (InputManager()->GetCamera() == m_cameraController) {
InputManager()->ClearCamera();
}
if (m_cameraController) {
InputManager()->UnRegister(m_cameraController->GetNavController());
if (NavController() == m_cameraController->GetNavController()) {
Lego()->SetNavController(NULL);
}
}
LegoPathControllerListCursor pathControllerCursor(&m_list0x68);
while (pathControllerCursor.Next(controller)) {
controller->Enable(FALSE);
m_set0xd0.insert(controller);
}
GetViewManager()->RemoveAll(NULL);
}
}
// FUNCTION: LEGO1 0x10022080
MxResult LegoWorld::Tickle()
{
if (!m_worldStarted) {
switch (m_startupTicks) {
case e_start:
m_worldStarted = TRUE;
SetAppCursor(0);
ReadyWorld();
return TRUE;
case e_two:
if (PresentersPending()) {
break;
}
default:
m_startupTicks--;
}
}
return TRUE;
}
// FUNCTION: LEGO1 0x100220e0
MxBool LegoWorld::PresentersPending()
{
MxPresenterListCursor controlPresenterCursor(&m_controlPresenters);
MxPresenter* presenter;
while (controlPresenterCursor.Next(presenter)) {
if (presenter->IsEnabled() && !presenter->HasTickleStatePassed(MxPresenter::e_starting)) {
return TRUE;
}
}
MxPresenterListCursor animPresenterCursor(&m_animPresenters);
while (animPresenterCursor.Next(presenter)) {
if (presenter->IsEnabled()) {
if (presenter->IsA("LegoLocomotionAnimPresenter")) {
if (!presenter->HasTickleStatePassed(MxPresenter::e_ready)) {
return TRUE;
}
}
else {
if (!presenter->HasTickleStatePassed(MxPresenter::e_starting)) {
return TRUE;
}
}
}
}
for (MxCoreSet::iterator it = m_set0xa8.begin(); it != m_set0xa8.end(); it++) {
if ((*it)->IsA("MxPresenter")) {
presenter = (MxPresenter*) *it;
if (presenter->IsEnabled() && !presenter->HasTickleStatePassed(MxPresenter::e_starting)) {
return TRUE;
}
}
}
return FALSE;
}
// FUNCTION: LEGO1 0x10022340
void LegoWorld::ReadyWorld()
{
TickleManager()->UnregisterClient(this);
}