Move clone vehicle code to game command

This commit is contained in:
duncanspumpkin 2021-02-17 07:53:25 +00:00 committed by Aaron van Geffen
parent e9aa64527a
commit af3588ef6b
9 changed files with 206 additions and 150 deletions

View File

@ -2205,3 +2205,4 @@ strings:
2150: Improved
2151: "Modify vehicle"
2152: "Clone vehicle"
2153: "Can't clone vehicle..."

View File

@ -27,7 +27,7 @@ namespace OpenLoco::GameCommands
static loco_global<uintptr_t[80], 0x004F9548> _4F9548;
// 0x004F9688
bool _gameCommandCanBeUsedWhenPaused[80] = {
bool _gameCommandCanBeUsedWhenPaused[81] = {
true,
true,
true,
@ -108,6 +108,7 @@ namespace OpenLoco::GameCommands
false,
false,
false,
true,
};
static loco_global<tile_element*, 0x009C68D0> _9C68D0;
@ -119,87 +120,88 @@ namespace OpenLoco::GameCommands
static loco_global<string_id[8], 0x112C826> _commonFormatArgs;
using GameCommandFunc = void (*)(registers& regs);
static GameCommandFunc _gameCommandFunctions[80] = {
nullptr, // vehicle_rearrange = 0, 0x004AF1DF
nullptr, // vehicle_place = 1, 0x004B01B6
nullptr, // vehicle_pickup = 2, 0x004B0826
nullptr, // vehicle_reverse = 3, 0x004ADAA8
nullptr, // 4 0x004B0B50
Vehicles::create, // vehicle_create = 5,
nullptr, // vehicle_sell = 6, 0x004AED34
nullptr, // 7 0x0049BB98
nullptr, // 8 0x0049C7F2
nullptr, // build_vehicle = 9, 0x0046DE88
nullptr, // vehicle_rename = 10, 0x004B6572
nullptr, // change_station_name = 11, 0x00490756
nullptr, // vehicle_local_express = 12, 0x004B694B
nullptr, // 13 0x00488BDB
nullptr, // 14 0x004891E4
nullptr, // 15 0x0048BB20
nullptr, // 16 0x0048C402
nullptr, // 17 0x004A6479
nullptr, // 18 0x004A668A
nullptr, // change_company_colour_scheme = 19, 0x0043483D
nullptr, // pause_game = 20, 0x00431E32
nullptr, // load_save_quit_game = 21, 0x0043BFCB
nullptr, // 22 0x004BB392
nullptr, // 23 0x004BB138
nullptr, // change_land_material = 24, 0x00468EDD
nullptr, // raise_land = 25, 0x00463702
nullptr, // lower_land = 26, 0x004638C6
nullptr, // lower_raise_land_mountain = 27, 0x00462DCE
nullptr, // raise_water = 28, 0x004C4F19
nullptr, // lower_water = 29, 0x004C5126
nullptr, // 30 0x00434914
nullptr, // 31 0x00434A58
nullptr, // 32 0x004C436C
nullptr, // 33 0x004C466C
nullptr, // 34 0x004C4717
nullptr, // vehicle_order_insert = 35, 0x0047036E
nullptr, // vehicle_order_delete = 36, 0x0047057A
nullptr, // vehicle_order_skip = 37, 0x0047071A
nullptr, // 38 0x00475FBC
nullptr, // 39 0x004775A5
nullptr, // 40 0x0047A21E
nullptr, // 41 0x0047A42F
nullptr, // 42 0x0048C708
nullptr, // 43 0x0048D2AC
nullptr, // 44 0x0042D133
nullptr, // 45 0x0042D74E
nullptr, // change_company_name = 46, 0x0049B11E
nullptr, // 47 0x0045436B
nullptr, // 48 0x00455943
nullptr, // 49 0x00496C22
nullptr, // 50 0x0049711F
nullptr, // 51 0x004A6FDC
nullptr, // 52 0x004A734F
nullptr, // 53 0x0047AF0B
nullptr, // remove_industry = 54, 0x0042ECFC
nullptr, // build_company_headquarters = 55, 0x0042EEAF
nullptr, // 56 0x00492C41
nullptr, // 57 0x00493559
nullptr, // 58 0x004267BE
nullptr, // vehicle_abort_pickup_air = 59, 0x00426B29
nullptr, // 60 0x00493AA7
nullptr, // 61 0x00494570
nullptr, // 62 0x0042773C
nullptr, // vehicle_abort_pickup_water = 63, 0x004279CC
nullptr, // 63 0x0042F6DB
nullptr, // 64 0x00435506
nullptr, // change_company_face = 66, 0x00469CCB
nullptr, // load_multiplayer_map = 67, 0x00444DA0
nullptr, // 68 0x0046F8A5
nullptr, // 69 0x004454BE
nullptr, // 70 0x004456C8
nullptr, // send_chat_message = 71, 0x0046F976
nullptr, // multiplayer_save = 72, 0x004A0ACD
nullptr, // update_owner_status = 73, 0x004383CA
nullptr, // vehicle_speed_control = 74, 0x004BAB63
nullptr, // vehicle_order_up = 75, 0x00470CD2
nullptr, // vehicle_order_down = 76, 0x00470E06
nullptr, // vehicle_apply_shunt_cheat = 77, 0x004BAC53
nullptr, // apply_free_cash_cheat = 78, 0x00438A08
nullptr, // rename_industry = 79, 0x00455029
static GameCommandFunc _gameCommandFunctions[81] = {
nullptr, // vehicle_rearrange = 0, 0x004AF1DF
nullptr, // vehicle_place = 1, 0x004B01B6
nullptr, // vehicle_pickup = 2, 0x004B0826
nullptr, // vehicle_reverse = 3, 0x004ADAA8
nullptr, // 4 0x004B0B50
Vehicles::create, // vehicle_create = 5,
nullptr, // vehicle_sell = 6, 0x004AED34
nullptr, // 7 0x0049BB98
nullptr, // 8 0x0049C7F2
nullptr, // build_vehicle = 9, 0x0046DE88
nullptr, // vehicle_rename = 10, 0x004B6572
nullptr, // change_station_name = 11, 0x00490756
nullptr, // vehicle_local_express = 12, 0x004B694B
nullptr, // 13 0x00488BDB
nullptr, // 14 0x004891E4
nullptr, // 15 0x0048BB20
nullptr, // 16 0x0048C402
nullptr, // 17 0x004A6479
nullptr, // 18 0x004A668A
nullptr, // change_company_colour_scheme = 19, 0x0043483D
nullptr, // pause_game = 20, 0x00431E32
nullptr, // load_save_quit_game = 21, 0x0043BFCB
nullptr, // 22 0x004BB392
nullptr, // 23 0x004BB138
nullptr, // change_land_material = 24, 0x00468EDD
nullptr, // raise_land = 25, 0x00463702
nullptr, // lower_land = 26, 0x004638C6
nullptr, // lower_raise_land_mountain = 27, 0x00462DCE
nullptr, // raise_water = 28, 0x004C4F19
nullptr, // lower_water = 29, 0x004C5126
nullptr, // 30 0x00434914
nullptr, // 31 0x00434A58
nullptr, // 32 0x004C436C
nullptr, // 33 0x004C466C
nullptr, // 34 0x004C4717
nullptr, // vehicle_order_insert = 35, 0x0047036E
nullptr, // vehicle_order_delete = 36, 0x0047057A
nullptr, // vehicle_order_skip = 37, 0x0047071A
nullptr, // 38 0x00475FBC
nullptr, // 39 0x004775A5
nullptr, // 40 0x0047A21E
nullptr, // 41 0x0047A42F
nullptr, // 42 0x0048C708
nullptr, // 43 0x0048D2AC
nullptr, // 44 0x0042D133
nullptr, // 45 0x0042D74E
nullptr, // change_company_name = 46, 0x0049B11E
nullptr, // 47 0x0045436B
nullptr, // 48 0x00455943
nullptr, // 49 0x00496C22
nullptr, // 50 0x0049711F
nullptr, // 51 0x004A6FDC
nullptr, // 52 0x004A734F
nullptr, // 53 0x0047AF0B
nullptr, // remove_industry = 54, 0x0042ECFC
nullptr, // build_company_headquarters = 55, 0x0042EEAF
nullptr, // 56 0x00492C41
nullptr, // 57 0x00493559
nullptr, // 58 0x004267BE
nullptr, // vehicle_abort_pickup_air = 59, 0x00426B29
nullptr, // 60 0x00493AA7
nullptr, // 61 0x00494570
nullptr, // 62 0x0042773C
nullptr, // vehicle_abort_pickup_water = 63, 0x004279CC
nullptr, // 63 0x0042F6DB
nullptr, // 64 0x00435506
nullptr, // change_company_face = 66, 0x00469CCB
nullptr, // load_multiplayer_map = 67, 0x00444DA0
nullptr, // 68 0x0046F8A5
nullptr, // 69 0x004454BE
nullptr, // 70 0x004456C8
nullptr, // send_chat_message = 71, 0x0046F976
nullptr, // multiplayer_save = 72, 0x004A0ACD
nullptr, // update_owner_status = 73, 0x004383CA
nullptr, // vehicle_speed_control = 74, 0x004BAB63
nullptr, // vehicle_order_up = 75, 0x00470CD2
nullptr, // vehicle_order_down = 76, 0x00470E06
nullptr, // vehicle_apply_shunt_cheat = 77, 0x004BAC53
nullptr, // apply_free_cash_cheat = 78, 0x00438A08
nullptr, // rename_industry = 79, 0x00455029
Vehicles::cloneVehicle // vehicle_clone = 80, *NEW*
};
void registerHooks()

View File

@ -65,6 +65,7 @@ namespace OpenLoco::GameCommands
vehicle_apply_shunt_cheat = 77,
apply_free_cash_cheat = 78,
rename_industry = 79,
vehicle_clone = 80,
};
constexpr uint32_t FAILURE = 0x80000000;
@ -113,14 +114,14 @@ namespace OpenLoco::GameCommands
}
// Build vehicle
inline bool do_5(uint16_t vehicle_type, uint16_t vehicle_id = 0xFFFF)
inline uint32_t do_5(uint16_t vehicle_type, uint16_t vehicle_id = 0xFFFF)
{
registers regs;
regs.bl = GameCommandFlag::apply;
regs.di = vehicle_id;
regs.edx = vehicle_type;
return doCommand(5, regs) != FAILURE;
return doCommand(5, regs);
}
// Build vehicle
@ -571,4 +572,12 @@ namespace OpenLoco::GameCommands
regs.edi = edi; // part of name buffer
doCommand(79, regs);
}
inline bool do_80(uint16_t head)
{
registers regs;
regs.bl = GameCommandFlag::apply;
regs.ax = head;
return GameCommands::doCommand(static_cast<int32_t>(GameCommand::vehicle_clone), regs) != FAILURE;
}
}

View File

@ -1481,4 +1481,5 @@ namespace OpenLoco::StringIds
constexpr string_id generator_improved = 2150;
constexpr string_id dropdown_modify_vehicle = 2151;
constexpr string_id dropdown_clone_vehicle = 2152;
constexpr string_id cant_clone_vehicle = 2153;
}

View File

@ -0,0 +1,101 @@
#include "../GameCommands.h"
#include "../Interop/Interop.hpp"
#include "../Things/ThingManager.h"
#include "../Ui/WindowManager.h"
#include "Orders.h"
#include "Vehicle.h"
using namespace OpenLoco::Interop;
namespace OpenLoco::Vehicles
{
static uint32_t cloneVehicle(uint16_t head, uint8_t flags)
{
static loco_global<uint16_t, 0x0113642A> _113642A;
Vehicles::Vehicle existingTrain(head);
Vehicles::VehicleHead* newHead = nullptr;
// Get total cost for a new vehicle
if (!(flags & GameCommands::apply))
{
uint32_t totalCost = 0;
for (auto& car : existingTrain.cars)
{
auto cost = GameCommands::queryDo_5(car.front->object_id, -1);
if (cost == GameCommands::FAILURE)
{
totalCost = GameCommands::FAILURE;
break;
}
else
{
totalCost += cost;
}
}
if (totalCost == GameCommands::FAILURE)
{
return GameCommands::FAILURE;
}
return totalCost;
}
uint32_t totalCost = 0;
for (auto& car : existingTrain.cars)
{
uint32_t cost = 0;
if (newHead == nullptr)
{
cost = GameCommands::do_5(car.front->object_id, -1);
auto* newVeh = ThingManager::get<Vehicles::VehicleBase>(_113642A);
if (newVeh == nullptr)
{
return GameCommands::FAILURE;
}
newHead = ThingManager::get<Vehicles::VehicleHead>(newVeh->getHead());
}
else
{
cost = GameCommands::do_5(car.front->object_id, newHead->head);
}
if (cost == GameCommands::FAILURE)
{
totalCost = GameCommands::FAILURE;
break;
}
else
{
totalCost += cost;
}
}
if (totalCost == GameCommands::FAILURE || newHead == nullptr)
{
return GameCommands::FAILURE;
}
// Copy orders
std::vector<std::shared_ptr<Vehicles::Order>> clonedOrders;
for (auto& existingOrders : Vehicles::OrderTableView(existingTrain.head->orderTableOffset))
{
clonedOrders.push_back(existingOrders.clone());
}
for (auto& order : clonedOrders)
{
auto chosenOffset = newHead->orderTableOffset + newHead->sizeOfOrderTable - 1;
GameCommands::do_35(newHead->id, order->getRaw(), chosenOffset);
}
// Copy express/local
if (existingTrain.veh1->var_48 & (1 << 1))
{
GameCommands::do12(newHead->id, 2);
}
return totalCost;
}
void cloneVehicle(registers& regs)
{
regs.ebx = cloneVehicle(regs.ax, regs.bl);
}
}

View File

@ -13,6 +13,7 @@ namespace OpenLoco::Vehicles
constexpr auto max_vehicle_length = 176; // TODO: Units?
void create(OpenLoco::Interop::registers& regs);
void cloneVehicle(OpenLoco::Interop::registers& regs);
namespace Flags0C // commands?
{

View File

@ -759,7 +759,7 @@ namespace OpenLoco::Ui::BuildVehicle
gGameCommandErrorTitle = StringIds::cant_add_pop_5_string_id_string_id;
}
if (!GameCommands::do_5(item, _buildTargetVehicle))
if (GameCommands::do_5(item, _buildTargetVehicle) == GameCommands::FAILURE)
{
return;
}

View File

@ -185,8 +185,6 @@ namespace OpenLoco::Ui::Vehicle
makeWidget({ 240, 152 }, { 24, 12 }, widget_type::wt_9, 1, ImageIds::red_arrow_down, StringIds::tooltip_route_move_order_down),
widgetEnd(),
};
static void addNewOrder(window* const self, const Vehicles::Order& order);
}
static loco_global<uint8_t, 0x00525FC5> _525FC5;
@ -948,72 +946,14 @@ namespace OpenLoco::Ui::Vehicle
{
static loco_global<uint16_t, 0x0113642A> _113642A;
auto head = Common::getVehicle(self);
Vehicles::Vehicle train(head);
Vehicles::VehicleHead* newHead = nullptr;
// Get total cost for a new vehicle (note this should be removed when turned into a game command)
uint32_t totalCost = 0;
for (auto& car : train.cars)
gGameCommandErrorTitle = StringIds::cant_clone_vehicle;
if (GameCommands::do_80(head->head))
{
auto cost = GameCommands::queryDo_5(car.front->object_id, -1);
if (cost == GameCommands::FAILURE)
auto* newVehicle = ThingManager::get<Vehicles::VehicleBase>(_113642A);
if (newVehicle != nullptr)
{
totalCost = GameCommands::FAILURE;
break;
OpenLoco::Ui::Vehicle::Details::open(newVehicle);
}
else
{
totalCost += cost;
}
}
if (totalCost == GameCommands::FAILURE)
{
return;
}
// Check total cost for a new vehicle, taken from GameCommands::loc_4313C6 (note this should be removed when turned into a game command)
registers regs2;
regs2.ebp = totalCost;
call(0x0046DD06, regs2);
if (static_cast<uint32_t>(regs2.ebp) == GameCommands::FAILURE)
{
return;
}
for (auto& car : train.cars)
{
if (newHead == nullptr)
{
GameCommands::do_5(car.front->object_id, -1);
newHead = ThingManager::get<Vehicles::VehicleHead>(ThingManager::get<Vehicles::VehicleBase>(_113642A)->getHead());
}
else
{
GameCommands::do_5(car.front->object_id, newHead->head);
}
}
auto* newWnd = Vehicle::Details::open(newHead);
// Copy orders
// Route::addNewOrder expects window on Route tab but we can just pretend
// TODO: Change this in the future.
newWnd->var_842 = 0;
std::vector<std::shared_ptr<Vehicles::Order>> clonedOrders;
for (auto& existingOrders : Vehicles::OrderTableView(head->orderTableOffset))
{
clonedOrders.push_back(existingOrders.clone());
}
for (auto& order : clonedOrders)
{
Route::addNewOrder(newWnd, *order);
}
newWnd->var_842 = 0;
// Copy express/local
if (train.veh1->var_48 & (1 << 1))
{
GameCommands::do12(newHead->id, 2);
}
}

View File

@ -110,6 +110,7 @@
<ClCompile Include="Ui\WindowManager.cpp" />
<ClCompile Include="Utility\Numeric.cpp" />
<ClCompile Include="Utility\String.cpp" />
<ClCompile Include="Vehicles\CloneVehicle.cpp" />
<ClCompile Include="Vehicles\CreateVehicle.cpp" />
<ClCompile Include="Vehicles\Orders.cpp" />
<ClCompile Include="Vehicles\Vehicle.cpp" />