327 lines
10 KiB
C++
327 lines
10 KiB
C++
#include "GameCommands.h"
|
|
#include "Company.h"
|
|
#include "CompanyManager.h"
|
|
#include "StationManager.h"
|
|
#include "audio/audio.h"
|
|
#include "map/tile.h"
|
|
#include "objects/objectmgr.h"
|
|
#include "objects/road_object.h"
|
|
#include "objects/track_object.h"
|
|
#include "things/vehicle.h"
|
|
#include "ui/WindowManager.h"
|
|
#include <cassert>
|
|
|
|
using namespace openloco::ui;
|
|
using namespace openloco::map;
|
|
|
|
namespace openloco::game_commands
|
|
{
|
|
static loco_global<company_id_t, 0x009C68EB> _updating_company_id;
|
|
static loco_global<uint8_t, 0x00508F08> game_command_nest_level;
|
|
static loco_global<company_id_t[2], 0x00525E3C> _player_company;
|
|
static loco_global<uint8_t, 0x00508F17> paused_state;
|
|
static loco_global<uint8_t, 0x00508F1A> game_speed;
|
|
static loco_global<uint16_t, 0x0050A004> _50A004;
|
|
|
|
static uint16_t _gameCommandFlags;
|
|
static loco_global<uintptr_t[80], 0x004F9548> _4F9548;
|
|
static loco_global<uint8_t[80], 0x004F9688> _4F9688;
|
|
|
|
static loco_global<tile_element*, 0x009C68D0> _9C68D0;
|
|
|
|
static loco_global<coord_t, 0x009C68E4> _game_command_map_z;
|
|
static loco_global<string_id, 0x009C68E6> gGameCommandErrorText;
|
|
static loco_global<string_id, 0x009C68E8> gGameCommandErrorTitle;
|
|
static loco_global<uint8_t, 0x009C68EE> _errorCompanyId;
|
|
static loco_global<string_id[8], 0x112C826> _commonFormatArgs;
|
|
|
|
void registerHooks()
|
|
{
|
|
registerHook(
|
|
0x00431315,
|
|
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
|
|
registers backup = regs;
|
|
auto ebx = doCommand(regs.esi, backup);
|
|
|
|
regs = backup;
|
|
regs.ebx = ebx;
|
|
return 0;
|
|
});
|
|
|
|
registerHook(
|
|
0x004AE5E4,
|
|
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
|
|
registers backup = regs;
|
|
auto ebx = things::vehicle::create(regs.bl, regs.dx, regs.di);
|
|
|
|
regs = backup;
|
|
regs.ebx = ebx;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
static uint32_t loc_4314EA();
|
|
static uint32_t loc_4313C6(int esi, const registers& regs);
|
|
|
|
// 0x00431315
|
|
uint32_t doCommand(int esi, const registers& regs)
|
|
{
|
|
uint16_t flags = regs.bx;
|
|
|
|
_gameCommandFlags = regs.bx;
|
|
if (game_command_nest_level != 0)
|
|
return loc_4313C6(esi, regs);
|
|
|
|
if ((flags & GameCommandFlag::apply) == 0)
|
|
{
|
|
return loc_4313C6(esi, regs);
|
|
}
|
|
|
|
if ((flags & (GameCommandFlag::flag_4 | GameCommandFlag::flag_6)) != 0
|
|
&& _4F9688[esi] == 1
|
|
&& _updating_company_id == _player_company[0])
|
|
{
|
|
if (getPauseFlags() & 1)
|
|
{
|
|
paused_state = paused_state ^ 1;
|
|
WindowManager::invalidate(WindowType::timeToolbar);
|
|
audio::unpauseSound();
|
|
_50A004 = _50A004 | 1;
|
|
}
|
|
|
|
if (game_speed != 0)
|
|
{
|
|
game_speed = 0;
|
|
WindowManager::invalidate(WindowType::timeToolbar);
|
|
}
|
|
|
|
if (isPaused())
|
|
{
|
|
gGameCommandErrorText = string_ids::empty;
|
|
return 0x80000000;
|
|
}
|
|
}
|
|
|
|
if (_updating_company_id == _player_company[0] && isNetworked())
|
|
{
|
|
assert(false);
|
|
registers fnRegs = regs;
|
|
call(0x0046E34A, fnRegs); // some network stuff. Untested
|
|
}
|
|
|
|
return loc_4313C6(esi, regs);
|
|
}
|
|
|
|
static uint32_t loc_4313C6(int esi, const registers& regs)
|
|
{
|
|
uint16_t flags = regs.bx;
|
|
gGameCommandErrorText = string_ids::null;
|
|
game_command_nest_level++;
|
|
|
|
auto addr = _4F9548[esi];
|
|
|
|
uint16_t flagsBackup = _gameCommandFlags;
|
|
registers fnRegs1 = regs;
|
|
fnRegs1.bl &= ~GameCommandFlag::apply;
|
|
call(addr, fnRegs1);
|
|
int32_t ebx = fnRegs1.ebx;
|
|
_gameCommandFlags = flagsBackup;
|
|
|
|
if (ebx != static_cast<int32_t>(0x80000000))
|
|
{
|
|
if (isEditorMode())
|
|
ebx = 0;
|
|
|
|
if (game_command_nest_level == 1)
|
|
{
|
|
if ((_gameCommandFlags & GameCommandFlag::flag_2) == 0
|
|
&& (_gameCommandFlags & GameCommandFlag::flag_6) == 0
|
|
&& ebx != 0)
|
|
{
|
|
registers regs2;
|
|
regs2.ebp = ebx;
|
|
call(0x0046DD06, regs2);
|
|
ebx = regs2.ebp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ebx == static_cast<int32_t>(0x80000000))
|
|
{
|
|
if (flags & GameCommandFlag::apply)
|
|
{
|
|
return loc_4314EA();
|
|
}
|
|
else
|
|
{
|
|
game_command_nest_level--;
|
|
return ebx;
|
|
}
|
|
}
|
|
|
|
if ((flags & 1) == 0)
|
|
{
|
|
game_command_nest_level--;
|
|
return ebx;
|
|
}
|
|
|
|
uint16_t flagsBackup2 = _gameCommandFlags;
|
|
registers fnRegs2 = regs;
|
|
call(addr, fnRegs2);
|
|
int32_t ebx2 = fnRegs2.ebx;
|
|
_gameCommandFlags = flagsBackup2;
|
|
|
|
if (ebx2 == static_cast<int32_t>(0x80000000))
|
|
{
|
|
return loc_4314EA();
|
|
}
|
|
|
|
if (isEditorMode())
|
|
{
|
|
ebx = 0;
|
|
}
|
|
|
|
if (ebx2 < ebx)
|
|
{
|
|
ebx = ebx2;
|
|
}
|
|
|
|
game_command_nest_level--;
|
|
if (game_command_nest_level != 0)
|
|
return ebx;
|
|
|
|
if ((flagsBackup2 & GameCommandFlag::flag_5) != 0)
|
|
return ebx;
|
|
|
|
{
|
|
// Apply to company money
|
|
registers fnRegs;
|
|
fnRegs.ebx = ebx;
|
|
call(0x0046DE2B, fnRegs);
|
|
}
|
|
|
|
if (ebx != 0 && _updating_company_id == _player_company[0])
|
|
{
|
|
// Add flying cost text
|
|
registers fnRegs;
|
|
fnRegs.ebx = ebx;
|
|
_game_command_map_z = _game_command_map_z + 24;
|
|
call(0x0046DC9F, fnRegs);
|
|
_game_command_map_z = _game_command_map_z - 24;
|
|
}
|
|
|
|
return ebx;
|
|
}
|
|
|
|
static uint32_t loc_4314EA()
|
|
{
|
|
game_command_nest_level--;
|
|
if (game_command_nest_level != 0)
|
|
return 0x80000000;
|
|
|
|
if (_updating_company_id != _player_company[0])
|
|
return 0x80000000;
|
|
|
|
if (_gameCommandFlags & GameCommandFlag::flag_3)
|
|
return 0x80000000;
|
|
|
|
if (gGameCommandErrorText != 0xFFFE)
|
|
{
|
|
windows::showError(gGameCommandErrorTitle, gGameCommandErrorText);
|
|
return 0x80000000;
|
|
}
|
|
|
|
// advanced errors
|
|
if (_9C68D0 != (void*)-1)
|
|
{
|
|
auto tile = (tile_element*)_9C68D0;
|
|
|
|
switch (tile->type())
|
|
{
|
|
case element_type::track: // 4
|
|
{
|
|
auto trackElement = tile->asTrack();
|
|
if (trackElement == nullptr)
|
|
break; // throw exception?
|
|
|
|
track_object* pObject = objectmgr::get<track_object>(trackElement->trackObjectId());
|
|
if (pObject == nullptr)
|
|
break;
|
|
|
|
_commonFormatArgs[0] = pObject->name;
|
|
_commonFormatArgs[1] = companymgr::get(_errorCompanyId)->name;
|
|
windows::error::openWithCompetitor(gGameCommandErrorTitle, string_ids::error_reason_stringid_belongs_to, _errorCompanyId);
|
|
return 0x80000000;
|
|
}
|
|
|
|
case element_type::road: //0x1C
|
|
{
|
|
auto roadElement = tile->asRoad();
|
|
if (roadElement == nullptr)
|
|
break; // throw exception?
|
|
|
|
road_object* pObject = objectmgr::get<road_object>(roadElement->roadObjectId());
|
|
if (pObject == nullptr)
|
|
break;
|
|
|
|
_commonFormatArgs[0] = pObject->name;
|
|
_commonFormatArgs[1] = companymgr::get(_errorCompanyId)->name;
|
|
windows::error::openWithCompetitor(gGameCommandErrorTitle, string_ids::error_reason_stringid_belongs_to, _errorCompanyId);
|
|
return 0x80000000;
|
|
}
|
|
|
|
case element_type::station: // 8
|
|
{
|
|
auto stationElement = tile->asStation();
|
|
if (stationElement == nullptr)
|
|
break; // throw exception?
|
|
|
|
station* pStation = stationmgr::get(stationElement->stationId());
|
|
if (pStation == nullptr)
|
|
break;
|
|
|
|
_commonFormatArgs[0] = pStation->name;
|
|
_commonFormatArgs[1] = pStation->town;
|
|
_commonFormatArgs[2] = companymgr::get(_errorCompanyId)->name;
|
|
windows::error::openWithCompetitor(gGameCommandErrorTitle, string_ids::error_reason_stringid_belongs_to, _errorCompanyId);
|
|
return 0x80000000;
|
|
}
|
|
|
|
case element_type::signal: // 0x0C
|
|
{
|
|
_commonFormatArgs[0] = companymgr::get(_errorCompanyId)->name;
|
|
windows::error::openWithCompetitor(gGameCommandErrorTitle, string_ids::error_reason_signal_belongs_to, _errorCompanyId);
|
|
return 0x80000000;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// fallback
|
|
_commonFormatArgs[0] = companymgr::get(_errorCompanyId)->name;
|
|
windows::error::openWithCompetitor(gGameCommandErrorTitle, string_ids::error_reason_stringid_belongs_to, _errorCompanyId);
|
|
return 0x80000000;
|
|
}
|
|
|
|
// 0x00431E6A
|
|
// al : company
|
|
// esi : tile
|
|
bool sub_431E6A(const company_id_t company, map::tile_element* const tile /*= nullptr*/)
|
|
{
|
|
if (company == company_id::neutral)
|
|
{
|
|
return true;
|
|
}
|
|
if (_updating_company_id == company || _updating_company_id == company_id::neutral)
|
|
{
|
|
return true;
|
|
}
|
|
gGameCommandErrorText = -2;
|
|
_errorCompanyId = company;
|
|
_9C68D0 = tile == nullptr ? reinterpret_cast<map::tile_element*>(-1) : tile;
|
|
return false;
|
|
}
|
|
}
|