(svn r9874) -Feature: advanced vehicle lists a.k.a. group interface. Now you can make groups of vehicles and perform all kinds of tasks on that given group. Original code by nycom and graphics by skidd13.

This commit is contained in:
rubidium 2007-05-19 09:40:18 +00:00
parent 816d31f0aa
commit 7d4be11516
39 changed files with 1678 additions and 67 deletions

BIN
bin/data/group.grf Normal file

Binary file not shown.

View File

@ -452,6 +452,9 @@
<File
RelativePath=".\..\src\gfxinit.h">
</File>
<File
RelativePath=".\..\src\group.h">
</File>
<File
RelativePath=".\..\src\gui.h">
</File>
@ -702,6 +705,9 @@
<File
RelativePath=".\..\src\graph_gui.cpp">
</File>
<File
RelativePath=".\..\src\group_gui.cpp">
</File>
<File
RelativePath=".\..\src\industry_gui.cpp">
</File>
@ -790,6 +796,9 @@
<File
RelativePath=".\..\src\dummy_land.cpp">
</File>
<File
RelativePath=".\..\src\group_cmd.cpp">
</File>
<File
RelativePath=".\..\src\industry_cmd.cpp">
</File>

View File

@ -831,6 +831,10 @@
RelativePath=".\..\src\gfxinit.h"
>
</File>
<File
RelativePath=".\..\src\group.h"
>
</File>
<File
RelativePath=".\..\src\gui.h"
>
@ -1163,6 +1167,10 @@
RelativePath=".\..\src\graph_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\group_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\industry_gui.cpp"
>
@ -1279,6 +1287,10 @@
RelativePath=".\..\src\dummy_land.cpp"
>
</File>
<File
RelativePath=".\..\src\group_cmd.cpp"
>
</File>
<File
RelativePath=".\..\src\industry_cmd.cpp"
>

View File

@ -118,6 +118,7 @@ functions.h
genworld.h
gfx.h
gfxinit.h
group.h
gui.h
hal.h
heightmap.h
@ -202,6 +203,7 @@ dock_gui.cpp
engine_gui.cpp
genworld_gui.cpp
graph_gui.cpp
group_gui.cpp
industry_gui.cpp
intro_gui.cpp
main_gui.cpp
@ -232,6 +234,7 @@ aircraft_cmd.cpp
clear_cmd.cpp
disaster_cmd.cpp
dummy_land.cpp
group_cmd.cpp
industry_cmd.cpp
misc_cmd.cpp
order_cmd.cpp

View File

@ -1684,7 +1684,7 @@ static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *
/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
* unless it is due for renewal but the engine is no longer available */
if (v->owner == _local_player && (
EngineHasReplacementForPlayer(p, v->engine_type) ||
EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
)) {
@ -1742,7 +1742,7 @@ static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
/* only the vehicle owner needs to calculate the rest (locally) */
const Player* p = GetPlayer(v->owner);
if (EngineHasReplacementForPlayer(p, v->engine_type) ||
if (EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
/* send the aircraft to the hangar at next airport */
_current_player = _local_player;

View File

@ -16,6 +16,7 @@
#include "train.h"
#include "aircraft.h"
#include "cargotype.h"
#include "group.h"
/*
@ -136,8 +137,17 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost)
char vehicle_name[32];
CargoID replacement_cargo_type;
new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type);
if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type;
/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
* If not, chek if an global auto replacement is defined */
new_engine_type = (IsValidGroupID(old_v->group_id) && GetGroup(old_v->group_id)->replace_protection) ?
INVALID_ENGINE :
EngineReplacementForPlayer(p, old_v->engine_type, DEFAULT_GROUP);
/* If we don't set new_egnine_type previously, we try to check if an autoreplacement was defined
* for the group and the engine_type of the vehicle */
if (new_engine_type == INVALID_ENGINE && !IsDefaultGroupID(old_v->group_id)) {
new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type, old_v->group_id);
}
replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type);
@ -165,6 +175,7 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost)
new_v = GetVehicle(_new_vehicle_id);
*w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is
new_v->group_id = old_v->group_id;
/* refit if needed */
if (replacement_cargo_type != CT_NO_REFIT) {
if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, GetCmdRefitVeh(new_v)))) {
@ -194,6 +205,7 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost)
new_v->profit_this_year = old_v->profit_this_year;
new_v->profit_last_year = old_v->profit_last_year;
new_v->service_interval = old_v->service_interval;
new_v->group_id = old_v->group_id;
new_front = true;
new_v->unitnumber = old_v->unitnumber; // use the same unit number
new_v->dest_tile = old_v->dest_tile;
@ -211,6 +223,10 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost)
if (temp_v != NULL) {
DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
}
} else if (!IsDefaultGroupID(old_v->group_id) && IsValidGroupID(old_v->group_id)) {
/* Increase the new num engines of the group for the ships, aircraft, and road vehicles
The old new num engine is decrease in the destroyvehicle function */
GetGroup(old_v->group_id)->num_engines[new_v->engine_type]++;
}
}
/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
@ -305,8 +321,17 @@ int32 MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs)
if (!p->engine_renew ||
w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old
w->max_age == 0) { // rail cars got a max age of 0
if (!EngineHasReplacementForPlayer(p, w->engine_type)) // updates to a new model
/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
If not, chek if an global auto remplacement is defined */
if (IsValidGroupID(w->group_id)) {
if (!EngineHasReplacementForPlayer(p, w->engine_type, w->group_id) && (
GetGroup(w->group_id)->replace_protection ||
!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP))) {
continue;
}
} else if (!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP)) {
continue;
}
}
/* Now replace the vehicle */

View File

@ -14,6 +14,7 @@
#include "variables.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "group.h"
static RailType _railtype_selected_in_replace_gui;
@ -150,8 +151,11 @@ static void GenerateReplaceVehList(Window *w, bool draw_left)
if (type == VEH_TRAIN && !GenerateReplaceRailList(e, draw_left, WP(w, replaceveh_d).wagon_btnstate)) continue; // special rules for trains
if (draw_left) {
const GroupID selected_group = WP(w, replaceveh_d).sel_group;
const uint num_engines = IsDefaultGroupID(selected_group) ? p->num_engines[e] : GetGroup(selected_group)->num_engines[e];
/* Skip drawing the engines we don't have any of and haven't set for replacement */
if (p->num_engines[e] == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e) == INVALID_ENGINE) continue;
if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e, selected_group) == INVALID_ENGINE) continue;
} else {
/* This is for engines we can replace to and they should depend on what we selected to replace from */
if (!IsEngineBuildable(e, type, _local_player)) continue; // we need to be able to build the engine
@ -202,7 +206,7 @@ static void GenerateLists(Window *w)
}
void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count);
void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
{
@ -231,6 +235,7 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
Player *p = GetPlayer(_local_player);
EngineID selected_id[2];
const GroupID selected_group = WP(w,replaceveh_d).sel_group;
selected_id[0] = WP(w, replaceveh_d).sel_engine[0];
selected_id[1] = WP(w, replaceveh_d).sel_engine[1];
@ -242,15 +247,15 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
SetWindowWidgetDisabledState(w, 4,
selected_id[0] == INVALID_ENGINE ||
selected_id[1] == INVALID_ENGINE ||
EngineReplacementForPlayer(p, selected_id[1]) != INVALID_ENGINE ||
EngineReplacementForPlayer(p, selected_id[0]) == selected_id[1]);
EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE ||
EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]);
/* Disable the "Stop Replacing" button if:
* The left list (existing vehicle) is empty
* or The selected vehicle has no replacement set up */
SetWindowWidgetDisabledState(w, 6,
selected_id[0] == INVALID_ENGINE ||
!EngineHasReplacementForPlayer(p, selected_id[0]));
!EngineHasReplacementForPlayer(p, selected_id[0], selected_group));
/* now the actual drawing of the window itself takes place */
SetDParam(0, _vehicle_type_names[w->window_number]);
@ -277,10 +282,10 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
/* sets up the string for the vehicle that is being replaced to */
if (selected_id[0] != INVALID_ENGINE) {
if (!EngineHasReplacementForPlayer(p, selected_id[0])) {
if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) {
SetDParam(0, STR_NOT_REPLACING);
} else {
SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0])));
SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0], selected_group)));
}
} else {
SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
@ -296,7 +301,7 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
EngineID end = min((i == 0 ? w->vscroll.cap : w->vscroll2.cap) + start, EngList_Count(&list));
/* Do the actual drawing */
DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0);
DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0, selected_group);
/* Also draw the details if an engine is selected */
if (WP(w, replaceveh_d).sel_engine[i] != INVALID_ENGINE) {
@ -328,12 +333,12 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
case 4: { /* Start replacing */
EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
EngineID veh_to = WP(w, replaceveh_d).sel_engine[1];
DoCommandP(0, 3, veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
} break;
case 6: { /* Stop replacing */
EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
DoCommandP(0, 3, veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
} break;
case 7:
@ -509,4 +514,37 @@ void ShowReplaceVehicleWindow(VehicleType vehicletype)
w->caption_color = _local_player;
w->vscroll2.cap = w->vscroll.cap; // these two are always the same
WP(w, replaceveh_d).sel_group = DEFAULT_GROUP;
}
void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
{
Window *w;
DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
switch (vehicletype) {
default: NOT_REACHED();
case VEH_TRAIN:
w = AllocateWindowDescFront(&_replace_rail_vehicle_desc, vehicletype);
w->vscroll.cap = 8;
w->resize.step_height = 14;
WP(w, replaceveh_d).wagon_btnstate = true;
break;
case VEH_ROAD:
w = AllocateWindowDescFront(&_replace_road_vehicle_desc, vehicletype);
w->vscroll.cap = 8;
w->resize.step_height = 14;
break;
case VEH_SHIP:
case VEH_AIRCRAFT:
w = AllocateWindowDescFront(&_replace_ship_aircraft_vehicle_desc, vehicletype);
w->vscroll.cap = 4;
w->resize.step_height = 24;
break;
}
w->caption_color = _local_player;
WP(w, replaceveh_d).sel_group = id_g;
w->vscroll2.cap = w->vscroll.cap; // these two are always the same
}

View File

@ -27,6 +27,7 @@
#include "date.h"
#include "strings.h"
#include "cargotype.h"
#include "group.h"
enum BuildVehicleWidgets {
@ -759,7 +760,7 @@ static void DrawVehicleEngine(byte type, int x, int y, EngineID engine, SpriteID
* @param selected_id what engine to highlight as selected, if any
* @param show_count Display the number of vehicles (used by autoreplace)
*/
void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count)
void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
{
byte step_size = GetVehicleListHeight(type);
byte x_offset = 0;
@ -795,11 +796,12 @@ void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, u
for (; min < max; min++, y += step_size) {
const EngineID engine = eng_list[min];
const uint num_engines = IsDefaultGroupID(selected_group) ? p->num_engines[engine] : GetGroup(selected_group)->num_engines[engine];
DrawString(x + x_offset, y, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && p->num_engines[engine] == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
if (show_count) {
SetDParam(0, p->num_engines[engine]);
SetDParam(0, num_engines);
DrawStringRightAligned(213, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, 0);
}
}
@ -833,7 +835,7 @@ static void DrawBuildVehicleWindow(Window *w)
SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles
DrawWindowWidgets(w);
DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false);
DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false, DEFAULT_GROUP);
if (bv->sel_engine != INVALID_ENGINE) {
const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];

View File

@ -168,6 +168,12 @@ DEF_COMMAND(CmdMassStartStopVehicle);
DEF_COMMAND(CmdDepotSellAllVehicles);
DEF_COMMAND(CmdDepotMassAutoReplace);
DEF_COMMAND(CmdCreateGroup);
DEF_COMMAND(CmdRenameGroup);
DEF_COMMAND(CmdDeleteGroup);
DEF_COMMAND(CmdAddVehicleGroup);
DEF_COMMAND(CmdAddSharedVehicleGroup);
DEF_COMMAND(CmdRemoveAllVehiclesGroup);
/* The master command table */
static const Command _command_proc_table[] = {
{CmdBuildRailroadTrack, 0}, /* 0 */
@ -313,6 +319,12 @@ static const Command _command_proc_table[] = {
{CmdMassStartStopVehicle, 0}, /* 117 */
{CmdDepotSellAllVehicles, 0}, /* 118 */
{CmdDepotMassAutoReplace, 0}, /* 119 */
{CmdCreateGroup, 0}, /* 120 */
{CmdDeleteGroup, 0}, /* 121 */
{CmdRenameGroup, 0}, /* 122 */
{CmdAddVehicleGroup, 0}, /* 123 */
{CmdAddSharedVehicleGroup, 0}, /* 124 */
{CmdRemoveAllVehiclesGroup, 0}, /* 125 */
};
/* This function range-checks a cmd, and checks if the cmd is not NULL */

View File

@ -143,6 +143,12 @@ enum {
CMD_MASS_START_STOP = 117,
CMD_DEPOT_SELL_ALL_VEHICLES = 118,
CMD_DEPOT_MASS_AUTOREPLACE = 119,
CMD_CREATE_GROUP = 120,
CMD_DELETE_GROUP = 121,
CMD_RENAME_GROUP = 122,
CMD_ADD_VEHICLE_GROUP = 123,
CMD_ADD_SHARED_VEHICLE_GROUP = 124,
CMD_REMOVE_ALL_VEHICLES_GROUP = 125,
};
enum {

View File

@ -38,6 +38,7 @@
#include "date.h"
#include "cargotype.h"
#include "player_face.h"
#include "group.h"
/* Score info */
const ScoreInfo _score_info[] = {
@ -359,6 +360,7 @@ void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
DeleteVehicle(v);
} else {
v->owner = new_player;
v->group_id = DEFAULT_GROUP;
if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
switch (v->type) {
case VEH_TRAIN: if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;

View File

@ -20,6 +20,7 @@
#include "newgrf_cargo.h"
#include "date.h"
#include "table/engines.h"
#include "group.h"
EngineInfo _engine_info[TOTAL_NUM_ENGINES];
RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
@ -481,6 +482,7 @@ static EngineRenew *AllocateEngineRenew()
er->to = INVALID_ENGINE;
er->next = NULL;
er->group_id = DEFAULT_GROUP;
return er;
}
@ -493,12 +495,12 @@ static EngineRenew *AllocateEngineRenew()
/**
* Retrieves the EngineRenew that specifies the replacement of the given
* engine type from the given renewlist */
static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
{
EngineRenew *er = (EngineRenew *)erl;
while (er) {
if (er->from == engine) return er;
if (er->from == engine && er->group_id == group) return er;
er = er->next;
}
return NULL;
@ -517,18 +519,18 @@ void RemoveAllEngineReplacement(EngineRenewList *erl)
*erl = NULL; // Empty list
}
EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
{
const EngineRenew *er = GetEngineReplacement(erl, engine);
const EngineRenew *er = GetEngineReplacement(erl, engine, group);
return er == NULL ? INVALID_ENGINE : er->to;
}
int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags)
{
EngineRenew *er;
/* Check if the old vehicle is already in the list */
er = GetEngineReplacement(*erl, old_engine);
er = GetEngineReplacement(*erl, old_engine, group);
if (er != NULL) {
if (flags & DC_EXEC) er->to = new_engine;
return 0;
@ -540,6 +542,7 @@ int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID n
if (flags & DC_EXEC) {
er->from = old_engine;
er->to = new_engine;
er->group_id = group;
/* Insert before the first element */
er->next = (EngineRenew *)(*erl);
@ -549,14 +552,14 @@ int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID n
return 0;
}
int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, uint32 flags)
{
EngineRenew *er = (EngineRenew *)(*erl);
EngineRenew *prev = NULL;
while (er)
{
if (er->from == engine) {
if (er->from == engine && er->group_id == group) {
if (flags & DC_EXEC) {
if (prev == NULL) { // First element
/* The second becomes the new first element */
@ -577,11 +580,11 @@ int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flag
}
static const SaveLoad _engine_renew_desc[] = {
SLE_VAR(EngineRenew, from, SLE_UINT16),
SLE_VAR(EngineRenew, to, SLE_UINT16),
SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
SLE_VAR(EngineRenew, from, SLE_UINT16),
SLE_VAR(EngineRenew, to, SLE_UINT16),
SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
SLE_END()
};
@ -607,6 +610,9 @@ static void Load_ERNW()
er = GetEngineRenew(index);
SlObject(er, _engine_renew_desc);
/* Advanced vehicle lists got added */
if (CheckSavegameVersion(60)) er->group_id = DEFAULT_GROUP;
}
}

View File

@ -272,6 +272,7 @@ struct EngineRenew {
EngineID from;
EngineID to;
EngineRenew *next;
GroupID group_id;
};
/**
@ -317,7 +318,7 @@ void RemoveAllEngineReplacement(EngineRenewList* erl);
* @return The engine type to replace with, or INVALID_ENGINE if no
* replacement is in the list.
*/
EngineID EngineReplacement(EngineRenewList erl, EngineID engine);
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group);
/**
* Add an engine replacement to the given renewlist.
@ -327,7 +328,7 @@ EngineID EngineReplacement(EngineRenewList erl, EngineID engine);
* @param flags The calling command flags.
* @return 0 on success, CMD_ERROR on failure.
*/
int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, uint32 flags);
int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags);
/**
* Remove an engine replacement from a given renewlist.
@ -336,7 +337,7 @@ int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID n
* @param flags The calling command flags.
* @return 0 on success, CMD_ERROR on failure.
*/
int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags);
int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, GroupID group, uint32 flags);
/** When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists
* @param type The type of engine

View File

@ -393,6 +393,9 @@ static void LoadSpriteTables()
assert(load_index == SPR_ROADSTOP_BASE);
load_index += LoadGrfFile("roadstops.grf", load_index, i++);
assert(load_index == SPR_GROUP_BASE);
load_index += LoadGrfFile("group.grf", load_index, i++);
/* Initialize the unicode to sprite mapping table */
InitializeUnicodeGlyphMap();

97
src/group.h Normal file
View File

@ -0,0 +1,97 @@
/* $Id$ */
/** @file group.h */
#ifndef GROUP_H
#define GROUP_H
#include "oldpool.h"
enum {
DEFAULT_GROUP = 0xFFFE,
INVALID_GROUP = 0xFFFF,
};
struct Group {
StringID string_id; ///< Group Name
uint16 num_vehicle; ///< Number of vehicles wich belong to the group
PlayerID owner; ///< Group Owner
GroupID index; ///< Array index
VehicleTypeByte vehicle_type; ///< Vehicle type of the group
bool replace_protection; ///< If set to true, the global autoreplace have no effect on the group
uint16 num_engines[TOTAL_NUM_ENGINES]; ///< Caches the number of engines of each type the player owns (no need to save this)
};
DECLARE_OLD_POOL(Group, Group, 5, 2047)
static inline bool IsValidGroup(const Group *g)
{
return g->string_id != STR_NULL;
}
static inline void DestroyGroup(Group *g)
{
DeleteName(g->string_id);
}
static inline void DeleteGroup(Group *g)
{
DestroyGroup(g);
g->string_id = STR_NULL;
}
static inline bool IsValidGroupID(GroupID index)
{
return index < GetGroupPoolSize() && IsValidGroup(GetGroup(index));
}
static inline bool IsDefaultGroupID(GroupID index)
{
return (index == DEFAULT_GROUP);
}
static inline StringID GetGroupName(GroupID index)
{
if (!IsValidGroupID(index)) return STR_NULL;
return GetGroup(index)->string_id;
}
#define FOR_ALL_GROUPS_FROM(g, start) for (g = GetGroup(start); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) if (IsValidGroup(g))
#define FOR_ALL_GROUPS(g) FOR_ALL_GROUPS_FROM(g, 0)
/**
* Get the current size of the GroupPool
*/
static inline uint GetGroupArraySize(void)
{
const Group *g;
uint num = 0;
FOR_ALL_GROUPS(g) num++;
return num;
}
static inline void IncreaseGroupNumVehicle(GroupID id_g)
{
if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle++;
}
static inline void DecreaseGroupNumVehicle(GroupID id_g)
{
if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle--;
}
void InitializeGroup();
void SetTrainGroupID(Vehicle *v, GroupID grp);
void UpdateTrainGroupID(Vehicle *v);
void RemoveVehicleFromGroup(const Vehicle *v);
void RemoveAllGroupsForPlayer(const Player *p);
#endif /* GROUP_H */

390
src/group_cmd.cpp Normal file
View File

@ -0,0 +1,390 @@
/* $Id$ */
/** @file group_cmd.cpp Handling of the engine groups */
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "player.h"
#include "table/strings.h"
#include "command.h"
#include "vehicle.h"
#include "saveload.h"
#include "debug.h"
#include "group.h"
#include "train.h"
#include "aircraft.h"
#include "string.h"
/**
* Update the num engines of a groupID. Decrease the old one and increase the new one
* @note called in SetTrainGroupID and UpdateTrainGroupID
* @param i EngineID we have to update
* @param old_g index of the old group
* @param new_g index of the new group
*/
static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
{
if (old_g != new_g) {
/* Decrease the num engines of EngineID i of the old group if it's not the default one */
if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
/* Increase the num engines of EngineID i of the new group if it's not the new one */
if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
}
}
/**
* Called if a new block is added to the group-pool
*/
static void GroupPoolNewBlock(uint start_item)
{
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
* TODO - This is just a temporary stage, this will be removed. */
for (Group *g = GetGroup(start_item); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) g->index = start_item++;
}
DEFINE_OLD_POOL(Group, Group, GroupPoolNewBlock, NULL)
static Group *AllocateGroup(void)
{
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
* TODO - This is just a temporary stage, this will be removed. */
for (Group *g = GetGroup(0); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) {
if (!IsValidGroup(g)) {
const GroupID index = g->index;
memset(g, 0, sizeof(*g));
g->index = index;
return g;
}
}
/* Check if we can add a block to the pool */
return (AddBlockToPool(&_Group_pool)) ? AllocateGroup() : NULL;
}
void InitializeGroup(void)
{
CleanPool(&_Group_pool);
AddBlockToPool(&_Group_pool);
}
/**
* Add a vehicle to a group
* @param tile unused
* @param p1 vehicle type
* @param p2 unused
*/
int32 CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
VehicleType vt = (VehicleType)p1;
if (!IsPlayerBuildableVehicleType(vt)) return CMD_ERROR;
Group *g = AllocateGroup();
if (g == NULL) return CMD_ERROR;
if (flags & DC_EXEC) {
g->owner = _current_player;
g->string_id = STR_SV_GROUP_NAME;
g->replace_protection = false;
g->vehicle_type = vt;
}
return 0;
}
/**
* Add a vehicle to a group
* @param tile unused
* @param p1 index of array group
* - p1 bit 0-15 : GroupID
* @param p2 unused
*/
int32 CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
if (!IsValidGroupID(p1)) return CMD_ERROR;
Group *g = GetGroup(p1);
if (g->owner != _current_player) return CMD_ERROR;
if (flags & DC_EXEC) {
Vehicle *v;
/* Add all vehicles belong to the group to the default group */
FOR_ALL_VEHICLES(v) {
if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
}
/* If we set an autoreplace for the group we delete, remove it. */
if (_current_player < MAX_PLAYERS) {
Player *p;
EngineRenew *er;
p = GetPlayer(_current_player);
FOR_ALL_ENGINE_RENEWS(er) {
if (er->group_id == g->index) RemoveEngineReplacementForPlayer(p, er->from, g->index, flags);
}
}
/* Delete the Replace Vehicle Windows */
DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
DeleteGroup(g);
}
return 0;
}
/**
* Rename a group
* @param tile unused
* @param p1 index of array group
* - p1 bit 0-15 : GroupID
* @param p2 unused
*/
int32 CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
if (!IsValidGroupID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
/* Create the name */
StringID str = AllocateName(_cmd_text, 0);
if (str == STR_NULL) return CMD_ERROR;
if (flags & DC_EXEC) {
Group *g = GetGroup(p1);
/* Delete the old name */
DeleteName(g->string_id);
/* Assign the new one */
g->string_id = str;
g->owner = _current_player;
}
return 0;
}
/**
* Add a vehicle to a group
* @param tile unused
* @param p1 index of array group
* - p1 bit 0-15 : GroupID
* @param p2 vehicle to add to a group
* - p2 bit 0-15 : VehicleID
*/
int32 CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
GroupID new_g = p1;
if (!IsValidVehicleID(p2) || !IsValidGroupID(new_g)) return CMD_ERROR;
Vehicle *v = GetVehicle(p2);
if (v->owner != _current_player || (v->type == VEH_TRAIN && !IsFrontEngine(v))) return CMD_ERROR;
if (flags & DC_EXEC) {
DecreaseGroupNumVehicle(v->group_id);
IncreaseGroupNumVehicle(new_g);
switch (v->type) {
default: NOT_REACHED();
case VEH_TRAIN:
SetTrainGroupID(v, new_g);
break;
case VEH_ROAD:
case VEH_SHIP:
case VEH_AIRCRAFT:
if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
v->group_id = new_g;
break;
}
/* Update the Replace Vehicle Windows */
InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
}
return 0;
}
/**
* Add all shared vehicles of all vehicles from a group
* @param tile unused
* @param p1 index of group array
* - p1 bit 0-15 : GroupID
* @param p2 type of vehicles
*/
int32 CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
VehicleType type = (VehicleType)p2;
if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
if (flags & DC_EXEC) {
Vehicle *v;
VehicleType type = (VehicleType)p2;
GroupID id_g = p1;
uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
/* Find the first front engine which belong to the group id_g
* then add all shared vehicles of this front engine to the group id_g */
FOR_ALL_VEHICLES(v) {
if ((v->type == type) && (
(type == VEH_TRAIN && IsFrontEngine(v)) ||
(type != VEH_TRAIN && v->subtype <= subtype))) {
if (v->group_id != id_g) continue;
/* For each shared vehicles add it to the group */
for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) {
if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
}
}
}
}
return 0;
}
/**
* Remove all vehicles from a group
* @param tile unused
* @param p1 index of group array
* - p1 bit 0-15 : GroupID
* @param p2 type of vehicles
*/
int32 CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
VehicleType type = (VehicleType)p2;
if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
if (flags & DC_EXEC) {
GroupID old_g = p1;
uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
Vehicle *v;
/* Find each Vehicle that belongs to the group old_g and add it to the default group */
FOR_ALL_VEHICLES(v) {
if ((v->type == type) && (
(type == VEH_TRAIN && IsFrontEngine(v)) ||
(type != VEH_TRAIN && v->subtype <= subtype))) {
if (v->group_id != old_g) continue;
/* Add The Vehicle to the default group */
CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
}
}
}
return 0;
}
/**
* Decrease the num_vehicle variable before delete an front engine from a group
* @note Called in CmdSellRailWagon and DeleteLasWagon,
* @param v FrontEngine of the train we want to remove.
*/
void RemoveVehicleFromGroup(const Vehicle *v)
{
if (!IsValidVehicle(v) || v->type != VEH_TRAIN || !IsFrontEngine(v)) return;
if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
}
/**
* Affect the groupID of a train to new_g.
* @note called in CmdAddVehicleGroup and CmdMoveRailVehicle
* @param v First vehicle of the chain.
* @param new_g index of array group
*/
void SetTrainGroupID(Vehicle *v, GroupID new_g)
{
if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
assert(IsValidVehicle(v) && v->type == VEH_TRAIN && IsFrontEngine(v));
for (Vehicle *u = v; u != NULL; u = u->next) {
if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
u->group_id = new_g;
}
/* Update the Replace Vehicle Windows */
InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
}
/**
* Recalculates the groupID of a train. Should be called each time a vehicle is added
* to/removed from the chain,.
* @note this needs to be called too for 'wagon chains' (in the depot, without an engine)
* @note Called in CmdBuildRailVehicle, CmdBuildRailWagon, CmdMoveRailVehicle, CmdSellRailWagon
* @param v First vehicle of the chain.
*/
void UpdateTrainGroupID(Vehicle *v)
{
assert(IsValidVehicle(v) && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
for (Vehicle *u = v; u != NULL; u = u->next) {
if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
u->group_id = new_g;
}
/* Update the Replace Vehicle Windows */
InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
}
void RemoveAllGroupsForPlayer(const Player *p)
{
Group *g;
FOR_ALL_GROUPS(g) {
if (p->index == g->owner) DeleteGroup(g);
}
}
static const SaveLoad _group_desc[] = {
SLE_VAR(Group, string_id, SLE_UINT16),
SLE_VAR(Group, num_vehicle, SLE_UINT16),
SLE_VAR(Group, owner, SLE_UINT8),
SLE_VAR(Group, vehicle_type, SLE_UINT8),
SLE_VAR(Group, replace_protection, SLE_BOOL),
SLE_END()
};
static void Save_GROUP(void)
{
Group *g;
FOR_ALL_GROUPS(g) {
SlSetArrayIndex(g->index);
SlObject(g, _group_desc);
}
}
static void Load_GROUP(void)
{
int index;
while ((index = SlIterateArray()) != -1) {
if (!AddBlockIfNeeded(&_Group_pool, index)) {
error("Groups: failed loading savegame: too many groups");
}
Group *g = GetGroup(index);
SlObject(g, _group_desc);
}
}
extern const ChunkHandler _group_chunk_handlers[] = {
{ 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
};

795
src/group_gui.cpp Normal file
View File

@ -0,0 +1,795 @@
/* $Id$ */
/** @file group_gui.cpp */
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "table/strings.h"
#include "table/sprites.h"
#include "window.h"
#include "gui.h"
#include "gfx.h"
#include "vehicle.h"
#include "command.h"
#include "engine.h"
#include "vehicle_gui.h"
#include "depot.h"
#include "train.h"
#include "date.h"
#include "group.h"
#include "helpers.hpp"
#include "viewport.h"
#include "strings.h"
#include "debug.h"
struct Sorting {
Listing aircraft;
Listing roadveh;
Listing ship;
Listing train;
};
static Sorting _sorting;
static void BuildGroupList(grouplist_d* gl, PlayerID owner, VehicleType vehicle_type)
{
const Group** list;
const Group *g;
uint n = 0;
if (!(gl->l.flags & VL_REBUILD)) return;
list = MallocT<const Group*>(GetGroupArraySize());
if (list == NULL) {
error("Could not allocate memory for the group-sorting-list");
}
FOR_ALL_GROUPS(g) {
if (g->owner == owner && g->vehicle_type == vehicle_type) list[n++] = g;
}
free((void*)gl->sort_list);
gl->sort_list = MallocT<const Group *>(n);
if (n != 0 && gl->sort_list == NULL) {
error("Could not allocate memory for the group-sorting-list");
}
gl->l.list_length = n;
for (uint i = 0; i < n; ++i) gl->sort_list[i] = list[i];
free((void*)list);
gl->l.flags &= ~VL_REBUILD;
gl->l.flags |= VL_RESORT;
}
static int CDECL GroupNameSorter(const void *a, const void *b)
{
static const Group *last_group[2] = { NULL, NULL };
static char last_name[2][64] = { "", "" };
const Group *ga = *(const Group**)a;
const Group *gb = *(const Group**)b;
int r;
if (ga != last_group[0]) {
last_group[0] = ga;
SetDParam(0, ga->index);
GetString(last_name[0], ga->string_id, lastof(last_name[0]));
}
if (gb != last_group[1]) {
last_group[1] = gb;
SetDParam(0, gb->index);
GetString(last_name[1], gb->string_id, lastof(last_name[1]));
}
r = strcmp(last_name[0], last_name[1]); // sort by name
if (r == 0) return ga->index - gb->index;
return r;
}
static void SortGroupList(grouplist_d *gl)
{
if (!(gl->l.flags & VL_RESORT)) return;
qsort((void*)gl->sort_list, gl->l.list_length, sizeof(gl->sort_list[0]), GroupNameSorter);
gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
gl->l.flags &= ~VL_RESORT;
}
enum GroupListWidgets {
GRP_WIDGET_CLOSEBOX = 0,
GRP_WIDGET_CAPTION,
GRP_WIDGET_STICKY,
GRP_WIDGET_EMPTY_TOP_LEFT,
GRP_WIDGET_ALL_VEHICLES,
GRP_WIDGET_LIST_GROUP,
GRP_WIDGET_LIST_GROUP_SCROLLBAR,
GRP_WIDGET_SORT_BY_ORDER,
GRP_WIDGET_SORT_BY_TEXT,
GRP_WIDGET_SORT_BY_DROPDOWN,
GRP_WIDGET_EMPTY_TOP_RIGHT,
GRP_WIDGET_LIST_VEHICLE,
GRP_WIDGET_LIST_VEHICLE_SCROLLBAR,
GRP_WIDGET_CREATE_GROUP,
GRP_WIDGET_DELETE_GROUP,
GRP_WIDGET_RENAME_GROUP,
GRP_WIDGET_EMPTY1,
GRP_WIDGET_REPLACE_PROTECTION,
GRP_WIDGET_EMPTY2,
GRP_WIDGET_AVAILABLE_VEHICLES,
GRP_WIDGET_MANAGE_VEHICLES,
GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
GRP_WIDGET_STOP_ALL,
GRP_WIDGET_START_ALL,
GRP_WIDGET_EMPTY_BOTTOM_RIGHT,
GRP_WIDGET_RESIZE,
};
static const Widget _group_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 513, 0, 13, 0x0, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_STICKYBOX, RESIZE_LR, 14, 514, 525, 0, 13, 0x0, STR_STICKY_BUTTON},
{ WWT_PANEL, RESIZE_NONE, 14, 0, 200, 14, 25, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 14, 0, 200, 26, 39, 0x0, STR_NULL},
{ WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 188, 39, 220, 0x701, STR_GROUPS_CLICK_ON_GROUP_FOR_TIP},
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 189, 200, 26, 220, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 201, 281, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP},
{ WWT_PANEL, RESIZE_NONE, 14, 282, 435, 14, 25, 0x0, STR_SORT_CRITERIA_TIP},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 436, 447, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP},
{ WWT_PANEL, RESIZE_RIGHT, 14, 448, 525, 14, 25, 0x0, STR_NULL},
{ WWT_MATRIX, RESIZE_RB, 14, 201, 513, 26, 233, 0x701, STR_NULL},
{ WWT_SCROLL2BAR, RESIZE_LRB, 14, 514, 525, 26, 233, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 0, 23, 221, 245, 0x0, STR_GROUP_CREATE_TIP},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 24, 47, 221, 245, 0x0, STR_GROUP_DELETE_TIP},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 48, 71, 221, 245, 0x0, STR_GROUP_RENAME_TIP},
{ WWT_PANEL, RESIZE_TB, 14, 72, 164, 221, 245, 0x0, STR_NULL},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 165, 188, 221, 245, 0x0, STR_GROUP_REPLACE_PROTECTION_TIP},
{ WWT_PANEL, RESIZE_TB, 14, 189, 200, 221, 245, 0x0, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 201, 306, 234, 245, 0x0, STR_AVAILABLE_ENGINES_TIP},
{ WWT_TEXTBTN, RESIZE_TB, 14, 307, 411, 234, 245, STR_MANAGE_LIST, STR_MANAGE_LIST_TIP},
{ WWT_TEXTBTN, RESIZE_TB, 14, 412, 423, 234, 245, STR_0225, STR_MANAGE_LIST_TIP},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 424, 435, 234, 245, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
{ WWT_PUSHIMGBTN, RESIZE_TB, 14, 436, 447, 234, 245, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
{ WWT_PANEL, RESIZE_RTB, 14, 448, 513, 234, 245, 0x0, STR_NULL},
{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 514, 525, 234, 245, 0x0, STR_RESIZE_BUTTON},
{ WIDGETS_END},
};
static void CreateVehicleGroupWindow(Window *w)
{
const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
groupveh_d *gv = &WP(w, groupveh_d);
grouplist_d *gl = &WP(w, groupveh_d).gl;
w->caption_color = owner;
w->hscroll.cap = 10 * 29;
w->resize.step_width = 1;
switch (gv->vehicle_type) {
default: NOT_REACHED();
case VEH_TRAIN:
case VEH_ROAD:
w->vscroll.cap = 14;
w->vscroll2.cap = 8;
w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
break;
case VEH_SHIP:
case VEH_AIRCRAFT:
w->vscroll.cap = 10;
w->vscroll2.cap = 4;
w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG2;
break;
}
w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
switch (gv->vehicle_type) {
default: NOT_REACHED(); break;
case VEH_TRAIN: gv->_sorting = &_sorting.train; break;
case VEH_ROAD: gv->_sorting = &_sorting.roadveh; break;
case VEH_SHIP: gv->_sorting = &_sorting.ship; break;
case VEH_AIRCRAFT: gv->_sorting = &_sorting.aircraft; break;
}
gv->sort_list = NULL;
gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
gv->l.sort_type = gv->_sorting->criteria;
gv->l.flags = VL_REBUILD | (gv->_sorting->order ? VL_DESC : VL_NONE);
gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
gl->sort_list = NULL;
gl->l.flags = VL_REBUILD | VL_NONE;
gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
gv->group_sel = DEFAULT_GROUP;
switch (gv->vehicle_type) {
case VEH_TRAIN:
w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_TRAIN;
w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_TRAIN;
w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_TRAIN;
break;
case VEH_ROAD:
w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_901A_ROAD_VEHICLES_CLICK_ON;
w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_ROADVEH;
w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_ROADVEH;
w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_ROADVEH;
break;
case VEH_SHIP:
w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_SHIP;
w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_SHIP;
w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_SHIP;
break;
case VEH_AIRCRAFT:
w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_AIRCRAFT;
w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_AIRCRAFT;
w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_AIRCRAFT;
break;
default: NOT_REACHED();
}
}
/**
* bitmask for w->window_number
* 0-7 PlayerID (owner)
* 11-15 vehicle type
**/
static void GroupWndProc(Window *w, WindowEvent *e)
{
const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
const Player *p = GetPlayer(owner);
groupveh_d *gv = &WP(w, groupveh_d);
grouplist_d *gl = &WP(w, groupveh_d).gl;
gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
switch(e->event) {
case WE_CREATE:
CreateVehicleGroupWindow(w);
break;
case WE_PAINT: {
int x = 203;
int y2 = PLY_WND_PRC__OFFSET_TOP_WIDGET;
int y1 = PLY_WND_PRC__OFFSET_TOP_WIDGET + 2;
int max;
int i;
/* If we select the default group, gv->list will contain all vehicles of the player
* else gv->list will contain all vehicles which belong to the selected group */
BuildVehicleList(gv, owner, gv->group_sel, IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST);
SortVehicleList(gv);
BuildGroupList(gl, owner, gv->vehicle_type);
SortGroupList(gl);
SetVScrollCount(w, gl->l.list_length);
SetVScroll2Count(w, gv->l.list_length);
/* Disable all lists management button when the list is empty */
SetWindowWidgetsDisabledState(w, gv->l.list_length == 0,
GRP_WIDGET_STOP_ALL,
GRP_WIDGET_START_ALL,
GRP_WIDGET_MANAGE_VEHICLES,
GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
WIDGET_LIST_END);
/* Disable the group specific function when we select the default group */
SetWindowWidgetsDisabledState(w, IsDefaultGroupID(gv->group_sel),
GRP_WIDGET_DELETE_GROUP,
GRP_WIDGET_RENAME_GROUP,
GRP_WIDGET_REPLACE_PROTECTION,
WIDGET_LIST_END);
/* If selected_group == DEFAULT_GROUP, draw the standard caption
We list all vehicles */
if (IsDefaultGroupID(gv->group_sel)) {
SetDParam(0, p->name_1);
SetDParam(1, p->name_2);
SetDParam(2, gv->l.list_length);
switch (gv->vehicle_type) {
case VEH_TRAIN:
w->widget[GRP_WIDGET_CAPTION].data = STR_881B_TRAINS;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_TRAIN;
break;
case VEH_ROAD:
w->widget[GRP_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_ROADVEH;
break;
case VEH_SHIP:
w->widget[GRP_WIDGET_CAPTION].data = STR_9805_SHIPS;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_SHIP;
break;
case VEH_AIRCRAFT:
w->widget[GRP_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_AIRCRAFT;
break;
default: NOT_REACHED(); break;
}
} else {
const Group *g = GetGroup(gv->group_sel);
SetDParam(0, g->index);
SetDParam(1, g->num_vehicle);
switch (gv->vehicle_type) {
case VEH_TRAIN:
w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_TRAINS_CAPTION;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_TRAIN : SPR_GROUP_REPLACE_OFF_TRAIN;
break;
case VEH_ROAD:
w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_ROADVEH_CAPTION;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_ROADVEH : SPR_GROUP_REPLACE_OFF_ROADVEH;
break;
case VEH_SHIP:
w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_SHIPS_CAPTION;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_SHIP : SPR_GROUP_REPLACE_OFF_SHIP;
break;
case VEH_AIRCRAFT:
w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_AIRCRAFTS_CAPTION;
w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_AIRCRAFT : SPR_GROUP_REPLACE_OFF_AIRCRAFT;
break;
default: NOT_REACHED(); break;
}
}
DrawWindowWidgets(w);
/* Draw Matrix Group
* The selected group is drawn in white */
StringID str;
switch (gv->vehicle_type) {
case VEH_TRAIN: str = STR_GROUP_ALL_TRAINS; break;
case VEH_ROAD: str = STR_GROUP_ALL_ROADS; break;
case VEH_SHIP: str = STR_GROUP_ALL_SHIPS; break;
case VEH_AIRCRAFT: str = STR_GROUP_ALL_AIRCRAFTS; break;
default: NOT_REACHED(); break;
}
DrawString(10, y1, str, IsDefaultGroupID(gv->group_sel) ? 12 : 16);
max = min(w->vscroll.pos + w->vscroll.cap, gl->l.list_length);
for (i = w->vscroll.pos ; i < max ; ++i) {
const Group *g = gl->sort_list[i];
assert(g->owner == owner);
y1 += PLY_WND_PRC__SIZE_OF_ROW_TINY;
/* draw the selected group in white, else we draw it in black */
SetDParam(0, g->index);
DrawString(10, y1, STR_SV_GROUP_NAME, (gv->group_sel == g->index) ? 12 : 16);
/* draw the number of vehicles of the group */
SetDParam(0, g->num_vehicle);
DrawStringRightAligned(187, y1 + 1, STR_GROUP_TINY_NUM, (gv->group_sel == g->index) ? 12 : 16);
}
/* Draw Matrix Vehicle according to the vehicle list built before */
DrawString(285, 15, _vehicle_sort_listing[gv->l.sort_type], 0x10);
DoDrawString(gv->l.flags & VL_DESC ? DOWNARROW : UPARROW, 269, 15, 0x10);
max = min(w->vscroll2.pos + w->vscroll2.cap, gv->l.list_length);
for (i = w->vscroll2.pos ; i < max ; ++i) {
const Vehicle* v = gv->sort_list[i];
StringID str;
assert(v->type == gv->vehicle_type && v->owner == owner);
DrawVehicleImage(v, x + 19, y2 + 6, w->hscroll.cap, 0, gv->vehicle_sel);
DrawVehicleProfitButton(v, x, y2 + 13);
if (IsVehicleInDepot(v)) {
str = STR_021F;
} else {
str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
}
SetDParam(0, v->unitnumber);
DrawString(x, y2 + 2, str, 0);
if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG2) DrawSmallOrderList(v, x + 138, y2);
if (v->profit_this_year < 0) {
str = v->profit_last_year < 0 ?
STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR :
STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR;
} else {
str = v->profit_last_year < 0 ?
STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR :
STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR;
}
SetDParam(0, v->profit_this_year);
SetDParam(1, v->profit_last_year);
DrawString(x + 19, y2 + w->resize.step_height - 8, str, 0);
if (IsValidGroupID(v->group_id)) {
SetDParam(0, v->group_id);
DrawString(x + 19, y2, STR_GROUP_TINY_NAME, 16);
}
y2 += w->resize.step_height;
}
break;
}
case WE_CLICK:
switch(e->we.click.widget) {
case GRP_WIDGET_SORT_BY_ORDER: // Flip sorting method ascending/descending
gv->l.flags ^= VL_DESC;
gv->l.flags |= VL_RESORT;
gv->_sorting->order = !!(gv->l.flags & VL_DESC);
SetWindowDirty(w);
break;
case GRP_WIDGET_SORT_BY_TEXT:
case GRP_WIDGET_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
ShowDropDownMenu(w, _vehicle_sort_listing, gv->l.sort_type, GRP_WIDGET_SORT_BY_DROPDOWN, 0, 0);
return;
case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
if (!IsDefaultGroupID(gv->group_sel)) {
gv->group_sel = DEFAULT_GROUP;
gv->l.flags |= VL_REBUILD;
SetWindowDirty(w);
}
break;
case GRP_WIDGET_LIST_GROUP: { // Matrix Group
uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
if (id_g >= w->vscroll.cap) return;
id_g += w->vscroll.pos;
if (id_g >= gl->l.list_length) return;
gv->group_sel = gl->sort_list[id_g]->index;;
gv->l.flags |= VL_REBUILD;
SetWindowDirty(w);
break;
}
case GRP_WIDGET_LIST_VEHICLE: { // Matrix Vehicle
uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
const Vehicle *v;
if (id_v >= w->vscroll2.cap) return; // click out of bounds
id_v += w->vscroll2.pos;
if (id_v >= gv->l.list_length) return; // click out of list bound
v = gv->sort_list[id_v];
gv->vehicle_sel = v->index;
if (IsValidVehicle(v)) {
CursorID image;
switch (gv->vehicle_type) {
case VEH_TRAIN: image = GetTrainImage(v, DIR_W); break;
case VEH_ROAD: image = GetRoadVehImage(v, DIR_W); break;
case VEH_SHIP: image = GetShipImage(v, DIR_W); break;
case VEH_AIRCRAFT: image = GetAircraftImage(v, DIR_W); break;
default: NOT_REACHED(); break;
}
SetObjectToPlaceWnd(image, GetVehiclePalette(v), 4, w);
}
SetWindowDirty(w);
break;
}
case GRP_WIDGET_CREATE_GROUP: // Create a new group
if (!CmdFailed(DoCommandP(0, gv->vehicle_type, 0, NULL, CMD_CREATE_GROUP | CMD_MSG(STR_GROUP_CAN_T_CREATE)))) {
SetWindowDirty(w);
gl->l.flags |= VL_REBUILD;
}
break;
case GRP_WIDGET_DELETE_GROUP: // Delete the selected group
if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_DELETE_GROUP | CMD_MSG(STR_GROUP_CAN_T_DELETE)))) {
gv->group_sel = DEFAULT_GROUP;
gv->l.flags |= VL_REBUILD;
gl->l.flags |= VL_REBUILD;
SetWindowDirty(w);
}
break;
case GRP_WIDGET_RENAME_GROUP: { // Rename the selected roup
assert(!IsDefaultGroupID(gv->group_sel));
const Group *g = GetGroup(gv->group_sel);
SetDParam(0, g->index);
ShowQueryString(g->string_id, STR_GROUP_RENAME_CAPTION, 31, 150, w, CS_ALPHANUMERAL);
} break;
case GRP_WIDGET_AVAILABLE_VEHICLES:
ShowBuildVehicleWindow(0, gv->vehicle_type);
break;
case GRP_WIDGET_MANAGE_VEHICLES:
case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
static StringID action_str[] = {
STR_REPLACE_VEHICLES,
STR_SEND_FOR_SERVICING,
STR_SEND_TRAIN_TO_DEPOT,
STR_NULL,
STR_NULL,
INVALID_STRING_ID
};
action_str[3] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_ADD_SHARED_VEHICLE;
action_str[4] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_REMOVE_ALL_VEHICLES;
ShowDropDownMenu(w, action_str, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, 0);
break;
}
case GRP_WIDGET_START_ALL:
case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
| (1 << 6)
| (e->we.click.widget == GRP_WIDGET_START_ALL ? (1 << 5) : 0)
| gv->vehicle_type, NULL, CMD_MASS_START_STOP);
break;
}
case GRP_WIDGET_REPLACE_PROTECTION:
if (!IsDefaultGroupID(gv->group_sel)) {
Group *g = GetGroup(gv->group_sel);
g->replace_protection = !g->replace_protection;
}
break;
}
break;
case WE_DRAGDROP: {
switch (e->we.click.widget) {
case GRP_WIDGET_ALL_VEHICLES: // All trains
if (!CmdFailed(DoCommandP(0, DEFAULT_GROUP , gv->vehicle_sel, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
gv->l.flags |= VL_REBUILD;
}
gv->vehicle_sel = INVALID_VEHICLE;
SetWindowDirty(w);
break;
case GRP_WIDGET_LIST_GROUP: { // Maxtrix group
uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
const VehicleID vindex = gv->vehicle_sel;
gv->vehicle_sel = INVALID_VEHICLE;
SetWindowDirty(w);
if (id_g >= w->vscroll.cap) return;
id_g += w->vscroll.pos;
if (id_g >= gl->l.list_length) return;
if (!CmdFailed(DoCommandP(0, gl->sort_list[id_g]->index , vindex, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
gv->l.flags |= VL_REBUILD;
}
break;
}
case GRP_WIDGET_LIST_VEHICLE: { // Maxtrix vehicle
uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
const Vehicle *v;
const VehicleID vindex = gv->vehicle_sel;
gv->vehicle_sel = INVALID_VEHICLE;
SetWindowDirty(w);
if (id_v >= w->vscroll2.cap) return; // click out of bounds
id_v += w->vscroll2.pos;
if (id_v >= gv->l.list_length) return; // click out of list bound
v = gv->sort_list[id_v];
if (vindex == v->index) {
switch (gv->vehicle_type) {
default: NOT_REACHED(); break;
case VEH_TRAIN: ShowTrainViewWindow(v); break;
case VEH_ROAD: ShowRoadVehViewWindow(v); break;
case VEH_SHIP: ShowShipViewWindow(v); break;
case VEH_AIRCRAFT: ShowAircraftViewWindow(v); break;
}
}
break;
}
}
break;
}
case WE_ON_EDIT_TEXT:
if (!StrEmpty(e->we.edittext.str)) {
_cmd_text = e->we.edittext.str;
if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_RENAME_GROUP | CMD_MSG(STR_GROUP_CAN_T_RENAME)))) {
SetWindowDirty(w);
gl->l.flags |= VL_REBUILD;
}
}
break;
case WE_RESIZE:
w->hscroll.cap += e->we.sizing.diff.x;
w->vscroll.cap += e->we.sizing.diff.y / PLY_WND_PRC__SIZE_OF_ROW_TINY;
w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
break;
case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
switch (e->we.dropdown.button) {
case GRP_WIDGET_SORT_BY_DROPDOWN:
if (gv->l.sort_type != e->we.dropdown.index) {
gv->l.flags |= VL_RESORT;
gv->l.sort_type = e->we.dropdown.index;
gv->_sorting->criteria = gv->l.sort_type;
}
break;
case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
assert(gv->l.list_length != 0);
switch (e->we.dropdown.index) {
case 0: // Replace window
ShowReplaceGroupVehicleWindow(gv->group_sel, gv->vehicle_type);
break;
case 1: // Send for servicing
DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
| DEPOT_MASS_SEND
| DEPOT_SERVICE, NULL, GetCmdSendToDepot(gv->vehicle_type));
break;
case 2: // Send to Depots
DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
| DEPOT_MASS_SEND, NULL, GetCmdSendToDepot(gv->vehicle_type));
break;
case 3: // Add shared Vehicles
assert(!IsDefaultGroupID(gv->group_sel));
if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_SHARED_VEHICLE)))) {
gv->l.flags |= VL_REBUILD;
}
break;
case 4: // Remove all Vehicles from the selected group
assert(!IsDefaultGroupID(gv->group_sel));
if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES)))) {
gv->l.flags |= VL_REBUILD;
}
break;
default: NOT_REACHED();
}
break;
default: NOT_REACHED();
}
SetWindowDirty(w);
break;
case WE_DESTROY:
free((void*)gv->sort_list);
free((void*)gl->sort_list);
break;
case WE_TICK: // resort the lists every 20 seconds orso (10 days)
if (--gv->l.resort_timer == 0) {
gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
gv->l.flags |= VL_RESORT;
SetWindowDirty(w);
}
if (--gl->l.resort_timer == 0) {
gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
gl->l.flags |= VL_RESORT;
SetWindowDirty(w);
}
break;
}
}
static const WindowDesc _group_desc = {
WDP_AUTO, WDP_AUTO, 526, 246,
WC_TRAINS_LIST, WC_NONE,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
_group_widgets,
GroupWndProc
};
void ShowPlayerGroup(PlayerID player, VehicleType vehicle_type)
{
WindowClass wc;
switch (vehicle_type) {
default: NOT_REACHED();
case VEH_TRAIN: wc = WC_TRAINS_LIST; break;
case VEH_ROAD: wc = WC_ROADVEH_LIST; break;
case VEH_SHIP: wc = WC_SHIPS_LIST; break;
case VEH_AIRCRAFT: wc = WC_AIRCRAFT_LIST; break;
}
WindowNumber num = (vehicle_type << 11) | VLW_GROUP_LIST | player;
DeleteWindowById(wc, num);
Window *w = AllocateWindowDescFront(&_group_desc, num);
if (w == NULL) return;
w->window_class = wc;
switch (vehicle_type) {
default: NOT_REACHED();
case VEH_ROAD:
ResizeWindow(w, -66, 0);
/* FALL THROUGH */
case VEH_TRAIN:
w->resize.height = w->height - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 4); // Minimum of 4 vehicles
break;
case VEH_SHIP:
case VEH_AIRCRAFT:
ResizeWindow(w, -66, -52);
w->resize.height = w->height; // Minimum of 4 vehicles
break;
}
/* Set the minimum window size to the current window size */
w->resize.width = w->width;
}

View File

@ -139,4 +139,6 @@ VARDEF PlaceProc *_place_proc;
/* vehicle_gui.cpp */
void InitializeGUI();
void ShowPlayerGroup(PlayerID player, VehicleType veh);
#endif /* GUI_H */

View File

@ -1098,6 +1098,7 @@ STR_CONFIG_PATCHES_SCROLLWHEEL_SCROLL :Scroll map
STR_CONFIG_PATCHES_SCROLLWHEEL_OFF :Off
STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER :{LTBLUE}Map scrollwheel speed: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME :{LTBLUE}Automatically pause when starting a new game: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS :{LTBLUE}Use the advanced vehicle list: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_MAX_TRAINS :{LTBLUE}Max trains per player: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_MAX_ROADVEH :{LTBLUE}Max road vehicles per player: {ORANGE}{STRING1}
@ -2012,6 +2013,8 @@ STR_SV_STNAME_LOWER :Lower {STRING1}
STR_SV_STNAME_HELIPORT :{STRING1} Heliport
STR_SV_STNAME_FOREST :{STRING1} Forest
STR_SV_GROUP_NAME :{GROUP}
############ end of savegame specific region!
##id 0x6800
@ -3187,3 +3190,41 @@ STR_TRANSPARENT_INDUSTRIES_DESC :{BLACK}Toggle t
STR_TRANSPARENT_BUILDINGS_DESC :{BLACK}Toggle transparency for buildables like stations, depots, waypoints and catenary
STR_TRANSPARENT_BRIDGES_DESC :{BLACK}Toggle transparency for bridges
STR_TRANSPARENT_STRUCTURES_DESC :{BLACK}Toggle transparency for structures like lighthouses and antennas, maybe in future for eyecandy
##### Mass Order
STR_GROUP_NAME_FORMAT :Group {COMMA}
STR_GROUP_TINY_NAME :{TINYFONT}{GROUP}
STR_GROUP_ALL_TRAINS :All trains
STR_GROUP_ALL_ROADS :All road vehicles
STR_GROUP_ALL_SHIPS :All ships
STR_GROUP_ALL_AIRCRAFTS :All aircraft
STR_GROUP_TINY_NUM :{TINYFONT}{COMMA}
STR_GROUP_ADD_SHARED_VEHICLE :Add shared vehicles
STR_GROUP_REMOVE_ALL_VEHICLES :Remove all vehicles
STR_GROUP_TRAINS_CAPTION :{WHITE}{GROUP} - {COMMA} Train{P "" s}
STR_GROUP_ROADVEH_CAPTION :{WHITE}{GROUP} - {COMMA} Road Vehicle{P "" s}
STR_GROUP_SHIPS_CAPTION :{WHITE}{GROUP} - {COMMA} Ship{P "" s}
STR_GROUP_AIRCRAFTS_CAPTION :{WHITE}{GROUP} - {COMMA} Aircraft
STR_GROUP_RENAME_CAPTION :{BLACK}Rename a group
STR_GROUP_REPLACE_CAPTION :{WHITE}Replace Vehicles of "{GROUP}"
STR_GROUP_CAN_T_CREATE :{WHITE}Can't create group...
STR_GROUP_CAN_T_DELETE :{WHITE}Can't delete this group...
STR_GROUP_CAN_T_RENAME :{WHITE}Can't rename group...
STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES :{WHITE}Can't remove all vehicles from this group...
STR_GROUP_CAN_T_ADD_VEHICLE :{WHITE}Can't add the vehicle to this group...
STR_GROUP_CAN_T_ADD_SHARED_VEHICLE :{WHITE}Can't add shared vehicles to group...
STR_GROUPS_CLICK_ON_GROUP_FOR_TIP :{BLACK}Groups - Click on a group to list all vehicles of this group
STR_GROUP_CREATE_TIP :{BLACK}Click to create a group
STR_GROUP_DELETE_TIP :{BLACK}Delete the selected group
STR_GROUP_RENAME_TIP :{BLACK}Rename the selected group
STR_GROUP_REPLACE_PROTECTION_TIP :{BLACK}Click to protect this group from global autoreplace
STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
########

View File

@ -22,6 +22,7 @@
#include "newgrf_house.h"
#include "date.h"
#include "cargotype.h"
#include "group.h"
char _name_array[512][32];
@ -120,6 +121,7 @@ void InitializeGame(int mode, uint size_x, uint size_y)
InitializeWaypoints();
InitializeDepots();
InitializeOrders();
InitializeGroup();
InitNewsItemStructs();
InitializeLandscape();

View File

@ -63,6 +63,7 @@
#include "newgrf_house.h"
#include "newgrf_commons.h"
#include "player_face.h"
#include "group.h"
#include "bridge_map.h"
#include "clear_map.h"
@ -294,6 +295,7 @@ static void UnInitializeGame()
CleanPool(&_Vehicle_pool);
CleanPool(&_Sign_pool);
CleanPool(&_Order_pool);
CleanPool(&_Group_pool);
free((void*)_town_sort);
free((void*)_industry_sort);
@ -1954,6 +1956,20 @@ bool AfterLoadGame()
_opt.diff.number_towns++;
}
/* Recalculate */
Group *g;
FOR_ALL_GROUPS(g) {
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (!IsEngineCountable(v)) continue;
if (v->group_id != g->index || v->type != g->vehicle_type || v->owner != g->owner) continue;
g->num_engines[v->engine_type]++;
}
}
return true;
}

View File

@ -40,6 +40,7 @@ struct Town;
struct NewsItem;
struct Industry;
struct DrawPixelInfo;
struct Group;
typedef byte VehicleOrderID; ///< The index of an order within its current vehicle (not pool related)
typedef byte CargoID;
typedef byte LandscapeID;
@ -63,6 +64,7 @@ typedef uint16 DepotID;
typedef uint16 WaypointID;
typedef uint16 OrderID;
typedef uint16 SignID;
typedef uint16 GroupID;
typedef uint16 EngineRenewID;
typedef uint16 DestinationID;

View File

@ -311,7 +311,7 @@ static inline void RemoveAllEngineReplacementForPlayer(Player *p) { RemoveAllEng
* @return The engine type to replace with, or INVALID_ENGINE if no
* replacement is in the list.
*/
static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacement(p->engine_renew_list, engine); }
static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacement(p->engine_renew_list, engine, group); }
/**
* Check if a player has a replacement set up for the given engine.
@ -319,7 +319,7 @@ static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engi
* @param engine Engine type to be replaced.
* @return true if a replacement was set up, false otherwise.
*/
static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacementForPlayer(p, engine) != INVALID_ENGINE; }
static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacementForPlayer(p, engine, group) != INVALID_ENGINE; }
/**
* Add an engine replacement for the player.
@ -329,7 +329,7 @@ static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engin
* @param flags The calling command flags.
* @return 0 on success, CMD_ERROR on failure.
*/
static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, flags); }
static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, group, flags); }
/**
* Remove an engine replacement for the player.
@ -338,7 +338,7 @@ static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine
* @param flags The calling command flags.
* @return 0 on success, CMD_ERROR on failure.
*/
static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, flags); }
static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, GroupID group, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, group, flags); }
/**
* Reset the livery schemes to the player's primary colour.

View File

@ -27,6 +27,7 @@
#include "date.h"
#include "window.h"
#include "player_face.h"
#include "group.h"
/**
* Sets the local player and updates the patch settings that are set on a
@ -638,6 +639,7 @@ static void DeletePlayerStuff(PlayerID pi)
* if p1 = 2, then
* - p2 = minimum amount of money available
* if p1 = 3, then:
* - p1 bits 8-15 = engine group
* - p2 bits 0-15 = old engine type
* - p2 bits 16-31 = new engine type
* if p1 = 4, then:
@ -693,8 +695,11 @@ int32 CmdSetAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
case 3: {
EngineID old_engine_type = GB(p2, 0, 16);
EngineID new_engine_type = GB(p2, 16, 16);
GroupID id_g = GB(p1, 16, 8);
int32 cost;
if (!IsValidGroupID(id_g)) return CMD_ERROR;
if (new_engine_type != INVALID_ENGINE) {
/* First we make sure that it's a valid type the user requested
* check that it's an engine that is in the engine array */
@ -714,9 +719,9 @@ int32 CmdSetAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (!HASBIT(GetEngine(new_engine_type)->player_avail, _current_player))
return CMD_ERROR;
cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, flags);
cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, id_g, flags);
} else {
cost = RemoveEngineReplacementForPlayer(p, old_engine_type, flags);
cost = RemoveEngineReplacementForPlayer(p, old_engine_type,id_g, flags);
}
if (IsLocalPlayer()) InvalidateAutoreplaceWindow(old_engine_type);
@ -901,6 +906,7 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
p->is_active = false;
}
RemoveAllEngineReplacementForPlayer(p);
RemoveAllGroupsForPlayer(p);
} break;

View File

@ -29,7 +29,7 @@
#include <setjmp.h>
#include <list>
extern const uint16 SAVEGAME_VERSION = 59;
extern const uint16 SAVEGAME_VERSION = 60;
uint16 _sl_version; ///< the major savegame version identifier
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
@ -1257,6 +1257,7 @@ extern const ChunkHandler _industry_chunk_handlers[];
extern const ChunkHandler _economy_chunk_handlers[];
extern const ChunkHandler _animated_tile_chunk_handlers[];
extern const ChunkHandler _newgrf_chunk_handlers[];
extern const ChunkHandler _group_chunk_handlers[];
static const ChunkHandler * const _chunk_handlers[] = {
_misc_chunk_handlers,
@ -1274,6 +1275,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
_player_chunk_handlers,
_animated_tile_chunk_handlers,
_newgrf_chunk_handlers,
_group_chunk_handlers,
NULL,
};

View File

@ -1342,6 +1342,7 @@ const SettingDesc _patch_settings[] = {
SDT_VAR(Patches, scrollwheel_scrolling,SLE_UINT8,S,MS, 0, 0, 2, 0, STR_CONFIG_PATCHES_SCROLLWHEEL_SCROLLING, NULL),
SDT_VAR(Patches,scrollwheel_multiplier,SLE_UINT8,S, 0, 5, 1, 15, 1, STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER,NULL),
SDT_BOOL(Patches, pause_on_newgame, S, 0, false, STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME, NULL),
SDT_BOOL(Patches, advanced_vehicle_list, S, 0, true, STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS, NULL),
/***************************************************************************/
/* Construction section of the GUI-configure patches window */

View File

@ -598,6 +598,7 @@ static const char *_patches_ui[] = {
"scrollwheel_scrolling",
"scrollwheel_multiplier",
"pause_on_newgame",
"advanced_vehicle_list",
};
static const char *_patches_construction[] = {

View File

@ -506,6 +506,7 @@ static const CmdStruct _cmd_structs[] = {
{"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
{"STATION", EmitSingleChar, SCC_STATION_NAME, 1, 0},
{"TOWN", EmitSingleChar, SCC_TOWN_NAME, 1, 0},
{"GROUP", EmitSingleChar, SCC_GROUP_NAME, 1, 0},
// 0x9D is used for the pseudo command SETCASE
// 0x9E is used for case switching

View File

@ -25,6 +25,8 @@
#include "industry.h"
#include "helpers.hpp"
#include "cargotype.h"
#include "group.h"
#include "debug.h"
/* for opendir/readdir/closedir */
# include "fios.h"
@ -840,6 +842,18 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
break;
}
case SCC_GROUP_NAME: { // {GROUP}
const Group *g = GetGroup(GetInt32(&argv));
int32 args[1];
assert(IsValidGroup(g));
args[0] = g->index;
buff = GetStringWithArgs(buff, (g->string_id == STR_SV_GROUP_NAME) ? (StringID)STR_GROUP_NAME_FORMAT : g->string_id, args, last);
break;
}
case SCC_CURRENCY_64: { // {CURRENCY64}
buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
break;

View File

@ -26,6 +26,7 @@ enum {
SCC_WAYPOINT_NAME,
SCC_STATION_NAME,
SCC_TOWN_NAME,
SCC_GROUP_NAME,
SCC_CURRENCY_COMPACT,
SCC_CURRENCY_COMPACT_64,

View File

@ -62,4 +62,5 @@ static MD5File files_openttd[] = {
{ "openttd.grf", { 0x85, 0x4f, 0xf6, 0xb5, 0xd2, 0xf7, 0xbc, 0x1e, 0xb9, 0xdc, 0x44, 0xef, 0x35, 0x5f, 0x64, 0x9b } },
{ "trkfoundw.grf", { 0x12, 0x33, 0x3f, 0xa3, 0xd1, 0x86, 0x8b, 0x04, 0x53, 0x18, 0x9c, 0xee, 0xf9, 0x2d, 0xf5, 0x95 } },
{ "roadstops.grf", { 0x8c, 0xd9, 0x45, 0x21, 0x28, 0x82, 0x96, 0x45, 0x33, 0x22, 0x7a, 0xb9, 0x0d, 0xf3, 0x67, 0x4a } },
{ "group.grf", { 0xe8, 0x52, 0x5f, 0x1c, 0x3e, 0xf9, 0x91, 0x9d, 0x0f, 0x70, 0x8c, 0x8a, 0x21, 0xa4, 0xc7, 0x02 } },
};

View File

@ -128,6 +128,28 @@ enum Sprites {
SPR_TRUCK_STOP_DT_X_W = SPR_ROADSTOP_BASE + 6,
SPR_TRUCK_STOP_DT_X_E = SPR_ROADSTOP_BASE + 7,
SPR_GROUP_BASE = SPR_ROADSTOP_BASE + 8, // The sprites used for the group interface
SPR_GROUP_CREATE_TRAIN = SPR_GROUP_BASE,
SPR_GROUP_CREATE_ROADVEH = SPR_GROUP_BASE + 1,
SPR_GROUP_CREATE_SHIP = SPR_GROUP_BASE + 2,
SPR_GROUP_CREATE_AIRCRAFT = SPR_GROUP_BASE + 3,
SPR_GROUP_DELETE_TRAIN = SPR_GROUP_BASE + 4,
SPR_GROUP_DELETE_ROADVEH = SPR_GROUP_BASE + 5,
SPR_GROUP_DELETE_SHIP = SPR_GROUP_BASE + 6,
SPR_GROUP_DELETE_AIRCRAFT = SPR_GROUP_BASE + 7,
SPR_GROUP_RENAME_TRAIN = SPR_GROUP_BASE + 8,
SPR_GROUP_RENAME_ROADVEH = SPR_GROUP_BASE + 9,
SPR_GROUP_RENAME_SHIP = SPR_GROUP_BASE + 10,
SPR_GROUP_RENAME_AIRCRAFT = SPR_GROUP_BASE + 11,
SPR_GROUP_REPLACE_ON_TRAIN = SPR_GROUP_BASE + 12,
SPR_GROUP_REPLACE_ON_ROADVEH = SPR_GROUP_BASE + 13,
SPR_GROUP_REPLACE_ON_SHIP = SPR_GROUP_BASE + 14,
SPR_GROUP_REPLACE_ON_AIRCRAFT = SPR_GROUP_BASE + 15,
SPR_GROUP_REPLACE_OFF_TRAIN = SPR_GROUP_BASE + 16,
SPR_GROUP_REPLACE_OFF_ROADVEH = SPR_GROUP_BASE + 17,
SPR_GROUP_REPLACE_OFF_SHIP = SPR_GROUP_BASE + 18,
SPR_GROUP_REPLACE_OFF_AIRCRAFT = SPR_GROUP_BASE + 19,
/* Manager face sprites */
SPR_GRADIENT = 874, // background gradient behind manager face

View File

@ -37,6 +37,7 @@
#include "yapf/yapf.h"
#include "date.h"
#include "cargotype.h"
#include "group.h"
static bool TrainCheckIfLineEnds(Vehicle *v);
static void TrainController(Vehicle *v, bool update_image);
@ -637,12 +638,15 @@ static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
v->cur_image = 0xAC2;
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
AddArticulatedParts(vl);
_new_vehicle_id = v->index;
VehiclePositionChanged(v);
TrainConsistChanged(GetFirstVehicleInChain(v));
UpdateTrainGroupID(GetFirstVehicleInChain(v));
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
if (IsLocalPlayer()) {
@ -797,6 +801,8 @@ int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
v->vehicle_flags = 0;
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
v->group_id = DEFAULT_GROUP;
v->subtype = 0;
SetFrontEngine(v);
SetTrainEngine(v);
@ -818,6 +824,7 @@ int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
TrainConsistChanged(v);
UpdateTrainAcceleration(v);
UpdateTrainGroupID(v);
if (!HASBIT(p2, 1)) { // check if the cars should be added to the new vehicle
NormalizeTrainVehInDepot(v);
@ -1113,6 +1120,16 @@ int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
for (Vehicle *u = src_head; u != NULL; u = u->next) u->first = NULL;
for (Vehicle *u = dst_head; u != NULL; u = u->next) u->first = NULL;
/* If we move the front Engine and if the second vehicle is not an engine
add the whole vehicle to the DEFAULT_GROUP */
if (IsFrontEngine(src) && !IsDefaultGroupID(src->group_id)) {
const Vehicle *v = GetNextVehicle(src);
if (v != NULL && !IsTrainEngine(v)) {
DoCommand(tile, DEFAULT_GROUP, v->index, flags, CMD_ADD_VEHICLE_GROUP);
}
}
if (HASBIT(p2, 0)) {
/* unlink ALL wagons */
if (src != src_head) {
@ -1142,6 +1159,14 @@ int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
SetFrontEngine(src);
assert(src->orders == NULL);
src->num_orders = 0;
// Decrease the engines number of the src engine_type
if (!IsDefaultGroupID(src->group_id) && IsValidGroupID(src->group_id)) {
GetGroup(src->group_id)->num_engines[src->engine_type]--;
}
// If we move an engine to a new line affect it to the DEFAULT_GROUP
src->group_id = DEFAULT_GROUP;
}
} else {
SetFreeWagon(src);
@ -1203,13 +1228,18 @@ int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
* To do this, CmdMoveRailVehicle must be called once more
* we can't loop forever here because next time we reach this line we will have a front engine */
if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
/* As in CmdMoveRailVehicle src_head->group_id will be equal to DEFAULT_GROUP
* we need to save the group and reaffect it to src_head */
const GroupID tmp_g = src_head->group_id;
CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
SetTrainGroupID(src_head, tmp_g);
src_head = NULL; // don't do anything more to this train since the new call will do it
}
if (src_head != NULL) {
NormaliseTrainConsist(src_head);
TrainConsistChanged(src_head);
UpdateTrainGroupID(src_head);
if (IsFrontEngine(src_head)) {
UpdateTrainAcceleration(src_head);
InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
@ -1224,6 +1254,7 @@ int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (dst_head != NULL) {
NormaliseTrainConsist(dst_head);
TrainConsistChanged(dst_head);
UpdateTrainGroupID(dst_head);
if (IsFrontEngine(dst_head)) {
UpdateTrainAcceleration(dst_head);
InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
@ -1364,6 +1395,8 @@ int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (first->next_shared != NULL) {
first->next_shared->prev_shared = new_f;
new_f->next_shared = first->next_shared;
} else {
RemoveVehicleFromGroup(v);
}
/*
@ -1394,6 +1427,7 @@ int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (first != NULL) {
NormaliseTrainConsist(first);
TrainConsistChanged(first);
UpdateTrainGroupID(first);
if (IsFrontEngine(first)) {
InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@ -1447,6 +1481,7 @@ int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
first = UnlinkWagon(v, first);
DeleteDepotHighlightOfVehicle(v);
DeleteVehicle(v);
RemoveVehicleFromGroup(v);
}
}
@ -1454,6 +1489,7 @@ int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (flags & DC_EXEC && first != NULL) {
NormaliseTrainConsist(first);
TrainConsistChanged(first);
UpdateTrainGroupID(first);
if (IsFrontEngine(first)) UpdateTrainAcceleration(first);
InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@ -3063,6 +3099,9 @@ static void DeleteLastWagon(Vehicle *v)
BeginVehicleMove(v);
EndVehicleMove(v);
if (IsFrontEngine(v)) RemoveVehicleFromGroup(v);
DeleteVehicle(v);
if (v->u.rail.track != TRACK_BIT_DEPOT && v->u.rail.track != TRACK_BIT_WORMHOLE)
@ -3148,6 +3187,7 @@ static void HandleCrashedTrain(Vehicle *v)
if (state >= 4440 && !(v->tick_counter&0x1F)) {
DeleteLastWagon(v);
InvalidateWindow(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
}
}

View File

@ -130,6 +130,7 @@ struct Patches {
bool measure_tooltip; // Show a permanent tooltip when dragging tools
byte liveries; // Options for displaying company liveries, 0=none, 1=self, 2=all
bool prefer_teamchat; // Choose the chat message target with <ENTER>, true=all players, false=your team
bool advanced_vehicle_list; // Use the "advanced" vehicle list
uint8 toolbar_pos; // position of toolbars, 0=left, 1=center, 2=right
uint8 window_snap_radius; // Windows snap at each other if closer than this

View File

@ -40,6 +40,7 @@
#include "newgrf_engine.h"
#include "newgrf_sound.h"
#include "helpers.hpp"
#include "group.h"
#include "economy.h"
#define INVALID_COORD (-0x8000)
@ -111,7 +112,7 @@ bool VehicleNeedsService(const Vehicle *v)
return false; // Crashed vehicles don't need service anymore
if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type); /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type, v->group_id); /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
}
return _patches.servint_ispercent ?
@ -284,6 +285,8 @@ static Vehicle *InitializeVehicle(Vehicle *v)
v->prev_shared = NULL;
v->depot_list = NULL;
v->random_bits = 0;
v->group_id = DEFAULT_GROUP;
return v;
}
@ -580,6 +583,8 @@ void DestroyVehicle(Vehicle *v)
if (IsEngineCountable(v)) {
GetPlayer(v->owner)->num_engines[v->engine_type]--;
if (v->owner == _local_player) InvalidateAutoreplaceWindow(v->engine_type);
if (!IsDefaultGroupID(v->group_id) && IsValidGroupID(v->group_id)) GetGroup(v->group_id)->num_engines[v->engine_type]--;
}
DeleteVehicleNews(v->index, INVALID_STRING_ID);
@ -1821,6 +1826,12 @@ int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
_new_vehicle_id = w_front->index;
}
if (flags & DC_EXEC) {
/* Cloned vehicles belong to the same group */
DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
}
/* Take care of refitting. */
w = w_front;
v = v_front;
@ -1973,6 +1984,7 @@ void BuildDepotVehicleList(VehicleType type, TileIndex tile, Vehicle ***engine_l
<li>VLW_SHARED_ORDERS: index of order to generate a list for<li>
<li>VLW_STANDARD: not used<li>
<li>VLW_DEPOT_LIST: TileIndex of the depot/hangar to make the list for</li>
<li>VLW_GROUP_LIST: index of group to generate a list for</li>
</ul>
* @param window_type tells what kind of window the list is for. Use the VLW flags in vehicle_gui.h
* @return the number of vehicles added to the list
@ -2051,6 +2063,19 @@ uint GenerateVehicleSortList(const Vehicle ***sort_list, uint16 *length_of_array
break;
}
case VLW_GROUP_LIST:
FOR_ALL_VEHICLES(v) {
if (v->type == type && (
(type == VEH_TRAIN && IsFrontEngine(v)) ||
(type != VEH_TRAIN && v->subtype <= subtype)
) && v->owner == owner && v->group_id == index) {
if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles() / 4);
(*sort_list)[n++] = v;
}
}
break;
default: NOT_REACHED(); break;
}
@ -2667,6 +2692,8 @@ extern const SaveLoad _common_veh_desc[] = {
SLE_REF(Vehicle, next_shared, REF_VEHICLE),
SLE_REF(Vehicle, prev_shared, REF_VEHICLE),
SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
/* reserve extra space in savegame here. (currently 10 bytes) */
SLE_CONDNULL(10, 2, SL_MAX_VERSION),
@ -2865,6 +2892,9 @@ static void Load_VEHS()
v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
v->current_order.type.m_val &= 0x0F;
}
/* Advanced vehicle lists got added */
if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
}
/* Check for shared order-lists (we now use pointers for that) */

View File

@ -310,6 +310,8 @@ struct Vehicle {
TileIndex cargo_loaded_at_xy; ///< tile index where feeder cargo was loaded
uint32 value;
GroupID group_id; ///< Index of group Pool array
union {
VehicleRail rail;
VehicleAir air;

View File

@ -31,6 +31,7 @@
#include "depot.h"
#include "helpers.hpp"
#include "cargotype.h"
#include "group.h"
struct Sorting {
Listing aircraft;
@ -41,15 +42,6 @@ struct Sorting {
static Sorting _sorting;
struct vehiclelist_d {
const Vehicle** sort_list; // List of vehicles (sorted)
Listing *_sorting; // pointer to the appropiate subcategory of _sorting
uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
VehicleType vehicle_type; // The vehicle type that is sorted
list_d l; // General list struct
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
static bool _internal_sort_order; // descending/ascending
typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
@ -78,7 +70,7 @@ static VehicleSortListingTypeFunction* const _vehicle_sorter[] = {
&VehicleValueSorter,
};
static const StringID _vehicle_sort_listing[] = {
const StringID _vehicle_sort_listing[] = {
STR_SORT_BY_NUMBER,
STR_SORT_BY_DROPDOWN_NAME,
STR_SORT_BY_AGE,
@ -134,7 +126,7 @@ void ResortVehicleLists()
}
}
static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, uint16 index, uint16 window_type)
void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type)
{
if (!(vl->l.flags & VL_REBUILD)) return;
@ -146,7 +138,7 @@ static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, uint16 index, ui
vl->l.flags |= VL_RESORT;
}
static void SortVehicleList(vehiclelist_d *vl)
void SortVehicleList(vehiclelist_d *vl)
{
if (!(vl->l.flags & VL_RESORT)) return;
@ -749,16 +741,6 @@ void ChangeVehicleViewWindow(const Vehicle *from_v, const Vehicle *to_v)
}
}
/*
* Start of functions regarding vehicle list windows
*/
enum {
PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
PLY_WND_PRC__SIZE_OF_ROW_BIG = 36,
};
enum VehicleListWindowWidgets {
VLW_WIDGET_CLOSEBOX = 0,
VLW_WIDGET_CAPTION,
@ -926,7 +908,7 @@ static void CreateVehicleListWindow(Window *w)
vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
}
static void DrawSmallOrderList(const Vehicle *v, int x, int y)
void DrawSmallOrderList(const Vehicle *v, int x, int y)
{
const Order *order;
int sel, i = 0;
@ -1275,7 +1257,11 @@ static void ShowVehicleListWindowLocal(PlayerID player, uint16 VLW_flag, Vehicle
void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type)
{
ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
if (player == _local_player && _patches.advanced_vehicle_list) {
ShowPlayerGroup(player, vehicle_type);
} else {
ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
}
}
void ShowVehicleListWindow(const Vehicle *v)

View File

@ -15,21 +15,35 @@ void InitializeVehiclesGuiList();
/* sorter stuff */
void RebuildVehicleLists();
void ResortVehicleLists();
void SortVehicleList(vehiclelist_d *vl);
void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type);
#define PERIODIC_RESORT_DAYS 10
extern const StringID _vehicle_sort_listing[];
/* Start of functions regarding vehicle list windows */
enum {
PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
PLY_WND_PRC__SIZE_OF_ROW_TINY = 13,
PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
PLY_WND_PRC__SIZE_OF_ROW_BIG = 36,
PLY_WND_PRC__SIZE_OF_ROW_BIG2 = 39,
};
/* Vehicle List Window type flags */
enum {
VLW_STANDARD = 0 << 8,
VLW_SHARED_ORDERS = 1 << 8,
VLW_STATION_LIST = 2 << 8,
VLW_DEPOT_LIST = 3 << 8,
VLW_GROUP_LIST = 4 << 8,
VLW_MASK = 0x700,
};
static inline bool ValidVLWFlags(uint16 flags)
{
return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST);
return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST || flags == VLW_GROUP_LIST);
}
void PlayerVehWndProc(Window *w, WindowEvent *e);
@ -54,6 +68,8 @@ void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, StationID
void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, TileIndex depot_tile);
void ShowReplaceVehicleWindow(VehicleType vehicletype);
void DrawSmallOrderList(const Vehicle *v, int x, int y);
void ShowReplaceGroupVehicleWindow(GroupID group, VehicleType veh);
static inline void DrawVehicleImage(const Vehicle *v, int x, int y, int count, int skip, VehicleID selection)
{

View File

@ -348,6 +348,7 @@ struct replaceveh_d {
bool update_left;
bool update_right;
bool init_lists;
GroupID sel_group;
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(replaceveh_d));
@ -478,6 +479,28 @@ struct dropdown_d {
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(dropdown_d));
struct vehiclelist_d {
const Vehicle** sort_list; // List of vehicles (sorted)
Listing *_sorting; // pointer to the appropiate subcategory of _sorting
uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
VehicleType vehicle_type; // The vehicle type that is sorted
list_d l; // General list struct
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
struct grouplist_d {
const Group **sort_list;
list_d l; // General list struct
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(grouplist_d));
struct groupveh_d : vehiclelist_d {
GroupID group_sel;
VehicleID vehicle_sel;
grouplist_d gl;
};
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(groupveh_d));
/****************** THESE ARE NOT WIDGET TYPES!!!!! *******************/
enum WindowWidgetBehaviours {