mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-01 18:13:57 +00:00
Some checks failed
CI / clang-format (push) Has been cancelled
CI / ${{ matrix.name }} (false, --toolchain /usr/local/vitasdk/share/vita.toolchain.cmake, false, false, Ninja, Vita, ubuntu-latest, true, true) (push) Has been cancelled
CI / ${{ matrix.name }} (false, -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0, false, false, Visual Studio 17 2022, true, Xbox One, windows-latest, amd64, false, true) (push) Has been cancelled
CI / ${{ matrix.name }} (false, -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake, false, devkitpro/devkitarm:latest, false, Ninja, true, Nintendo 3DS, ubuntu-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (false, -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake, false, devkitpro/devkita64:latest, false, Ninja, Nintendo Switch, true, ubuntu-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (false, emcmake, false, false, true, Ninja, Emscripten, ubuntu-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (false, false, false, Ninja, true, MSVC (arm64), windows-latest, amd64_arm64, false) (push) Has been cancelled
CI / ${{ matrix.name }} (false, false, true, Ninja, true, MSVC (x86), windows-latest, amd64_x86, false) (push) Has been cancelled
CI / ${{ matrix.name }} (false, true, false, Ninja, true, MSVC (x64), windows-latest, amd64, false) (push) Has been cancelled
CI / ${{ matrix.name }} (false, true, true, false, Ninja, true, MSVC (x64 Debug), windows-latest, amd64, false) (push) Has been cancelled
CI / ${{ matrix.name }} (true, false, -DCMAKE_SYSTEM_NAME=iOS, false, false, Xcode, true, iOS, macos-15, true) (push) Has been cancelled
CI / ${{ matrix.name }} (true, false, false, false, Ninja, Android, ubuntu-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (true, false, true, false, Ninja, macOS, macos-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (true, true, false, Ninja, true, mingw-w64-x86_64, mingw64, msys2 mingw64, windows-latest, msys2 {0}, true) (push) Has been cancelled
CI / ${{ matrix.name }} (true, true, true, false, Ninja, true, Linux (Debug), ubuntu-latest, true) (push) Has been cancelled
CI / ${{ matrix.name }} (true, true, true, false, Ninja, true, Linux, ubuntu-latest, true) (push) Has been cancelled
CI / Flatpak (${{ matrix.arch }}) (aarch64, ubuntu-22.04-arm) (push) Has been cancelled
CI / Flatpak (${{ matrix.arch }}) (x86_64, ubuntu-latest) (push) Has been cancelled
CI / C++ (push) Has been cancelled
Docker / Publish web port (push) Has been cancelled
CI / Release (push) Has been cancelled
Introduces a third person camera system with orbit camera, input handling (mouse/keyboard/touch/gamepad), display actor cloning, and camera-relative movement. Includes shared character utilities (animator, cloner, customizer) and an IExtraAnimHandler interface for optional animation extensions. Also includes generic base game fixes and extension system improvements.
1127 lines
28 KiB
C++
1127 lines
28 KiB
C++
#include "legonavcontroller.h"
|
|
|
|
#include "3dmanager/lego3dmanager.h"
|
|
#include "act3.h"
|
|
#include "extensions/thirdpersoncamera.h"
|
|
#include "infocenter.h"
|
|
#include "legoanimationmanager.h"
|
|
#include "legocameracontroller.h"
|
|
#include "legocharactermanager.h"
|
|
#include "legogamestate.h"
|
|
#include "legoinputmanager.h"
|
|
#include "legolocations.h"
|
|
#include "legomain.h"
|
|
#include "legoplantmanager.h"
|
|
#include "legosoundmanager.h"
|
|
#include "legoutils.h"
|
|
#include "legovideomanager.h"
|
|
#include "legoworld.h"
|
|
#include "misc.h"
|
|
#include "mxbackgroundaudiomanager.h"
|
|
#include "mxdebug.h"
|
|
#include "mxmisc.h"
|
|
#include "mxtimer.h"
|
|
#include "mxtransitionmanager.h"
|
|
#include "mxutilities.h"
|
|
#include "realtime/realtime.h"
|
|
#include "realtime/realtimeview.h"
|
|
#include "viewmanager/viewmanager.h"
|
|
|
|
#include <SDL3/SDL_stdinc.h>
|
|
#include <vec.h>
|
|
|
|
using namespace Extensions;
|
|
|
|
DECOMP_SIZE_ASSERT(LegoNavController, 0x70)
|
|
|
|
// MSVC 4.20 didn't define a macro for this key
|
|
#ifndef VK_OEM_MINUS
|
|
#define VK_OEM_MINUS 0xBD
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.1416
|
|
#endif
|
|
#ifdef DTOR
|
|
#undef DTOR
|
|
#endif
|
|
#define DTOR(angle) ((angle) * M_PI / 180.)
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// GLOBAL: LEGO1 0x100f4c28
|
|
int LegoNavController::g_defdeadZone = 40;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c2c
|
|
float LegoNavController::g_defzeroThreshold = 0.001f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c30
|
|
float LegoNavController::g_defmaxLinearVel = 40.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c34
|
|
float LegoNavController::g_defmaxRotationalVel = 20.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c38
|
|
float LegoNavController::g_defmaxLinearAccel = 15.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c3c
|
|
float LegoNavController::g_defmaxRotationalAccel = 30.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c40
|
|
float LegoNavController::g_defminLinearAccel = 4.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c44
|
|
float LegoNavController::g_defminRotationalAccel = 15.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c48
|
|
float LegoNavController::g_defmaxLinearDeccel = 50.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c4c
|
|
float LegoNavController::g_defmaxRotationalDeccel = 50.0f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c50
|
|
float LegoNavController::g_defrotSensitivity = 0.4f;
|
|
|
|
// GLOBAL: LEGO1 0x100f4c54
|
|
MxBool LegoNavController::g_defuseRotationalVel = FALSE;
|
|
|
|
// GLOBAL: LEGO1 0x100f66a0
|
|
MxU32 g_changeLight = FALSE;
|
|
|
|
// GLOBAL: LEGO1 0x100f66a4
|
|
MxS32 g_locationCalcStep = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f66a8
|
|
MxS32 g_nextLocation = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f66ac
|
|
MxBool g_resetPlants = FALSE;
|
|
|
|
// GLOBAL: LEGO1 0x100f66b0
|
|
MxS32 g_animationCalcStep = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f66b4
|
|
MxS32 g_nextAnimation = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f66b8
|
|
MxU32 g_switchAct = FALSE;
|
|
|
|
// GLOBAL: LEGO1 0x100f66bc
|
|
LegoAnimationManager::PlayMode g_unk0x100f66bc = LegoAnimationManager::e_unk2;
|
|
|
|
// GLOBAL: LEGO1 0x100f66c0
|
|
SDL_Keycode g_debugPassword[] = {
|
|
// "OGEL"
|
|
SDLK_O,
|
|
SDLK_G,
|
|
SDLK_E,
|
|
SDLK_L,
|
|
0,
|
|
};
|
|
|
|
// GLOBAL: LEGO1 0x100f66c8
|
|
SDL_Keycode* g_currentInput = g_debugPassword;
|
|
|
|
// GLOBAL: LEGO1 0x100f66cc
|
|
MxS32 g_nextCharacter = -1;
|
|
|
|
// GLOBAL: LEGO1 0x100f66d0
|
|
MxBool g_enableMusic = TRUE;
|
|
|
|
// GLOBAL: LEGO1 0x100f66d4
|
|
MxU32 g_fpsEnabled = TRUE;
|
|
|
|
// FUNCTION: LEGO1 0x10054ac0
|
|
LegoNavController::LegoNavController()
|
|
{
|
|
SetToDefaultParams();
|
|
|
|
m_linearVel = 0.0f;
|
|
m_rotationalVel = 0.0f;
|
|
m_targetLinearVel = 0.0f;
|
|
m_targetRotationalVel = 0.0f;
|
|
m_linearAccel = 0.0f;
|
|
m_rotationalAccel = 0.0f;
|
|
m_trackDefault = FALSE;
|
|
m_keyPressed = FALSE;
|
|
m_isAccelerating = FALSE;
|
|
m_additionalScale = 0.0f;
|
|
m_additionalRotationY = 0.0f;
|
|
m_additionalHeightOffset = 0.0f;
|
|
|
|
m_lastTime = Timer()->GetTime();
|
|
|
|
InputManager()->Register(this);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054c30
|
|
LegoNavController::~LegoNavController()
|
|
{
|
|
InputManager()->UnRegister(this);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054ca0
|
|
void LegoNavController::SetControlMax(int p_hMax, int p_vMax)
|
|
{
|
|
m_hMax = p_hMax;
|
|
m_vMax = p_vMax;
|
|
|
|
if (VideoManager()->GetVideoParam().Flags().GetFullScreen()) {
|
|
m_hMax = 640;
|
|
m_vMax = 480;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054cd0
|
|
// FUNCTION: BETA10 0x1009ad76
|
|
void LegoNavController::SetToDefaultParams()
|
|
{
|
|
m_deadZone = g_defdeadZone;
|
|
m_zeroThreshold = g_defzeroThreshold;
|
|
m_maxRotationalAccel = g_defmaxRotationalAccel;
|
|
m_maxLinearAccel = g_defmaxLinearAccel;
|
|
m_minRotationalAccel = g_defminRotationalAccel;
|
|
m_minLinearAccel = g_defminLinearAccel;
|
|
m_maxRotationalDeccel = g_defmaxRotationalDeccel;
|
|
m_maxLinearDeccel = g_defmaxLinearDeccel;
|
|
m_maxRotationalVel = g_defmaxRotationalVel;
|
|
m_maxLinearVel = g_defmaxLinearVel;
|
|
m_useRotationalVel = g_defuseRotationalVel;
|
|
m_rotSensitivity = g_defrotSensitivity;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054d40
|
|
void LegoNavController::GetDefaults(
|
|
int* p_dz,
|
|
float* p_lv,
|
|
float* p_rv,
|
|
float* p_la,
|
|
float* p_ra,
|
|
float* p_ld,
|
|
float* p_rd,
|
|
float* p_lmina,
|
|
float* p_rmina,
|
|
float* p_rs,
|
|
MxBool* p_urs
|
|
)
|
|
{
|
|
*p_dz = g_defdeadZone;
|
|
*p_lv = g_defmaxLinearVel;
|
|
*p_rv = g_defmaxRotationalVel;
|
|
*p_la = g_defmaxLinearAccel;
|
|
*p_ra = g_defmaxRotationalAccel;
|
|
*p_ld = g_defmaxLinearDeccel;
|
|
*p_rd = g_defmaxRotationalDeccel;
|
|
*p_lmina = g_defminLinearAccel;
|
|
*p_rmina = g_defminRotationalAccel;
|
|
*p_rs = g_defrotSensitivity;
|
|
*p_urs = g_defuseRotationalVel;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054dd0
|
|
void LegoNavController::SetDefaults(
|
|
int p_dz,
|
|
float p_lv,
|
|
float p_rv,
|
|
float p_la,
|
|
float p_ra,
|
|
float p_ld,
|
|
float p_rd,
|
|
float p_lmina,
|
|
float p_rmina,
|
|
float p_rs,
|
|
MxBool p_urs
|
|
)
|
|
{
|
|
g_defdeadZone = p_dz;
|
|
g_defmaxLinearVel = p_lv;
|
|
g_defmaxRotationalVel = p_rv;
|
|
g_defmaxLinearAccel = p_la;
|
|
g_defmaxRotationalAccel = p_ra;
|
|
g_defmaxLinearDeccel = p_ld;
|
|
g_defmaxRotationalDeccel = p_rd;
|
|
g_defminLinearAccel = p_lmina;
|
|
g_defminRotationalAccel = p_rmina;
|
|
g_defrotSensitivity = p_rs;
|
|
g_defuseRotationalVel = p_urs;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054e40
|
|
void LegoNavController::SetTargets(int p_hPos, int p_vPos, MxBool p_accel)
|
|
{
|
|
if (m_trackDefault != FALSE) {
|
|
SetToDefaultParams();
|
|
}
|
|
|
|
if (p_accel != FALSE) {
|
|
m_targetRotationalVel = CalculateNewTargetVel(p_hPos, m_hMax / 2, m_maxRotationalVel);
|
|
m_targetLinearVel = CalculateNewTargetVel(m_vMax - p_vPos, m_vMax / 2, m_maxLinearVel);
|
|
m_rotationalAccel = CalculateNewAccel(p_hPos, m_hMax / 2, m_maxRotationalAccel, (int) m_minRotationalAccel);
|
|
m_linearAccel = CalculateNewAccel(m_vMax - p_vPos, m_vMax / 2, m_maxLinearAccel, (int) m_minLinearAccel);
|
|
}
|
|
else {
|
|
m_targetRotationalVel = 0;
|
|
m_targetLinearVel = 0;
|
|
m_linearAccel = m_maxLinearDeccel;
|
|
m_rotationalAccel = m_maxRotationalDeccel;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054f10
|
|
float LegoNavController::CalculateNewTargetVel(int p_pos, int p_center, float p_max)
|
|
{
|
|
float newVel;
|
|
int diff = p_pos - p_center;
|
|
|
|
if (diff > m_deadZone) {
|
|
newVel = (diff - m_deadZone) * p_max / (p_center - m_deadZone);
|
|
}
|
|
else if (diff < -m_deadZone) {
|
|
newVel = (diff + m_deadZone) * p_max / (p_center - m_deadZone);
|
|
}
|
|
else {
|
|
newVel = 0.0;
|
|
}
|
|
|
|
return newVel;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054f90
|
|
float LegoNavController::CalculateNewAccel(int p_pos, int p_center, float p_max, int p_min)
|
|
{
|
|
float newAccel;
|
|
int diff = p_pos - p_center;
|
|
|
|
newAccel = Abs(diff) * p_max / p_center;
|
|
|
|
if (newAccel < p_min) {
|
|
newAccel = (float) p_min;
|
|
}
|
|
|
|
return newAccel;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10054fe0
|
|
float LegoNavController::CalculateNewVel(float p_targetVel, float p_currentVel, float p_accel, float p_time)
|
|
{
|
|
float newVel = p_currentVel;
|
|
|
|
float velDiff = p_targetVel - p_currentVel;
|
|
int vSign = velDiff > 0 ? 1 : -1;
|
|
|
|
if (Abs(velDiff) > m_zeroThreshold) {
|
|
float deltaVel = p_accel * p_time;
|
|
newVel = p_currentVel + (deltaVel * vSign);
|
|
|
|
if (vSign > 0) {
|
|
newVel = Min(newVel, p_targetVel);
|
|
}
|
|
else {
|
|
newVel = Max(newVel, p_targetVel);
|
|
}
|
|
}
|
|
|
|
return newVel;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055080
|
|
// FUNCTION: BETA10 0x1009b26b
|
|
MxBool LegoNavController::CalculateNewPosDir(
|
|
const Vector3& p_curPos,
|
|
const Vector3& p_curDir,
|
|
Vector3& p_newPos,
|
|
Vector3& p_newDir,
|
|
const Vector3* p_up
|
|
)
|
|
{
|
|
if (!g_isWorldActive) {
|
|
return FALSE;
|
|
}
|
|
|
|
MxBool changed = FALSE;
|
|
MxBool rotatedY = FALSE;
|
|
|
|
MxTime currentTime = Timer()->GetTime();
|
|
float deltaTime = Min((currentTime - m_lastTime) / 1000.0, 1. / 10.);
|
|
m_lastTime = currentTime;
|
|
|
|
if (ProcessKeyboardInput() == FAILURE) {
|
|
ProcessJoystickInput(rotatedY);
|
|
}
|
|
|
|
if (Extension<
|
|
ThirdPersonCameraExt>::Call(TP::HandleNavOverride, this, p_curPos, p_curDir, p_newPos, p_newDir, deltaTime)
|
|
.value_or(FALSE)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (m_useRotationalVel) {
|
|
m_rotationalVel = CalculateNewVel(m_targetRotationalVel, m_rotationalVel, m_rotationalAccel * 40.0f, deltaTime);
|
|
}
|
|
else {
|
|
m_rotationalVel = m_targetRotationalVel * m_maxRotationalVel * deltaTime;
|
|
}
|
|
|
|
m_linearVel = CalculateNewVel(m_targetLinearVel, m_linearVel, m_linearAccel, deltaTime);
|
|
|
|
if (rotatedY || (Abs(m_rotationalVel) > m_zeroThreshold) || (Abs(m_linearVel) > m_zeroThreshold)) {
|
|
float rot_mat[3][3];
|
|
Mx3DPointFloat delta_pos, new_dir, new_pos;
|
|
|
|
if (m_linearVel < -(m_maxLinearVel * 0.4f)) {
|
|
m_linearVel = -(m_maxLinearVel * 0.4f);
|
|
}
|
|
|
|
VXS3(delta_pos, p_curDir, m_linearVel * deltaTime);
|
|
VPV3(p_newPos, p_curPos, delta_pos);
|
|
|
|
float delta_rad;
|
|
if (m_useRotationalVel) {
|
|
delta_rad = DTOR(m_rotationalVel * deltaTime);
|
|
}
|
|
else {
|
|
delta_rad = DTOR(m_rotationalVel * m_rotSensitivity);
|
|
}
|
|
|
|
if (p_up != NULL && (*p_up)[1] < 0.0f) {
|
|
delta_rad = -delta_rad;
|
|
}
|
|
|
|
IDENTMAT3(rot_mat);
|
|
rot_mat[0][0] = rot_mat[2][2] = cos(delta_rad);
|
|
rot_mat[0][2] = rot_mat[2][0] = sin(delta_rad);
|
|
rot_mat[0][2] *= -1.0f;
|
|
VXM3(p_newDir, p_curDir, rot_mat);
|
|
|
|
changed = TRUE;
|
|
}
|
|
|
|
if (m_keyPressed) {
|
|
float rot_mat[3][3];
|
|
Mx3DPointFloat delta_pos, new_pos, new_dir;
|
|
|
|
if (changed) {
|
|
SET3(new_pos, p_newPos);
|
|
SET3(new_dir, p_newDir);
|
|
}
|
|
else {
|
|
SET3(new_pos, p_curPos);
|
|
SET3(new_dir, p_curDir);
|
|
}
|
|
|
|
if (m_additionalScale != 0.0f) {
|
|
delta_pos[0] = new_dir[0] * m_additionalScale;
|
|
delta_pos[1] = new_dir[1] * m_additionalScale;
|
|
delta_pos[2] = new_dir[2] * m_additionalScale;
|
|
}
|
|
else {
|
|
FILLVEC3(delta_pos, 0.0f);
|
|
}
|
|
|
|
delta_pos[1] += m_additionalHeightOffset;
|
|
VPV3(p_newPos, new_pos, delta_pos);
|
|
|
|
if (m_additionalRotationY != 0.0f) {
|
|
float delta_rad = DTOR(m_additionalRotationY);
|
|
IDENTMAT3(rot_mat);
|
|
rot_mat[0][0] = rot_mat[2][2] = cos(delta_rad);
|
|
rot_mat[0][2] = rot_mat[2][0] = sin(delta_rad);
|
|
rot_mat[0][2] *= -1.0f;
|
|
VXM3(p_newDir, new_dir, rot_mat);
|
|
}
|
|
else {
|
|
SET3(p_newDir, new_dir);
|
|
}
|
|
|
|
m_additionalHeightOffset = m_additionalScale = m_additionalRotationY = 0.0f;
|
|
m_keyPressed = FALSE;
|
|
changed = TRUE;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055500
|
|
// FUNCTION: BETA10 0x1009bff8
|
|
MxResult LegoNavController::UpdateLocation(const char* p_location)
|
|
{
|
|
MxResult result = FAILURE;
|
|
|
|
for (MxS32 i = 0; i < (MxS32) sizeOfArray(g_locations); i++) {
|
|
if (!SDL_strcasecmp(p_location, g_locations[i].m_name)) {
|
|
MxMatrix mat;
|
|
LegoROI* viewROI = VideoManager()->GetViewROI();
|
|
|
|
CalcLocalTransform(g_locations[i].m_position, g_locations[i].m_direction, g_locations[i].m_up, mat);
|
|
|
|
Mx3DPointFloat vec;
|
|
vec.Clear();
|
|
|
|
viewROI->SetWorldVelocity(vec);
|
|
viewROI->WrappedSetLocal2WorldWithWorldDataUpdate(mat);
|
|
VideoManager()->Get3DManager()->Moved(*viewROI);
|
|
|
|
SoundManager()->UpdateListener(
|
|
viewROI->GetWorldPosition(),
|
|
viewROI->GetWorldDirection(),
|
|
viewROI->GetWorldUp(),
|
|
viewROI->GetWorldVelocity()
|
|
);
|
|
|
|
result = SUCCESS;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055620
|
|
// FUNCTION: BETA10 0x1009c145
|
|
MxResult LegoNavController::UpdateLocation(MxU32 p_location)
|
|
{
|
|
MxResult result = FAILURE;
|
|
|
|
if (p_location < sizeOfArray(g_locations)) {
|
|
MxMatrix mat;
|
|
LegoROI* viewROI = VideoManager()->GetViewROI();
|
|
|
|
CalcLocalTransform(
|
|
g_locations[p_location].m_position,
|
|
g_locations[p_location].m_direction,
|
|
g_locations[p_location].m_up,
|
|
mat
|
|
);
|
|
|
|
Mx3DPointFloat vec;
|
|
vec.Clear();
|
|
|
|
viewROI->SetWorldVelocity(vec);
|
|
viewROI->WrappedSetLocal2WorldWithWorldDataUpdate(mat);
|
|
VideoManager()->Get3DManager()->Moved(*viewROI);
|
|
|
|
SoundManager()->UpdateListener(
|
|
viewROI->GetWorldPosition(),
|
|
viewROI->GetWorldDirection(),
|
|
viewROI->GetWorldUp(),
|
|
viewROI->GetWorldVelocity()
|
|
);
|
|
|
|
result = SUCCESS;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055720
|
|
// FUNCTION: BETA10 0x1009c259
|
|
LegoLocation* LegoNavController::GetLocation(MxU32 p_location)
|
|
{
|
|
if (p_location < sizeOfArray(g_locations)) {
|
|
return &g_locations[p_location];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055740
|
|
MxS32 LegoNavController::GetNumLocations()
|
|
{
|
|
return sizeOfArray(g_locations);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055750
|
|
MxResult LegoNavController::ProcessJoystickInput(MxBool& p_rotatedY)
|
|
{
|
|
LegoOmni* instance = LegoOmni::GetInstance();
|
|
|
|
if (instance->GetInputManager()) {
|
|
MxS32 joystickX;
|
|
MxS32 joystickY;
|
|
MxS32 povPosition;
|
|
|
|
if (instance->GetInputManager()
|
|
->GetJoystickState((MxU32*) &joystickX, (MxU32*) &joystickY, (MxU32*) &povPosition) != FAILURE) {
|
|
MxU32 yVal = (joystickY * m_vMax) / 100;
|
|
MxU32 xVal = (joystickX * m_hMax) / 100;
|
|
|
|
if (joystickX <= 45 || joystickX >= 55 || joystickY <= 45 || joystickY >= 55) {
|
|
m_targetLinearVel = CalculateNewTargetVel(m_vMax - yVal, m_vMax / 2, m_maxLinearVel);
|
|
m_linearAccel = CalculateNewAccel(m_vMax - yVal, m_vMax / 2, m_maxLinearAccel, (int) m_minLinearAccel);
|
|
m_targetRotationalVel = CalculateNewTargetVel(xVal, m_hMax / 2, m_maxRotationalVel);
|
|
m_rotationalAccel =
|
|
CalculateNewAccel(xVal, m_hMax / 2, m_maxRotationalAccel, (int) m_minRotationalAccel);
|
|
}
|
|
else {
|
|
m_targetRotationalVel = 0.0;
|
|
m_targetLinearVel = 0.0;
|
|
m_linearAccel = m_maxLinearDeccel;
|
|
m_rotationalAccel = m_maxRotationalDeccel;
|
|
}
|
|
|
|
if (povPosition >= 0) {
|
|
LegoWorld* world = CurrentWorld();
|
|
|
|
if (world && world->GetCameraController()) {
|
|
world->GetCameraController()->RotateY(DTOR(povPosition));
|
|
p_rotatedY = TRUE;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x100558b0
|
|
MxResult LegoNavController::ProcessKeyboardInput()
|
|
{
|
|
MxBool skipRotationVelAndAccelCalc = FALSE;
|
|
MxBool skipLinearVelAndAccelCalc = FALSE;
|
|
LegoInputManager* inputManager = LegoOmni::GetInstance()->GetInputManager();
|
|
MxU32 keyFlags;
|
|
|
|
if (inputManager == NULL || inputManager->GetNavigationKeyStates(keyFlags) == FAILURE) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (keyFlags == 0) {
|
|
if (m_isAccelerating) {
|
|
m_targetRotationalVel = 0.0;
|
|
m_targetLinearVel = 0.0;
|
|
m_rotationalAccel = m_maxRotationalDeccel;
|
|
m_linearAccel = m_maxLinearDeccel;
|
|
m_isAccelerating = FALSE;
|
|
}
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
m_isAccelerating = TRUE;
|
|
|
|
MxS32 hMax;
|
|
switch (keyFlags & LegoInputManager::c_leftOrRight) {
|
|
case LegoInputManager::c_left:
|
|
hMax = 0;
|
|
break;
|
|
case LegoInputManager::c_right:
|
|
hMax = m_hMax;
|
|
break;
|
|
default:
|
|
m_targetRotationalVel = 0.0;
|
|
m_rotationalAccel = m_maxRotationalDeccel;
|
|
skipRotationVelAndAccelCalc = TRUE;
|
|
break;
|
|
}
|
|
|
|
MxS32 vMax;
|
|
switch (keyFlags & LegoInputManager::c_upOrDown) {
|
|
case LegoInputManager::c_up:
|
|
vMax = 0;
|
|
break;
|
|
case LegoInputManager::c_down:
|
|
vMax = m_vMax;
|
|
break;
|
|
default:
|
|
m_targetLinearVel = 0.0;
|
|
m_linearAccel = m_maxLinearDeccel;
|
|
skipLinearVelAndAccelCalc = TRUE;
|
|
break;
|
|
}
|
|
|
|
MxFloat maxAccelDivisor = keyFlags & LegoInputManager::c_ctrl ? 1.0f : 4.0f;
|
|
MxFloat minAccelDivisor = keyFlags & LegoInputManager::c_ctrl ? 1.0f : 2.0f;
|
|
|
|
if (!skipRotationVelAndAccelCalc) {
|
|
m_targetRotationalVel = CalculateNewTargetVel(hMax, m_hMax / 2, m_maxRotationalVel);
|
|
m_rotationalAccel = CalculateNewAccel(
|
|
hMax,
|
|
m_hMax / 2,
|
|
m_maxRotationalAccel / maxAccelDivisor,
|
|
(int) (m_minRotationalAccel / minAccelDivisor)
|
|
);
|
|
}
|
|
|
|
if (!skipLinearVelAndAccelCalc) {
|
|
m_targetLinearVel = CalculateNewTargetVel(m_vMax - vMax, m_vMax / 2, m_maxLinearVel);
|
|
m_linearAccel = CalculateNewAccel(
|
|
m_vMax - vMax,
|
|
m_vMax / 2,
|
|
m_maxLinearAccel / maxAccelDivisor,
|
|
(int) (m_minLinearAccel / minAccelDivisor)
|
|
);
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10055a60
|
|
// FUNCTION: BETA10 0x1009c712
|
|
MxLong LegoNavController::Notify(MxParam& p_param)
|
|
{
|
|
if (((MxNotificationParam&) p_param).GetNotification() == c_notificationKeyPress) {
|
|
m_keyPressed = TRUE;
|
|
SDL_Keycode originKey = ((LegoEventNotificationParam&) p_param).GetKey();
|
|
SDL_Keycode key = originKey;
|
|
|
|
// This is necessary so any players using the WASD movement option can
|
|
// also use Debug Mode, which normally makes use of the WASD keys.
|
|
//
|
|
// For those players, we just swap the two and remap
|
|
// those conflicting debug keys to the arrow keys.
|
|
if (LegoOmni::GetInstance()->GetInputManager()->GetWasd()) {
|
|
switch (originKey) {
|
|
case SDLK_W:
|
|
key = SDLK_UP;
|
|
break;
|
|
case SDLK_A:
|
|
key = SDLK_LEFT;
|
|
break;
|
|
case SDLK_S:
|
|
key = SDLK_DOWN;
|
|
break;
|
|
case SDLK_D:
|
|
key = SDLK_RIGHT;
|
|
break;
|
|
case SDLK_UP:
|
|
key = SDLK_W;
|
|
break;
|
|
case SDLK_LEFT:
|
|
key = SDLK_A;
|
|
break;
|
|
case SDLK_DOWN:
|
|
key = SDLK_S;
|
|
break;
|
|
case SDLK_RIGHT:
|
|
key = SDLK_D;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (key) {
|
|
case SDLK_PAUSE: // Pause game
|
|
if (Lego()->IsPaused()) {
|
|
Lego()->Resume();
|
|
}
|
|
else {
|
|
Lego()->Pause();
|
|
}
|
|
break;
|
|
case SDLK_ESCAPE: { // Return to infocenter
|
|
LegoWorld* currentWorld = CurrentWorld();
|
|
if (currentWorld != NULL) {
|
|
InfocenterState* state = (InfocenterState*) GameState()->GetState("InfocenterState");
|
|
assert(state);
|
|
|
|
if (state != NULL && state->m_state != InfocenterState::e_exitQueried && currentWorld->Escape()) {
|
|
BackgroundAudioManager()->Stop();
|
|
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
|
|
state->m_state = InfocenterState::e_exitQueried;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SDLK_SPACE: // Interrupt/end animations or free navigation
|
|
AnimationManager()->FUN_10061010(TRUE);
|
|
break;
|
|
case SDLK_Z: { // Make nearby plants "dance"
|
|
LegoOmni* omni = Lego();
|
|
|
|
if (omni->GetCurrentWorld() != NULL && omni->GetCurrentWorld()->GetWorldId() == LegoOmni::e_act1) {
|
|
LegoVideoManager* videoMgr = LegoOmni::GetInstance()->GetVideoManager();
|
|
ViewROI* roi = videoMgr->GetViewROI();
|
|
ViewManager* view = videoMgr->Get3DManager()->GetLego3DView()->GetViewManager();
|
|
LegoPlantManager* plantMgr = LegoOmni::GetInstance()->GetPlantManager();
|
|
Mx3DPointFloat viewPosition(roi->GetWorldPosition());
|
|
MxS32 numPlants = plantMgr->GetNumPlants();
|
|
|
|
for (MxS32 i = 0; i < numPlants; i++) {
|
|
LegoEntity* entity = plantMgr->CreatePlant(i, NULL, LegoOmni::e_act1);
|
|
|
|
if (entity != NULL && !entity->IsInteraction(LegoEntity::c_disabled)) {
|
|
LegoROI* roi = entity->GetROI();
|
|
|
|
if (roi != NULL && roi->GetVisibility()) {
|
|
const BoundingBox& box = roi->GetWorldBoundingBox();
|
|
|
|
if (view->IsBoundingBoxInFrustum(box)) {
|
|
Mx3DPointFloat roiPosition(roi->GetWorldPosition());
|
|
roiPosition -= viewPosition;
|
|
|
|
if (roiPosition.LenSquared() < 2000.0 || roi->GetLodLevel() > 0) {
|
|
entity->ClickAnimation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SDLK_KP_PLUS:
|
|
case SDLK_KP_MINUS: { // Cycles through characters and puts them in front of you
|
|
if (g_nextCharacter == -1) {
|
|
g_nextCharacter = 0;
|
|
}
|
|
else {
|
|
CharacterManager()->ReleaseActor(CharacterManager()->GetActorName(g_nextCharacter));
|
|
|
|
if (key == SDLK_KP_PLUS) {
|
|
g_nextCharacter++;
|
|
if (g_nextCharacter >= CharacterManager()->GetNumActors()) {
|
|
g_nextCharacter = 0;
|
|
}
|
|
}
|
|
else {
|
|
g_nextCharacter--;
|
|
if (g_nextCharacter < 0) {
|
|
g_nextCharacter = CharacterManager()->GetNumActors() - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
LegoROI* roi = CharacterManager()->GetActorROI(CharacterManager()->GetActorName(g_nextCharacter), TRUE);
|
|
if (roi != NULL) {
|
|
MxMatrix mat;
|
|
ViewROI* viewRoi = LegoOmni::GetInstance()->GetVideoManager()->GetViewROI();
|
|
const float* position = viewRoi->GetWorldPosition();
|
|
const float* direction = viewRoi->GetWorldDirection();
|
|
const float* up = viewRoi->GetWorldUp();
|
|
CalcLocalTransform(position, direction, up, mat);
|
|
mat.TranslateBy(direction[0] * 2.0f, direction[1] - 1.0, direction[2] * 2.0f);
|
|
roi->UpdateTransformationRelativeToParent(mat);
|
|
}
|
|
break;
|
|
}
|
|
case SDLK_F12: { // Saves the game
|
|
InfocenterState* state = (InfocenterState*) GameState()->GetState("InfocenterState");
|
|
if (state && state->HasRegistered()) {
|
|
GameState()->Save(0);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// Check if the the key is part of the debug password
|
|
if (!*g_currentInput) {
|
|
// password "protected" debug shortcuts
|
|
switch (key) {
|
|
case SDLK_TAB:
|
|
VideoManager()->ToggleFPS(g_fpsEnabled);
|
|
if (g_fpsEnabled) {
|
|
g_fpsEnabled = FALSE;
|
|
}
|
|
else {
|
|
g_fpsEnabled = TRUE;
|
|
}
|
|
default:
|
|
m_keyPressed = FALSE;
|
|
break;
|
|
case SDLK_0:
|
|
case SDLK_1:
|
|
case SDLK_2:
|
|
case SDLK_3:
|
|
case SDLK_4:
|
|
case SDLK_5:
|
|
case SDLK_6:
|
|
case SDLK_7:
|
|
case SDLK_8:
|
|
case SDLK_9:
|
|
if (g_changeLight && key <= '1') {
|
|
LegoROI* roi = VideoManager()->GetViewROI();
|
|
Tgl::FloatMatrix4 matrix;
|
|
Matrix4 in(matrix);
|
|
roi->GetLocalTransform(in);
|
|
VideoManager()->Get3DManager()->GetLego3DView()->SetLightTransform(key - '0', matrix);
|
|
g_changeLight = FALSE;
|
|
}
|
|
else if (g_locationCalcStep) {
|
|
if (g_locationCalcStep == 1) {
|
|
// Calculate base offset into g_locations
|
|
g_nextLocation = (key - '0') * 10;
|
|
g_locationCalcStep = 2;
|
|
}
|
|
else {
|
|
// Add to base g_locations offset
|
|
g_nextLocation += key - '0';
|
|
g_locationCalcStep = 0;
|
|
|
|
LegoPathActor* userActor = UserActor();
|
|
if (userActor != NULL && (MxU32) g_nextLocation < sizeOfArray(g_locations)) {
|
|
LegoWorld* world = CurrentWorld();
|
|
LegoLocation::Boundary* boundary = &g_locations[g_nextLocation].m_boundaryA;
|
|
if (world != NULL && boundary->m_name != NULL) {
|
|
world->PlaceActor(
|
|
userActor,
|
|
boundary->m_name,
|
|
boundary->m_src,
|
|
boundary->m_srcScale,
|
|
boundary->m_dest,
|
|
boundary->m_destScale
|
|
);
|
|
}
|
|
userActor->SetLocation(
|
|
g_locations[g_nextLocation].m_position,
|
|
g_locations[g_nextLocation].m_direction,
|
|
g_locations[g_nextLocation].m_up,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else if (g_animationCalcStep) {
|
|
if (g_animationCalcStep == 1) {
|
|
// Calculate base offset into possible animation object IDs (up to 999)
|
|
g_nextAnimation = (key - '0') * 100;
|
|
g_animationCalcStep = 2;
|
|
}
|
|
else if (g_animationCalcStep == 2) {
|
|
// Add to animation object ID offset
|
|
g_nextAnimation += (key - '0') * 10;
|
|
g_animationCalcStep = 3;
|
|
}
|
|
else {
|
|
// Add to animation object ID offset
|
|
g_nextAnimation += key - '0';
|
|
g_animationCalcStep = 0;
|
|
AnimationManager()->FUN_10060dc0(
|
|
g_nextAnimation,
|
|
NULL,
|
|
TRUE,
|
|
g_unk0x100f66bc,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
g_unk0x100f66bc = LegoAnimationManager::e_unk2;
|
|
}
|
|
}
|
|
|
|
if (g_switchAct && key >= SDLK_1 && key <= SDLK_5) {
|
|
switch (GameState()->GetCurrentAct()) {
|
|
case LegoGameState::e_act1:
|
|
GameState()->m_currentArea = LegoGameState::e_isle;
|
|
break;
|
|
case LegoGameState::e_act2:
|
|
GameState()->m_currentArea = LegoGameState::e_act2main;
|
|
break;
|
|
case LegoGameState::e_act3:
|
|
GameState()->m_currentArea = LegoGameState::e_act3script;
|
|
break;
|
|
}
|
|
|
|
switch (key) {
|
|
case SDLK_1:
|
|
GameState()->SetCurrentAct(LegoGameState::e_act1);
|
|
GameState()->SwitchArea(LegoGameState::e_isle);
|
|
break;
|
|
case SDLK_2:
|
|
GameState()->SwitchArea(LegoGameState::e_act2main);
|
|
break;
|
|
case SDLK_3:
|
|
GameState()->SwitchArea(LegoGameState::e_act3script);
|
|
break;
|
|
case SDLK_4: {
|
|
Act3State* act3State = (Act3State*) GameState()->GetState("Act3State");
|
|
if (act3State == NULL) {
|
|
act3State = new Act3State();
|
|
assert(act3State);
|
|
GameState()->RegisterState(act3State);
|
|
}
|
|
|
|
GameState()->SetCurrentAct(LegoGameState::e_act3);
|
|
act3State->m_state = Act3State::e_goodEnding;
|
|
GameState()->m_currentArea = LegoGameState::e_act3script;
|
|
GameState()->SwitchArea(LegoGameState::e_infomain);
|
|
break;
|
|
}
|
|
case SDLK_5: {
|
|
Act3State* act3State = (Act3State*) GameState()->GetState("Act3State");
|
|
if (act3State == NULL) {
|
|
act3State = new Act3State();
|
|
assert(act3State);
|
|
GameState()->RegisterState(act3State);
|
|
}
|
|
|
|
GameState()->SetCurrentAct(LegoGameState::e_act3);
|
|
act3State->m_state = Act3State::e_badEnding;
|
|
GameState()->m_currentArea = LegoGameState::e_act3script;
|
|
GameState()->SwitchArea(LegoGameState::e_infomain);
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_switchAct = FALSE;
|
|
}
|
|
else {
|
|
MxDSAction action;
|
|
action.SetObjectId(key - '0');
|
|
action.SetAtomId(MxAtomId("q:\\lego\\media\\model\\common\\common", e_lowerCase2));
|
|
LegoOmni::GetInstance()->Start(&action);
|
|
}
|
|
break;
|
|
case SDLK_A:
|
|
if (g_animationCalcStep == 1) {
|
|
Lego()->m_initialized = TRUE;
|
|
AnimationManager()->FUN_10060570(TRUE);
|
|
g_animationCalcStep = 0;
|
|
}
|
|
else {
|
|
LegoWorld* world = CurrentWorld();
|
|
if (world != NULL) {
|
|
MxDSAction action;
|
|
action.SetObjectId(1);
|
|
action.SetAtomId(world->GetAtomId());
|
|
LegoOmni::GetInstance()->Start(&action);
|
|
}
|
|
}
|
|
break;
|
|
case SDLK_C:
|
|
g_locationCalcStep = 1;
|
|
break;
|
|
case SDLK_D:
|
|
m_additionalHeightOffset = -1.0;
|
|
break;
|
|
case SDLK_F:
|
|
RealtimeView::SetUserMaxLOD(0.0);
|
|
break;
|
|
case SDLK_G:
|
|
g_switchAct = TRUE;
|
|
break;
|
|
case SDLK_H:
|
|
RealtimeView::SetUserMaxLOD(5.0);
|
|
break;
|
|
case SDLK_I: {
|
|
LegoROI* roi = VideoManager()->GetViewROI();
|
|
MxMatrix mat;
|
|
mat.SetIdentity();
|
|
mat.RotateX(0.2618f);
|
|
roi->WrappedUpdateWorldDataWithTransform(mat);
|
|
break;
|
|
}
|
|
case SDLK_J: {
|
|
LegoROI* roi = VideoManager()->GetViewROI();
|
|
MxMatrix mat;
|
|
mat.SetIdentity();
|
|
mat.RotateZ(0.2618f);
|
|
roi->WrappedUpdateWorldDataWithTransform(mat);
|
|
break;
|
|
}
|
|
case SDLK_K: {
|
|
MxMatrix mat;
|
|
LegoROI* roi = LegoOmni::GetInstance()->GetVideoManager()->GetViewROI();
|
|
mat.SetIdentity();
|
|
mat.RotateZ(-0.2618f);
|
|
roi->WrappedUpdateWorldDataWithTransform(mat);
|
|
break;
|
|
}
|
|
case SDLK_L:
|
|
g_changeLight = TRUE;
|
|
break;
|
|
case SDLK_M: {
|
|
LegoROI* roi = LegoOmni::GetInstance()->GetVideoManager()->GetViewROI();
|
|
MxMatrix mat;
|
|
mat.SetIdentity();
|
|
mat.RotateX(-0.2618f);
|
|
roi->WrappedUpdateWorldDataWithTransform(mat);
|
|
break;
|
|
}
|
|
case SDLK_N:
|
|
if (VideoManager()) {
|
|
VideoManager()->SetRender3D(!VideoManager()->GetRender3D());
|
|
}
|
|
break;
|
|
case SDLK_P:
|
|
if (!g_resetPlants) {
|
|
PlantManager()->LoadWorldInfo(LegoOmni::e_act1);
|
|
g_resetPlants = TRUE;
|
|
}
|
|
else {
|
|
PlantManager()->Reset(LegoOmni::e_act1);
|
|
g_resetPlants = FALSE;
|
|
}
|
|
break;
|
|
case SDLK_S:
|
|
g_enableMusic = g_enableMusic == FALSE;
|
|
BackgroundAudioManager()->Enable(g_enableMusic);
|
|
break;
|
|
case SDLK_U:
|
|
m_additionalHeightOffset = 1.0;
|
|
break;
|
|
case SDLK_V:
|
|
if (g_nextAnimation > 0 && g_animationCalcStep == 0) {
|
|
AnimationManager()->FUN_10061010(FALSE);
|
|
}
|
|
|
|
if (g_animationCalcStep != 0) {
|
|
g_unk0x100f66bc = LegoAnimationManager::e_unk2;
|
|
}
|
|
|
|
g_nextAnimation = 0;
|
|
g_animationCalcStep = 1;
|
|
break;
|
|
case SDLK_W: {
|
|
MxMatrix mat;
|
|
LegoROI* roi = LegoOmni::GetInstance()->GetVideoManager()->GetViewROI();
|
|
const float* position = roi->GetWorldPosition();
|
|
const float* direction = roi->GetWorldDirection();
|
|
const float* up = roi->GetWorldUp();
|
|
|
|
MxTrace(
|
|
"pos: %f, %f, %f\ndir: %f, %f, %f\nup: %f, %f, %f\n",
|
|
EXPAND3(position),
|
|
EXPAND3(direction),
|
|
EXPAND3(up)
|
|
);
|
|
break;
|
|
}
|
|
case SDLK_X:
|
|
RealtimeView::SetUserMaxLOD(3.6);
|
|
break;
|
|
case SDLK_KP_MULTIPLY: {
|
|
MxU8 newActor = GameState()->GetActorId() + 1;
|
|
|
|
if (newActor > LegoActor::c_laura) {
|
|
newActor = LegoActor::c_pepper;
|
|
}
|
|
|
|
GameState()->SetActorId(newActor);
|
|
break;
|
|
}
|
|
case SDLK_KP_DIVIDE:
|
|
GameState()->SetActorId(LegoActor::c_brickster);
|
|
break;
|
|
case SDLK_F11:
|
|
if (GameState()->m_isDirty) {
|
|
GameState()->m_isDirty = FALSE;
|
|
}
|
|
else {
|
|
GameState()->m_isDirty = TRUE;
|
|
}
|
|
break;
|
|
case SDLK_MINUS:
|
|
g_unk0x100f66bc = LegoAnimationManager::e_unk1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (*g_currentInput == ((LegoEventNotificationParam&) p_param).GetKey()) {
|
|
g_currentInput++;
|
|
}
|
|
else {
|
|
g_currentInput = g_debugPassword;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|