Merge branch 'tracerestrict-sx' into jgrpp
# Conflicts: # src/command.cpp # src/group_gui.cpp # src/lang/english.txt # src/saveload/extended_ver_sl.cpp # src/settings_gui.cpp # src/tracerestrict.cpp # src/tracerestrict.h # src/tracerestrict_gui.cpp # src/vehicle_gui.cpp # src/vehicle_gui_base.h # src/vehiclelist.cpp # src/window_type.h
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "order_base.h"
|
||||
#include "cargotype.h"
|
||||
#include "group.h"
|
||||
#include "string_func.h"
|
||||
#include "pathfinder/yapf/yapf_cache.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
@@ -60,6 +61,9 @@
|
||||
TraceRestrictProgramPool _tracerestrictprogram_pool("TraceRestrictProgram");
|
||||
INSTANTIATE_POOL_METHODS(TraceRestrictProgram)
|
||||
|
||||
TraceRestrictSlotPool _tracerestrictslot_pool("TraceRestrictSlot");
|
||||
INSTANTIATE_POOL_METHODS(TraceRestrictSlot)
|
||||
|
||||
/**
|
||||
* TraceRestrictRefId --> TraceRestrictProgramID (Pool ID) mapping
|
||||
* The indirection is mainly to enable shared programs
|
||||
@@ -350,6 +354,12 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
|
||||
break;
|
||||
}
|
||||
|
||||
case TRIT_COND_SLOT: {
|
||||
const TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(GetTraceRestrictValue(item));
|
||||
result = TestBinaryConditionCommon(item, slot != NULL && slot->IsOccupant(v->index));
|
||||
break;
|
||||
}
|
||||
|
||||
case TRIT_COND_PHYS_PROP: {
|
||||
switch (static_cast<TraceRestrictPhysPropCondAuxField>(GetTraceRestrictAuxField(item))) {
|
||||
case TRPPCAF_WEIGHT:
|
||||
@@ -443,6 +453,44 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
|
||||
}
|
||||
break;
|
||||
|
||||
case TRIT_WAIT_AT_PBS:
|
||||
if (GetTraceRestrictValue(item)) {
|
||||
out.flags &= ~TRPRF_WAIT_AT_PBS;
|
||||
} else {
|
||||
out.flags |= TRPRF_WAIT_AT_PBS;
|
||||
}
|
||||
break;
|
||||
|
||||
case TRIT_SLOT: {
|
||||
if (!input.permitted_slot_operations) break;
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(GetTraceRestrictValue(item));
|
||||
if (slot == NULL) break;
|
||||
switch (static_cast<TraceRestrictSlotCondOpField>(GetTraceRestrictCondOp(item))) {
|
||||
case TRSCOF_ACQUIRE_WAIT:
|
||||
if (input.permitted_slot_operations & TRPISP_ACQUIRE) {
|
||||
if (!slot->Occupy(v->index)) out.flags |= TRPRF_WAIT_AT_PBS;
|
||||
}
|
||||
break;
|
||||
|
||||
case TRSCOF_ACQUIRE_TRY:
|
||||
if (input.permitted_slot_operations & TRPISP_ACQUIRE) slot->Occupy(v->index);
|
||||
break;
|
||||
|
||||
case TRSCOF_RELEASE_BACK:
|
||||
if (input.permitted_slot_operations & TRPISP_RELEASE_BACK) slot->Vacate(v->index);
|
||||
break;
|
||||
|
||||
case TRSCOF_RELEASE_FRONT:
|
||||
if (input.permitted_slot_operations & TRPISP_RELEASE_FRONT) slot->Vacate(v->index);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
@@ -530,6 +578,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
|
||||
case TRIT_COND_ENTRY_DIRECTION:
|
||||
case TRIT_COND_PBS_ENTRY_SIGNAL:
|
||||
case TRIT_COND_TRAIN_GROUP:
|
||||
case TRIT_COND_SLOT:
|
||||
case TRIT_COND_PHYS_PROP:
|
||||
case TRIT_COND_PHYS_RATIO:
|
||||
case TRIT_COND_TRAIN_OWNER:
|
||||
@@ -553,12 +602,40 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
|
||||
actions_used_flags |= TRPAUF_LONG_RESERVE;
|
||||
break;
|
||||
|
||||
case TRIT_WAIT_AT_PBS:
|
||||
actions_used_flags |= TRPAUF_WAIT_AT_PBS;
|
||||
break;
|
||||
|
||||
case TRIT_SLOT:
|
||||
switch (static_cast<TraceRestrictSlotCondOpField>(GetTraceRestrictCondOp(item))) {
|
||||
case TRSCOF_ACQUIRE_WAIT:
|
||||
actions_used_flags |= TRPAUF_SLOT_ACQUIRE | TRPAUF_WAIT_AT_PBS;
|
||||
break;
|
||||
|
||||
case TRSCOF_ACQUIRE_TRY:
|
||||
actions_used_flags |= TRPAUF_SLOT_ACQUIRE;
|
||||
break;
|
||||
|
||||
case TRSCOF_RELEASE_BACK:
|
||||
actions_used_flags |= TRPAUF_SLOT_RELEASE_BACK;
|
||||
break;
|
||||
|
||||
case TRSCOF_RELEASE_FRONT:
|
||||
actions_used_flags |= TRPAUF_SLOT_RELEASE_FRONT;
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!condstack.empty()) {
|
||||
if (!condstack.empty()) {
|
||||
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK);
|
||||
}
|
||||
return CommandCost();
|
||||
@@ -611,6 +688,7 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp
|
||||
case TRVT_FORCE:
|
||||
case TRVT_POWER_WEIGHT_RATIO:
|
||||
case TRVT_FORCE_WEIGHT_RATIO:
|
||||
case TRVT_WAIT_AT_PBS:
|
||||
SetTraceRestrictValue(item, 0);
|
||||
if (!IsTraceRestrictTypeAuxSubtype(GetTraceRestrictType(item))) {
|
||||
SetTraceRestrictAuxField(item, 0);
|
||||
@@ -648,6 +726,11 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp
|
||||
SetTraceRestrictAuxField(item, 0);
|
||||
break;
|
||||
|
||||
case TRVT_SLOT_INDEX:
|
||||
SetTraceRestrictValue(item, INVALID_TRACE_RESTRICT_SLOT_ID);
|
||||
SetTraceRestrictAuxField(item, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
break;
|
||||
@@ -1320,3 +1403,307 @@ void TraceRestrictUpdateCompanyID(CompanyID old_company, CompanyID new_company)
|
||||
// update windows
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
|
||||
}
|
||||
|
||||
static std::unordered_multimap<VehicleID, TraceRestrictSlotID> slot_vehicle_index;
|
||||
|
||||
/**
|
||||
* Add vehicle ID to occupants if possible and not already an occupant
|
||||
* @param id Vehicle ID
|
||||
* @param force Add the vehicle even if the slot is at/over capacity
|
||||
* @return whether vehicle ID is now an occupant
|
||||
*/
|
||||
bool TraceRestrictSlot::Occupy(VehicleID id, bool force)
|
||||
{
|
||||
if (this->IsOccupant(id)) return true;
|
||||
if (this->occupants.size() >= this->max_occupancy && !force) return false;
|
||||
this->occupants.push_back(id);
|
||||
slot_vehicle_index.emplace(id, this->index);
|
||||
SetBit(Train::Get(id)->flags, VRF_HAVE_SLOT);
|
||||
SetWindowDirty(WC_VEHICLE_DETAILS, id);
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove vehicle ID from occupants
|
||||
* @param id Vehicle ID
|
||||
*/
|
||||
void TraceRestrictSlot::Vacate(VehicleID id)
|
||||
{
|
||||
if (container_unordered_remove(this->occupants, id)) {
|
||||
this->DeIndex(id);
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove all occupants */
|
||||
void TraceRestrictSlot::Clear()
|
||||
{
|
||||
for (VehicleID id : this->occupants) {
|
||||
this->DeIndex(id);
|
||||
}
|
||||
this->occupants.clear();
|
||||
}
|
||||
|
||||
void TraceRestrictSlot::DeIndex(VehicleID id)
|
||||
{
|
||||
auto range = slot_vehicle_index.equal_range(id);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == this->index) {
|
||||
auto next = slot_vehicle_index.erase(it);
|
||||
if (it == range.first && next == range.second) {
|
||||
/* Only one item, which we've just erased, clear the vehicle flag */
|
||||
ClrBit(Train::Get(id)->flags, VRF_HAVE_SLOT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetWindowDirty(WC_VEHICLE_DETAILS, id);
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
/** Rebuild slot vehicle index after loading */
|
||||
void TraceRestrictSlot::RebuildVehicleIndex()
|
||||
{
|
||||
slot_vehicle_index.clear();
|
||||
const TraceRestrictSlot *slot;
|
||||
FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
|
||||
for (VehicleID id : slot->occupants) {
|
||||
slot_vehicle_index.emplace(id, slot->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Slot pool is about to be cleared */
|
||||
void TraceRestrictSlot::PreCleanPool()
|
||||
{
|
||||
slot_vehicle_index.clear();
|
||||
}
|
||||
|
||||
/** Remove vehicle ID from all slot occupants */
|
||||
void TraceRestrictRemoveVehicleFromAllSlots(VehicleID id)
|
||||
{
|
||||
auto range = slot_vehicle_index.equal_range(id);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::Get(it->second);
|
||||
container_unordered_remove(slot->occupants, id);
|
||||
}
|
||||
slot_vehicle_index.erase(range.first, range.second);
|
||||
if (range.first != range.second) InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
/** Replace all instance of a vehicle ID with another, in all slot occupants */
|
||||
void TraceRestrictTransferVehicleOccupantInAllSlots(VehicleID from, VehicleID to)
|
||||
{
|
||||
auto range = slot_vehicle_index.equal_range(from);
|
||||
std::vector<TraceRestrictSlotID> slots;
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
slots.push_back(it->second);
|
||||
}
|
||||
slot_vehicle_index.erase(range.first, range.second);
|
||||
for (TraceRestrictSlotID slot_id : slots) {
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::Get(slot_id);
|
||||
for (VehicleID &id : slot->occupants) {
|
||||
if (id == from) {
|
||||
id = to;
|
||||
slot_vehicle_index.emplace(to, slot_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!slots.empty()) InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
/** Get list of slots occupied by a vehicle ID */
|
||||
void TraceRestrictGetVehicleSlots(VehicleID id, std::vector<TraceRestrictSlotID> &out)
|
||||
{
|
||||
auto range = slot_vehicle_index.equal_range(id);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
out.push_back(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a slot is about to be deleted
|
||||
* Scan program pool and change any references to it to the invalid group ID, to avoid dangling references
|
||||
*/
|
||||
void TraceRestrictRemoveSlotID(TraceRestrictSlotID index)
|
||||
{
|
||||
TraceRestrictProgram *prog;
|
||||
|
||||
FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) {
|
||||
for (size_t i = 0; i < prog->items.size(); i++) {
|
||||
TraceRestrictItem &item = prog->items[i]; // note this is a reference,
|
||||
if ((GetTraceRestrictType(item) == TRIT_SLOT || GetTraceRestrictType(item) == TRIT_COND_SLOT) && GetTraceRestrictValue(item) == index) {
|
||||
SetTraceRestrictValueDefault(item, TRVT_SLOT_INDEX); // this updates the instruction in-place
|
||||
}
|
||||
if (IsTraceRestrictDoubleItem(item)) i++;
|
||||
}
|
||||
}
|
||||
|
||||
// update windows
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
|
||||
}
|
||||
|
||||
static bool IsUniqueSlotName(const char *name)
|
||||
{
|
||||
const TraceRestrictSlot *slot;
|
||||
FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
|
||||
if (slot->name == name) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new slot.
|
||||
* @param tile unused
|
||||
* @param flags type of operation
|
||||
* @param p1 unused
|
||||
* @param p2 unused
|
||||
* @param text new slot name
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdCreateTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
if (!TraceRestrictSlot::CanAllocateItem()) return CMD_ERROR;
|
||||
if (StrEmpty(text)) return CMD_ERROR;
|
||||
|
||||
size_t length = Utf8StringLength(text);
|
||||
if (length <= 0) return CMD_ERROR;
|
||||
if (length >= MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS) return CMD_ERROR;
|
||||
if (!IsUniqueSlotName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
TraceRestrictSlot *slot = new TraceRestrictSlot(_current_company);
|
||||
slot->name = text;
|
||||
|
||||
// update windows
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a slot.
|
||||
* @param tile unused
|
||||
* @param flags type of operation
|
||||
* @param p1 index of array group
|
||||
* - p1 bit 0-15 : Slot ID
|
||||
* @param p2 unused
|
||||
* @param text unused
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdDeleteTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(p1);
|
||||
if (slot == NULL || slot->owner != _current_company) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* notify tracerestrict that group is about to be deleted */
|
||||
TraceRestrictRemoveSlotID(slot->index);
|
||||
|
||||
delete slot;
|
||||
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter a slot
|
||||
* @param tile unused
|
||||
* @param flags type of operation
|
||||
* @param p1 index of array group
|
||||
* - p1 bit 0-15 : GroupID
|
||||
* - p1 bit 16: 0 - Rename grouop
|
||||
* 1 - Change max occupancy
|
||||
* @param p2 new max occupancy
|
||||
* @param text the new name
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdAlterTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(GB(p1, 0, 16));
|
||||
if (slot == NULL || slot->owner != _current_company) return CMD_ERROR;
|
||||
|
||||
if (!HasBit(p1, 16)) {
|
||||
/* Rename slot */
|
||||
|
||||
if (StrEmpty(text)) return CMD_ERROR;
|
||||
size_t length = Utf8StringLength(text);
|
||||
if (length <= 0) return CMD_ERROR;
|
||||
if (length >= MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS) return CMD_ERROR;
|
||||
if (!IsUniqueSlotName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
slot->name = text;
|
||||
}
|
||||
} else {
|
||||
/* Change max occupancy */
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
slot->max_occupancy = p2;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
// update windows
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
|
||||
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vehicle to a slot
|
||||
* @param tile unused
|
||||
* @param flags type of operation
|
||||
* @param p1 index of array group
|
||||
* - p1 bit 0-15 : GroupID
|
||||
* @param p2 index of vehicle
|
||||
* - p2 bit 0-19 : VehicleID
|
||||
* @param text unused
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdAddVehicleTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(p1);
|
||||
Vehicle *v = Vehicle::GetIfValid(p2);
|
||||
if (slot == NULL || slot->owner != _current_company) return CMD_ERROR;
|
||||
if (v == NULL || v->owner != _current_company) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
slot->Occupy(v->index, true);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a vehicle from a slot
|
||||
* @param tile unused
|
||||
* @param flags type of operation
|
||||
* @param p1 index of array group
|
||||
* - p1 bit 0-15 : GroupID
|
||||
* @param p2 index of vehicle
|
||||
* - p2 bit 0-19 : VehicleID
|
||||
* @param text unused
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdRemoveVehicleTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(p1);
|
||||
Vehicle *v = Vehicle::GetIfValid(p2);
|
||||
if (slot == NULL || slot->owner != _current_company) return CMD_ERROR;
|
||||
if (v == NULL) return CMD_ERROR; // permit removing vehicles of other owners from your own slot
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
slot->Vacate(v->index);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
Reference in New Issue
Block a user