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:
Jonathan G Rennison
2017-04-05 19:00:24 +01:00
24 changed files with 1719 additions and 40 deletions

View File

@@ -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();
}