Vehicle create (#460)
* Start vehicle_create Further work on CreateVehicle Continue create vehicle length check Further work * Start actual create body function Further work Complete the body creation function * Fix some small mistakes Make minor corrections * Further work Further Further works * Implement create head * Implement the create vehicle 1, 2, tail functions Further implementation * Make compilable and testable Add nullptr protection to keep ci's happy clang-format * Use templates to simplify the creation functions Change assert to keep ci's happy * Implement remaining cleanup functions Fix formatting * Move member functions to vehicle.cpp file * Implement initial review comments * Make suggested changes * Add further constants. Fix crash due to initial value * Make review changes and add game command knowledge
This commit is contained in:
parent
433c35344b
commit
5e2c51539d
|
@ -7,6 +7,7 @@
|
|||
#include "objects/road_object.h"
|
||||
#include "objects/track_object.h"
|
||||
#include "stationmgr.h"
|
||||
#include "things/vehicle.h"
|
||||
#include "ui/WindowManager.h"
|
||||
#include <cassert>
|
||||
|
||||
|
@ -42,6 +43,17 @@ namespace openloco::game_commands
|
|||
registers backup = regs;
|
||||
auto ebx = do_command(regs.esi, backup);
|
||||
|
||||
regs = backup;
|
||||
regs.ebx = ebx;
|
||||
return 0;
|
||||
});
|
||||
|
||||
register_hook(
|
||||
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;
|
||||
|
@ -292,4 +304,23 @@ namespace openloco::game_commands
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "interop/interop.hpp"
|
||||
#include "localisation/FormatArguments.hpp"
|
||||
#include "localisation/string_ids.h"
|
||||
#include "things/thingmgr.h"
|
||||
#include "ui/WindowManager.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
@ -41,6 +43,31 @@ namespace openloco
|
|||
call(0x00430762, regs);
|
||||
}
|
||||
|
||||
// 0x00437ED0
|
||||
void company::recalculateTransportCounts()
|
||||
{
|
||||
// Reset all counts to 0
|
||||
for (auto& count : transportTypeCount)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
auto companyId = id();
|
||||
auto v = thingmgr::first<openloco::vehicle>();
|
||||
while (v != nullptr)
|
||||
{
|
||||
auto next = v->next_vehicle();
|
||||
|
||||
if (v->owner == companyId)
|
||||
{
|
||||
transportTypeCount[static_cast<uint8_t>(v->vehicleType)]++;
|
||||
}
|
||||
v = next;
|
||||
}
|
||||
|
||||
ui::WindowManager::invalidate(ui::WindowType::company, companyId);
|
||||
}
|
||||
|
||||
// Converts performance index to rating
|
||||
// 0x437D60
|
||||
constexpr CorporateRating performanceToRating(int16_t performanceIndex)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
namespace openloco
|
||||
{
|
||||
using company_id_t = uint8_t;
|
||||
|
||||
namespace company_id
|
||||
{
|
||||
|
@ -101,6 +100,7 @@ namespace openloco
|
|||
bool empty() const;
|
||||
void ai_think();
|
||||
bool isVehicleIndexUnlocked(const uint8_t vehicleIndex) const;
|
||||
void recalculateTransportCounts();
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
|
|
@ -19,10 +19,20 @@ namespace openloco::game_commands
|
|||
flag_6 = 1 << 6, // 0x40
|
||||
};
|
||||
|
||||
enum class GameCommand : uint8_t
|
||||
{
|
||||
vehicle_rearange = 0,
|
||||
vehicle_place = 1,
|
||||
vehicle_pickup = 2,
|
||||
vehicle_create = 5,
|
||||
vehicle_sell = 6,
|
||||
};
|
||||
|
||||
constexpr uint32_t FAILURE = 0x80000000;
|
||||
|
||||
void registerHooks();
|
||||
uint32_t do_command(int esi, const registers& registers);
|
||||
bool sub_431E6A(const company_id_t company, map::tile_element* const tile = nullptr);
|
||||
|
||||
// Build vehicle
|
||||
inline bool do_5(uint16_t vehicle_type, uint16_t vehicle_id = 0xFFFF)
|
||||
|
|
|
@ -138,6 +138,7 @@ namespace openloco::string_ids
|
|||
constexpr string_id menu_underground_view = 145;
|
||||
constexpr string_id menu_hide_foreground_tracks_roads = 146;
|
||||
|
||||
constexpr string_id too_many_objects_in_game = 171;
|
||||
constexpr string_id menu_rotate_clockwise = 172;
|
||||
constexpr string_id menu_rotate_anti_clockwise = 173;
|
||||
|
||||
|
@ -228,6 +229,8 @@ namespace openloco::string_ids
|
|||
constexpr string_id tooltip_bridge_stats = 278;
|
||||
constexpr string_id tooltip_select_station_type = 279;
|
||||
|
||||
constexpr string_id incompatible_vehicle = 335;
|
||||
constexpr string_id too_many_vehicles = 336;
|
||||
constexpr string_id buffer_337 = 337;
|
||||
constexpr string_id buffer_338 = 338;
|
||||
|
||||
|
@ -597,6 +600,10 @@ namespace openloco::string_ids
|
|||
constexpr string_id tooltip_sort_by_profit = 1154;
|
||||
constexpr string_id tooltip_sort_by_age = 1155;
|
||||
constexpr string_id tooltip_sort_by_reliability = 1156;
|
||||
constexpr string_id vehicle_must_be_stopped = 1157;
|
||||
constexpr string_id vehicle_has_crashed = 1158;
|
||||
constexpr string_id vehicle_has_broken_down = 1159;
|
||||
constexpr string_id vehicle_is_stuck = 1160;
|
||||
|
||||
constexpr string_id cant_add_pop_5_string_id_string_id = 1184;
|
||||
constexpr string_id cant_build_pop_5_string_id = 1185;
|
||||
|
@ -630,6 +637,7 @@ namespace openloco::string_ids
|
|||
constexpr string_id station_cargo = 1211;
|
||||
constexpr string_id station_cargo_en_route_start = 1212;
|
||||
constexpr string_id station_cargo_en_route_end = 1213;
|
||||
constexpr string_id no_space_for_more_vehicle_orders = 1214;
|
||||
|
||||
constexpr string_id build_trains = 1240;
|
||||
constexpr string_id build_buses = 1241;
|
||||
|
@ -811,6 +819,7 @@ namespace openloco::string_ids
|
|||
constexpr string_id tooltip_station_cargo = 1448;
|
||||
constexpr string_id tooltip_station_cargo_ratings = 1449;
|
||||
|
||||
constexpr string_id vehicle_too_long = 1452;
|
||||
constexpr string_id tooltip_build_or_move_headquarters = 1453;
|
||||
constexpr string_id tooltip_change_owner_name = 1454;
|
||||
constexpr string_id not_yet_constructed = 1455;
|
||||
|
|
|
@ -36,6 +36,13 @@ namespace openloco
|
|||
diesel_exhaust2,
|
||||
ship_wake
|
||||
};
|
||||
|
||||
namespace sprite_ind
|
||||
{
|
||||
constexpr uint8_t null = 0xFF;
|
||||
constexpr uint8_t flag_unk7 = (1 << 7); // Set on electric multiple unit
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct vehicle_object_sound_1
|
||||
{
|
||||
|
@ -95,11 +102,22 @@ namespace openloco
|
|||
struct vehicle_object_unk
|
||||
{
|
||||
uint8_t length; // 0x00
|
||||
uint8_t pad_01[0x04 - 0x01];
|
||||
uint8_t sprite_ind; // 0x04
|
||||
uint8_t pad_01;
|
||||
uint8_t front_bogie_sprite_ind; // 0x02 index of bogie_sprites struct
|
||||
uint8_t back_bogie_sprite_ind; // 0x03 index of bogie_sprites struct
|
||||
uint8_t body_sprite_ind; // 0x04 index of a sprites struct
|
||||
uint8_t var_05;
|
||||
};
|
||||
|
||||
struct vehicle_object_bogie_sprite
|
||||
{
|
||||
uint8_t pad_00[0x02 - 0x00];
|
||||
uint8_t var_02;
|
||||
uint8_t var_03;
|
||||
uint8_t var_04;
|
||||
uint8_t pad_05[0x12 - 0x5];
|
||||
};
|
||||
|
||||
struct vehicle_object_sprite
|
||||
{
|
||||
uint8_t num_dir; // 0x00
|
||||
|
@ -110,7 +128,9 @@ namespace openloco
|
|||
uint8_t var_05;
|
||||
uint8_t bogey_position; // 0x06
|
||||
uint8_t flags; // 0x07
|
||||
uint8_t pad_08[0x0B - 0x08];
|
||||
uint8_t var_08;
|
||||
uint8_t var_09;
|
||||
uint8_t var_0A;
|
||||
uint8_t var_0B;
|
||||
uint8_t var_0C;
|
||||
uint8_t pad_0D;
|
||||
|
@ -120,12 +140,14 @@ namespace openloco
|
|||
|
||||
namespace flags_E0
|
||||
{
|
||||
constexpr uint16_t flag_02 = 1 << 2; // rollable? APT Passenger carriage
|
||||
constexpr uint16_t flag_03 = 1 << 3; // rollable? APT Driving carriage
|
||||
constexpr uint16_t rack_rail = 1 << 6;
|
||||
constexpr uint16_t unk_09 = 1 << 9; //anytrack??
|
||||
constexpr uint16_t unk_11 = 1 << 10; //cancouple??
|
||||
constexpr uint16_t unk_12 = 1 << 6; //dualhead??
|
||||
constexpr uint16_t refittable = 1 << 9;
|
||||
constexpr uint16_t unk_15 = 1 << 10; //noannounce??
|
||||
constexpr uint16_t can_couple = 1 << 11;
|
||||
constexpr uint16_t unk_12 = 1 << 12; //dualhead??
|
||||
constexpr uint16_t refittable = 1 << 14;
|
||||
constexpr uint16_t unk_15 = 1 << 15; //noannounce??
|
||||
}
|
||||
|
||||
struct vehicle_object
|
||||
|
@ -133,21 +155,21 @@ namespace openloco
|
|||
string_id name; // 0x00
|
||||
TransportMode mode; // 0x02
|
||||
VehicleType type; // 0x03
|
||||
uint8_t pad_04;
|
||||
uint8_t var_04;
|
||||
uint8_t track_type; // 0x05
|
||||
uint8_t num_mods; // 0x06
|
||||
uint8_t cost_ind; // 0x07
|
||||
int16_t cost_fact; // 0x08
|
||||
uint8_t cost_index; // 0x07
|
||||
int16_t cost_factor; // 0x08
|
||||
uint8_t reliability; // 0x0A
|
||||
uint8_t run_cost_ind; // 0x0B
|
||||
int16_t run_cost_fact; // 0x0C
|
||||
uint8_t run_cost_index; // 0x0B
|
||||
int16_t run_cost_factor; // 0x0C
|
||||
uint8_t colour_type; // 0x0E
|
||||
uint8_t num_compat; // 0x0F
|
||||
uint8_t pad_10[0x20 - 0x10];
|
||||
uint16_t compatible_vehicles[8]; // 0x10 array of compatible vehicle_types
|
||||
uint8_t required_track_extras[4]; // 0x20
|
||||
vehicle_object_unk var_24[4];
|
||||
vehicle_object_sprite sprites[4]; // 0x3C
|
||||
uint8_t pad_B4[0xD8 - 0xB4];
|
||||
vehicle_object_bogie_sprite bogie_sprites[2]; // 0xB4
|
||||
uint16_t power; // 0xD8
|
||||
uint16_t speed; // 0xDA
|
||||
uint16_t rack_speed; // 0xDC
|
||||
|
@ -176,4 +198,5 @@ namespace openloco
|
|||
uint8_t var_15B[0x15E - 0x15B]; // sound array size num_sounds/tbc??
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(vehicle_object) == 0x15E);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
<ClCompile Include="scenariomgr.cpp" />
|
||||
<ClCompile Include="station.cpp" />
|
||||
<ClCompile Include="stationmgr.cpp" />
|
||||
<ClCompile Include="things\CreateVehicle.cpp" />
|
||||
<ClCompile Include="things\misc.cpp" />
|
||||
<ClCompile Include="things\thing.cpp" />
|
||||
<ClCompile Include="things\thingmgr.cpp" />
|
||||
|
|
|
@ -0,0 +1,952 @@
|
|||
#include "../company.h"
|
||||
#include "../companymgr.h"
|
||||
#include "../core/Optional.hpp"
|
||||
#include "../date.h"
|
||||
#include "../game_commands.h"
|
||||
#include "../management/Expenditures.h"
|
||||
#include "../map/tile.h"
|
||||
#include "../objects/objectmgr.h"
|
||||
#include "../objects/road_object.h"
|
||||
#include "../objects/track_object.h"
|
||||
#include "../objects/vehicle_object.h"
|
||||
#include "../ui/WindowManager.h"
|
||||
#include "thingmgr.h"
|
||||
#include "vehicle.h"
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
using namespace openloco;
|
||||
using namespace openloco::interop;
|
||||
using namespace openloco::objectmgr;
|
||||
using namespace openloco::game_commands;
|
||||
|
||||
namespace openloco::things::vehicle
|
||||
{
|
||||
constexpr uint32_t max_orders = 256000;
|
||||
constexpr auto max_ai_vehicles = 500;
|
||||
constexpr auto max_num_car_components_in_car = 4; // TODO: Move to vehicle_object
|
||||
constexpr auto num_vehicle_components_in_car_component = 3; // Bogie bogie body
|
||||
constexpr auto num_vehicle_components_in_base = 4; // head unk_1 unk_2 tail
|
||||
constexpr auto max_num_vehicle_components_in_car = num_vehicle_components_in_car_component * max_num_car_components_in_car;
|
||||
constexpr thing_id_t allocated_but_free_routing_station = -2; // Indicates that this array is allocated to a vehicle but no station has been set.
|
||||
|
||||
static loco_global<company_id_t, 0x009C68EB> _updating_company_id;
|
||||
static loco_global<uint16_t, 0x009C68E0> gameCommandMapX;
|
||||
static loco_global<uint16_t, 0x009C68E2> gameCommandMapY;
|
||||
static loco_global<uint16_t, 0x009C68E4> gameCommandMapZ;
|
||||
static loco_global<string_id, 0x009C68E6> gGameCommandErrorText;
|
||||
static loco_global<uint8_t, 0x009C68EA> gGameCommandExpenditureType; // premultiplied by 4
|
||||
static loco_global<uint8_t, 0x009C68EE> _errorCompanyId;
|
||||
static loco_global<map::tile_element*, 0x009C68D0> _9C68D0;
|
||||
static loco_global<ColourScheme, 0x01136140> _1136140; // primary colour
|
||||
static loco_global<int32_t, 0x011360FC> _11360FC;
|
||||
static loco_global<openloco::vehicle_head*, 0x01136240> _backupVeh0;
|
||||
static loco_global<int16_t, 0x01136248> _backup2E;
|
||||
static loco_global<int16_t, 0x0113624C> _backup2C;
|
||||
static loco_global<int16_t, 0x01136250> _backupX;
|
||||
static loco_global<int16_t, 0x01136254> _backupY;
|
||||
static loco_global<uint8_t, 0x01136258> _backupZ;
|
||||
static loco_global<uint16_t, 0x0113642A> _113642A; // used by build window and others
|
||||
static loco_global<uint32_t[32], 0x00525E5E> currencyMultiplicationFactor;
|
||||
static loco_global<uint8_t, 0x00525FC5> _525FC5;
|
||||
static loco_global<uint32_t, 0x00525FB8> _525FB8; // total used length of _987C5C
|
||||
static loco_global<thing_id_t[64][1000], 0x0096885C> _96885C; // Likely routing related
|
||||
static loco_global<uint8_t[max_orders], 0x00987C5C> _987C5C; // ?orders? ?routing related?
|
||||
|
||||
// 0x004B1D96
|
||||
static bool aiIsBelowVehicleLimit()
|
||||
{
|
||||
if (is_player_company(_updating_company_id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& companies = companymgr::companies();
|
||||
auto totalAiVehicles = std::accumulate(companies.begin(), companies.end(), 0, [](int32_t& total, const auto& company) {
|
||||
if (company.empty())
|
||||
return total;
|
||||
if (is_player_company(company.id()))
|
||||
return total;
|
||||
return total + std::accumulate(std::begin(company.transportTypeCount), std::end(company.transportTypeCount), 0);
|
||||
});
|
||||
|
||||
if (totalAiVehicles > max_ai_vehicles)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::too_many_vehicles;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x004B1E44
|
||||
static bool isEmptyVehicleSlotAvailable()
|
||||
{
|
||||
if (!aiIsBelowVehicleLimit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < 1000; i++)
|
||||
{
|
||||
auto id = _96885C[i][0];
|
||||
if (id == thing_id::null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
gGameCommandErrorText = string_ids::too_many_vehicles;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sub_4B0BDD(openloco::vehicle_head* const head)
|
||||
{
|
||||
switch (head->var_5D)
|
||||
{
|
||||
case 8:
|
||||
gGameCommandErrorText = string_ids::vehicle_has_crashed;
|
||||
return false;
|
||||
case 9:
|
||||
gGameCommandErrorText = string_ids::vehicle_is_stuck;
|
||||
return false;
|
||||
case 7:
|
||||
gGameCommandErrorText = string_ids::vehicle_has_broken_down;
|
||||
return false;
|
||||
default:
|
||||
{
|
||||
auto veh1 = reinterpret_cast<openloco::vehicle*>(head)->next_car();
|
||||
auto veh2 = veh1->next_car()->as_vehicle_2();
|
||||
if (head->vehicleType == VehicleType::plane || head->vehicleType == VehicleType::ship)
|
||||
{
|
||||
if (veh2->var_73 & (1 << 0))
|
||||
{
|
||||
gGameCommandErrorText = string_ids::vehicle_has_broken_down;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (head->tile_x == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (head->var_5D != 6 && head->var_5D != 1)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::vehicle_must_be_stopped;
|
||||
return false;
|
||||
}
|
||||
if (veh2->var_56 == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
gGameCommandErrorText = string_ids::vehicle_must_be_stopped;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (head->tile_x == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (veh2->var_56 == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (veh1->var_3C <= 13961)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
gGameCommandErrorText = string_ids::vehicle_must_be_stopped;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004B08DD
|
||||
static void liftUpVehicle(openloco::vehicle_head* const head)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = reinterpret_cast<uint32_t>(head);
|
||||
call(0x004B08DD, regs);
|
||||
}
|
||||
|
||||
// 0x00470039
|
||||
static openloco::vehicle_base* createVehicleThing()
|
||||
{
|
||||
registers regs{};
|
||||
call(0x00470039, regs);
|
||||
return reinterpret_cast<openloco::vehicle_base*>(regs.esi);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T* createVehicleThing()
|
||||
{
|
||||
auto* const base = createVehicleThing();
|
||||
base->base_type = thing_base_type::vehicle;
|
||||
base->type = T::VehicleThingType;
|
||||
return reinterpret_cast<T*>(base);
|
||||
}
|
||||
|
||||
// 0x004BA873
|
||||
// esi : vehBogie
|
||||
static void sub_4BA873(openloco::vehicle_bogie* const vehBogie)
|
||||
{
|
||||
vehBogie->var_68 = 0xFFFF;
|
||||
if (vehBogie->reliability != 0)
|
||||
{
|
||||
int32_t reliabilityFactor = vehBogie->reliability / 256;
|
||||
reliabilityFactor *= reliabilityFactor;
|
||||
reliabilityFactor /= 16;
|
||||
|
||||
auto& prng = gprng();
|
||||
int32_t randVal = (prng.rand_next(65535) * reliabilityFactor / 2) / 65536;
|
||||
reliabilityFactor -= reliabilityFactor / 4;
|
||||
reliabilityFactor += randVal;
|
||||
vehBogie->var_68 = static_cast<uint16_t>(std::max(4, reliabilityFactor));
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004AE8F1, 0x004AEA9E
|
||||
static openloco::vehicle_bogie* createBogie(const thing_id_t head, const uint16_t vehicleTypeId, const vehicle_object& vehObject, const uint8_t bodyNumber, openloco::vehicle* const lastVeh, const ColourScheme colourScheme)
|
||||
{
|
||||
auto newBogie = createVehicleThing<vehicle_bogie>();
|
||||
newBogie->owner = _updating_company_id;
|
||||
newBogie->head = head;
|
||||
newBogie->body_index = bodyNumber;
|
||||
newBogie->track_type = lastVeh->track_type;
|
||||
newBogie->mode = lastVeh->mode;
|
||||
newBogie->tile_x = -1;
|
||||
newBogie->tile_y = 0;
|
||||
newBogie->tile_base_z = 0;
|
||||
newBogie->var_2E = 0;
|
||||
newBogie->var_2C = 0;
|
||||
newBogie->var_36 = lastVeh->var_36;
|
||||
newBogie->object_id = vehicleTypeId;
|
||||
|
||||
auto& prng = gprng();
|
||||
newBogie->var_44 = prng.rand_next();
|
||||
newBogie->creation_day = current_day();
|
||||
newBogie->var_46 = 0;
|
||||
newBogie->var_47 = 0;
|
||||
newBogie->accepted_cargo_types = 0;
|
||||
newBogie->cargo_type = 0xFF;
|
||||
newBogie->var_51 = 0;
|
||||
newBogie->var_5E = 0;
|
||||
newBogie->var_5F = 0;
|
||||
newBogie->var_60 = 0; // different to createbody
|
||||
newBogie->var_61 = 0; // different to createbody
|
||||
|
||||
newBogie->var_14 = 1;
|
||||
newBogie->var_09 = 1;
|
||||
newBogie->var_15 = 1;
|
||||
|
||||
newBogie->colour_scheme = colourScheme;
|
||||
lastVeh->next_car_id = newBogie->id;
|
||||
return newBogie;
|
||||
}
|
||||
|
||||
// 0x4AE8F1
|
||||
static openloco::vehicle_bogie* createFirstBogie(const thing_id_t head, const uint16_t vehicleTypeId, const vehicle_object& vehObject, const uint8_t bodyNumber, openloco::vehicle* const lastVeh, const ColourScheme colourScheme)
|
||||
{
|
||||
auto newBogie = createBogie(head, vehicleTypeId, vehObject, bodyNumber, lastVeh, colourScheme);
|
||||
if (newBogie == nullptr) // Can never happen
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
newBogie->var_38 = 0;
|
||||
|
||||
int32_t reliability = vehObject.reliability * 256;
|
||||
if (current_year() + 2 > vehObject.designed)
|
||||
{
|
||||
// Reduce reliability by an eighth after 2 years past design
|
||||
reliability -= reliability / 8;
|
||||
if (current_year() + 3 > vehObject.designed)
|
||||
{
|
||||
// Reduce reliability by a further eighth (quarter total) after 3 years past design
|
||||
reliability -= reliability / 8;
|
||||
}
|
||||
}
|
||||
if (reliability != 0)
|
||||
{
|
||||
reliability += 255;
|
||||
}
|
||||
newBogie->reliability = reliability;
|
||||
sub_4BA873(newBogie);
|
||||
|
||||
// Calculate refund cost == 7/8 * cost
|
||||
// TODO: use FixedPoint with 6 {(1 << 6) == 64} decimals for cost_index
|
||||
auto cost = (vehObject.cost_factor * currencyMultiplicationFactor[vehObject.cost_index]) / 64;
|
||||
newBogie->refund_cost = cost - cost / 8;
|
||||
|
||||
if (bodyNumber == 0)
|
||||
{
|
||||
// Only front car components front bogie can have cargo
|
||||
// stores only secondary cargo presumably due to space constraints
|
||||
// in the front car component body
|
||||
if (vehObject.num_simultaneous_cargo_types > 1)
|
||||
{
|
||||
newBogie->max_cargo = vehObject.max_secondary_cargo;
|
||||
newBogie->accepted_cargo_types = vehObject.secondary_cargo_types;
|
||||
auto cargoType = utility::bitscanforward(newBogie->accepted_cargo_types);
|
||||
if (cargoType != -1)
|
||||
{
|
||||
newBogie->cargo_type = cargoType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newBogie->object_sprite_type = vehObject.var_24[bodyNumber].front_bogie_sprite_ind;
|
||||
if (newBogie->object_sprite_type != sprite_ind::null)
|
||||
{
|
||||
newBogie->var_14 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_02;
|
||||
newBogie->var_09 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_03;
|
||||
newBogie->var_15 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_04;
|
||||
}
|
||||
return newBogie;
|
||||
}
|
||||
|
||||
// 0x004AEA9E
|
||||
static openloco::vehicle_bogie* createSecondBogie(const thing_id_t head, const uint16_t vehicleTypeId, const vehicle_object& vehObject, const uint8_t bodyNumber, openloco::vehicle* const lastVeh, const ColourScheme colourScheme)
|
||||
{
|
||||
auto newBogie = createBogie(head, vehicleTypeId, vehObject, bodyNumber, lastVeh, colourScheme);
|
||||
if (newBogie == nullptr) // Can never happen
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
newBogie->var_38 = flags_38::unk_1;
|
||||
newBogie->object_sprite_type = vehObject.var_24[bodyNumber].back_bogie_sprite_ind;
|
||||
if (newBogie->object_sprite_type != sprite_ind::null)
|
||||
{
|
||||
newBogie->var_14 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_02;
|
||||
newBogie->var_09 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_03;
|
||||
newBogie->var_15 = vehObject.bogie_sprites[newBogie->object_sprite_type].var_04;
|
||||
}
|
||||
return newBogie;
|
||||
}
|
||||
|
||||
// 0x004AEA9E
|
||||
static openloco::vehicle_body* createBody(const thing_id_t head, const uint16_t vehicleTypeId, const vehicle_object& vehObject, const uint8_t bodyNumber, openloco::vehicle* const lastVeh, const ColourScheme colourScheme)
|
||||
{
|
||||
auto newBody = createVehicleThing<vehicle_body>();
|
||||
// TODO: move this into the create function somehow
|
||||
newBody->type = bodyNumber == 0 ? vehicle_thing_type::vehicle_body_start : vehicle_thing_type::vehicle_body_cont;
|
||||
newBody->owner = _updating_company_id;
|
||||
newBody->head = head;
|
||||
newBody->body_index = bodyNumber;
|
||||
newBody->track_type = lastVeh->track_type;
|
||||
newBody->mode = lastVeh->mode;
|
||||
newBody->tile_x = -1;
|
||||
newBody->tile_y = 0;
|
||||
newBody->tile_base_z = 0;
|
||||
newBody->var_2E = 0;
|
||||
newBody->var_2C = 0;
|
||||
newBody->var_36 = lastVeh->var_36;
|
||||
newBody->var_38 = flags_38::unk_0; // different to create bogie
|
||||
newBody->object_id = vehicleTypeId;
|
||||
|
||||
auto& prng = gprng();
|
||||
newBody->var_44 = prng.rand_next();
|
||||
newBody->creation_day = current_day();
|
||||
newBody->var_46 = 0;
|
||||
newBody->var_47 = 0;
|
||||
newBody->accepted_cargo_types = 0;
|
||||
newBody->cargo_type = 0xFF;
|
||||
newBody->var_51 = 0;
|
||||
newBody->var_55 = 0; // different to create bogie
|
||||
newBody->var_5E = 0;
|
||||
newBody->var_5F = 0;
|
||||
|
||||
// different to create bogie
|
||||
if (bodyNumber == 0)
|
||||
{
|
||||
// If the car carries any type of cargo then it will have a primary
|
||||
// cargo in the first body of the first car component of the car.
|
||||
// Locomotives do not carry any cargo.
|
||||
if (vehObject.num_simultaneous_cargo_types != 0)
|
||||
{
|
||||
newBody->max_cargo = vehObject.max_primary_cargo;
|
||||
newBody->accepted_cargo_types = vehObject.primary_cargo_types;
|
||||
auto cargoType = utility::bitscanforward(newBody->accepted_cargo_types);
|
||||
if (cargoType != -1)
|
||||
{
|
||||
newBody->cargo_type = cargoType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newBody->var_14 = 1;
|
||||
newBody->var_09 = 1;
|
||||
newBody->var_15 = 1;
|
||||
|
||||
// different onwards to create bogie
|
||||
auto spriteType = vehObject.var_24[bodyNumber].body_sprite_ind;
|
||||
if (spriteType != sprite_ind::null)
|
||||
{
|
||||
if (spriteType & sprite_ind::flag_unk7)
|
||||
{
|
||||
newBody->var_38 |= flags_38::unk_1;
|
||||
spriteType &= ~sprite_ind::flag_unk7;
|
||||
}
|
||||
}
|
||||
newBody->object_sprite_type = spriteType;
|
||||
|
||||
if (newBody->object_sprite_type != sprite_ind::null)
|
||||
{
|
||||
newBody->var_14 = vehObject.sprites[newBody->object_sprite_type].var_08;
|
||||
newBody->var_09 = vehObject.sprites[newBody->object_sprite_type].var_09;
|
||||
newBody->var_15 = vehObject.sprites[newBody->object_sprite_type].var_0A;
|
||||
}
|
||||
|
||||
newBody->colour_scheme = colourScheme; // same as create bogie
|
||||
|
||||
if (bodyNumber == 0 && vehObject.flags & flags_E0::flag_02)
|
||||
{
|
||||
newBody->var_38 |= flags_38::unk_3;
|
||||
}
|
||||
|
||||
if (bodyNumber + 1 == vehObject.var_04 && vehObject.flags & flags_E0::flag_03)
|
||||
{
|
||||
newBody->var_38 |= flags_38::unk_3;
|
||||
}
|
||||
|
||||
lastVeh->next_car_id = newBody->id; // same as create bogie
|
||||
return newBody;
|
||||
}
|
||||
|
||||
static void sub_4B7CC3(openloco::vehicle_head* const head)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = reinterpret_cast<int32_t>(head);
|
||||
call(0x004B7CC3, regs);
|
||||
}
|
||||
|
||||
// 0x004AE86D
|
||||
static bool createCar(openloco::vehicle_head* const head, const uint16_t vehicleTypeId)
|
||||
{
|
||||
if (!thingmgr::checkNumFreeThings(max_num_vehicle_components_in_car))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Car insertion location
|
||||
// lastVeh will point to the vehicle component prior to the tail (head, unk_1, unk_2 *here*, tail) or (... bogie, bogie, body *here*, tail)
|
||||
openloco::vehicle* lastVeh = nullptr; // will be of type vehicle_body_start or unk_2 at end of loop
|
||||
openloco::vehicle* tail = reinterpret_cast<openloco::vehicle*>(head); // will be of type vehicle_6 at end of loop
|
||||
while (tail->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
lastVeh = tail;
|
||||
tail = lastVeh->next_car();
|
||||
}
|
||||
|
||||
const auto vehObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
const auto company = companymgr::get(_updating_company_id);
|
||||
_1136140 = company->mainColours; // Copy to global variable. Can be removed when all global uses confirmed
|
||||
auto colourScheme = company->mainColours;
|
||||
if (company->customVehicleColoursSet & (1 << vehObject->colour_type))
|
||||
{
|
||||
_1136140 = company->vehicleColours[vehObject->colour_type - 1]; // Copy to global variable. Can be removed when all global uses confirmed
|
||||
colourScheme = company->vehicleColours[vehObject->colour_type - 1];
|
||||
}
|
||||
|
||||
openloco::vehicle_bogie* newCarStart = nullptr;
|
||||
for (auto bodyNumber = 0; bodyNumber < vehObject->var_04; ++bodyNumber)
|
||||
{
|
||||
auto* const firstBogie = createFirstBogie(head->id, vehicleTypeId, *vehObject, bodyNumber, lastVeh, colourScheme);
|
||||
lastVeh = reinterpret_cast<openloco::vehicle*>(firstBogie);
|
||||
|
||||
auto* const secondBogie = createSecondBogie(head->id, vehicleTypeId, *vehObject, bodyNumber, lastVeh, colourScheme);
|
||||
lastVeh = reinterpret_cast<openloco::vehicle*>(secondBogie);
|
||||
|
||||
auto* const body = createBody(head->id, vehicleTypeId, *vehObject, bodyNumber, lastVeh, colourScheme);
|
||||
lastVeh = reinterpret_cast<openloco::vehicle*>(body);
|
||||
|
||||
if (newCarStart == nullptr)
|
||||
{
|
||||
newCarStart = firstBogie;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastVeh == nullptr) // can never happen
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lastVeh->next_car_id = tail->id;
|
||||
sub_4B7CC3(head);
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<uint16_t> sub_4B1E00()
|
||||
{
|
||||
if (!aiIsBelowVehicleLimit())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// ?Routing? related. Max 64 routing stops.
|
||||
for (auto i = 0; i < 1000; i++)
|
||||
{
|
||||
auto id = _96885C[i][0];
|
||||
if (id == thing_id::null)
|
||||
{
|
||||
for (auto j = 0; j < 64; ++j)
|
||||
{
|
||||
_96885C[i][j] = allocated_but_free_routing_station;
|
||||
}
|
||||
return { i };
|
||||
}
|
||||
}
|
||||
gGameCommandErrorText = string_ids::too_many_vehicles;
|
||||
return {};
|
||||
}
|
||||
|
||||
static void sub_470312(vehicle_head* const newHead)
|
||||
{
|
||||
_987C5C[_525FB8] = 0;
|
||||
newHead->length_of_var_4C = _525FB8;
|
||||
_525FB8++;
|
||||
newHead->var_4A = 0;
|
||||
newHead->var_4C = 1;
|
||||
}
|
||||
|
||||
// 0x004B64F9
|
||||
static uint16_t createUniqueTypeNumber(const VehicleType type)
|
||||
{
|
||||
std::array<bool, 1000> _unkArr{};
|
||||
auto v = thingmgr::first<openloco::vehicle>();
|
||||
while (v != nullptr)
|
||||
{
|
||||
auto next = v->next_vehicle();
|
||||
|
||||
if (v->owner == _updating_company_id && v->vehicleType == type)
|
||||
{
|
||||
if (v->var_44 != 0)
|
||||
{
|
||||
_unkArr[v->var_44 - 1] = true;
|
||||
}
|
||||
}
|
||||
v = next;
|
||||
}
|
||||
|
||||
uint16_t newNum = 0;
|
||||
for (; newNum < _unkArr.size(); ++newNum)
|
||||
{
|
||||
if (!_unkArr[newNum])
|
||||
break;
|
||||
}
|
||||
return newNum + 1;
|
||||
}
|
||||
|
||||
// 0x004AE34B
|
||||
static openloco::vehicle_head* createHead(const uint8_t trackType, const TransportMode mode, const uint16_t orderId, const VehicleType vehicleType)
|
||||
{
|
||||
auto* const newHead = createVehicleThing<vehicle_head>();
|
||||
thingmgr::moveSpriteToList(newHead, thingmgr::thing_list::vehicle_head);
|
||||
newHead->owner = _updating_company_id;
|
||||
newHead->head = newHead->id;
|
||||
newHead->var_0C |= (1 << 1);
|
||||
newHead->track_type = trackType;
|
||||
newHead->mode = mode;
|
||||
newHead->tile_x = -1;
|
||||
newHead->tile_y = 0;
|
||||
newHead->tile_base_z = 0;
|
||||
newHead->var_28 = 0;
|
||||
newHead->var_2E = 0;
|
||||
newHead->var_2C = 0;
|
||||
newHead->var_36 = orderId;
|
||||
newHead->var_14 = 0;
|
||||
newHead->var_09 = 0;
|
||||
newHead->var_15 = 0;
|
||||
newHead->var_38 = 0;
|
||||
newHead->var_3C = 0;
|
||||
newHead->vehicleType = vehicleType;
|
||||
newHead->var_22 = static_cast<uint8_t>(vehicleType) + 4;
|
||||
newHead->var_44 = 0; // Reset to null value so ignored in next function
|
||||
newHead->var_44 = createUniqueTypeNumber(vehicleType);
|
||||
newHead->var_5D = 0;
|
||||
newHead->var_54 = -1;
|
||||
newHead->var_5F = 0;
|
||||
newHead->var_60 = -1;
|
||||
newHead->var_61 = -1;
|
||||
newHead->var_69 = 0;
|
||||
newHead->var_77 = 0;
|
||||
newHead->var_79 = 0;
|
||||
sub_470312(newHead);
|
||||
return newHead;
|
||||
}
|
||||
|
||||
// 0x004AE40E
|
||||
static openloco::vehicle_1* createVehicle1(const thing_id_t head, openloco::vehicle* const lastVeh)
|
||||
{
|
||||
auto* const newVeh1 = createVehicleThing<vehicle_1>();
|
||||
newVeh1->owner = _updating_company_id;
|
||||
newVeh1->head = head;
|
||||
newVeh1->track_type = lastVeh->track_type;
|
||||
newVeh1->mode = lastVeh->mode;
|
||||
newVeh1->tile_x = -1;
|
||||
newVeh1->tile_y = 0;
|
||||
newVeh1->tile_base_z = 0;
|
||||
newVeh1->var_28 = 0;
|
||||
newVeh1->var_2E = 0;
|
||||
newVeh1->var_2C = 0;
|
||||
newVeh1->var_36 = lastVeh->var_36;
|
||||
newVeh1->var_14 = 0;
|
||||
newVeh1->var_09 = 0;
|
||||
newVeh1->var_15 = 0;
|
||||
newVeh1->var_38 = 0;
|
||||
newVeh1->var_3C = 0;
|
||||
newVeh1->var_44 = 0;
|
||||
newVeh1->var_46 = 0;
|
||||
newVeh1->var_48 = 0;
|
||||
newVeh1->var_52 = 0;
|
||||
newVeh1->var_4E = 0;
|
||||
newVeh1->var_50 = 0;
|
||||
newVeh1->var_53 = -1;
|
||||
lastVeh->next_car_id = newVeh1->id;
|
||||
return newVeh1;
|
||||
}
|
||||
|
||||
// 0x004AE4A0
|
||||
static openloco::vehicle_2* createVehicle2(const thing_id_t head, openloco::vehicle* const lastVeh)
|
||||
{
|
||||
auto* const newVeh2 = createVehicleThing<vehicle_2>();
|
||||
newVeh2->owner = _updating_company_id;
|
||||
newVeh2->head = head;
|
||||
newVeh2->track_type = lastVeh->track_type;
|
||||
newVeh2->mode = lastVeh->mode;
|
||||
newVeh2->tile_x = -1;
|
||||
newVeh2->tile_y = 0;
|
||||
newVeh2->tile_base_z = 0;
|
||||
newVeh2->var_28 = 0;
|
||||
newVeh2->var_2E = 0;
|
||||
newVeh2->var_2C = 0;
|
||||
newVeh2->var_36 = lastVeh->var_36;
|
||||
newVeh2->var_14 = 0;
|
||||
newVeh2->var_09 = 0;
|
||||
newVeh2->var_15 = 0;
|
||||
newVeh2->var_38 = 0;
|
||||
|
||||
newVeh2->var_56 = 0;
|
||||
newVeh2->var_5A = 0;
|
||||
newVeh2->var_5B = 0;
|
||||
newVeh2->var_44 = -1;
|
||||
newVeh2->var_48 = -1;
|
||||
newVeh2->var_48 = 0;
|
||||
newVeh2->var_4A = 0;
|
||||
newVeh2->var_5E = 0;
|
||||
newVeh2->refund_cost = 0;
|
||||
newVeh2->var_66 = 0;
|
||||
newVeh2->var_6A = 0;
|
||||
newVeh2->var_6E = 0;
|
||||
newVeh2->var_72 = 0;
|
||||
newVeh2->var_73 = 0;
|
||||
lastVeh->next_car_id = newVeh2->id;
|
||||
return newVeh2;
|
||||
}
|
||||
|
||||
// 0x004AE54E
|
||||
static openloco::vehicle_tail* createVehicleTail(const thing_id_t head, openloco::vehicle* const lastVeh)
|
||||
{
|
||||
auto* const newTail = createVehicleThing<vehicle_tail>();
|
||||
newTail->owner = _updating_company_id;
|
||||
newTail->head = head;
|
||||
newTail->track_type = lastVeh->track_type;
|
||||
newTail->mode = lastVeh->mode;
|
||||
newTail->tile_x = -1;
|
||||
newTail->tile_y = 0;
|
||||
newTail->tile_base_z = 0;
|
||||
newTail->var_28 = 0;
|
||||
newTail->var_2E = 0;
|
||||
newTail->var_2C = 0;
|
||||
newTail->var_36 = lastVeh->var_36;
|
||||
newTail->var_14 = 0;
|
||||
newTail->var_09 = 0;
|
||||
newTail->var_15 = 0;
|
||||
newTail->var_38 = 0;
|
||||
|
||||
newTail->var_44 = -1;
|
||||
newTail->var_48 = -1;
|
||||
newTail->var_4A = 0;
|
||||
lastVeh->next_car_id = newTail->id;
|
||||
newTail->next_car_id = thing_id::null;
|
||||
return newTail;
|
||||
}
|
||||
// 0x004AE318
|
||||
static std::optional<openloco::vehicle_head*> createBaseVehicle(const TransportMode mode, const VehicleType type, const uint8_t trackType)
|
||||
{
|
||||
if (!thingmgr::checkNumFreeThings(num_vehicle_components_in_base))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (_525FB8 >= max_orders)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::no_space_for_more_vehicle_orders;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto orderId = sub_4B1E00();
|
||||
if (!orderId)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* const head = createHead(trackType, mode, *orderId, type);
|
||||
openloco::vehicle* lastVeh = reinterpret_cast<openloco::vehicle*>(head);
|
||||
if (lastVeh == nullptr) // Can never happen
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* const veh1 = createVehicle1(head->id, lastVeh);
|
||||
lastVeh = reinterpret_cast<openloco::vehicle*>(veh1);
|
||||
if (lastVeh == nullptr) // Can never happen
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* const veh2 = createVehicle2(head->id, lastVeh);
|
||||
lastVeh = reinterpret_cast<openloco::vehicle*>(veh2);
|
||||
if (lastVeh == nullptr) // Can never happen
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
createVehicleTail(head->id, lastVeh);
|
||||
|
||||
sub_4B7CC3(head);
|
||||
return { head };
|
||||
}
|
||||
|
||||
static void sub_4AF7A4(openloco::vehicle_head* const veh0)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = reinterpret_cast<int32_t>(veh0);
|
||||
call(0x004AF7A4, regs);
|
||||
}
|
||||
|
||||
// 0x004B05E4
|
||||
static void placeDownVehicle(openloco::vehicle_head* const head, const coord_t x, const coord_t y, const uint8_t baseZ, const uint16_t unk1, const uint16_t unk2)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = reinterpret_cast<int32_t>(head);
|
||||
regs.ax = x;
|
||||
regs.cx = y;
|
||||
regs.bx = unk2;
|
||||
regs.dl = baseZ;
|
||||
regs.ebp = unk1;
|
||||
call(0x004B05E4, regs);
|
||||
}
|
||||
|
||||
// 0x004B1E77
|
||||
// Free orderId?
|
||||
static void sub_4B1E77(const uint16_t orderId)
|
||||
{
|
||||
uint16_t baseOrderId = orderId & ~(0x3F);
|
||||
for (auto i = 0; i < 64; ++i)
|
||||
{
|
||||
_96885C[baseOrderId][i] = thing_id::null;
|
||||
}
|
||||
}
|
||||
|
||||
static void sub_470795(const uint32_t var46, const int16_t var4C)
|
||||
{
|
||||
auto v = thingmgr::first<openloco::vehicle>();
|
||||
while (v != nullptr)
|
||||
{
|
||||
auto next = v->next_vehicle();
|
||||
|
||||
auto head = v->as_vehicle_head();
|
||||
v = next;
|
||||
if (head == nullptr) // Can never happen
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (head->length_of_var_4C >= var46)
|
||||
{
|
||||
head->length_of_var_4C += var4C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x00470334
|
||||
// Remove vehicle ?orders?
|
||||
static void sub_470334(openloco::vehicle_head* const head)
|
||||
{
|
||||
sub_470795(head->length_of_var_4C, head->var_4C * -1);
|
||||
auto length = _525FB8 - head->length_of_var_4C - head->var_4C;
|
||||
memmove(&_987C5C[head->length_of_var_4C], &_987C5C[head->var_4C + head->length_of_var_4C], length);
|
||||
|
||||
_525FB8 = _525FB8 - head->var_4C;
|
||||
}
|
||||
|
||||
// 0x0042851C
|
||||
// Delete related news items??
|
||||
static void sub_42851C(const thing_id_t id, const uint8_t type)
|
||||
{
|
||||
registers regs{};
|
||||
regs.al = type;
|
||||
regs.dx = id;
|
||||
call(0x0042851C, regs);
|
||||
}
|
||||
|
||||
// 0x004AE6DE
|
||||
static void updateWholeVehicle(vehicle_head* const head)
|
||||
{
|
||||
sub_4AF7A4(head);
|
||||
auto company = companymgr::get(_updating_company_id);
|
||||
company->recalculateTransportCounts();
|
||||
|
||||
if (_backupVeh0 != reinterpret_cast<openloco::vehicle_head*>(-1))
|
||||
{
|
||||
placeDownVehicle(_backupVeh0, _backupX, _backupY, _backupZ, _backup2C, _backup2E);
|
||||
}
|
||||
|
||||
ui::WindowManager::invalidate(ui::WindowType::vehicleList, head->owner);
|
||||
}
|
||||
|
||||
// 0x004AE74E
|
||||
static uint32_t createNewVehicle(const uint8_t flags, const uint16_t vehicleTypeId)
|
||||
{
|
||||
gameCommandMapX = location::null;
|
||||
if (!thingmgr::checkNumFreeThings(max_num_vehicle_components_in_car + num_vehicle_components_in_base))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (!isEmptyVehicleSlotAvailable())
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (flags & game_commands::GameCommandFlag::apply)
|
||||
{
|
||||
auto vehObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
|
||||
auto head = createBaseVehicle(vehObject->mode, vehObject->type, vehObject->track_type);
|
||||
if (!head)
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
auto _head = *head;
|
||||
_113642A = _head->id;
|
||||
if (createCar(_head, vehicleTypeId))
|
||||
{
|
||||
// 0x004AE6DE
|
||||
updateWholeVehicle(_head);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup and delete base vehicle before exit.
|
||||
sub_4B1E77(_head->var_36);
|
||||
sub_470334(_head);
|
||||
sub_42851C(_head->id, 3);
|
||||
auto veh1 = reinterpret_cast<openloco::vehicle*>(_head)->next_car();
|
||||
auto veh2 = veh1->next_car();
|
||||
auto tail = veh2->next_car();
|
||||
// Get all vehicles before freeing
|
||||
thingmgr::freeThing(_head);
|
||||
thingmgr::freeThing(veh1);
|
||||
thingmgr::freeThing(veh2);
|
||||
thingmgr::freeThing(tail);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
// 0x4AE733
|
||||
auto vehObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
// TODO: use FixedPoint with 6 {(1 << 6) == 64} decimals for cost_index
|
||||
auto cost = (vehObject->cost_factor * currencyMultiplicationFactor[vehObject->cost_index]) / 64;
|
||||
return cost;
|
||||
}
|
||||
|
||||
// 0x004AE5FF
|
||||
static uint32_t addCarToVehicle(const uint8_t flags, const uint16_t vehicleTypeId, const uint16_t vehicleThingId)
|
||||
{
|
||||
auto veh0 = thingmgr::get<openloco::vehicle>(vehicleThingId);
|
||||
auto head = veh0->as_vehicle_head();
|
||||
auto veh2 = veh0->next_car()->next_car()->as_vehicle_2();
|
||||
if (veh2 == nullptr || head == nullptr)
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
gameCommandMapX = veh2->x;
|
||||
gameCommandMapY = veh2->y;
|
||||
gameCommandMapZ = veh2->z;
|
||||
|
||||
if (!sub_431E6A(head->owner))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (!sub_4B0BDD(head))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (!head->isVehicleTypeCompatible(vehicleTypeId))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (!thingmgr::checkNumFreeThings(max_num_vehicle_components_in_car))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (flags & game_commands::GameCommandFlag::apply)
|
||||
{
|
||||
if (head->tile_x != -1)
|
||||
{
|
||||
_backupX = head->tile_x;
|
||||
_backupY = head->tile_y;
|
||||
_backupZ = head->tile_base_z;
|
||||
_backup2C = head->var_2C;
|
||||
_backup2E = head->var_2E;
|
||||
_backupVeh0 = head;
|
||||
liftUpVehicle(head);
|
||||
}
|
||||
|
||||
if (createCar(head, vehicleTypeId))
|
||||
{
|
||||
updateWholeVehicle(head);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_backupVeh0 == reinterpret_cast<openloco::vehicle_head*>(-1))
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
vehicle_head* veh0backup = _backupVeh0;
|
||||
// If it has an existing body
|
||||
if (reinterpret_cast<openloco::vehicle*>(veh0backup)->next_car()->next_car()->next_car()->type == vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
placeDownVehicle(_backupVeh0, _backupX, _backupY, _backupZ, _backup2C, _backup2E);
|
||||
}
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
// 0x4AE733
|
||||
auto vehObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
// TODO: use FixedPoint with 6 {(1 << 6) == 64} decimals for cost_index
|
||||
auto cost = (vehObject->cost_factor * currencyMultiplicationFactor[vehObject->cost_index]) / 64;
|
||||
return cost;
|
||||
}
|
||||
|
||||
// 0x004AE5E4
|
||||
uint32_t create(const uint8_t flags, const uint16_t vehicleTypeId, const uint16_t vehicleThingId)
|
||||
{
|
||||
gGameCommandExpenditureType = static_cast<uint8_t>(ExpenditureType::VehiclePurchases) * 4;
|
||||
_backupVeh0 = reinterpret_cast<openloco::vehicle_head*>(-1);
|
||||
if (vehicleThingId == (uint16_t)-1)
|
||||
{
|
||||
return createNewVehicle(flags, vehicleTypeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return addCarToVehicle(flags, vehicleTypeId, vehicleThingId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,13 +7,20 @@ using namespace openloco::interop;
|
|||
namespace openloco::thingmgr
|
||||
{
|
||||
loco_global<thing_id_t[num_thing_lists], 0x00525E40> _heads;
|
||||
loco_global<uint16_t[num_thing_lists], 0x00525E4C> _listCounts;
|
||||
loco_global<Thing[max_things], 0x006DB6DC> _things;
|
||||
static loco_global<string_id, 0x009C68E6> gGameCommandErrorText;
|
||||
|
||||
thing_id_t first_id(thing_list list)
|
||||
{
|
||||
return _heads[(size_t)list];
|
||||
}
|
||||
|
||||
uint16_t getListCount(const thing_list list)
|
||||
{
|
||||
return _listCounts[static_cast<size_t>(list)];
|
||||
}
|
||||
|
||||
template<>
|
||||
vehicle* first()
|
||||
{
|
||||
|
@ -39,6 +46,14 @@ namespace openloco::thingmgr
|
|||
return (thing_base*)regs.esi;
|
||||
}
|
||||
|
||||
// 0x0047024A
|
||||
void freeThing(thing_base* const thing)
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = reinterpret_cast<uint32_t>(thing);
|
||||
call(0x0047024A, regs);
|
||||
}
|
||||
|
||||
// 0x004A8826
|
||||
void update_vehicles()
|
||||
{
|
||||
|
@ -59,4 +74,24 @@ namespace openloco::thingmgr
|
|||
{
|
||||
call(0x004402F4);
|
||||
}
|
||||
|
||||
// 0x0047019F
|
||||
void moveSpriteToList(thing_base* const thing, const thing_list list)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = reinterpret_cast<uint32_t>(thing);
|
||||
regs.ecx = static_cast<int8_t>(list);
|
||||
call(0x0047019F, regs);
|
||||
}
|
||||
|
||||
// 0x00470188
|
||||
bool checkNumFreeThings(const size_t numNewThings)
|
||||
{
|
||||
if (thingmgr::getListCount(thingmgr::thing_list::null) <= numNewThings)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::too_many_objects_in_game;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace openloco::thingmgr
|
|||
{
|
||||
null,
|
||||
vehicle,
|
||||
misc = 3
|
||||
misc = 3,
|
||||
vehicle_head,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -34,7 +35,12 @@ namespace openloco::thingmgr
|
|||
T* first();
|
||||
|
||||
thing_base* create_thing();
|
||||
void freeThing(thing_base* const thing);
|
||||
|
||||
void update_vehicles();
|
||||
void update_misc_things();
|
||||
|
||||
uint16_t getListCount(const thing_list list);
|
||||
void moveSpriteToList(thing_base* const thing, const thing_list list);
|
||||
bool checkNumFreeThings(const size_t numNewThings);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ loco_global<uint16_t[2047], 0x00500B50> vehicle_arr_500B50;
|
|||
loco_global<int16_t[128], 0x00503B6A> factorXY503B6A;
|
||||
loco_global<uint8_t[44], 0x004F8A7C> vehicle_arr_4F8A7C; // bools
|
||||
loco_global<uint8_t, 0x00525FAE> vehicle_var_525FAE; // boolean
|
||||
static loco_global<string_id, 0x009C68E6> gGameCommandErrorText;
|
||||
|
||||
// 0x00503E5C
|
||||
static constexpr uint8_t vehicleBodyIndexToPitch[] = {
|
||||
|
@ -114,7 +115,7 @@ bool vehicle::update()
|
|||
case vehicle_thing_type::vehicle_bogie:
|
||||
result = call(0x004AA008, regs);
|
||||
break;
|
||||
case vehicle_thing_type::vehicle_body_end:
|
||||
case vehicle_thing_type::vehicle_body_start:
|
||||
case vehicle_thing_type::vehicle_body_cont:
|
||||
result = as_vehicle_body()->update();
|
||||
break;
|
||||
|
@ -142,7 +143,7 @@ void vehicle::sub_4BA8D4()
|
|||
return;
|
||||
}
|
||||
|
||||
auto v = next_car()->next_car()->next_car();
|
||||
auto v = next_car()->next_car()->next_car(); // bogie or tail
|
||||
if (v->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
while (true)
|
||||
|
@ -151,7 +152,7 @@ void vehicle::sub_4BA8D4()
|
|||
{
|
||||
if ((scenario_ticks() & 3) == 0)
|
||||
{
|
||||
auto v2 = v->next_car()->next_car();
|
||||
auto v2 = v->next_car()->next_car(); // body
|
||||
smoke::create(loc16(v2->x, v2->y, v2->z + 4));
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ void vehicle::sub_4BA8D4()
|
|||
}
|
||||
}
|
||||
|
||||
v = v->next_car()->next_car()->next_car();
|
||||
v = v->next_car()->next_car()->next_car(); // next bogie
|
||||
vehicle* u;
|
||||
do
|
||||
{
|
||||
|
@ -181,9 +182,9 @@ void vehicle::sub_4BA8D4()
|
|||
return;
|
||||
}
|
||||
u = v->next_car()->next_car();
|
||||
if (u->type != vehicle_thing_type::vehicle_body_end)
|
||||
if (u->type != vehicle_thing_type::vehicle_body_start)
|
||||
v = u->next_car();
|
||||
} while (u->type != vehicle_thing_type::vehicle_body_end);
|
||||
} while (u->type != vehicle_thing_type::vehicle_body_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +236,7 @@ int32_t openloco::vehicle_body::update()
|
|||
// 0x004AAC4E
|
||||
void openloco::vehicle_body::animation_update()
|
||||
{
|
||||
if (var_38 & (1 << 4))
|
||||
if (var_38 & things::vehicle::flags_38::unk_4)
|
||||
return;
|
||||
|
||||
vehicle* veh = vehicle_1136118;
|
||||
|
@ -243,7 +244,7 @@ void openloco::vehicle_body::animation_update()
|
|||
return;
|
||||
|
||||
auto vehicleObject = object();
|
||||
int32_t var_05 = vehicleObject->var_24[var_54].var_05;
|
||||
int32_t var_05 = vehicleObject->var_24[body_index].var_05;
|
||||
if (var_05 == 0)
|
||||
{
|
||||
return;
|
||||
|
@ -286,7 +287,7 @@ void openloco::vehicle_body::animation_update()
|
|||
void openloco::vehicle_body::sub_4AAB0B()
|
||||
{
|
||||
int32_t eax = vehicle_var_1136130 >> 3;
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
eax = -eax;
|
||||
}
|
||||
|
@ -326,7 +327,7 @@ void openloco::vehicle_body::sub_4AAB0B()
|
|||
|
||||
if (ah < 0)
|
||||
{
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
ah = 2;
|
||||
if (al != 0 && al != ah)
|
||||
|
@ -345,7 +346,7 @@ void openloco::vehicle_body::sub_4AAB0B()
|
|||
}
|
||||
else if (ah > 0)
|
||||
{
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
ah = 1;
|
||||
if (al != 0 && al != ah)
|
||||
|
@ -976,7 +977,8 @@ uint8_t openloco::vehicle_body::update_sprite_yaw_4(int16_t x_offset, int16_t y_
|
|||
void openloco::vehicle_body::secondary_animation_update()
|
||||
{
|
||||
auto vehicleObject = object();
|
||||
int32_t var_05 = vehicleObject->var_24[var_54].var_05;
|
||||
|
||||
uint8_t var_05 = vehicleObject->var_24[body_index].var_05;
|
||||
if (var_05 == 0)
|
||||
return;
|
||||
|
||||
|
@ -1035,7 +1037,7 @@ void openloco::vehicle_body::steam_puffs_animation_update(uint8_t num, int32_t v
|
|||
|
||||
auto _var_44 = var_44;
|
||||
// Reversing
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
_var_44 = -_var_44;
|
||||
|
@ -1189,12 +1191,12 @@ void openloco::vehicle_body::diesel_exhaust1_animation_update(uint8_t num, int32
|
|||
vehicle* veh_3 = vehicle_1136120;
|
||||
auto vehicleObject = object();
|
||||
|
||||
if (veh->var_5E == 5)
|
||||
if (veh->vehicleType == VehicleType::ship)
|
||||
{
|
||||
if (veh_3->var_56 == 0)
|
||||
return;
|
||||
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
}
|
||||
|
@ -1219,7 +1221,7 @@ void openloco::vehicle_body::diesel_exhaust1_animation_update(uint8_t num, int32
|
|||
if (veh_3->var_5A != 1)
|
||||
return;
|
||||
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
}
|
||||
|
@ -1280,7 +1282,7 @@ void openloco::vehicle_body::diesel_exhaust2_animation_update(uint8_t num, int32
|
|||
if (veh_3->var_56 > 917504)
|
||||
return;
|
||||
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
}
|
||||
|
@ -1353,7 +1355,7 @@ void openloco::vehicle_body::electric_spark1_animation_update(uint8_t num, int32
|
|||
return;
|
||||
|
||||
auto _var_44 = var_44;
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
_var_44 = -var_44;
|
||||
|
@ -1413,7 +1415,7 @@ void openloco::vehicle_body::electric_spark2_animation_update(uint8_t num, int32
|
|||
return;
|
||||
|
||||
auto _var_44 = var_44;
|
||||
if (var_38 & (1 << 1))
|
||||
if (var_38 & things::vehicle::flags_38::unk_1)
|
||||
{
|
||||
var_05 = -var_05;
|
||||
_var_44 = -var_44;
|
||||
|
@ -1456,7 +1458,7 @@ void openloco::vehicle_body::electric_spark2_animation_update(uint8_t num, int32
|
|||
loc.y += yFactor;
|
||||
|
||||
auto yaw = (sprite_yaw + 16) & 0x3F;
|
||||
auto firstBogie = var_38 & (1 << 1) ? backBogie : frontBogie;
|
||||
auto firstBogie = var_38 & things::vehicle::flags_38::unk_1 ? backBogie : frontBogie;
|
||||
xyFactor = 5;
|
||||
if (!(vehicle_arr_4F8A7C[firstBogie->var_2C / 8] & 1))
|
||||
{
|
||||
|
@ -1549,3 +1551,167 @@ void openloco::vehicle_body::ship_wake_animation_update(uint8_t num, int32_t)
|
|||
|
||||
exhaust::create(loc, vehicleObject->animation[num].object_id);
|
||||
}
|
||||
|
||||
// 0x004B90F0
|
||||
// eax : newVehicleTypeId
|
||||
// ebx : sourceVehicleTypeId;
|
||||
static bool sub_4B90F0(const uint16_t newVehicleTypeId, const uint16_t sourceVehicleTypeId)
|
||||
{
|
||||
auto newObject = objectmgr::get<vehicle_object>(newVehicleTypeId); //edi
|
||||
auto sourceObject = objectmgr::get<vehicle_object>(sourceVehicleTypeId); // esi
|
||||
|
||||
if ((newObject->flags & flags_E0::can_couple) && (sourceObject->flags & flags_E0::can_couple))
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newVehicleTypeId == sourceVehicleTypeId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < newObject->num_compat; ++i)
|
||||
{
|
||||
if (newObject->compatible_vehicles[i] == sourceVehicleTypeId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceObject->num_compat != 0)
|
||||
{
|
||||
for (auto i = 0; i < sourceObject->num_compat; ++i)
|
||||
{
|
||||
if (sourceObject->compatible_vehicles[i] == newVehicleTypeId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((newObject->num_compat != 0) || (sourceObject->num_compat != 0))
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x004B9780
|
||||
// used by road vehicles only maybe??
|
||||
static uint32_t getVehicleTypeLength(const uint16_t vehicleTypeId)
|
||||
{
|
||||
auto vehObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
auto length = 0;
|
||||
for (auto i = 0; i < vehObject->var_04; ++i)
|
||||
{
|
||||
if (vehObject->var_24[i].body_sprite_ind == 0xFF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto unk = vehObject->var_24[i].body_sprite_ind & 0x7F;
|
||||
length += vehObject->sprites[unk].bogey_position * 2;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
// 0x004B97B7
|
||||
// used by road vehicles only maybe??
|
||||
uint32_t vehicle_head::getVehicleTotalLength() // TODO: const
|
||||
{
|
||||
auto totalLength = 0;
|
||||
auto veh = reinterpret_cast<openloco::vehicle*>(this)->next_car()->next_car()->next_car();
|
||||
while (veh->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
if (veh->type == vehicle_thing_type::vehicle_body_start)
|
||||
{
|
||||
totalLength += getVehicleTypeLength(veh->object_id);
|
||||
}
|
||||
veh = veh->next_car();
|
||||
}
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// 0x004B8FA2
|
||||
// esi : self
|
||||
// ax : vehicleTypeId
|
||||
bool vehicle_head::isVehicleTypeCompatible(const uint16_t vehicleTypeId) // TODO: const
|
||||
{
|
||||
auto newObject = objectmgr::get<vehicle_object>(vehicleTypeId);
|
||||
if (newObject->mode == TransportMode::air || newObject->mode == TransportMode::water)
|
||||
{
|
||||
auto veh = reinterpret_cast<openloco::vehicle*>(this)->next_car()->next_car()->next_car();
|
||||
if (veh->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newObject->track_type != track_type)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (newObject->mode != mode)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newObject->type != vehicleType)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::incompatible_vehicle;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto veh = reinterpret_cast<openloco::vehicle*>(this)->next_car()->next_car()->next_car();
|
||||
if (veh->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
while (veh->type != vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
if (!sub_4B90F0(vehicleTypeId, veh->object_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vehicle_body* vehUnk = nullptr;
|
||||
do
|
||||
{
|
||||
veh = veh->next_car()->next_car()->next_car();
|
||||
if (veh->type == vehicle_thing_type::vehicle_6)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
vehUnk = veh->next_car()->next_car()->as_vehicle_body();
|
||||
} while (vehUnk != nullptr && vehUnk->type == vehicle_thing_type::vehicle_body_cont);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode != TransportMode::road)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (track_type != 0xFF)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto curTotalLength = getVehicleTotalLength();
|
||||
auto additionalNewLength = getVehicleTypeLength(vehicleTypeId);
|
||||
if (curTotalLength + additionalNewLength > openloco::things::vehicle::max_vehicle_length)
|
||||
{
|
||||
gGameCommandErrorText = string_ids::vehicle_too_long;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,28 @@
|
|||
|
||||
namespace openloco
|
||||
{
|
||||
namespace things::vehicle
|
||||
{
|
||||
constexpr auto max_vehicle_length = 176; // TODO: Units?
|
||||
|
||||
uint32_t create(const uint8_t flags, const uint16_t vehicleTypeId, const uint16_t vehicleThingId);
|
||||
|
||||
namespace flags_38
|
||||
{
|
||||
constexpr uint8_t unk_0 = 1 << 0;
|
||||
constexpr uint8_t unk_1 = 1 << 1;
|
||||
constexpr uint8_t unk_3 = 1 << 3;
|
||||
constexpr uint8_t unk_4 = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
struct vehicle_head;
|
||||
struct vehicle_1;
|
||||
struct vehicle_2;
|
||||
struct vehicle_bogie;
|
||||
struct vehicle_body;
|
||||
struct vehicle_tail;
|
||||
|
||||
struct vehicle_26;
|
||||
|
||||
namespace flags_5f
|
||||
|
@ -25,7 +45,7 @@ namespace openloco
|
|||
vehicle_1,
|
||||
vehicle_2,
|
||||
vehicle_bogie,
|
||||
vehicle_body_end,
|
||||
vehicle_body_start,
|
||||
vehicle_body_cont,
|
||||
vehicle_6,
|
||||
};
|
||||
|
@ -57,14 +77,24 @@ namespace openloco
|
|||
template<typename TType, vehicle_thing_type TClass>
|
||||
TType* as() const
|
||||
{
|
||||
// This can not use reinterpret_cast due to being a const member without considerable more code
|
||||
return type == TClass ? (TType*)this : nullptr;
|
||||
}
|
||||
|
||||
template<typename TType>
|
||||
TType* as() const
|
||||
{
|
||||
return as<TType, TType::VehicleThingType>();
|
||||
}
|
||||
|
||||
public:
|
||||
vehicle_bogie* as_vehicle_bogie() const { return as<vehicle_bogie, vehicle_thing_type::vehicle_bogie>(); }
|
||||
vehicle_head* as_vehicle_head() const { return as<vehicle_head>(); }
|
||||
vehicle_1* as_vehicle_1() const { return as<vehicle_1>(); }
|
||||
vehicle_2* as_vehicle_2() const { return as<vehicle_2>(); }
|
||||
vehicle_bogie* as_vehicle_bogie() const { return as<vehicle_bogie>(); }
|
||||
vehicle_body* as_vehicle_body() const
|
||||
{
|
||||
auto vehicle = as<vehicle_body, vehicle_thing_type::vehicle_body_end>();
|
||||
auto vehicle = as<vehicle_body, vehicle_thing_type::vehicle_body_start>();
|
||||
if (vehicle != nullptr)
|
||||
return vehicle;
|
||||
return as<vehicle_body, vehicle_thing_type::vehicle_body_cont>();
|
||||
|
@ -76,6 +106,7 @@ namespace openloco
|
|||
return vehicle;
|
||||
return as<vehicle_26, vehicle_thing_type::vehicle_6>();
|
||||
}
|
||||
vehicle_tail* as_vehicle_tail() const { return as<vehicle_tail>(); }
|
||||
};
|
||||
|
||||
struct vehicle : vehicle_base
|
||||
|
@ -91,14 +122,14 @@ namespace openloco
|
|||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35
|
||||
uint8_t pad_36[0x38 - 0x36];
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t pad_39; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint8_t pad_3C[0x40 - 0x3C];
|
||||
uint16_t object_id; // 0x40
|
||||
TransportMode mode; // 0x42
|
||||
uint32_t var_3C; // veh1 speed?
|
||||
uint16_t object_id; // 0x40 not used in all vehicles **be careful**
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
int16_t var_44; // used for name on vehicle_0 will be unique (for type) number
|
||||
uint8_t pad_46;
|
||||
|
@ -116,7 +147,7 @@ namespace openloco
|
|||
uint8_t var_5A;
|
||||
uint8_t pad_5B[0x5D - 0x5B];
|
||||
uint8_t var_5D;
|
||||
uint8_t var_5E;
|
||||
VehicleType vehicleType; // 0x5E
|
||||
uint8_t var_5F; // 0x5F (bit 1 = can break down)
|
||||
uint8_t pad_60[0x6A - 0x60];
|
||||
uint8_t var_6A;
|
||||
|
@ -134,20 +165,168 @@ namespace openloco
|
|||
bool update();
|
||||
void sub_4BAA76();
|
||||
};
|
||||
static_assert(sizeof(vehicle) == 0x74); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_26 : vehicle_base
|
||||
{
|
||||
uint8_t pad_20[0x44 - 0x20];
|
||||
uint8_t pad_20[0x40 - 0x20];
|
||||
uint16_t object_id; // 0x40
|
||||
uint8_t pad_42[0x44 - 0x42];
|
||||
uint8_t sound_id; // 0x44
|
||||
uint8_t pad_45[0x4A - 0x45];
|
||||
uint16_t var_4A; // sound-related flag(s)
|
||||
ui::window_number sound_window_number; // 0x4C
|
||||
ui::WindowType sound_window_type; // 0x4E
|
||||
uint8_t pad_4F[0x56 - 0x4F];
|
||||
uint32_t var_56;
|
||||
uint8_t pad_5A[0x73 - 0x53];
|
||||
uint8_t var_73;
|
||||
};
|
||||
|
||||
struct vehicle_head : vehicle_base
|
||||
{
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_0;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint16_t var_22;
|
||||
uint8_t pad_24[0x26 - 0x24];
|
||||
thing_id_t head; // 0x26
|
||||
uint32_t var_28;
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t pad_39; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint32_t var_3C; // 0x3C
|
||||
uint8_t pad_40[0x2]; // 0x40
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
int16_t var_44;
|
||||
uint32_t length_of_var_4C; // 0x46
|
||||
uint16_t var_4A;
|
||||
uint16_t var_4C; // 0x4C index into ?order? array
|
||||
uint8_t pad_4E[0x2]; // 0x4E
|
||||
uint8_t pad_50;
|
||||
uint8_t pad_51; // 0x51
|
||||
uint8_t var_52;
|
||||
uint8_t pad_53;
|
||||
int16_t var_54;
|
||||
uint8_t pad_56[0x4];
|
||||
uint8_t pad_5A;
|
||||
uint8_t pad_5B[0x5D - 0x5B];
|
||||
uint8_t var_5D;
|
||||
VehicleType vehicleType; // 0x5E
|
||||
uint8_t var_5F; // 0x5F
|
||||
uint8_t var_60;
|
||||
uint16_t var_61;
|
||||
uint8_t pad_63[0x69 - 0x63];
|
||||
uint32_t var_69;
|
||||
uint8_t pad_6D[0x77 - 0x6D];
|
||||
uint16_t var_77; //
|
||||
uint8_t var_79;
|
||||
|
||||
public:
|
||||
bool isVehicleTypeCompatible(const uint16_t vehicleTypeId);
|
||||
|
||||
private:
|
||||
uint32_t getVehicleTotalLength();
|
||||
};
|
||||
static_assert(sizeof(vehicle_head) == 0x7A); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_1 : vehicle_base
|
||||
{
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_1;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint8_t pad_22[0x26 - 0x22];
|
||||
thing_id_t head; // 0x26
|
||||
uint32_t var_28;
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t pad_39; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint32_t var_3C; // 0x3C
|
||||
uint8_t pad_40[0x2]; // 0x40
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
uint16_t var_44;
|
||||
uint16_t var_46;
|
||||
uint8_t var_48;
|
||||
uint8_t pad_49[0x4E - 0x49];
|
||||
uint16_t var_4E;
|
||||
uint16_t var_50;
|
||||
uint8_t var_52;
|
||||
int32_t var_53;
|
||||
};
|
||||
static_assert(sizeof(vehicle_1) == 0x57); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_2 : vehicle_base
|
||||
{
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_2;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint8_t pad_22[0x26 - 0x22];
|
||||
thing_id_t head; // 0x26
|
||||
uint32_t var_28;
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t pad_39; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint8_t pad_3C[0x42 - 0x3C]; // 0x3C
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
uint8_t var_44;
|
||||
uint8_t pad_45[0x48 - 0x45];
|
||||
int16_t var_48;
|
||||
uint16_t var_4A;
|
||||
uint8_t pad_4C[0x56 - 0x4C];
|
||||
uint32_t var_56;
|
||||
uint8_t var_5A;
|
||||
uint8_t var_5B;
|
||||
uint8_t pad_5C[0x5E - 0x5C];
|
||||
uint32_t var_5E;
|
||||
uint32_t refund_cost;
|
||||
uint32_t var_66;
|
||||
uint32_t var_6A;
|
||||
uint32_t var_6E;
|
||||
uint8_t var_72;
|
||||
uint8_t var_73; // 0x73 (bit 0 = broken down)
|
||||
};
|
||||
static_assert(sizeof(vehicle_2) == 0x74); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_body : vehicle_base
|
||||
{
|
||||
uint8_t pad_20[0x38 - 0x20];
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_body_cont;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint8_t pad_22[0x24 - 0x22];
|
||||
ColourScheme colour_scheme; // 0x24
|
||||
thing_id_t head; // 0x26
|
||||
uint8_t pad_28[0x2C - 0x28];
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t object_sprite_type; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
|
@ -157,11 +336,19 @@ namespace openloco
|
|||
uint8_t pad_43;
|
||||
int16_t var_44;
|
||||
uint8_t var_46;
|
||||
uint8_t pad_47[0x54 - 0x47];
|
||||
uint8_t var_54;
|
||||
uint8_t var_47;
|
||||
uint32_t accepted_cargo_types; // 0x48
|
||||
uint8_t cargo_type; // 0x4C
|
||||
uint8_t max_cargo; // 0x4D
|
||||
uint8_t pad_4E[0x51 - 0x4E];
|
||||
uint8_t var_51;
|
||||
uint8_t pad_52[0x54 - 0x52];
|
||||
uint8_t body_index; // 0x54
|
||||
int8_t var_55;
|
||||
uint8_t pad_56[0x5E - 0x56];
|
||||
uint32_t creation_day; // 0x56
|
||||
uint8_t pad_5A[0x5E - 0x5A];
|
||||
uint8_t var_5E;
|
||||
uint8_t var_5F;
|
||||
|
||||
vehicle_object* object() const;
|
||||
int32_t update();
|
||||
|
@ -185,5 +372,81 @@ namespace openloco
|
|||
uint8_t update_sprite_yaw_3(int16_t x_offset, int16_t y_offset);
|
||||
uint8_t update_sprite_yaw_4(int16_t x_offset, int16_t y_offset);
|
||||
};
|
||||
static_assert(sizeof(vehicle_body) == 0x60); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_bogie : vehicle_base
|
||||
{
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_bogie;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint8_t pad_22[0x24 - 0x22];
|
||||
ColourScheme colour_scheme; // 0x24
|
||||
thing_id_t head; // 0x26
|
||||
uint8_t pad_28[0x2C - 0x28];
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t object_sprite_type; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint8_t pad_3C[0x40 - 0x3C];
|
||||
uint16_t object_id; // 0x40
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
uint16_t var_44;
|
||||
uint8_t var_46;
|
||||
uint8_t var_47;
|
||||
uint32_t accepted_cargo_types; // 0x48 front car component front bogie only
|
||||
uint8_t cargo_type; // 0x4C front car component front bogie only
|
||||
uint8_t max_cargo; // 0x4D front car component front bogie only
|
||||
uint8_t pad_4E[0x51 - 0x4E];
|
||||
uint8_t var_51;
|
||||
uint8_t pad_52[0x54 - 0x52];
|
||||
uint8_t body_index; // 0x54
|
||||
uint8_t pad_55;
|
||||
uint32_t creation_day; // 0x56
|
||||
uint8_t pad_5A[0x5E - 0x5A];
|
||||
uint8_t var_5E;
|
||||
uint8_t var_5F;
|
||||
uint8_t var_60;
|
||||
uint8_t var_61;
|
||||
uint32_t refund_cost; // 0x62 front bogies only
|
||||
uint16_t reliability; // 0x66 front bogies only
|
||||
uint16_t var_68;
|
||||
};
|
||||
static_assert(sizeof(vehicle_bogie) == 0x6A); // Can't use offset_of change this to last field if more found
|
||||
|
||||
struct vehicle_tail : vehicle_base
|
||||
{
|
||||
static constexpr auto VehicleThingType = vehicle_thing_type::vehicle_6;
|
||||
uint8_t pad_20;
|
||||
company_id_t owner; // 0x21
|
||||
uint8_t pad_22[0x26 - 0x22];
|
||||
thing_id_t head; // 0x26
|
||||
uint32_t var_28;
|
||||
uint16_t var_2C;
|
||||
uint16_t var_2E;
|
||||
int16_t tile_x; // 0x30
|
||||
int16_t tile_y; // 0x32
|
||||
uint8_t tile_base_z; // 0x34
|
||||
uint8_t track_type; // 0x35 field same in all vehicles
|
||||
uint16_t var_36; // 0x36 field same in all vehicles
|
||||
uint8_t var_38;
|
||||
uint8_t pad_39; // 0x39
|
||||
thing_id_t next_car_id; // 0x3A
|
||||
uint8_t pad_3C[0x42 - 0x3C]; // 0x3C
|
||||
TransportMode mode; // 0x42 field same in all vehicles
|
||||
uint8_t pad_43;
|
||||
uint8_t var_44;
|
||||
uint8_t pad_45[0x48 - 0x45];
|
||||
int16_t var_48;
|
||||
uint16_t var_4A;
|
||||
};
|
||||
static_assert(sizeof(vehicle_tail) == 0x4C); // Can't use offset_of change this to last field if more found
|
||||
|
||||
#pragma pack(pop)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace openloco
|
||||
{
|
||||
using coord_t = int16_t;
|
||||
using company_id_t = uint8_t;
|
||||
using currency32_t = int32_t;
|
||||
using station_id_t = uint16_t;
|
||||
using industry_id_t = uint16_t;
|
||||
|
|
|
@ -281,7 +281,7 @@ namespace openloco::ui::build_vehicle
|
|||
if (!tabMode)
|
||||
{
|
||||
auto veh = thingmgr::get<openloco::vehicle>(vehicle);
|
||||
tab += veh->var_5E;
|
||||
tab += static_cast<uint8_t>(veh->vehicleType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -309,7 +309,7 @@ namespace openloco::ui::build_vehicle
|
|||
{
|
||||
_buildTargetVehicle = vehicle;
|
||||
auto veh = thingmgr::get<openloco::vehicle>(vehicle);
|
||||
window->current_tab = veh->var_5E;
|
||||
window->current_tab = static_cast<uint8_t>(veh->vehicleType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -391,19 +391,6 @@ namespace openloco::ui::build_vehicle
|
|||
});
|
||||
}
|
||||
|
||||
static bool sub_4B8FA2(openloco::vehicle* esi, uint32_t eax)
|
||||
{
|
||||
registers regs;
|
||||
regs.eax = eax;
|
||||
regs.esi = (uintptr_t)esi;
|
||||
if (esi == nullptr)
|
||||
{
|
||||
regs.esi = -1;
|
||||
}
|
||||
|
||||
return call(0x004B8FA2, regs) & (X86_FLAG_CARRY << 8);
|
||||
}
|
||||
|
||||
/* 0x4B9165
|
||||
* Works out which vehicles are able to be built for this vehicle_type or vehicle
|
||||
*/
|
||||
|
@ -443,7 +430,8 @@ namespace openloco::ui::build_vehicle
|
|||
|
||||
if (vehicle)
|
||||
{
|
||||
if (sub_4B8FA2(vehicle, vehicleObjIndex))
|
||||
auto* const head = vehicle->as_vehicle_head();
|
||||
if (head && !head->isVehicleTypeCompatible(vehicleObjIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -943,14 +931,14 @@ namespace openloco::ui::build_vehicle
|
|||
auto buffer = const_cast<char*>(stringmgr::get_string(string_ids::buffer_1250));
|
||||
|
||||
{
|
||||
auto cost = (vehicleObj->cost_fact * currencyMultiplicationFactor[vehicleObj->cost_ind]) / 64;
|
||||
auto cost = (vehicleObj->cost_factor * currencyMultiplicationFactor[vehicleObj->cost_index]) / 64;
|
||||
FormatArguments args{};
|
||||
args.push(cost);
|
||||
buffer = stringmgr::format_string(buffer, string_ids::stats_cost, &args);
|
||||
}
|
||||
|
||||
{
|
||||
auto runningCost = (vehicleObj->run_cost_fact * currencyMultiplicationFactor[vehicleObj->run_cost_ind]) / 1024;
|
||||
auto runningCost = (vehicleObj->run_cost_factor * currencyMultiplicationFactor[vehicleObj->run_cost_index]) / 1024;
|
||||
FormatArguments args{};
|
||||
args.push(runningCost);
|
||||
buffer = stringmgr::format_string(buffer, string_ids::stats_running_cost, &args);
|
||||
|
|
|
@ -410,10 +410,10 @@ namespace openloco::ui::windows::toolbar_top::game
|
|||
if (v->owner != player_company_id)
|
||||
continue;
|
||||
|
||||
if ((v->var_38 & (1 << 4)) != 0)
|
||||
if ((v->var_38 & things::vehicle::flags_38::unk_4) != 0)
|
||||
continue;
|
||||
|
||||
vehicle_counts[v->var_5E]++;
|
||||
vehicle_counts[static_cast<uint8_t>(v->vehicleType)]++;
|
||||
}
|
||||
|
||||
uint8_t ddIndex = 0;
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace openloco::ui::vehicle
|
|||
|
||||
auto vehicle = thingmgr::get<openloco::vehicle>(w->number);
|
||||
|
||||
if (vehicle->tile_x != -1 && (vehicle->var_38 & (1 << 4)) == 0)
|
||||
if (vehicle->tile_x != -1 && (vehicle->var_38 & things::vehicle::flags_38::unk_4) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ namespace openloco::ui::vehicle
|
|||
}
|
||||
|
||||
auto vehicle = thingmgr::get<openloco::vehicle>(w->number);
|
||||
if (vehicle->tile_x != -1 && (vehicle->var_38 & (1 << 4)) == 0)
|
||||
if (vehicle->tile_x != -1 && (vehicle->var_38 & things::vehicle::flags_38::unk_4) == 0)
|
||||
return;
|
||||
|
||||
if (!WindowManager::isInFrontAlt(w))
|
||||
|
|
Loading…
Reference in New Issue