Merge branch 'master' into zimd

This commit is contained in:
Anders Jenbo 2025-06-13 00:38:17 +02:00 committed by GitHub
commit c99d98a543
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 815 additions and 381 deletions

View File

@ -40,6 +40,7 @@ jobs:
- { name: 'msys2 mingw32', os: 'windows-latest', dx5: false, config: false, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' }
- { name: 'msys2 mingw64', os: 'windows-latest', dx5: false, config: true, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' }
- { name: 'macOS', os: 'macos-latest', dx5: false, config: true, build-type: 'Debug', brew: true, werror: true, clang-tidy: false }
- { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, build-type: 'Debug', emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' }
steps:
- name: Setup vcvars
if: ${{ !!matrix.msvc }}
@ -74,6 +75,10 @@ jobs:
brew install cmake ninja llvm qt6
echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@master
if: ${{ matrix.emsdk }}
- name: Setup ninja
if: ${{ matrix.msvc }}
uses: ashutoshvarma/setup-ninja@master
@ -82,7 +87,7 @@ jobs:
- name: Configure (CMake)
run: |
cmake -S . -B build -GNinja \
${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
-DISLE_USE_DX5=${{ !!matrix.dx5 }} \
-DISLE_BUILD_CONFIG=${{ matrix.config }} \

View File

@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
project(isle LANGUAGES CXX C VERSION 0.1)
if (EMSCRIPTEN)
add_compile_options(-pthread)
add_link_options(-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1)
set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(CheckCXXSourceCompiles)
@ -34,6 +40,8 @@ option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON)
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll")
set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)")
cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF)
message(STATUS "Isle app: ${ISLE_BUILD_APP}")
message(STATUS "Config app: ${ISLE_BUILD_CONFIG}")
@ -59,9 +67,11 @@ if (DOWNLOAD_DEPENDENCIES)
GIT_TAG "main"
EXCLUDE_FROM_ALL
)
set(BUILD_DOCS off)
set(BUILD_SHARED_LIBS off)
FetchContent_MakeAvailable(iniparser)
block()
set(BUILD_DOCS off)
set(BUILD_SHARED_LIBS off)
FetchContent_MakeAvailable(iniparser)
endblock()
else()
# find_package looks for already-installed system packages.
# Configure with `-DCMAKE_PREFIX_PATH="/path/to/package1;/path/to/package2"`
@ -129,7 +139,7 @@ target_link_directories(DirectX5::DirectX5 INTERFACE "${CMAKE_SOURCE_DIR}/3rdpar
add_library(Vec::Vec INTERFACE IMPORTED)
target_include_directories(Vec::Vec INTERFACE "${CMAKE_SOURCE_DIR}/3rdparty/vec")
add_library(lego1 SHARED
add_library(lego1
LEGO1/main.cpp
)
target_precompile_headers(lego1 PRIVATE "LEGO1/lego1_pch.h")
@ -497,6 +507,15 @@ if (ISLE_BUILD_APP)
target_include_directories(isle PRIVATE ${valgrind_INCLUDE_PATH})
endif()
endif()
if(EMSCRIPTEN)
target_sources(isle PRIVATE
ISLE/emscripten/events.cpp
ISLE/emscripten/filesystem.cpp
ISLE/emscripten/messagebox.cpp
)
target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"")
set_property(TARGET isle PROPERTY SUFFIX ".html")
endif()
endif()
if (ISLE_BUILD_CONFIG)
@ -602,10 +621,18 @@ else()
endif()
set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Platform name of the package")
install(TARGETS isle lego1 ${install_extra_targets}
if(BUILD_SHARED_LIBS)
list(APPEND install_extra_targets lego1)
endif()
install(TARGETS isle ${install_extra_targets}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
if(EMSCRIPTEN)
install(FILES "$<TARGET_FILE_DIR:isle>/isle.js" "$<TARGET_FILE_DIR:isle>/isle.wasm"
DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
endif()
set(CPACK_PACKAGE_DIRECTORY "dist")
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}")

View File

@ -0,0 +1,38 @@
#include "events.h"
#include "mxdsaction.h"
#include <emscripten.h>
// clang-format off
void Emscripten_SendEvent(const char* p_event, const char* p_json)
{
MAIN_THREAD_EM_ASM({
const eventName = UTF8ToString($0);
let eventDetail = {};
if ($1 && UTF8ToString($1).length > 0) {
eventDetail = JSON.parse(UTF8ToString($1));
}
const targetElement = Module.canvas || window;
const event = new CustomEvent(eventName, {detail : eventDetail});
targetElement.dispatchEvent(event);
}, p_event, p_json);
}
// clang-format on
void Emscripten_SendPresenterProgress(MxPresenter* p_presenter, MxPresenter::TickleState p_tickleState)
{
char buf[128];
SDL_snprintf(
buf,
sizeof(buf),
"{\"objectId\": %d, \"objectName\": \"%s\", \"tickleState\": %d}",
p_presenter->GetAction() ? p_presenter->GetAction()->GetObjectId() : 0,
p_presenter->GetAction() ? p_presenter->GetAction()->GetObjectName() : "",
p_tickleState
);
Emscripten_SendEvent("presenterProgress", buf);
}

8
ISLE/emscripten/events.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef EMSCRIPTEN_EVENTS_H
#define EMSCRIPTEN_EVENTS_H
#include "mxpresenter.h"
void Emscripten_SendPresenterProgress(MxPresenter* p_presenter, MxPresenter::TickleState p_tickleState);
#endif // EMSCRIPTEN_EVENTS_H

View File

@ -0,0 +1,127 @@
#include "filesystem.h"
#include "legogamestate.h"
#include "misc.h"
#include "mxomni.h"
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_log.h>
#include <emscripten/wasmfs.h>
static backend_t opfs = nullptr;
static backend_t fetchfs = nullptr;
void Emscripten_SetupConfig(const char* p_iniConfig)
{
if (!p_iniConfig || !*p_iniConfig) {
return;
}
opfs = wasmfs_create_opfs_backend();
MxString iniConfig = p_iniConfig;
char* parse = iniConfig.GetData();
while ((parse = SDL_strchr(++parse, '/'))) {
*parse = '\0';
wasmfs_create_directory(iniConfig.GetData(), 0644, opfs);
*parse = '/';
}
}
void Emscripten_SetupFilesystem()
{
fetchfs = wasmfs_create_fetch_backend((MxString(Emscripten_streamHost) + MxString("/LEGO")).GetData(), 512 * 1024);
wasmfs_create_directory("/LEGO", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Act2", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Act3", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Build", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Garage", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Hospital", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Infocntr", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Isle", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Police", 0644, fetchfs);
wasmfs_create_directory("/LEGO/Scripts/Race", 0644, fetchfs);
wasmfs_create_directory("/LEGO/data", 0644, fetchfs);
const auto registerFile = [](const char* p_path) {
MxString path = MxString(Emscripten_bundledPath) + MxString(p_path);
path.MapPathToFilesystem();
if (SDL_GetPathInfo(path.GetData(), NULL)) {
SDL_Log("File %s is bundled and won't be streamed", p_path);
}
else {
wasmfs_create_file(p_path, 0644, fetchfs);
MxOmni::GetCDFiles().emplace_back(p_path);
SDL_Log("File %s set up for streaming", p_path);
}
};
registerFile("/LEGO/Scripts/CREDITS.SI");
registerFile("/LEGO/Scripts/INTRO.SI");
registerFile("/LEGO/Scripts/NOCD.SI");
registerFile("/LEGO/Scripts/SNDANIM.SI");
registerFile("/LEGO/Scripts/Act2/ACT2MAIN.SI");
registerFile("/LEGO/Scripts/Act3/ACT3.SI");
registerFile("/LEGO/Scripts/Build/COPTER.SI");
registerFile("/LEGO/Scripts/Build/DUNECAR.SI");
registerFile("/LEGO/Scripts/Build/JETSKI.SI");
registerFile("/LEGO/Scripts/Build/RACECAR.SI");
registerFile("/LEGO/Scripts/Garage/GARAGE.SI");
registerFile("/LEGO/Scripts/Hospital/HOSPITAL.SI");
registerFile("/LEGO/Scripts/Infocntr/ELEVBOTT.SI");
registerFile("/LEGO/Scripts/Infocntr/HISTBOOK.SI");
registerFile("/LEGO/Scripts/Infocntr/INFODOOR.SI");
registerFile("/LEGO/Scripts/Infocntr/INFOMAIN.SI");
registerFile("/LEGO/Scripts/Infocntr/INFOSCOR.SI");
registerFile("/LEGO/Scripts/Infocntr/REGBOOK.SI");
registerFile("/LEGO/Scripts/Isle/ISLE.SI");
registerFile("/LEGO/Scripts/Isle/JUKEBOX.SI");
registerFile("/LEGO/Scripts/Isle/JUKEBOXW.SI");
registerFile("/LEGO/Scripts/Police/POLICE.SI");
registerFile("/LEGO/Scripts/Race/CARRACE.SI");
registerFile("/LEGO/Scripts/Race/CARRACER.SI");
registerFile("/LEGO/Scripts/Race/JETRACE.SI");
registerFile("/LEGO/Scripts/Race/JETRACER.SI");
registerFile("/LEGO/data/ACT1INF.DTA");
registerFile("/LEGO/data/ACT2INF.DTA");
registerFile("/LEGO/data/ACT3INF.DTA");
registerFile("/LEGO/data/BLDDINF.DTA");
registerFile("/LEGO/data/BLDHINF.DTA");
registerFile("/LEGO/data/BLDJINF.DTA");
registerFile("/LEGO/data/BLDRINF.DTA");
registerFile("/LEGO/data/GMAININF.DTA");
registerFile("/LEGO/data/HOSPINF.DTA");
registerFile("/LEGO/data/ICUBEINF.DTA");
registerFile("/LEGO/data/IELEVINF.DTA");
registerFile("/LEGO/data/IISLEINF.DTA");
registerFile("/LEGO/data/IMAININF.DTA");
registerFile("/LEGO/data/IREGINF.DTA");
registerFile("/LEGO/data/OBSTINF.DTA");
registerFile("/LEGO/data/PMAININF.DTA");
registerFile("/LEGO/data/RACCINF.DTA");
registerFile("/LEGO/data/RACJINF.DTA");
registerFile("/LEGO/data/WORLD.WDB");
registerFile("/LEGO/data/testinf.dta");
if (GameState()->GetSavePath() && *GameState()->GetSavePath()) {
if (!opfs) {
opfs = wasmfs_create_opfs_backend();
}
MxString savePath = GameState()->GetSavePath();
if (savePath.GetData()[savePath.GetLength() - 1] != '/') {
savePath += "/";
}
char* parse = savePath.GetData();
while ((parse = SDL_strchr(++parse, '/'))) {
*parse = '\0';
wasmfs_create_directory(savePath.GetData(), 0644, opfs);
*parse = '/';
}
}
}

View File

@ -0,0 +1,16 @@
#ifndef EMSCRIPTEN_FILESYSTEM_H
#define EMSCRIPTEN_FILESYSTEM_H
#ifndef ISLE_EMSCRIPTEN_HOST
#define ISLE_EMSCRIPTEN_HOST ""
#endif
inline static const char* Emscripten_bundledPath = "/bundled";
inline static const char* Emscripten_savePath = "/save";
inline static const char* Emscripten_streamPath = "/";
inline static const char* Emscripten_streamHost = ISLE_EMSCRIPTEN_HOST;
void Emscripten_SetupConfig(const char* p_iniConfig);
void Emscripten_SetupFilesystem();
#endif // EMSCRIPTEN_FILESYSTEM_H

View File

@ -0,0 +1,14 @@
#include "messagebox.h"
#include <emscripten.h>
bool Emscripten_ShowSimpleMessageBox(
SDL_MessageBoxFlags flags,
const char* title,
const char* message,
SDL_Window* window
)
{
MAIN_THREAD_EM_ASM({alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1))}, title, message);
return true;
}

View File

@ -0,0 +1,13 @@
#ifndef EMSCRIPTEN_MESSAGE_BOX_H
#define EMSCRIPTEN_MESSAGE_BOX_H
#include <SDL3/SDL_messagebox.h>
bool Emscripten_ShowSimpleMessageBox(
SDL_MessageBoxFlags flags,
const char* title,
const char* message,
SDL_Window* window
);
#endif // EMSCRIPTEN_MESSAGE_BOX_H

View File

@ -44,6 +44,12 @@
#include <stdlib.h>
#include <time.h>
#ifdef __EMSCRIPTEN__
#include "emscripten/events.h"
#include "emscripten/filesystem.h"
#include "emscripten/messagebox.h"
#endif
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
// GLOBAL: ISLE 0x410030
@ -88,7 +94,11 @@ IsleApp::IsleApp()
m_cdPath = NULL;
m_deviceId = NULL;
m_savePath = NULL;
#ifdef __EMSCRIPTEN__
m_fullScreen = FALSE;
#else
m_fullScreen = TRUE;
#endif
m_flipSurfaces = FALSE;
m_backBuffersInVram = TRUE;
m_using8bit = FALSE;
@ -100,7 +110,7 @@ IsleApp::IsleApp()
m_useJoystick = FALSE;
m_joystickIndex = 0;
m_wideViewAngle = TRUE;
m_islandQuality = 1;
m_islandQuality = 2;
m_islandTexture = 1;
m_gameStarted = FALSE;
m_frameDelta = 10;
@ -246,6 +256,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
{
*appstate = NULL;
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)) {
char buffer[256];
SDL_snprintf(
@ -254,7 +267,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.\nSDL error: %s",
SDL_GetError()
);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", buffer, NULL);
Any_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", buffer, NULL);
return SDL_APP_FAILURE;
}
@ -266,7 +279,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
g_isle = new IsleApp();
if (g_isle->ParseArguments(argc, argv) != SUCCESS) {
SDL_ShowSimpleMessageBox(
Any_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"LEGO® Island Error",
"\"LEGO® Island\" failed to start. Invalid CLI arguments.",
@ -277,7 +290,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
// Create window
if (g_isle->SetupWindow() != SUCCESS) {
SDL_ShowSimpleMessageBox(
Any_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"LEGO® Island Error",
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.",
@ -288,6 +301,20 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
// Get reference to window
*appstate = g_isle->GetWindowHandle();
#ifdef __EMSCRIPTEN__
SDL_AddEventWatch(
[](void* userdata, SDL_Event* event) -> bool {
if (event->type == SDL_EVENT_TERMINATING && g_isle && g_isle->GetGameStarted()) {
GameState()->Save(0);
return false;
}
return true;
},
NULL
);
#endif
return SDL_APP_CONTINUE;
}
@ -298,7 +325,7 @@ SDL_AppResult SDL_AppIterate(void* appstate)
}
if (!g_isle->Tick()) {
SDL_ShowSimpleMessageBox(
Any_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"LEGO® Island Error",
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
@ -324,7 +351,7 @@ SDL_AppResult SDL_AppIterate(void* appstate)
if (g_mousedown && g_mousemoved && g_isle) {
if (!g_isle->Tick()) {
SDL_ShowSimpleMessageBox(
Any_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"LEGO® Island Error",
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
@ -355,9 +382,26 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
// [library:window]
// Remaining functionality to be implemented:
// Full screen - crashes when minimizing/maximizing, but this will probably be fixed once DirectDraw is replaced
// WM_TIMER - use SDL_Timer functionality instead
#ifdef __EMSCRIPTEN__
// Workaround for the fact we are getting both mouse & touch events on mobile devices running Emscripten.
// On desktops, we are only getting mouse events, but a touch device (pen_input) may also be present...
// See: https://github.com/libsdl-org/SDL/issues/13161
static bool detectedTouchEvents = false;
#endif
switch (event->type) {
case SDL_EVENT_MOUSE_MOTION:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
IDirect3DRMMiniwinDevice* device = GetD3DRMMiniwinDevice();
if (device && !device->ConvertEventToRenderCoordinates(event)) {
SDL_Log("Failed to convert event coordinates: %s", SDL_GetError());
}
break;
}
switch (event->type) {
case SDL_EVENT_WINDOW_FOCUS_GAINED:
if (!IsleDebug_Enabled()) {
@ -366,9 +410,12 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
}
break;
case SDL_EVENT_WINDOW_FOCUS_LOST:
if (!IsleDebug_Enabled()) {
if (!IsleDebug_Enabled() && g_isle->GetGameStarted()) {
g_isle->SetWindowActive(FALSE);
Lego()->Pause();
#ifdef __EMSCRIPTEN__
GameState()->Save(0);
#endif
}
break;
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
@ -390,6 +437,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
break;
}
case SDL_EVENT_MOUSE_MOTION:
#ifdef __EMSCRIPTEN__
if (detectedTouchEvents) {
break;
}
#endif
g_mousemoved = TRUE;
if (InputManager()) {
@ -406,7 +458,30 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
VideoManager()->MoveCursor(Min((MxS32) event->motion.x, 639), Min((MxS32) event->motion.y, 479));
}
break;
case SDL_EVENT_FINGER_MOTION: {
#ifdef __EMSCRIPTEN__
detectedTouchEvents = true;
#endif
g_mousemoved = TRUE;
float x = SDL_clamp(event->tfinger.x, 0, 1) * 640;
float y = SDL_clamp(event->tfinger.y, 0, 1) * 480;
if (InputManager()) {
InputManager()->QueueEvent(c_notificationMouseMove, LegoEventNotificationParam::c_lButtonState, x, y, 0);
}
if (g_isle->GetDrawCursor()) {
VideoManager()->MoveCursor(Min((MxS32) x, 639), Min((MxS32) y, 479));
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN:
#ifdef __EMSCRIPTEN__
if (detectedTouchEvents) {
break;
}
#endif
g_mousedown = TRUE;
if (InputManager()) {
@ -419,7 +494,32 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
);
}
break;
case SDL_EVENT_FINGER_DOWN: {
#ifdef __EMSCRIPTEN__
detectedTouchEvents = true;
#endif
g_mousedown = TRUE;
float x = SDL_clamp(event->tfinger.x, 0, 1) * 640;
float y = SDL_clamp(event->tfinger.y, 0, 1) * 480;
if (InputManager()) {
InputManager()->QueueEvent(c_notificationButtonDown, LegoEventNotificationParam::c_lButtonState, x, y, 0);
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_UP:
#ifdef __EMSCRIPTEN__
if (detectedTouchEvents) {
// Abusing the fact (bug?) that we are always getting mouse events on Emscripten.
// This functionality should be enabled in a more general way with touch events,
// but SDL touch event's don't have a "double tap" indicator right now.
if (event->button.clicks == 2) {
InputManager()->QueueEvent(c_notificationKeyPress, SDLK_SPACE, 0, 0, SDLK_SPACE);
}
break;
}
#endif
g_mousedown = FALSE;
if (InputManager()) {
@ -432,6 +532,20 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
);
}
break;
case SDL_EVENT_FINGER_UP: {
#ifdef __EMSCRIPTEN__
detectedTouchEvents = true;
#endif
g_mousedown = FALSE;
float x = SDL_clamp(event->tfinger.x, 0, 1) * 640;
float y = SDL_clamp(event->tfinger.y, 0, 1) * 480;
if (InputManager()) {
InputManager()->QueueEvent(c_notificationButtonUp, 0, x, y, 0);
}
break;
}
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
break;
@ -452,6 +566,23 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
break;
}
}
else if (event->user.type == g_legoSdlEvents.m_presenterProgress) {
MxPresenter* presenter = static_cast<MxPresenter*>(event->user.data1);
MxDSAction* action = presenter->GetAction();
MxPresenter::TickleState state = static_cast<MxPresenter::TickleState>(event->user.code);
#ifdef __EMSCRIPTEN__
if (!g_isle->GetGameStarted()) {
Emscripten_SendPresenterProgress(presenter, state);
}
#endif
if (!g_isle->GetGameStarted() && action && state == MxPresenter::e_ready &&
!SDL_strncmp(action->GetObjectName(), "Lego_Smk", 8)) {
g_isle->SetGameStarted(TRUE);
SDL_Log("Game started");
}
}
return SDL_APP_CONTINUE;
}
@ -555,6 +686,11 @@ MxResult IsleApp::SetupWindow()
}
GameState()->SetSavePath(m_savePath);
#ifdef __EMSCRIPTEN__
Emscripten_SetupFilesystem();
#endif
GameState()->SerializePlayersInfo(LegoStorage::c_read);
GameState()->SerializeScoreHistory(LegoStorage::c_read);
@ -621,6 +757,10 @@ bool IsleApp::LoadConfig()
}
SDL_Log("Reading configuration from \"%s\"", iniConfig);
#ifdef __EMSCRIPTEN__
Emscripten_SetupConfig(iniConfig);
#endif
dictionary* dict = iniparser_load(iniConfig);
// [library:config]
@ -677,12 +817,20 @@ bool IsleApp::LoadConfig()
fclose(iniFP);
}
#ifdef __EMSCRIPTEN__
const char* hdPath = Emscripten_bundledPath;
#else
const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath());
#endif
m_hdPath = new char[strlen(hdPath) + 1];
strcpy(m_hdPath, hdPath);
MxOmni::SetHD(m_hdPath);
#ifdef __EMSCRIPTEN__
const char* cdPath = Emscripten_streamPath;
#else
const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD());
#endif
m_cdPath = new char[strlen(cdPath) + 1];
strcpy(m_cdPath, cdPath);
MxOmni::SetCD(m_cdPath);
@ -729,7 +877,11 @@ bool IsleApp::LoadConfig()
// [library:config]
// The original game does not save any data if no savepath is given.
// Instead, we use SDLs prefPath as a default fallback and always save data.
#ifdef __EMSCRIPTEN__
const char* savePath = Emscripten_savePath;
#else
const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath);
#endif
m_savePath = new char[strlen(savePath) + 1];
strcpy(m_savePath, savePath);
@ -828,7 +980,6 @@ inline bool IsleApp::Tick()
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open ISLE.si: Failed to start initial action");
return false;
}
m_gameStarted = TRUE;
}
return true;
@ -871,7 +1022,6 @@ void IsleApp::SetupCursor(Cursor p_cursor)
MxResult IsleApp::ParseArguments(int argc, char** argv)
{
for (int i = 1, consumed; i < argc; i += consumed) {
consumed = -1;
@ -892,6 +1042,7 @@ MxResult IsleApp::ParseArguments(int argc, char** argv)
return FAILURE;
}
}
return SUCCESS;
}

View File

@ -49,8 +49,10 @@ class IsleApp {
SDL_Cursor* GetCursorBusy() { return m_cursorBusy; }
SDL_Cursor* GetCursorNo() { return m_cursorNo; }
MxS32 GetDrawCursor() { return m_drawCursor; }
MxS32 GetGameStarted() { return m_gameStarted; }
void SetWindowActive(MxS32 p_windowActive) { m_windowActive = p_windowActive; }
void SetGameStarted(MxS32 p_gameStarted) { m_gameStarted = p_gameStarted; }
MxResult ParseArguments(int argc, char** argv);

View File

@ -163,7 +163,7 @@ class LegoCarBuild : public LegoWorld {
MxS32 FUN_10025ee0(undefined4 p_param1);
// FUNCTION: BETA10 0x100735b0
void SetUnknown0x258(LegoCarBuildAnimPresenter* p_unk0x258) { m_unk0x258 = p_unk0x258; }
void SetCarBuildAnimPresenter(LegoCarBuildAnimPresenter* p_animPresenter) { m_animPresenter = p_animPresenter; }
// SYNTHETIC: LEGO1 0x10022a60
// LegoCarBuild::`scalar deleting destructor'
@ -195,8 +195,8 @@ class LegoCarBuild : public LegoWorld {
// This is likely a location in pixel space
MxS32 m_unk0x250[2]; // 0x250
LegoCarBuildAnimPresenter* m_unk0x258; // 0x258
MxQuaternionTransformer m_unk0x25c; // 0x25c
LegoCarBuildAnimPresenter* m_animPresenter; // 0x258
MxQuaternionTransformer m_unk0x25c; // 0x25c
// These two are likely locations in pixel space
MxS32 m_unk0x290[2]; // 0x290

View File

@ -13,6 +13,13 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
c_bit1 = 0x01
};
enum ShelfState {
e_undefined = -1,
e_selected = 0,
e_stopped = 1,
e_moving = 2
};
// SIZE 0x0c
struct UnknownListEntry {
// FUNCTION: LEGO1 0x100795c0
@ -69,7 +76,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
void FUN_10079050(MxS16 p_index);
void SwapNodesByName(LegoChar* p_param1, LegoChar* p_param2);
void FUN_10079160();
void InitBuildPlatform();
void FUN_100795d0(LegoChar* p_param);
void FUN_10079680(LegoChar* p_param);
LegoAnimNodeData* FindNodeDataByName(LegoTreeNode* p_treeNode, const LegoChar* p_name);
@ -78,7 +85,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
void RotateAroundYAxis(MxFloat p_angle);
MxBool FUN_10079c30(const LegoChar* p_name);
MxBool PartIsPlaced(const LegoChar* p_name);
void FUN_10079a90();
void MoveShelfForward();
MxBool StringEqualsPlatform(const LegoChar* p_string);
MxBool StringEqualsShelf(const LegoChar* p_string);
MxBool StringEndsOnY(const LegoChar* p_string);
@ -87,10 +94,10 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
void SetPartObjectIdByName(const LegoChar* p_name, MxS16 p_objectId);
// FUNCTION: BETA10 0x10070180
void SetUnknown0xbc(undefined2 p_unk0xbc) { m_unk0xbc = p_unk0xbc; }
void SetShelfState(MxU16 p_shelfState) { m_shelfState = p_shelfState; }
// FUNCTION: BETA10 0x100703b0
Matrix4& GetUnknown0xe0() { return m_unk0xe0; }
Matrix4& GetBuildViewMatrix() { return m_buildViewMatrix; }
MxBool StringEndsOnW(LegoChar* p_param);
MxBool StringEndsOnYOrN(const LegoChar* p_string);
@ -116,7 +123,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
private:
void Beta10Inline0x100733d0();
MxU16 m_unk0xbc; // 0xbc
MxU16 m_shelfState; // 0xbc
// variable name verified by BETA10 0x1007184f
MxS16 m_numberOfParts; // 0xbe
@ -124,21 +131,21 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter {
// name derived from LegoVehicleBuildState, field 0x4f
MxS16 m_placedPartCount; // 0xc0
LegoAnimNodeData* m_unk0xc4; // 0xc4
LegoAnim m_unk0xc8; // 0xc8
MxMatrix m_unk0xe0; // 0xe0
LegoAnimNodeData* m_platformAnimNodeData; // 0xc4
LegoAnim m_platformAnim; // 0xc8
MxMatrix m_buildViewMatrix; // 0xe0
// variable name verified by BETA10 0x100719f0
UnknownListEntry* m_parts; // 0x128
MxFloat m_unk0x12c; // 0x12c
MxFloat m_unk0x130; // 0x130
MxFloat m_unk0x134; // 0x134
MxFloat m_unk0x138; // 0x138
MxULong m_unk0x13c; // 0x13c
LegoEntity* m_unk0x140; // 0x140
MxS32 m_unk0x144; // 0x144
MxS32 m_unk0x148; // 0x148
MxFloat m_shelfFrameBuffer; // 0x12c
MxFloat m_shelfFrame; // 0x130
MxFloat m_shelfFrameMax; // 0x134
MxFloat m_shelfFrameInterval; // 0x138
MxULong m_unk0x13c; // 0x13c
LegoEntity* m_carBuildEntity; // 0x140
MxS32 m_unk0x144; // 0x144
MxS32 m_unk0x148; // 0x148
// name verified by BETA10 0x10070d63
LegoChar* m_mainSourceId; // 0x14c

View File

@ -224,6 +224,8 @@ class LegoGameState {
void FindLoadedAct();
void RegisterState(LegoState* p_state);
const char* GetSavePath() { return m_savePath; }
private:
MxResult WriteVariable(LegoStorage* p_storage, MxVariableTable* p_from, const char* p_variableName);
MxResult WriteEndOfVariables(LegoStorage* p_storage);

View File

@ -143,6 +143,7 @@ class LegoInputManager : public MxPresenter {
MxBool FUN_1005cdf0(LegoEventNotificationParam& p_param);
void GetKeyboardState();
MxResult GetNavigationKeyStates(MxU32& p_keyFlags);
MxResult GetNavigationTouchStates(MxU32& p_keyFlags);
// SYNTHETIC: LEGO1 0x1005b8d0
// LegoInputManager::`scalar deleting destructor'

View File

@ -10,32 +10,32 @@ class LegoTextureInfo;
// SIZE 0x20
class LegoPhoneme {
public:
LegoPhoneme(const char* p_name, undefined4 p_unk0x14)
LegoPhoneme(const char* p_name, MxU32 p_count)
{
m_name = p_name;
m_name.ToUpperCase();
Init();
m_unk0x14 = p_unk0x14;
m_count = p_count;
}
~LegoPhoneme();
virtual undefined4 VTable0x00(); // vtable+0x00
virtual void VTable0x04(undefined4 p_unk0x14); // vtable+0x04
virtual LegoTextureInfo* VTable0x08(); // vtable+0x08
virtual void VTable0x0c(LegoTextureInfo* p_unk0x18); // vtable+0x0c
virtual LegoTextureInfo* VTable0x10(); // vtable+0x10
virtual void VTable0x14(LegoTextureInfo* p_unk0x1c); // vtable+0x14
virtual void VTable0x18(); // vtable+0x18
virtual void Init(); // vtable+0x1c
virtual void VTable0x20(undefined4); // vtable+0x20
virtual MxU32 GetCount(); // vtable+0x00
virtual void SetCount(MxU32 p_count); // vtable+0x04
virtual LegoTextureInfo* GetTextureInfo(); // vtable+0x08
virtual void SetTextureInfo(LegoTextureInfo* p_textureInfo); // vtable+0x0c
virtual LegoTextureInfo* GetCachedTextureInfo(); // vtable+0x10
virtual void SetCachedTextureInfo(LegoTextureInfo* p_cachedTextureInfo); // vtable+0x14
virtual void VTable0x18(); // vtable+0x18
virtual void Init(); // vtable+0x1c
virtual void VTable0x20(undefined4); // vtable+0x20
MxString& GetName() { return m_name; }
private:
MxString m_name; // 0x04
undefined4 m_unk0x14; // 0x14
LegoTextureInfo* m_unk0x18; // 0x18
LegoTextureInfo* m_unk0x1c; // 0x1c
MxString m_name; // 0x04
MxU32 m_count; // 0x14
LegoTextureInfo* m_textureInfo; // 0x18
LegoTextureInfo* m_cachedTextureInfo; // 0x1c
};
#endif // LEGOPHONEME_H

View File

@ -52,11 +52,11 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore {
MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state);
LegoPlantInfo* GetInfoArray(MxS32& p_length);
LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId);
MxBool FUN_10026c50(LegoEntity* p_entity);
MxBool DecrementCounter(LegoEntity* p_entity);
void ScheduleAnimation(LegoEntity* p_entity, MxLong p_length);
MxResult FUN_10026410();
void FUN_10027120();
void FUN_10027200();
void ClearCounters();
void SetInitialCounters();
static void SetCustomizeAnimFile(const char* p_value);
@ -68,10 +68,10 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore {
private:
void RemovePlant(MxS32 p_index, LegoOmni::World p_worldId);
void FUN_10026860(MxS32 p_index);
void AdjustHeight(MxS32 p_index);
LegoPlantInfo* GetInfo(LegoEntity* p_entity);
MxBool FUN_10026c80(MxS32 p_index);
void FUN_100271b0(LegoEntity* p_entity, MxS32 p_adjust);
MxBool DecrementCounter(MxS32 p_index);
void AdjustCounter(LegoEntity* p_entity, MxS32 p_adjust);
static char* g_customizeAnimFile;
static MxS32 g_maxMove[4];

View File

@ -41,8 +41,8 @@ struct LegoPlantInfo {
MxU32 m_move; // 0x10
MxU8 m_mood; // 0x14
MxU8 m_color; // 0x15 - see enum for possible values
MxS8 m_unk0x16; // 0x16
MxS8 m_initialUnk0x16; // 0x17 - initial value loaded to m_unk0x16
MxS8 m_counter; // 0x16
MxS8 m_initialCounter; // 0x17 - initial value loaded to m_counter
const char* m_name; // 0x18
undefined4 m_unk0x1c; // 0x1c
float m_x; // 0x20

View File

@ -4,10 +4,8 @@
#include "actionsfwd.h"
#include "decomp.h"
#include "extra.h"
#include "lego1_export.h"
#include "mxtypes.h"
#include <SDL3/SDL_stdinc.h>
#ifdef MINIWIN
#include "miniwin/windows.h"
#else
@ -19,12 +17,6 @@
// name verified by BETA10 0x100d4054
#define DS_NOT_A_STREAM -1
struct LegoSdlEvents {
Uint32 m_windowsMessage;
};
LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents;
enum Cursor {
e_cursorArrow = 0,
e_cursorBusy,
@ -70,7 +62,6 @@ void PlayCamAnim(LegoPathActor* p_actor, MxBool p_unused, MxU32 p_location, MxBo
void FUN_1003eda0();
MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id);
void EnableAnimations(MxBool p_enable);
void InitSdlEvents();
void SetAppCursor(Cursor p_cursor);
MxBool FUN_1003ef60();
MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_worldAtom, MxS32 p_worldEntityId);

View File

@ -728,7 +728,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0f30[i] != -1; i++) {
if (plantInfo[g_unk0x100f0f30[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0f30[i]].m_counter) {
result = plantInfo[g_unk0x100f0f30[i]].m_entity;
break;
}
@ -742,7 +742,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0f50[i] != -1; i++) {
if (plantInfo[g_unk0x100f0f50[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0f50[i]].m_counter) {
result = plantInfo[g_unk0x100f0f50[i]].m_entity;
break;
}
@ -760,7 +760,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0f90[i] != -1; i++) {
if (plantInfo[g_unk0x100f0f90[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0f90[i]].m_counter) {
result = plantInfo[g_unk0x100f0f90[i]].m_entity;
break;
}
@ -782,7 +782,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0fa8[i] != -1; i++) {
if (plantInfo[g_unk0x100f0fa8[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0fa8[i]].m_counter) {
result = plantInfo[g_unk0x100f0fa8[i]].m_entity;
break;
}
@ -800,7 +800,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0fb8[i] != -1; i++) {
if (plantInfo[g_unk0x100f0fb8[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0fb8[i]].m_counter) {
result = plantInfo[g_unk0x100f0fb8[i]].m_entity;
break;
}
@ -814,7 +814,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f0fe8[i] != -1; i++) {
if (plantInfo[g_unk0x100f0fe8[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f0fe8[i]].m_counter) {
result = plantInfo[g_unk0x100f0fe8[i]].m_entity;
break;
}
@ -828,7 +828,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f1000[i] != -1; i++) {
if (plantInfo[g_unk0x100f1000[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f1000[i]].m_counter) {
result = plantInfo[g_unk0x100f1000[i]].m_entity;
break;
}
@ -842,7 +842,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
}
else {
for (i = 0; g_unk0x100f1018[i] != -1; i++) {
if (plantInfo[g_unk0x100f1018[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f1018[i]].m_counter) {
result = plantInfo[g_unk0x100f1018[i]].m_entity;
break;
}
@ -851,7 +851,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param)
break;
case 8:
for (i = 0; g_unk0x100f1030[i] != -1; i++) {
if (plantInfo[g_unk0x100f1030[i]].m_unk0x16) {
if (plantInfo[g_unk0x100f1030[i]].m_counter) {
result = plantInfo[g_unk0x100f1030[i]].m_entity;
break;
}

View File

@ -608,8 +608,8 @@ void Act3Brickster::Animate(float p_time)
assert(m_shootAnim && m_pInfo);
if (m_unk0x50 < p_time) {
while (m_pInfo->m_unk0x16) {
PlantManager()->FUN_10026c50(m_pInfo->m_entity);
while (m_pInfo->m_counter) {
PlantManager()->DecrementCounter(m_pInfo->m_entity);
}
assert(SoundManager()->GetCacheSoundManager());

View File

@ -99,7 +99,7 @@ LegoCarBuild::LegoCarBuild()
m_unk0x110 = 0;
m_unk0xf8 = c_unknownminusone;
m_unk0x2d4 = FALSE;
m_unk0x258 = 0;
m_animPresenter = NULL;
m_ColorBook_Bitmap = NULL;
m_Yellow_Ctl = NULL;
m_Red_Ctl = NULL;
@ -140,10 +140,10 @@ LegoCarBuild::~LegoCarBuild()
m_unk0x100 = 0;
m_unk0x110 = NULL;
if (m_unk0x258) {
m_unk0x258->SetUnknown0xbc(0);
m_unk0x258->SetTickleState(MxPresenter::e_idle);
m_unk0x258 = NULL;
if (m_animPresenter) {
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
m_animPresenter->SetTickleState(MxPresenter::e_idle);
m_animPresenter = NULL;
}
ControlManager()->Unregister(this);
@ -293,7 +293,7 @@ void LegoCarBuild::FUN_10022f00()
{
if (m_unk0x110) {
VTable0x6c();
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
m_unk0x100 = 5;
}
}
@ -306,13 +306,13 @@ void LegoCarBuild::FUN_10022f30()
FUN_10024f70(FALSE);
FUN_100250e0(FALSE);
if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) {
if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) {
m_PlaceBrick_Sound->Enable(FALSE);
m_PlaceBrick_Sound->Enable(TRUE);
}
m_unk0x258->SetUnknown0xbc(1);
m_unk0x258->PutFrame();
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped);
m_animPresenter->PutFrame();
m_unk0x110 = NULL;
m_unk0x100 = 0;
}
@ -483,12 +483,12 @@ void LegoCarBuild::FUN_100236d0()
FUN_10024f70(FALSE);
FUN_100250e0(FALSE);
m_unk0x258->FUN_10079790(m_unk0x110->GetName());
m_unk0x258->SetUnknown0xbc(1);
m_animPresenter->FUN_10079790(m_unk0x110->GetName());
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped);
m_unk0x110 = NULL;
m_unk0x100 = 0;
if (m_unk0x258->AllPartsPlaced()) {
if (m_animPresenter->AllPartsPlaced()) {
// Note the code duplication with LEGO1 0x10025ee0
switch (m_carId) {
case 1:
@ -545,7 +545,7 @@ MxResult LegoCarBuild::Tickle()
}
if (m_unk0x110) {
if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) {
if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) {
FUN_10022f30();
}
}
@ -827,7 +827,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y)
LegoROI* roi = PickROI(p_x, p_y);
if (!roi || !m_unk0x258->StringEndsOnYOrN(roi->GetName())) {
if (!roi || !m_animPresenter->StringEndsOnYOrN(roi->GetName())) {
return 0;
}
@ -838,7 +838,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y)
FUN_100250e0(TRUE);
}
if (m_unk0x100 == 5 && m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) {
if (m_unk0x100 == 5 && m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) {
m_unk0x2d4 = TRUE;
}
else {
@ -847,7 +847,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y)
FUN_10025450();
VTable0x70();
if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) {
if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) {
if (m_unk0x100 != 5) {
m_unk0x250[0] += m_unk0x290[0] - m_unk0x298[0];
m_unk0x250[1] += m_unk0x290[1] - m_unk0x298[1];
@ -858,8 +858,8 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y)
}
}
else {
if (m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) {
m_unk0x114 = m_unk0x258->FUN_10079e20();
if (m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) {
m_unk0x114 = m_animPresenter->FUN_10079e20();
}
}
@ -875,7 +875,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y)
m_GetBrick_Sound->Enable(FALSE);
m_GetBrick_Sound->Enable(TRUE);
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
return 1;
}
@ -895,7 +895,7 @@ undefined4 LegoCarBuild::FUN_100246e0(MxLong p_x, MxLong p_y)
result = 1;
break;
case 6:
if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName()) &&
if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName()) &&
SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) {
FUN_10024f70(FALSE);
FUN_100250e0(FALSE);
@ -903,9 +903,9 @@ undefined4 LegoCarBuild::FUN_100246e0(MxLong p_x, MxLong p_y)
m_unk0x110 = NULL;
m_PlaceBrick_Sound->Enable(FALSE);
m_PlaceBrick_Sound->Enable(TRUE);
m_unk0x258->SetUnknown0xbc(1);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped);
}
else if (m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) {
else if (m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) {
if (SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) {
m_PlaceBrick_Sound->Enable(FALSE);
m_PlaceBrick_Sound->Enable(TRUE);
@ -968,7 +968,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param)
DeleteObjects(&m_atomId, 500, 510);
}
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
m_destLocation = LegoGameState::e_infomain;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
result = 1;
@ -982,12 +982,12 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param)
DeleteObjects(&m_atomId, 500, 510);
}
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
if (GameState()->GetCurrentAct() == LegoGameState::e_act2) {
FUN_100243a0();
}
else if (m_unk0x258->AllPartsPlaced() || m_buildState->m_unk0x4d) {
else if (m_animPresenter->AllPartsPlaced() || m_buildState->m_unk0x4d) {
m_buildState->m_unk0x4d = TRUE;
InvokeAction(Extra::e_start, m_atomId, m_carId, NULL);
@ -1024,7 +1024,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param)
(m_Decals_Ctl5 && m_Decals_Ctl5->GetAction()->GetObjectId() == param->m_clickedObjectId) ||
(m_Decals_Ctl6 && m_Decals_Ctl6->GetAction()->GetObjectId() == param->m_clickedObjectId) ||
(m_Decals_Ctl7 && m_Decals_Ctl7->GetAction()->GetObjectId() == param->m_clickedObjectId)) {
m_unk0x258->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId);
m_animPresenter->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId);
m_Decal_Sound->Enable(FALSE);
m_Decal_Sound->Enable(TRUE);
}
@ -1057,16 +1057,16 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param)
if (param->m_unk0x28) {
switch (param->m_clickedObjectId) {
case CopterScript::c_Info_Ctl:
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
m_destLocation = LegoGameState::e_infomain;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
result = 1;
break;
case CopterScript::c_Exit_Ctl:
if (m_buildState->m_animationState != LegoVehicleBuildState::e_exiting) {
m_unk0x258->SetUnknown0xbc(0);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected);
if (m_unk0x258->AllPartsPlaced() || m_buildState->m_unk0x4d) {
if (m_animPresenter->AllPartsPlaced() || m_buildState->m_unk0x4d) {
m_buildState->m_unk0x4d = TRUE;
// GameState()->GetCurrentAct() returns an MxS16 in BETA10
@ -1128,7 +1128,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param)
(m_Decals_Ctl5 && m_Decals_Ctl5->GetAction()->GetObjectId() == param->m_clickedObjectId) ||
(m_Decals_Ctl6 && m_Decals_Ctl6->GetAction()->GetObjectId() == param->m_clickedObjectId) ||
(m_Decals_Ctl7 && m_Decals_Ctl7->GetAction()->GetObjectId() == param->m_clickedObjectId)) {
m_unk0x258->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId);
m_animPresenter->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId);
m_Decal_Sound->Enable(FALSE);
m_Decal_Sound->Enable(TRUE);
}
@ -1205,7 +1205,7 @@ undefined4 LegoCarBuild::FUN_10024c20(MxNotificationParam* p_param)
assert(destWorld);
m_buildState->m_animationState = LegoVehicleBuildState::e_exiting;
if (!m_unk0x258->AllPartsPlaced()) {
if (!m_animPresenter->AllPartsPlaced()) {
FUN_100243a0();
}
else {
@ -1264,7 +1264,7 @@ void LegoCarBuild::FUN_10024ef0()
void LegoCarBuild::FUN_10024f30()
{
FUN_10022f30();
m_unk0x258->SetUnknown0xbc(2);
m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_moving);
}
// FUNCTION: LEGO1 0x10024f50
@ -1272,14 +1272,14 @@ void LegoCarBuild::FUN_10024f30()
void LegoCarBuild::FUN_10024f50()
{
m_unk0x2d4 = FALSE;
m_unk0x258->RotateAroundYAxis(g_rotationAngleStepYAxis);
m_animPresenter->RotateAroundYAxis(g_rotationAngleStepYAxis);
}
// FUNCTION: LEGO1 0x10024f70
// FUNCTION: BETA10 0x1006e002
void LegoCarBuild::FUN_10024f70(MxBool p_enabled)
{
if (m_unk0x258->StringEndsOnY(m_unk0x110->GetName())) {
if (m_animPresenter->StringEndsOnY(m_unk0x110->GetName())) {
SetPresentersEnabled(p_enabled);
}
}
@ -1314,7 +1314,7 @@ void LegoCarBuild::TogglePresentersEnabled()
// FUNCTION: BETA10 0x1006e124
void LegoCarBuild::FUN_100250e0(MxBool p_enabled)
{
if (m_unk0x258->StringDoesNotEndOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) {
if (m_animPresenter->StringDoesNotEndOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) {
if (SDL_strncasecmp(m_unk0x110->GetName(), "JSFRNT", strlen("JSFRNT")) == 0) {
m_Decal_Bitmap->Enable(p_enabled);
m_Decals_Ctl->Enable(p_enabled);
@ -1395,7 +1395,7 @@ void LegoCarBuild::FUN_10025450()
m_unk0x1c0 = m_unk0x12c;
Vector3 lastColumnOfUnk0x1c0(m_unk0x1c0[3]);
lastColumnOfUnk0x1c0 = Vector3(m_unk0x258->GetUnknown0xe0()[3]);
lastColumnOfUnk0x1c0 = Vector3(m_animPresenter->GetBuildViewMatrix()[3]);
// This looks odd, but it improves the LEGO1 match while breaking the BETA10 match.
// I don't know whether this is due to compiler entropy.
@ -1404,7 +1404,7 @@ void LegoCarBuild::FUN_10025450()
MxMatrix* unk0x178 = &m_unk0x178;
*unk0x178 = m_unk0x12c;
if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) {
if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) {
m_unk0x2a4 = Vector4(m_unk0x110->GetWorldPosition());
if (!m_unk0x2d4) {
@ -1421,11 +1421,11 @@ void LegoCarBuild::FUN_10025450()
else {
const LegoChar* wiredName;
if (!m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) {
wiredName = m_unk0x258->GetWiredNameByPartName(m_unk0x110->GetName());
if (!m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) {
wiredName = m_animPresenter->GetWiredNameByPartName(m_unk0x110->GetName());
}
else {
wiredName = m_unk0x258->GetWiredNameOfLastPlacedPart();
wiredName = m_animPresenter->GetWiredNameOfLastPlacedPart();
}
LegoROI* parentROI = (LegoROI*) m_unk0x110->GetParentROI();

View File

@ -25,17 +25,17 @@ DECOMP_SIZE_ASSERT(LegoCarBuildAnimPresenter, 0x150)
// FUNCTION: BETA10 0x100707c0
LegoCarBuildAnimPresenter::LegoCarBuildAnimPresenter()
{
m_unk0xbc = 0;
m_shelfState = e_selected;
m_numberOfParts = 0;
m_placedPartCount = 0;
m_parts = NULL;
m_unk0xc4 = NULL;
m_unk0x130 = 0;
m_unk0x12c = 0;
m_unk0x134 = 0;
m_unk0x138 = 0;
m_platformAnimNodeData = NULL;
m_shelfFrame = 0;
m_shelfFrameBuffer = 0;
m_shelfFrameMax = 0;
m_shelfFrameInterval = 0;
m_unk0x13c = 0;
m_unk0x140 = NULL;
m_carBuildEntity = NULL;
m_unk0x144 = -1;
m_unk0x148 = -1;
m_mainSourceId = NULL;
@ -53,8 +53,8 @@ LegoCarBuildAnimPresenter::~LegoCarBuildAnimPresenter()
delete[] m_parts;
}
m_unk0xc8.GetRoot()->SetNumChildren(0);
*m_unk0xc8.GetRoot()->GetChildren() = NULL;
m_platformAnim.GetRoot()->SetNumChildren(0);
*m_platformAnim.GetRoot()->GetChildren() = NULL;
if (m_mainSourceId) {
delete[] m_mainSourceId;
@ -116,14 +116,14 @@ inline void LegoCarBuildAnimPresenter::Beta10Inline0x100733d0()
// FUNCTION: BETA10 0x10070ab1
void LegoCarBuildAnimPresenter::PutFrame()
{
switch (m_unk0xbc) {
case 0:
switch (m_shelfState) {
case e_selected:
break;
case 2:
FUN_10079a90();
case 1:
if (m_unk0x140->GetROI()) {
FUN_1006b9a0(m_anim, m_unk0x12c, NULL);
case e_moving:
MoveShelfForward();
case e_stopped:
if (m_carBuildEntity->GetROI()) {
FUN_1006b9a0(m_anim, m_shelfFrameBuffer, NULL);
}
default:
break;
@ -152,24 +152,24 @@ void LegoCarBuildAnimPresenter::ReadyTickle()
#endif
}
m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Dunebld");
m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Dunebld");
if (!m_unk0x140) {
m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Chptrbld");
if (!m_carBuildEntity) {
m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Chptrbld");
}
if (!m_unk0x140) {
m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Jetbld");
if (!m_carBuildEntity) {
m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Jetbld");
}
if (!m_unk0x140) {
m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "bldrace");
if (!m_carBuildEntity) {
m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "bldrace");
}
if (m_unk0x140) {
((LegoCarBuild*) m_currentWorld)->SetUnknown0x258(this);
if (m_carBuildEntity) {
((LegoCarBuild*) m_currentWorld)->SetCarBuildAnimPresenter(this);
m_placedPartCount = ((LegoCarBuild*) m_currentWorld)->GetPlacedPartCount();
SetUnknown0xbc(1);
SetShelfState(e_stopped);
m_previousTickleStates |= 1 << m_currentTickleState;
m_currentTickleState = e_starting;
m_compositePresenter->SendToCompositePresenter(Lego());
@ -184,7 +184,7 @@ void LegoCarBuildAnimPresenter::ReadyTickle()
// FUNCTION: BETA10 0x10070cdd
void LegoCarBuildAnimPresenter::StreamingTickle()
{
if (!m_unk0x140->GetROI()) {
if (!m_carBuildEntity->GetROI()) {
return;
}
@ -194,7 +194,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle()
strcpy(m_mainSourceId, m_action->GetAtomId().GetInternal());
m_mainSourceId[strlen(m_mainSourceId) - 1] = 'M';
FUN_10079160();
InitBuildPlatform();
if (GameState()->GetCurrentAct() == LegoGameState::e_act2) {
m_placedPartCount = 10;
@ -234,7 +234,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle()
Lego3DView* lego3dview = videoManager->Get3DManager()->GetLego3DView();
LegoROI* videoManagerROI = videoManager->GetViewROI();
LegoROI* local60 = m_unk0x140->GetROI();
LegoROI* local60 = m_carBuildEntity->GetROI();
LegoROI* camera = NULL;
MxFloat fov;
@ -273,7 +273,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle()
lego3dview->Moved(*videoManagerROI);
videoManager->Get3DManager()->SetFrustrum(fov, 0.1, 250.0);
m_unk0xe0 = local60->FindChildROI("VIEW", local60)->GetLocal2World();
m_buildViewMatrix = local60->FindChildROI("VIEW", local60)->GetLocal2World();
m_previousTickleStates |= 1 << m_currentTickleState;
m_currentTickleState = e_repeating;
@ -286,7 +286,7 @@ void LegoCarBuildAnimPresenter::EndAction()
if (m_action) {
AUTOLOCK(m_criticalSection);
MxVideoPresenter::EndAction();
m_unk0xbc = 0;
m_shelfState = e_selected;
}
}
@ -296,7 +296,7 @@ MxResult LegoCarBuildAnimPresenter::Serialize(LegoStorage* p_storage)
{
if (p_storage->IsReadMode()) {
p_storage->ReadS16(m_placedPartCount);
p_storage->ReadFloat(m_unk0x130);
p_storage->ReadFloat(m_shelfFrame);
for (MxS16 i = 0; i < m_numberOfParts; i++) {
p_storage->ReadString(m_parts[i].m_name);
p_storage->ReadString(m_parts[i].m_wiredName);
@ -305,7 +305,7 @@ MxResult LegoCarBuildAnimPresenter::Serialize(LegoStorage* p_storage)
}
else if (p_storage->IsWriteMode()) {
p_storage->WriteS16(m_placedPartCount);
p_storage->WriteFloat(m_unk0x130);
p_storage->WriteFloat(m_shelfFrame);
for (MxS16 i = 0; i < m_numberOfParts; i++) {
p_storage->WriteString(m_parts[i].m_name);
p_storage->WriteString(m_parts[i].m_wiredName);
@ -346,7 +346,7 @@ void LegoCarBuildAnimPresenter::SwapNodesByName(LegoChar* p_name1, LegoChar* p_n
// FUNCTION: LEGO1 0x10079160
// FUNCTION: BETA10 0x1007165d
void LegoCarBuildAnimPresenter::FUN_10079160()
void LegoCarBuildAnimPresenter::InitBuildPlatform()
{
LegoTreeNode* root;
LegoAnimNodeData* data2;
@ -357,16 +357,17 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
LegoAnimNodeData* destData;
LegoTreeNode** children;
// Get Platform data, Shelf Frame data, and number of build parts
for (i = 0; i < totalNodes; i++) {
LegoAnimNodeData* data = (LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData();
name = data->GetName();
if (StringEqualsPlatform(name)) {
m_unk0xc4 = data;
if (m_unk0xc4->GetNumRotationKeys() == 0) {
m_platformAnimNodeData = data;
if (m_platformAnimNodeData->GetNumRotationKeys() == 0) {
LegoRotationKey* key = new LegoRotationKey[1];
m_unk0xc4->SetNumRotationKeys(1);
m_unk0xc4->SetRotationKeys(key);
m_platformAnimNodeData->SetNumRotationKeys(1);
m_platformAnimNodeData->SetRotationKeys(key);
}
}
else {
@ -374,9 +375,9 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
m_numberOfParts++;
}
else {
if (m_unk0x134 == 0.0f && StringEqualsShelf(name)) {
m_unk0x134 = m_anim->GetDuration();
m_unk0x138 = m_unk0x134 / (data->GetNumTranslationKeys() - 1);
if (m_shelfFrameMax == 0.0f && StringEqualsShelf(name)) {
m_shelfFrameMax = m_anim->GetDuration();
m_shelfFrameInterval = m_shelfFrameMax / (data->GetNumTranslationKeys() - 1);
}
}
}
@ -386,6 +387,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
m_parts = new UnknownListEntry[m_numberOfParts];
assert(m_parts);
// Go through and add the wired name of each part
for (i = 0; i < totalNodes; i++) {
name = ((LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData())->GetName();
@ -404,6 +406,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
MxS16 counter = 0;
// Go through and add the normal name of each part
for (i = 0; i < totalNodes; i++) {
name = ((LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData())->GetName();
if (StringEndsOnYOrN(name)) {
@ -422,6 +425,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
}
}
// Set Platform root node
destNode = new LegoTreeNode();
assert(destNode);
destData = new LegoAnimNodeData();
@ -438,7 +442,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160()
*children = FindNodeByName(m_anim->GetRoot(), "PLATFORM");
destNode->SetChildren(children);
m_unk0xc8.SetRoot(destNode);
m_platformAnim.SetRoot(destNode);
}
// FUNCTION: LEGO1 0x100795d0
@ -563,8 +567,8 @@ void LegoCarBuildAnimPresenter::FUN_10079790(const LegoChar* p_name)
// FUNCTION: BETA10 0x1007225d
void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle)
{
if (m_unk0xc4) {
LegoRotationKey* rotationKey = m_unk0xc4->GetRotationKey(0);
if (m_platformAnimNodeData) {
LegoRotationKey* rotationKey = m_platformAnimNodeData->GetRotationKey(0);
Mx4DPointFloat
currentRotation(rotationKey->GetX(), rotationKey->GetY(), rotationKey->GetZ(), rotationKey->GetAngle());
@ -581,33 +585,33 @@ void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle)
rotationKey->FUN_100739a0(FALSE);
}
m_unk0xc4->GetRotationKey(0)->SetX(newRotation[0]);
m_unk0xc4->GetRotationKey(0)->SetY(newRotation[1]);
m_unk0xc4->GetRotationKey(0)->SetZ(newRotation[2]);
m_unk0xc4->GetRotationKey(0)->SetAngle(newRotation[3]);
m_platformAnimNodeData->GetRotationKey(0)->SetX(newRotation[0]);
m_platformAnimNodeData->GetRotationKey(0)->SetY(newRotation[1]);
m_platformAnimNodeData->GetRotationKey(0)->SetZ(newRotation[2]);
m_platformAnimNodeData->GetRotationKey(0)->SetAngle(newRotation[3]);
if (m_unk0x140->GetROI()) {
FUN_1006b9a0(&m_unk0xc8, m_unk0x12c, NULL);
if (m_carBuildEntity->GetROI()) {
FUN_1006b9a0(&m_platformAnim, m_shelfFrameBuffer, NULL);
}
}
}
// FUNCTION: LEGO1 0x10079a90
// FUNCTION: BETA10 0x10072412
void LegoCarBuildAnimPresenter::FUN_10079a90()
void LegoCarBuildAnimPresenter::MoveShelfForward()
{
if (m_unk0x12c >= m_unk0x134) {
m_unk0x130 = 0.0;
m_unk0x12c = m_unk0x130;
m_unk0xbc = 1;
if (m_shelfFrameBuffer >= m_shelfFrameMax) {
m_shelfFrame = 0.0;
m_shelfFrameBuffer = m_shelfFrame;
m_shelfState = e_stopped;
}
else if (m_unk0x12c >= m_unk0x138 + m_unk0x130) {
m_unk0x130 = m_unk0x138 + m_unk0x130;
m_unk0x12c = m_unk0x130;
m_unk0xbc = 1;
else if (m_shelfFrameBuffer >= m_shelfFrameInterval + m_shelfFrame) {
m_shelfFrame = m_shelfFrameInterval + m_shelfFrame;
m_shelfFrameBuffer = m_shelfFrame;
m_shelfState = e_stopped;
}
else {
m_unk0x12c = m_unk0x138 / 10.0f + m_unk0x12c;
m_shelfFrameBuffer = m_shelfFrameInterval / 10.0f + m_shelfFrameBuffer;
}
}
@ -708,6 +712,6 @@ void LegoCarBuildAnimPresenter::SetPartObjectIdByName(const LegoChar* p_name, Mx
// FUNCTION: BETA10 0x10072959
const BoundingSphere& LegoCarBuildAnimPresenter::FUN_10079e20()
{
LegoROI* roi = m_unk0x140->GetROI();
LegoROI* roi = m_carBuildEntity->GetROI();
return roi->FindChildROI(m_parts[m_placedPartCount].m_wiredName, roi)->GetWorldBoundingSphere();
}

View File

@ -8,39 +8,39 @@ LegoPhoneme::~LegoPhoneme()
}
// FUNCTION: LEGO1 0x10044eb0
undefined4 LegoPhoneme::VTable0x00()
MxU32 LegoPhoneme::GetCount()
{
return m_unk0x14;
return m_count;
}
// FUNCTION: LEGO1 0x10044ec0
void LegoPhoneme::VTable0x04(undefined4 p_unk0x14)
void LegoPhoneme::SetCount(MxU32 p_count)
{
m_unk0x14 = p_unk0x14;
m_count = p_count;
}
// FUNCTION: LEGO1 0x10044ed0
LegoTextureInfo* LegoPhoneme::VTable0x08()
LegoTextureInfo* LegoPhoneme::GetTextureInfo()
{
return m_unk0x18;
return m_textureInfo;
}
// FUNCTION: LEGO1 0x10044ee0
void LegoPhoneme::VTable0x0c(LegoTextureInfo* p_unk0x18)
void LegoPhoneme::SetTextureInfo(LegoTextureInfo* p_textureInfo)
{
m_unk0x18 = p_unk0x18;
m_textureInfo = p_textureInfo;
}
// FUNCTION: LEGO1 0x10044ef0
LegoTextureInfo* LegoPhoneme::VTable0x10()
LegoTextureInfo* LegoPhoneme::GetCachedTextureInfo()
{
return m_unk0x1c;
return m_cachedTextureInfo;
}
// FUNCTION: LEGO1 0x10044f00
void LegoPhoneme::VTable0x14(LegoTextureInfo* p_unk0x1c)
void LegoPhoneme::SetCachedTextureInfo(LegoTextureInfo* p_cachedTextureInfo)
{
m_unk0x1c = p_unk0x1c;
m_cachedTextureInfo = p_cachedTextureInfo;
}
// FUNCTION: LEGO1 0x10044f10
@ -51,9 +51,9 @@ void LegoPhoneme::VTable0x18()
// FUNCTION: LEGO1 0x10044f20
void LegoPhoneme::Init()
{
m_unk0x14 = 0;
m_unk0x18 = NULL;
m_unk0x1c = NULL;
m_count = 0;
m_textureInfo = NULL;
m_cachedTextureInfo = NULL;
}
// FUNCTION: LEGO1 0x10044f30

View File

@ -31,10 +31,10 @@ const char* g_plantLodNames[4][5] = {
};
// GLOBAL: LEGO1 0x100f16b0
float g_unk0x100f16b0[] = {0.1f, 0.7f, 0.5f, 0.9f};
float g_heightPerCount[] = {0.1f, 0.7f, 0.5f, 0.9f};
// GLOBAL: LEGO1 0x100f16c0
MxU8 g_unk0x100f16c0[] = {1, 2, 2, 3};
MxU8 g_counters[] = {1, 2, 2, 3};
// GLOBAL: LEGO1 0x100f315c
MxU32 LegoPlantManager::g_maxSound = 8;
@ -217,7 +217,7 @@ LegoEntity* LegoPlantManager::CreatePlant(MxS32 p_index, LegoWorld* p_world, Leg
if (p_index < sizeOfArray(g_plantInfo)) {
MxU32 world = 1 << (MxU8) p_worldId;
if (g_plantInfo[p_index].m_worlds & world && g_plantInfo[p_index].m_unk0x16 != 0) {
if (g_plantInfo[p_index].m_worlds & world && g_plantInfo[p_index].m_counter != 0) {
if (g_plantInfo[p_index].m_entity == NULL) {
char name[256];
char lodName[256];
@ -286,7 +286,7 @@ MxResult LegoPlantManager::Write(LegoStorage* p_storage)
if (p_storage->Write(&info->m_color, sizeof(info->m_color)) != SUCCESS) {
goto done;
}
if (p_storage->Write(&info->m_initialUnk0x16, sizeof(info->m_initialUnk0x16)) != SUCCESS) {
if (p_storage->Write(&info->m_initialCounter, sizeof(info->m_initialCounter)) != SUCCESS) {
goto done;
}
}
@ -321,12 +321,12 @@ MxResult LegoPlantManager::Read(LegoStorage* p_storage)
if (p_storage->Read(&info->m_color, sizeof(MxU8)) != SUCCESS) {
goto done;
}
if (p_storage->Read(&info->m_unk0x16, sizeof(MxS8)) != SUCCESS) {
if (p_storage->Read(&info->m_counter, sizeof(MxS8)) != SUCCESS) {
goto done;
}
info->m_initialUnk0x16 = info->m_unk0x16;
FUN_10026860(i);
info->m_initialCounter = info->m_counter;
AdjustHeight(i);
}
result = SUCCESS;
@ -337,13 +337,13 @@ MxResult LegoPlantManager::Read(LegoStorage* p_storage)
// FUNCTION: LEGO1 0x10026860
// FUNCTION: BETA10 0x100c5be0
void LegoPlantManager::FUN_10026860(MxS32 p_index)
void LegoPlantManager::AdjustHeight(MxS32 p_index)
{
MxU8 variant = g_plantInfo[p_index].m_variant;
if (g_plantInfo[p_index].m_unk0x16 >= 0) {
float value = g_unk0x100f16c0[variant] - g_plantInfo[p_index].m_unk0x16;
g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1] - value * g_unk0x100f16b0[variant];
if (g_plantInfo[p_index].m_counter >= 0) {
float value = g_counters[variant] - g_plantInfo[p_index].m_counter;
g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1] - value * g_heightPerCount[variant];
}
else {
g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1];
@ -411,7 +411,7 @@ MxBool LegoPlantManager::SwitchVariant(LegoEntity* p_entity)
{
LegoPlantInfo* info = GetInfo(p_entity);
if (info == NULL || info->m_unk0x16 != -1) {
if (info == NULL || info->m_counter != -1) {
return FALSE;
}
@ -551,7 +551,7 @@ void LegoPlantManager::SetCustomizeAnimFile(const char* p_value)
// FUNCTION: LEGO1 0x10026c50
// FUNCTION: BETA10 0x100c6349
MxBool LegoPlantManager::FUN_10026c50(LegoEntity* p_entity)
MxBool LegoPlantManager::DecrementCounter(LegoEntity* p_entity)
{
LegoPlantInfo* info = GetInfo(p_entity);
@ -559,12 +559,12 @@ MxBool LegoPlantManager::FUN_10026c50(LegoEntity* p_entity)
return FALSE;
}
return FUN_10026c80(info - g_plantInfo);
return DecrementCounter(info - g_plantInfo);
}
// FUNCTION: LEGO1 0x10026c80
// FUNCTION: BETA10 0x100c63eb
MxBool LegoPlantManager::FUN_10026c80(MxS32 p_index)
MxBool LegoPlantManager::DecrementCounter(MxS32 p_index)
{
if (p_index >= sizeOfArray(g_plantInfo)) {
return FALSE;
@ -578,23 +578,23 @@ MxBool LegoPlantManager::FUN_10026c80(MxS32 p_index)
MxBool result = TRUE;
if (info->m_unk0x16 < 0) {
info->m_unk0x16 = g_unk0x100f16c0[info->m_variant];
if (info->m_counter < 0) {
info->m_counter = g_counters[info->m_variant];
}
if (info->m_unk0x16 > 0) {
if (info->m_counter > 0) {
LegoROI* roi = info->m_entity->GetROI();
info->m_unk0x16--;
info->m_counter--;
if (info->m_unk0x16 == 1) {
info->m_unk0x16 = 0;
if (info->m_counter == 1) {
info->m_counter = 0;
}
if (info->m_unk0x16 == 0) {
if (info->m_counter == 0) {
roi->SetVisibility(FALSE);
}
else {
FUN_10026860(info - g_plantInfo);
AdjustHeight(info - g_plantInfo);
info->m_entity->SetLocation(info->m_position, info->m_direction, info->m_up, FALSE);
}
}
@ -624,7 +624,7 @@ void LegoPlantManager::ScheduleAnimation(LegoEntity* p_entity, MxLong p_length)
time += p_length;
entry->m_time = time + 1000;
FUN_100271b0(p_entity, -1);
AdjustCounter(p_entity, -1);
}
// FUNCTION: LEGO1 0x10026e00
@ -673,11 +673,11 @@ MxResult LegoPlantManager::Tickle()
if (entry->m_time < time) {
LegoPlantInfo* info = GetInfo(entry->m_entity);
if (info->m_unk0x16 == 0) {
if (info->m_counter == 0) {
entry->m_roi->SetVisibility(FALSE);
}
else {
FUN_10026860(info - g_plantInfo);
AdjustHeight(info - g_plantInfo);
info->m_entity->SetLocation(info->m_position, info->m_direction, info->m_up, FALSE);
}
@ -700,14 +700,14 @@ MxResult LegoPlantManager::Tickle()
}
// FUNCTION: LEGO1 0x10027120
void LegoPlantManager::FUN_10027120()
void LegoPlantManager::ClearCounters()
{
LegoWorld* world = CurrentWorld();
for (MxS32 i = 0; i < sizeOfArray(g_plantInfo); i++) {
g_plantInfo[i].m_unk0x16 = -1;
g_plantInfo[i].m_initialUnk0x16 = -1;
FUN_10026860(i);
g_plantInfo[i].m_counter = -1;
g_plantInfo[i].m_initialCounter = -1;
AdjustHeight(i);
if (g_plantInfo[i].m_entity != NULL) {
g_plantInfo[i].m_entity->SetLocation(
@ -721,28 +721,28 @@ void LegoPlantManager::FUN_10027120()
}
// FUNCTION: LEGO1 0x100271b0
void LegoPlantManager::FUN_100271b0(LegoEntity* p_entity, MxS32 p_adjust)
void LegoPlantManager::AdjustCounter(LegoEntity* p_entity, MxS32 p_adjust)
{
LegoPlantInfo* info = GetInfo(p_entity);
if (info != NULL) {
if (info->m_unk0x16 < 0) {
info->m_unk0x16 = g_unk0x100f16c0[info->m_variant];
if (info->m_counter < 0) {
info->m_counter = g_counters[info->m_variant];
}
if (info->m_unk0x16 > 0) {
info->m_unk0x16 += p_adjust;
if (info->m_unk0x16 <= 1 && p_adjust < 0) {
info->m_unk0x16 = 0;
if (info->m_counter > 0) {
info->m_counter += p_adjust;
if (info->m_counter <= 1 && p_adjust < 0) {
info->m_counter = 0;
}
}
}
}
// FUNCTION: LEGO1 0x10027200
void LegoPlantManager::FUN_10027200()
void LegoPlantManager::SetInitialCounters()
{
for (MxU32 i = 0; i < sizeOfArray(g_plantInfo); i++) {
g_plantInfo[i].m_initialUnk0x16 = g_plantInfo[i].m_unk0x16;
g_plantInfo[i].m_initialCounter = g_plantInfo[i].m_counter;
}
}

View File

@ -37,8 +37,6 @@
#include <string.h>
#include <vec.h>
LegoSdlEvents g_legoSdlEvents;
// FUNCTION: LEGO1 0x1003dd70
// FUNCTION: BETA10 0x100d3410
LegoROI* PickROI(MxLong p_x, MxLong p_y)
@ -568,17 +566,6 @@ void EnableAnimations(MxBool p_enable)
AnimationManager()->FUN_100604d0(p_enable);
}
void InitSdlEvents()
{
static bool g_initialized = false;
if (!g_initialized) {
g_initialized = true;
Uint32 event = SDL_RegisterEvents(1);
g_legoSdlEvents.m_windowsMessage = event + 0;
}
}
// FUNCTION: LEGO1 0x1003ef40
void SetAppCursor(Cursor p_cursor)
{

View File

@ -503,7 +503,7 @@ MxLong LegoEntity::Notify(MxParam& p_param)
case e_unk1:
break;
case e_plant:
PlantManager()->FUN_10026c50(this);
PlantManager()->DecrementCounter(this);
break;
case e_building:
BuildingManager()->FUN_10030000(this);

View File

@ -136,6 +136,8 @@ MxResult LegoInputManager::GetNavigationKeyStates(MxU32& p_keyFlags)
keyFlags |= c_ctrl;
}
GetNavigationTouchStates(keyFlags);
p_keyFlags = keyFlags;
return SUCCESS;
@ -542,3 +544,45 @@ void LegoInputManager::EnableInputProcessing()
g_unk0x100f31b0 = -1;
g_unk0x100f31b4 = NULL;
}
MxResult LegoInputManager::GetNavigationTouchStates(MxU32& p_keyStates)
{
int count;
SDL_TouchID* touchDevices = SDL_GetTouchDevices(&count);
if (touchDevices) {
auto applyFingerNavigation = [&p_keyStates](SDL_TouchID p_touchId) {
int count;
SDL_Finger** fingers = SDL_GetTouchFingers(p_touchId, &count);
if (fingers) {
for (int i = 0; i < count; i++) {
if (fingers[i]->y > 3.0 / 4.0) {
if (fingers[i]->x < 1.0 / 3.0) {
p_keyStates |= c_left;
}
else if (fingers[i]->x > 2.0 / 3.0) {
p_keyStates |= c_right;
}
else {
p_keyStates |= c_down;
}
}
else {
p_keyStates |= c_up;
}
}
SDL_free(fingers);
}
};
for (int i = 0; i < count; i++) {
applyFingerNavigation(touchDevices[i]);
}
SDL_free(touchDevices);
}
return SUCCESS;
}

View File

@ -281,8 +281,6 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
SetAppCursor(e_cursorBusy);
m_gameState->SetCurrentAct(LegoGameState::e_act1);
InitSdlEvents();
result = SUCCESS;
done:

View File

@ -566,27 +566,32 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage)
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numN);
edge.m_pointA = &m_nodes[s];
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numN);
edge.m_pointB = &m_nodes[s];
if (edge.m_flags & LegoOrientedEdge::c_hasFaceA) {
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numL);
edge.m_faceA = &m_boundaries[s];
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numE);
edge.m_ccwA = &m_edges[s];
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numE);
edge.m_cwA = &m_edges[s];
}
@ -594,16 +599,19 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage)
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numL);
edge.m_faceB = &m_boundaries[s];
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numE);
edge.m_ccwB = &m_edges[s];
if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) {
return FAILURE;
}
assert(s < m_numE);
edge.m_cwB = &m_edges[s];
}
@ -624,6 +632,9 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage)
MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage)
{
for (MxS32 i = 0; i < m_numL; i++) {
#ifdef BETA10
Mx4DPointFloat unused;
#endif
LegoPathBoundary& boundary = m_boundaries[i];
MxU8 numE;
MxU16 s;
@ -633,6 +644,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage)
return FAILURE;
}
assert(numE > 2);
boundary.m_edgeNormals = new Mx4DPointFloat[numE];
LegoOrientedEdge** edges = new LegoOrientedEdge*[numE];
@ -643,6 +656,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage)
return FAILURE;
}
assert(s < m_numE);
edges[j] = &m_edges[s];
}
@ -700,6 +715,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage)
return FAILURE;
}
assert(s < m_numT);
boundary.m_pathTrigger[j].m_pathStruct = &m_structs[s];
if (p_storage->Read(&boundary.m_pathTrigger[j].m_data, sizeof(boundary.m_pathTrigger[j].m_data)) !=

View File

@ -68,8 +68,8 @@ void LegoPhonemePresenter::StartingTickle()
CharacterManager()->SetHeadTexture(entityROI, textureInfo);
phoneme->VTable0x0c(m_textureInfo);
phoneme->VTable0x14(textureInfo);
phoneme->SetTextureInfo(m_textureInfo);
phoneme->SetCachedTextureInfo(textureInfo);
phonemeList->Append(phoneme);
m_textureInfo = textureInfo;
}
@ -78,7 +78,7 @@ void LegoPhonemePresenter::StartingTickle()
cursor.Current(phoneme);
delete newPhoneme;
phoneme->VTable0x04(phoneme->VTable0x00() + 1);
phoneme->SetCount(phoneme->GetCount() + 1);
cursor.SetValue(phoneme);
m_unk0x70 = TRUE;
@ -136,7 +136,7 @@ void LegoPhonemePresenter::EndAction()
cursor.Current(phoneme);
delete newPhoneme;
if (phoneme->VTable0x00() == 1) {
if (phoneme->GetCount() == 1) {
LegoROI* roi;
if (m_unk0x84) {
@ -154,12 +154,12 @@ void LegoPhonemePresenter::EndAction()
CharacterManager()->ReleaseActor(m_roiName.GetData());
}
TextureContainer()->EraseCached(phoneme->VTable0x10());
TextureContainer()->EraseCached(phoneme->VTable0x08());
TextureContainer()->EraseCached(phoneme->GetCachedTextureInfo());
TextureContainer()->EraseCached(phoneme->GetTextureInfo());
cursor.Destroy();
}
else {
phoneme->VTable0x04(phoneme->VTable0x00() - 1);
phoneme->SetCount(phoneme->GetCount() - 1);
cursor.SetValue(phoneme);
}

View File

@ -665,7 +665,7 @@ MxLong Act3::HandleTransitionEnd()
// FUNCTION: LEGO1 0x10073270
void Act3::ReadyWorld()
{
PlantManager()->FUN_10027200();
PlantManager()->SetInitialCounters();
BuildingManager()->FUN_10030800();
AnimationManager()->FUN_1005f6d0(FALSE);
VideoManager()->Get3DManager()->SetFrustrum(90.0f, 0.1f, 125.0f);

View File

@ -1389,7 +1389,7 @@ void Infocenter::Reset()
break;
}
PlantManager()->FUN_10027120();
PlantManager()->ClearCounters();
BuildingManager()->FUN_10030590();
AnimationManager()->Reset(FALSE);
CharacterManager()->ReleaseAllActors();

View File

@ -25,6 +25,7 @@ class LegoWEEdge {
// FUNCTION: BETA10 0x100373f0
LegoU32 IsEqual(LegoWEEdge* p_other) { return this == p_other; }
// FUNCTION: BETA10 0x100bd410
void SetEdges(LegoOrientedEdge** p_edges, LegoU8 p_numEdges)
{
m_edges = p_edges;

View File

@ -215,11 +215,6 @@ int LegoDeviceEnumerate::FUN_1009d210()
}
for (list<MxDriver>::iterator it = m_list.begin(); it != m_list.end();) {
if (!DriverSupportsRequiredDisplayMode(*it)) {
m_list.erase(it++);
continue;
}
MxDriver& driver = *it;
for (list<Direct3DDeviceInfo>::iterator it2 = driver.m_devices.begin(); it2 != driver.m_devices.end();) {
@ -246,23 +241,6 @@ int LegoDeviceEnumerate::FUN_1009d210()
return 0;
}
// FUNCTION: CONFIG 0x00402b00
// FUNCTION: LEGO1 0x1009d370
// FUNCTION: BETA10 0x1011d176
unsigned char LegoDeviceEnumerate::DriverSupportsRequiredDisplayMode(MxDriver& p_driver)
{
for (list<MxDisplayMode>::iterator it = p_driver.m_displayModes.begin(); it != p_driver.m_displayModes.end();
it++) {
if ((*it).m_width == 640 && (*it).m_height == 480) {
if ((*it).m_bitsPerPixel == 8 || (*it).m_bitsPerPixel == 16) {
return TRUE;
}
}
}
return FALSE;
}
// FUNCTION: CONFIG 0x00402b60
// FUNCTION: LEGO1 0x1009d3d0
// FUNCTION: BETA10 0x1011d235

View File

@ -17,7 +17,6 @@ class LegoDeviceEnumerate : public MxDeviceEnumerate {
int GetBestDevice();
static bool SupportsSIMD();
int FUN_1009d210();
unsigned char DriverSupportsRequiredDisplayMode(MxDriver& p_driver);
unsigned char FUN_1009d3d0(Direct3DDeviceInfo& p_device);
// SYNTHETIC: BETA10 0x100d8d10

View File

@ -167,14 +167,6 @@ BOOL MxDirect3D::D3DSetMode()
}
DeviceModesInfo::Mode mode = *CurrentMode();
if (IsFullScreen()) {
if (!IsSupportedMode(mode.width, mode.height, mode.bitsPerPixel)) {
Error("This device cannot support the current display mode", DDERR_GENERIC);
return FALSE;
}
}
LPDIRECTDRAWSURFACE frontBuffer = FrontBuffer();
LPDIRECTDRAWSURFACE backBuffer = BackBuffer();
@ -277,21 +269,6 @@ BOOL MxDirect3D::SetDevice(MxDeviceEnumerate& p_deviceEnumerate, MxDriver* p_dri
*d->m_deviceInfo->m_guid = *driver.m_guid;
}
d->m_deviceInfo->m_count = driver.m_displayModes.size();
if (d->m_deviceInfo->m_count > 0) {
int j = 0;
d->m_deviceInfo->m_modeArray = new DeviceModesInfo::Mode[d->m_deviceInfo->m_count];
for (list<MxDisplayMode>::iterator it2 = driver.m_displayModes.begin();
it2 != driver.m_displayModes.end();
it2++, j++) {
d->m_deviceInfo->m_modeArray[j].width = (*it2).m_width;
d->m_deviceInfo->m_modeArray[j].height = (*it2).m_height;
d->m_deviceInfo->m_modeArray[j].bitsPerPixel = (*it2).m_bitsPerPixel;
}
}
d->m_deviceInfo->m_ddcaps = driver.m_ddCaps;
if (i == 0) {

View File

@ -283,23 +283,6 @@ BOOL MxDirectDraw::DDInit(BOOL fullscreen)
return TRUE;
}
// FUNCTION: LEGO1 0x1009d9d0
// FUNCTION: BETA10 0x10120e45
BOOL MxDirectDraw::IsSupportedMode(int width, int height, int bpp)
{
DeviceModesInfo::Mode mode = {width, height, bpp};
assert(m_currentDevInfo);
for (int i = 0; i < m_currentDevInfo->m_count; i++) {
if (m_currentDevInfo->m_modeArray[i] == mode) {
return TRUE;
}
}
return FALSE;
}
// FUNCTION: LEGO1 0x1009da20
// FUNCTION: BETA10 0x10120efb
void EnableResizing(HWND p_hwnd, BOOL p_flag)
@ -339,12 +322,6 @@ BOOL MxDirectDraw::DDSetMode(int width, int height, int bpp)
}
#endif
if (!IsSupportedMode(width, height, bpp)) {
width = m_currentDevInfo->m_modeArray[0].width;
height = m_currentDevInfo->m_modeArray[0].height;
bpp = m_currentDevInfo->m_modeArray[0].bitsPerPixel;
}
m_bIgnoreWMSIZE = TRUE;
result = m_pDirectDraw->SetDisplayMode(width, height, bpp);
m_bIgnoreWMSIZE = FALSE;

View File

@ -55,8 +55,6 @@ class MxDirectDraw {
// FUNCTION: BETA10 0x1011c170
BOOL IsFullScreen() { return m_bFullScreen; }
BOOL IsSupportedMode(int width, int height, int bpp);
int Pause(BOOL);
BOOL RestoreSurfaces();

View File

@ -216,7 +216,6 @@ BOOL MxDeviceEnumerate::EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc
BuildErrorString("DirectDraw Create failed: %s\n", EnumerateErrorToString(result));
}
else {
lpDD->EnumDisplayModes(0, NULL, this, DisplayModesEnumerateCallback);
newDevice.m_ddCaps.dwSize = sizeof(newDevice.m_ddCaps);
result = lpDD->GetCaps(&newDevice.m_ddCaps, NULL);
@ -267,18 +266,6 @@ void MxDeviceEnumerate::BuildErrorString(const char* p_format, ...)
va_end(args);
}
// FUNCTION: CONFIG 0x00401bf0
// FUNCTION: LEGO1 0x1009c4f0
// FUNCTION: BETA10 0x1011e1dd
HRESULT CALLBACK MxDeviceEnumerate::DisplayModesEnumerateCallback(LPDDSURFACEDESC p_ddsd, LPVOID p_context)
{
if (p_context == NULL) {
assert(0);
}
return ((MxDeviceEnumerate*) p_context)->EnumDisplayModesCallback(p_ddsd);
}
// FUNCTION: CONFIG 0x00401c10
// FUNCTION: LEGO1 0x1009c510
// FUNCTION: BETA10 0x1011e226
@ -299,20 +286,6 @@ HRESULT CALLBACK MxDeviceEnumerate::DevicesEnumerateCallback(
->EnumDevicesCallback(p_guid, p_deviceDesc, p_deviceName, p_HWDesc, p_HELDesc);
}
// FUNCTION: CONFIG 0x00401c40
// FUNCTION: LEGO1 0x1009c540
// FUNCTION: BETA10 0x1011e27f
HRESULT MxDeviceEnumerate::EnumDisplayModesCallback(LPDDSURFACEDESC p_ddsd)
{
assert(m_list.size() > 0);
assert(p_ddsd);
// TODO: compat_mode?
MxDisplayMode displayMode(p_ddsd->dwWidth, p_ddsd->dwHeight, p_ddsd->ddpfPixelFormat.dwRGBBitCount);
m_list.back().m_displayModes.push_back(displayMode);
return DDENUMRET_OK;
}
// FUNCTION: CONFIG 0x00401cd0
// FUNCTION: LEGO1 0x1009c5d0
// FUNCTION: BETA10 0x1011e32f
@ -582,8 +555,4 @@ DeviceModesInfo::~DeviceModesInfo()
if (m_guid != NULL) {
delete m_guid;
}
if (m_modeArray != NULL) {
delete[] m_modeArray;
}
}

View File

@ -28,11 +28,10 @@ struct DeviceModesInfo {
DeviceModesInfo();
~DeviceModesInfo();
GUID* m_guid; // 0x00
Mode* m_modeArray; // 0x04
int m_count; // 0x08
DDCAPS m_ddcaps; // 0x0c
void* m_unk0x178; // 0x178
GUID* m_guid; // 0x00
int m_count; // 0x08
DDCAPS m_ddcaps; // 0x0c
void* m_unk0x178; // 0x178
// SYNTHETIC: BETA10 0x1011c650
// DeviceModesInfo::`scalar deleting destructor'
@ -128,7 +127,6 @@ struct MxDriver {
char* m_driverName; // 0x08
DDCAPS m_ddCaps; // 0x0c
list<Direct3DDeviceInfo> m_devices; // 0x178
list<MxDisplayMode> m_displayModes; // 0x184
int operator==(MxDriver) const { return 0; }
int operator<(MxDriver) const { return 0; }
@ -199,7 +197,6 @@ class MxDeviceEnumerate {
virtual int DoEnumerate(); // vtable+0x00
BOOL EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc, LPSTR p_driverName);
HRESULT EnumDisplayModesCallback(LPDDSURFACEDESC p_ddsd);
HRESULT EnumDevicesCallback(
LPGUID p_guid,
LPCSTR p_deviceDesc,
@ -211,7 +208,6 @@ class MxDeviceEnumerate {
static void BuildErrorString(const char*, ...);
static BOOL CALLBACK
DirectDrawEnumerateCallback(LPGUID p_guid, LPSTR p_driverDesc, LPSTR p_driverName, LPVOID p_context);
static HRESULT CALLBACK DisplayModesEnumerateCallback(LPDDSURFACEDESC p_ddsd, LPVOID p_context);
static HRESULT CALLBACK DevicesEnumerateCallback(
LPGUID p_guid,
LPSTR p_deviceDesc,

View File

@ -43,8 +43,8 @@ class MxOmni : public MxCore {
LEGO1_EXPORT static void SetCD(const char* p_cd);
LEGO1_EXPORT static void SetHD(const char* p_hd);
LEGO1_EXPORT static void SetSound3D(MxBool p_use3dSound);
static const vector<MxString>& GetHDFiles() { return g_hdFiles; }
static const vector<MxString>& GetCDFiles() { return g_cdFiles; }
static vector<MxString>& GetHDFiles() { return g_hdFiles; }
static vector<MxString>& GetCDFiles() { return g_cdFiles; }
MxOmni();
~MxOmni() override;

View File

@ -5,6 +5,9 @@
#include "mxcore.h"
#include "mxcriticalsection.h"
#include "mxgeometry.h"
#include "mxutilities.h"
#include <SDL3/SDL_events.h>
class MxCompositePresenter;
class MxDSAction;
@ -62,6 +65,12 @@ class MxPresenter : public MxCore {
{
m_previousTickleStates |= 1 << (MxU8) m_currentTickleState;
m_currentTickleState = p_tickleState;
SDL_Event event;
event.user.type = g_legoSdlEvents.m_presenterProgress;
event.user.code = m_currentTickleState;
event.user.data1 = (void*) this;
SDL_PushEvent(&event);
}
public:

View File

@ -1,10 +1,19 @@
#ifndef MXUTILITIES_H
#define MXUTILITIES_H
#include "lego1_export.h"
#include "mxtypes.h"
#include <SDL3/SDL_stdinc.h>
#include <string.h>
struct LegoSdlEvents {
Uint32 m_windowsMessage;
Uint32 m_presenterProgress;
};
LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents;
class MxDSFile;
class MxDSObject;
class MxDSAction;

View File

@ -12,6 +12,8 @@
#include <SDL3/SDL_stdinc.h>
#include <assert.h>
LegoSdlEvents g_legoSdlEvents;
// GLOBAL: LEGO1 0x101020e8
void (*g_omniUserMessage)(const char*, MxS32) = NULL;

View File

@ -162,6 +162,12 @@ MxResult MxOmni::Create(MxOmniCreateParam& p_param)
}
}
{
Uint32 event = SDL_RegisterEvents(2);
g_legoSdlEvents.m_windowsMessage = event + 0;
g_legoSdlEvents.m_presenterProgress = event + 1;
}
result = SUCCESS;
done:

View File

@ -15,6 +15,7 @@ Please note: this project is dedicated to achieving platform independence withou
| Windows | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| Linux | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| macOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
### Library substitutions
@ -22,7 +23,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl
| Library/subsystem | Substitution | Status | |
| - | - | - | - |
| Window, Events | [SDL3](https://www.libsdl.org/) | 🚧 | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Awindow%5D%22&type=code) |
| Window, Events | [SDL3](https://www.libsdl.org/) | | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Awindow%5D%22&type=code) |
| Windows Registry (Configuration) | [libiniparser](https://gitlab.com/iniparser/iniparser) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aconfig%5D%22&type=code) |
| Filesystem | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Afilesystem%5D%22&type=code) |
| Threads, Mutexes (Synchronization) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Asynchronization%5D%22&type=code) |
@ -32,7 +33,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl
| DirectDraw (2D video) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A2d%5D%22&type=code) |
| [Smacker](https://github.com/isledecomp/isle/tree/master/3rdparty/smacker) | [libsmacker](https://github.com/foxtacles/libsmacker) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable%20%22%2F%2F%20%5Blibrary%3Alibsmacker%5D%22&type=code) |
| Direct3D (3D video) | [SDL3](https://www.libsdl.org/), OpenGL, Software | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) |
| Direct3D Retained Mode | Custom re-implementation | 🚧 | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aretained%5D%22&type=code) |
| Direct3D Retained Mode | Custom re-implementation | | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aretained%5D%22&type=code) |
| [SmartHeap](https://github.com/isledecomp/isle/tree/master/3rdparty/smartheap) | Default memory allocator | - | - |
## Building

View File

@ -3,6 +3,7 @@
DEFINE_GUID(IID_IDirect3DRMMiniwinDevice, 0x6eb09673, 0x8d30, 0x4d8a, 0x8d, 0x81, 0x34, 0xea, 0x69, 0x30, 0x12, 0x01);
struct IDirect3DRMMiniwinDevice : virtual public IUnknown {
virtual bool ConvertEventToRenderCoordinates(SDL_Event* event) = 0;
virtual float GetShininessFactor() = 0;
virtual HRESULT SetShininessFactor(float factor) = 0;
};

View File

@ -144,6 +144,11 @@ float Direct3DRMDevice2Impl::GetShininessFactor()
return m_renderer->GetShininessFactor();
}
bool Direct3DRMDevice2Impl::ConvertEventToRenderCoordinates(SDL_Event* event)
{
return m_renderer->ConvertEventToRenderCoordinates(event);
}
HRESULT Direct3DRMDevice2Impl::SetShininessFactor(float factor)
{
return m_renderer->SetShininessFactor(factor);

View File

@ -17,6 +17,9 @@
SDL_Window* DDWindow;
SDL_Surface* DDBackBuffer;
SDL_Texture* HWBackBuffer;
SDL_PixelFormat HWBackBufferFormat;
SDL_Renderer* DDRenderer;
HRESULT DirectDrawImpl::QueryInterface(const GUID& riid, void** ppvObject)
{
@ -280,6 +283,7 @@ HRESULT DirectDrawImpl::RestoreDisplayMode()
HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags)
{
SDL_Window* sdlWindow = reinterpret_cast<SDL_Window*>(hWnd);
if (sdlWindow) {
bool fullscreen;
if ((dwFlags & DDSCL_NORMAL) == DDSCL_NORMAL) {
@ -293,9 +297,13 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags)
}
if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) {
#ifndef __EMSCRIPTEN__
return DDERR_GENERIC;
#endif
}
DDWindow = sdlWindow;
DDRenderer = SDL_CreateRenderer(DDWindow, NULL);
SDL_SetRenderLogicalPresentation(DDRenderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX);
}
return DD_OK;
}

View File

@ -55,6 +55,31 @@ static SDL_Rect ConvertRect(const RECT* r)
return {r->left, r->top, r->right - r->left, r->bottom - r->top};
}
bool SetupHWBackBuffer()
{
HWBackBuffer = SDL_CreateTextureFromSurface(DDRenderer, DDBackBuffer);
if (!HWBackBuffer) {
return false;
}
SDL_PropertiesID props = SDL_GetTextureProperties(HWBackBuffer);
if (!props) {
SDL_DestroyTexture(HWBackBuffer);
HWBackBuffer = nullptr;
return false;
}
HWBackBufferFormat =
(SDL_PixelFormat) SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, SDL_PIXELFORMAT_UNKNOWN);
if (HWBackBufferFormat == SDL_PIXELFORMAT_UNKNOWN) {
SDL_DestroyTexture(HWBackBuffer);
HWBackBuffer = nullptr;
return false;
}
return true;
}
HRESULT DirectDrawSurfaceImpl::Blt(
LPRECT lpDestRect,
LPDIRECTDRAWSURFACE lpDDSrcSurface,
@ -69,6 +94,9 @@ HRESULT DirectDrawSurfaceImpl::Blt(
}
if (m_autoFlip) {
DDBackBuffer = srcSurface->m_surface;
if (!HWBackBuffer && !SetupHWBackBuffer()) {
return DDERR_GENERIC;
}
return Flip(nullptr, DDFLIP_WAIT);
}
@ -126,18 +154,15 @@ HRESULT DirectDrawSurfaceImpl::BltFast(
HRESULT DirectDrawSurfaceImpl::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DDFlipFlags dwFlags)
{
if (!DDBackBuffer) {
if (!DDBackBuffer || !DDRenderer) {
return DDERR_GENERIC;
}
SDL_Surface* windowSurface = SDL_GetWindowSurface(DDWindow);
if (!windowSurface) {
return DDERR_GENERIC;
}
SDL_Rect srcRect{0, 0, DDBackBuffer->w, DDBackBuffer->h};
SDL_Surface* copy = SDL_ConvertSurface(DDBackBuffer, windowSurface->format);
SDL_BlitSurface(copy, &srcRect, windowSurface, &srcRect);
SDL_Surface* copy = SDL_ConvertSurface(DDBackBuffer, HWBackBufferFormat);
SDL_UpdateTexture(HWBackBuffer, nullptr, copy->pixels, copy->pitch);
SDL_DestroySurface(copy);
SDL_UpdateWindowSurface(DDWindow);
SDL_RenderTexture(DDRenderer, HWBackBuffer, nullptr, nullptr);
SDL_RenderPresent(DDRenderer);
return DD_OK;
}
@ -147,6 +172,9 @@ HRESULT DirectDrawSurfaceImpl::GetAttachedSurface(LPDDSCAPS lpDDSCaps, LPDIRECTD
return DDERR_INVALIDPARAMS;
}
DDBackBuffer = m_surface;
if (!SetupHWBackBuffer()) {
return DDERR_GENERIC;
}
*lplpDDAttachedSurface = static_cast<IDirectDrawSurface*>(this);
return DD_OK;
}

View File

@ -33,6 +33,7 @@ struct Direct3DRMDevice2Impl : public Direct3DRMObjectBaseImpl<IDirect3DRMDevice
HRESULT GetViewports(IDirect3DRMViewportArray** ppViewportArray) override;
// IDirect3DRMMiniwinDevice interface
bool ConvertEventToRenderCoordinates(SDL_Event* event) override;
float GetShininessFactor() override;
HRESULT SetShininessFactor(float factor) override;

View File

@ -31,6 +31,8 @@ struct SceneLight {
};
static_assert(sizeof(SceneLight) == 48);
extern SDL_Renderer* DDRenderer;
class Direct3DRMRenderer : public IDirect3DDevice2 {
public:
virtual void PushLights(const SceneLight* vertices, size_t count) = 0;
@ -51,6 +53,10 @@ class Direct3DRMRenderer : public IDirect3DDevice2 {
) = 0;
virtual HRESULT FinalizeFrame() = 0;
bool ConvertEventToRenderCoordinates(SDL_Event* event)
{
return SDL_ConvertEventToRenderCoordinates(DDRenderer, event);
}
float GetShininessFactor() { return m_shininessFactor; }
HRESULT SetShininessFactor(float factor)
{

View File

@ -8,6 +8,9 @@
extern SDL_Window* DDWindow;
extern SDL_Surface* DDBackBuffer;
extern SDL_Texture* HWBackBuffer;
extern SDL_PixelFormat HWBackBufferFormat;
extern SDL_Renderer* DDRenderer;
struct DirectDrawImpl : public IDirectDraw2, public IDirect3D2 {
// IUnknown interface

View File

@ -17,6 +17,14 @@
#define DDBitDepths DWORD
#endif
// SDL will not put the message box on the main thread by default.
// See: https://github.com/libsdl-org/SDL/issues/12943
#ifdef __EMSCRIPTEN__
#define Any_ShowSimpleMessageBox Emscripten_ShowSimpleMessageBox
#else
#define Any_ShowSimpleMessageBox SDL_ShowSimpleMessageBox
#endif
// Disable "identifier was truncated to '255' characters" warning.
// Impossible to avoid this if using STL map or set.
// This removes most (but not all) occurrences of the warning.