diff --git a/aircraft_cmd.c b/aircraft_cmd.c index b351fca6fd..691005349d 100644 --- a/aircraft_cmd.c +++ b/aircraft_cmd.c @@ -504,32 +504,38 @@ int32 CmdChangeAircraftServiceInt(int x, int y, uint32 flags, uint32 p1, uint32 return 0; } -// p1 = vehicle -// p2 = new cargo type(0xFF) -// p2 = skip check for stopped in hanger (0x0100) +/** Refits an aircraft to the specified cargo type. + * @param x,y unused + * @param p1 vehicle ID of the aircraft to refit + * @param p2 various bitstuffed elements + * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) + * - p2 = (bit 8) - skip check for stopped in hangar, used by autoreplace (p2 & 0x100) + * @todo p2 bit8 check NEEDS TO GO + */ int32 CmdRefitAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2) { - Vehicle *v,*u; + Vehicle *v; int pass, mail; int32 cost; - byte SkipStoppedInHangerCheck = (p2 & 0x100) >> 8; //excludes the cargo value - byte new_cargo_type = p2 & 0xFF; //gets the cargo number - AircraftVehicleInfo *avi; + bool SkipStoppedInHangerCheck = !!HASBIT(p2, 8); // XXX - needs to go, yes? + CargoID new_cid = p2 & 0xFF; //gets the cargo number + const AircraftVehicleInfo *avi; if (!IsVehicleIndex(p1)) return CMD_ERROR; v = GetVehicle(p1); - if (v->type != VEH_Aircraft) return CMD_ERROR; + if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR; + if (!SkipStoppedInHangerCheck && !CheckStoppedInHangar(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED); avi = AircraftVehInfo(v->engine_type); - if (!CheckOwnership(v->owner) || (!CheckStoppedInHangar(v) && !(SkipStoppedInHangerCheck))) - return CMD_ERROR; + /* Check cargo */ + if (new_cid > NUM_CARGO || !CanRefitTo(v, new_cid)) return CMD_ERROR; SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN); - switch (new_cargo_type) { + switch (new_cid) { case CT_PASSENGERS: pass = avi->passenger_capacity; break; @@ -548,24 +554,22 @@ int32 CmdRefitAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2) _aircraft_refit_capacity = pass; cost = 0; - if (IS_HUMAN_PLAYER(v->owner) && new_cargo_type != v->cargo_type) { + if (IS_HUMAN_PLAYER(v->owner) && new_cid != v->cargo_type) { cost = _price.aircraft_base >> 7; } if (flags & DC_EXEC) { + Vehicle *u; v->cargo_cap = pass; u = v->next; - mail = avi->mail_capacity; - if (new_cargo_type != CT_PASSENGERS) { - mail = 0; - } + mail = (new_cid != CT_PASSENGERS) ? 0 : avi->mail_capacity; u->cargo_cap = mail; //autorefitted planes wants to keep the cargo //it will be checked if the cargo is valid in CmdReplaceVehicle if (!(SkipStoppedInHangerCheck)) v->cargo_count = u->cargo_count = 0; - v->cargo_type = new_cargo_type; + v->cargo_type = new_cid; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); } diff --git a/aircraft_gui.c b/aircraft_gui.c index 60502ad33e..a263813bee 100644 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -205,64 +205,11 @@ static void ShowBuildAircraftWindow(uint tile) } } -#define MAX_REFIT 0xFF - -const byte _aircraft_refit_normal[] = { - CT_PASSENGERS, - CT_MAIL, - CT_GOODS, - CT_VALUABLES, - MAX_REFIT -}; - -const byte _aircraft_refit_arctic[] = { - CT_PASSENGERS, - CT_MAIL, - CT_GOODS, - CT_FOOD, - MAX_REFIT -}; - -const byte _aircraft_refit_desert[] = { - CT_PASSENGERS, - CT_MAIL, - CT_FRUIT, - CT_GOODS, - CT_DIAMONDS, - MAX_REFIT -}; - -const byte _aircraft_refit_candy[] = { - CT_PASSENGERS, - CT_SUGAR, - CT_TOYS, - CT_CANDY, - CT_COLA, - CT_COTTON_CANDY, - CT_BUBBLES, - CT_TOFFEE, - CT_BATTERIES, - CT_PLASTIC, - CT_FIZZY_DRINKS, - MAX_REFIT -}; - -const byte * const _aircraft_refit_types[4] = { - _aircraft_refit_normal, _aircraft_refit_arctic, _aircraft_refit_desert, _aircraft_refit_candy -}; - -#undef MAX_REFIT - static void AircraftRefitWndProc(Window *w, WindowEvent *e) { - switch(e->event) { + switch (e->event) { case WE_PAINT: { - Vehicle *v = GetVehicle(w->window_number); - const byte *b; - int sel; - int x,y; - byte color; - int cargo; + const Vehicle *v = GetVehicle(w->window_number); SetDParam(0, v->string_id); SetDParam(1, v->unitnumber); @@ -271,72 +218,31 @@ static void AircraftRefitWndProc(Window *w, WindowEvent *e) DrawString(1, 15, STR_A040_SELECT_CARGO_TYPE_TO_CARRY, 0); /* TODO: Support for custom GRFSpecial-specified refitting! --pasky */ + WP(w,refit_d).cargo = DrawVehicleRefitWindow(v, WP(w, refit_d).sel); - cargo = -1; - x = 6; - y = 25; - sel = WP(w,refit_d).sel; - -#define show_cargo(ctype) { \ - color = 16; \ - if (sel == 0) { \ - cargo = ctype; \ - color = 12; \ - } \ - sel--; \ - DrawString(x, y, _cargoc.names_s[ctype], color); \ - y += 10; \ - } - - if (_engine_refit_masks[v->engine_type]) { - uint32 mask = _engine_refit_masks[v->engine_type]; - int cid = 0; - - for (; mask; mask >>= 1, cid++) { - if (!(mask & 1)) // not this cid - continue; - if (!(_local_cargo_id_landscape[cid] & (1 << _opt.landscape))) // not in this landscape - continue; - - show_cargo(_local_cargo_id_ctype[cid]); - } - - } else { // generic refit list - b = _aircraft_refit_types[_opt.landscape]; - do { - show_cargo(*b); - } while (*++b != 0xFF); - } - -#undef show_cargo - - WP(w,refit_d).cargo = cargo; - - if (cargo != -1) { - int32 cost = DoCommandByTile(v->tile, v->index, cargo, DC_QUERY_COST, CMD_REFIT_AIRCRAFT); - if (cost != CMD_ERROR) { + if (WP(w,refit_d).cargo != CT_INVALID) { + int32 cost = DoCommandByTile(v->tile, v->index, WP(w,refit_d).cargo, DC_QUERY_COST, CMD_REFIT_AIRCRAFT); + if (!CmdFailed(cost)) { SetDParam(2, cost); - SetDParam(0, _cargoc.names_long_p[cargo]); + SetDParam(0, _cargoc.names_long_p[WP(w,refit_d).cargo]); SetDParam(1, _aircraft_refit_capacity); DrawString(1, 137, STR_A041_NEW_CAPACITY_COST_OF_REFIT, 0); } } - - break; - } + } break; case WE_CLICK: switch(e->click.widget) { case 2: { /* listbox */ - int y = e->click.pt.y - 25; - if (y >= 0) { - WP(w,refit_d).sel = y / 10; - SetWindowDirty(w); - } - } break; + int y = e->click.pt.y - 25; + if (y >= 0) { + WP(w,refit_d).sel = y / 10; + SetWindowDirty(w); + } + } break; case 4: /* refit button */ - if (WP(w,refit_d).cargo != 0xFF) { - Vehicle *v = GetVehicle(w->window_number); + if (WP(w,refit_d).cargo != CT_INVALID) { + const Vehicle *v = GetVehicle(w->window_number); if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo, NULL, CMD_REFIT_AIRCRAFT | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT))) DeleteWindow(w); } diff --git a/command.c b/command.c index 67fbb05f03..7eae3c4fdd 100644 --- a/command.c +++ b/command.c @@ -229,7 +229,7 @@ static CommandProc * const _command_proc_table[] = { CmdBuildAircraft, /* 61 */ CmdSendAircraftToHangar, /* 62 */ CmdChangeAircraftServiceInt, /* 63 */ - CmdRefitAircraft, /* 64 <-- REFIT: Hackykid */ + CmdRefitAircraft, /* 64 */ CmdPlaceSign, /* 65 */ CmdRenameSign, /* 66 */ @@ -263,7 +263,7 @@ static CommandProc * const _command_proc_table[] = { CmdBuildShip, /* 88 */ CmdSendShipToDepot, /* 89 */ CmdChangeShipServiceInt, /* 90 */ - CmdRefitShip, /* 91 <-- REFIT: Hackykid */ + CmdRefitShip, /* 91 */ NULL, /* 92 */ NULL, /* 93 */ @@ -284,7 +284,7 @@ static CommandProc * const _command_proc_table[] = { CmdLevelLand, /* 105 */ - CmdRefitRailVehicle, /* 106 <-- REFIT: Hackykid */ + CmdRefitRailVehicle, /* 106 */ CmdRestoreOrderIndex, /* 107 */ CmdBuildLock, /* 108 */ NULL, /* 109 */ diff --git a/engine.c b/engine.c index 64d2c826a2..bcbb87438a 100644 --- a/engine.c +++ b/engine.c @@ -20,34 +20,74 @@ enum { ENGINE_PREVIEWING = 4, }; -/* This maps per-landscape cargo ids to globally unique cargo ids usable ie. in - * the custom GRF files. It is basically just a transcribed table from - * TTDPatch's newgrf.txt. */ -byte _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO] = { - /* LT_NORMAL */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12 }, - /* LT_HILLY */ { 0, 1, 2, 3, 4, 5, 6, 7, 28, 11, 10, 12 }, - /* LT_DESERT */ { 0, 16, 2, 3, 13, 5, 6, 7, 14, 15, 10, 12 }, - /* LT_CANDY */ { 0, 17, 2, 18, 19, 20, 21, 22, 23, 24, 25, 26 }, - // 27 is paper in temperate climate in TTDPatch - // Following can be renumbered: - // 29 is the default cargo for the purpose of spritesets - // 30 is the purchase list image (the equivalent of 0xff) for the purpose of spritesets +/** TRANSLATE FROM LOCAL CARGO TO GLOBAL CARGO ID'S. + * This maps the per-landscape cargo ID's to globally unique cargo ID's usable ie. in + * the custom GRF files. It is basically just a transcribed table from TTDPatch's newgrf.txt. + */ +const CargoID _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO] = { + /* LT_NORMAL */ {GC_PASSENGERS, GC_COAL, GC_MAIL, GC_OIL, GC_LIVESTOCK, GC_GOODS, GC_GRAIN, GC_WOOD, GC_IRON_ORE, GC_STEEL, GC_VALUABLES, GC_PAPER_TEMP}, + /* LT_HILLY */ {GC_PASSENGERS, GC_COAL, GC_MAIL, GC_OIL, GC_LIVESTOCK, GC_GOODS, GC_GRAIN, GC_WOOD, GC_INVALID, GC_PAPER, GC_VALUABLES, GC_FOOD }, + /* LT_DESERT */ {GC_PASSENGERS, GC_RUBBER,GC_MAIL, GC_OIL, GC_FRUIT, GC_GOODS, GC_GRAIN, GC_WOOD, GC_COPPER_ORE, GC_WATER, GC_VALUABLES, GC_FOOD }, + /* LT_CANDY */ {GC_PASSENGERS, GC_SUGAR, GC_MAIL, GC_TOYS,GC_BATTERIES, GC_CANDY, GC_TOFFEE,GC_COLA, GC_COTTON_CANDY,GC_BUBBLES,GC_PLASTIC, GC_FIZZY_DRINKS }, + /** + * - GC_INVALID (255) means that cargo is not available for that climate + * - GC_PAPER_TEMP (27) is paper in temperate climate in TTDPatch + * Following can be renumbered: + * - GC_DEFAULT (29) is the defa ult cargo for the purpose of spritesets + * - GC_PURCHASE (30) is the purchase list image (the equivalent of 0xff) for the purpose of spritesets + */ }; -/* These two arrays provide a reverse mapping. */ -byte _local_cargo_id_ctype[NUM_CID] = { - CT_PASSENGERS, CT_COAL, CT_MAIL, CT_OIL, CT_LIVESTOCK, CT_GOODS, CT_GRAIN, CT_WOOD, // 0-7 - CT_IRON_ORE, CT_STEEL, CT_VALUABLES, CT_PAPER, CT_FOOD, CT_FRUIT, CT_COPPER_ORE, CT_WATER, // 8-15 - CT_RUBBER, CT_SUGAR, CT_TOYS, CT_BATTERIES, CT_CANDY, CT_TOFFEE, CT_COLA, CT_COTTON_CANDY, // 16-23 - CT_BUBBLES, CT_PLASTIC, CT_FIZZY_DRINKS, CT_PAPER /* unsup. */, CT_HILLY_UNUSED // 24-28 +/** BEGIN --- TRANSLATE FROM GLOBAL CARGO TO LOCAL CARGO ID'S **/ +/** Map global cargo ID's to local-cargo ID's */ +const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID] = { + CT_PASSENGERS,CT_COAL, CT_MAIL, CT_OIL, CT_LIVESTOCK,CT_GOODS, CT_GRAIN, CT_WOOD, /* 0- 7 */ + CT_IRON_ORE, CT_STEEL, CT_VALUABLES, CT_PAPER, CT_FOOD, CT_FRUIT, CT_COPPER_ORE, CT_WATER, /* 8-15 */ + CT_RUBBER, CT_SUGAR, CT_TOYS, CT_BATTERIES,CT_CANDY, CT_TOFFEE, CT_COLA, CT_COTTON_CANDY, /* 16-23 */ + CT_BUBBLES, CT_PLASTIC,CT_FIZZY_DRINKS,CT_PAPER /* unsup. */,CT_HILLY_UNUSED, /* 24-28 */ + CT_INVALID, CT_INVALID /* 29-30 */ }; -/* LT'th bit is set of the particular landscape if cargo available there. - * 1: LT_NORMAL, 2: LT_HILLY, 4: LT_DESERT, 8: LT_CANDY */ -byte _local_cargo_id_landscape[NUM_CID] = { - 15, 3, 15, 7, 3, 7, 7, 7, 1, 1, 7, 2, 7, // 0-12 - 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 2, // 13-28 +#define MC(cargo) (1 << cargo) +/** Bitmasked value where the global cargo ID is available in landscape + * 0: LT_NORMAL, 1: LT_HILLY, 2: LT_DESERT, 3: LT_CANDY */ +const uint32 _landscape_global_cargo_mask[NUM_LANDSCAPE] = +{ /* LT_NORMAL: temperate */ + MC(GC_PASSENGERS)|MC(GC_COAL)|MC(GC_MAIL)|MC(GC_OIL)|MC(GC_LIVESTOCK)|MC(GC_GOODS)|MC(GC_GRAIN)|MC(GC_WOOD)| + MC(GC_IRON_ORE)|MC(GC_STEEL)|MC(GC_VALUABLES)|MC(GC_FOOD)|MC(GC_UNDEFINED), + /* LT_HILLY: arctic */ + MC(GC_PASSENGERS)|MC(GC_COAL)|MC(GC_MAIL)|MC(GC_OIL)|MC(GC_LIVESTOCK)|MC(GC_GOODS)| + MC(GC_GRAIN)|MC(GC_WOOD)|MC(GC_VALUABLES)|MC(GC_PAPER)|MC(GC_FOOD)|MC(GC_UNDEFINED), + /* LT_DESERT: rainforest/desert */ + MC(GC_PASSENGERS)|MC(GC_MAIL)|MC(GC_OIL)|MC(GC_GOODS)|MC(GC_GRAIN)|MC(GC_WOOD)| + MC(GC_VALUABLES)|MC(GC_FOOD)|MC(GC_FRUIT)|MC(GC_COPPER_ORE)|MC(GC_WATER)|MC(GC_RUBBER), + /* LT_CANDY: toyland */ + MC(GC_PASSENGERS)|MC(GC_MAIL)|MC(GC_SUGAR)|MC(GC_TOYS)|MC(GC_BATTERIES)|MC(GC_CANDY)| + MC(GC_TOFFEE)|MC(GC_COLA)|MC(GC_COTTON_CANDY)|MC(GC_BUBBLES)|MC(GC_PLASTIC)|MC(GC_FIZZY_DRINKS) }; +/** END --- TRANSLATE FROM GLOBAL CARGO TO LOCAL CARGO ID'S **/ + +/** Bitmasked values of what type of cargo is refittable for the given vehicle-type. + * This coupled with the landscape information (_landscape_global_cargo_mask) gives + * us exactly what is refittable and what is not */ +const uint32 _default_refitmasks[NUM_VEHICLE_TYPES] = { + /* Trains */ + MC(GC_PASSENGERS)|MC(GC_COAL)|MC(GC_MAIL)|MC(GC_LIVESTOCK)|MC(GC_GOODS)|MC(GC_GRAIN)|MC(GC_WOOD)|MC(GC_IRON_ORE)| + MC(GC_STEEL)|MC(GC_VALUABLES)|MC(GC_PAPER)|MC(GC_FOOD)|MC(GC_FRUIT)|MC(GC_COPPER_ORE)|MC(GC_WATER)|MC(GC_SUGAR)| + MC(GC_TOYS)|MC(GC_CANDY)|MC(GC_TOFFEE)|MC(GC_COLA)|MC(GC_COTTON_CANDY)|MC(GC_BUBBLES)|MC(GC_PLASTIC)|MC(GC_FIZZY_DRINKS), + /* Road vehicles (not refittable by default) */ + 0, + /* Ships */ + MC(GC_COAL)|MC(GC_MAIL)|MC(GC_LIVESTOCK)|MC(GC_GOODS)|MC(GC_GRAIN)|MC(GC_WOOD)|MC(GC_IRON_ORE)|MC(GC_STEEL)|MC(GC_VALUABLES)| + MC(GC_PAPER)|MC(GC_FOOD)|MC(GC_FRUIT)|MC(GC_COPPER_ORE)|MC(GC_WATER)|MC(GC_RUBBER)|MC(GC_SUGAR)|MC(GC_TOYS)|MC(GC_BATTERIES)| + MC(GC_CANDY)|MC(GC_TOFFEE)|MC(GC_COLA)|MC(GC_COTTON_CANDY)|MC(GC_BUBBLES)|MC(GC_PLASTIC)|MC(GC_FIZZY_DRINKS), + /* Aircraft */ + MC(GC_PASSENGERS)|MC(GC_MAIL)|MC(GC_GOODS)|MC(GC_VALUABLES)|MC(GC_FOOD)|MC(GC_FRUIT)|MC(GC_SUGAR)|MC(GC_TOYS)| + MC(GC_BATTERIES)|MC(GC_CANDY)|MC(GC_TOFFEE)|MC(GC_COLA)|MC(GC_COTTON_CANDY)|MC(GC_BUBBLES)|MC(GC_PLASTIC)|MC(GC_FIZZY_DRINKS), + /* Special/Disaster */ + 0,0 +}; +#undef MC void ShowEnginePreviewWindow(int engine); @@ -267,7 +307,7 @@ byte _engine_original_sprites[TOTAL_NUM_ENGINES]; // (It isn't and shouldn't be like this in the GRF files since new cargo types // may appear in future - however it's more convenient to store it like this in // memory. --pasky) -static SpriteGroup _engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_CID]; +static SpriteGroup _engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID]; void SetCustomEngineSprites(byte engine, byte cargo, SpriteGroup *group) { @@ -462,10 +502,11 @@ static RealSpriteGroup* ResolveVehicleSpriteGroup(SpriteGroup *spritegroup, static SpriteGroup *GetVehicleSpriteGroup(byte engine, const Vehicle *v) { SpriteGroup *group; - byte cargo = CID_PURCHASE; + byte cargo = GC_PURCHASE; if (v != NULL) { cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; + assert(cargo != GC_INVALID); } group = &_engine_custom_sprites[engine][cargo]; @@ -483,7 +524,7 @@ int GetCustomEngineSprite(byte engine, const Vehicle *v, byte direction) { SpriteGroup *group; RealSpriteGroup *rsg; - byte cargo = CID_PURCHASE; + byte cargo = GC_PURCHASE; byte loaded = 0; bool in_motion = 0; int totalsets, spriteset; @@ -493,6 +534,8 @@ int GetCustomEngineSprite(byte engine, const Vehicle *v, byte direction) int capacity = v->cargo_cap; cargo = _global_cargo_id[_opt.landscape][v->cargo_type]; + assert(cargo != GC_INVALID); + if (capacity == 0) capacity = 1; loaded = (v->cargo_count * 100) / capacity; in_motion = (v->cur_speed != 0); diff --git a/engine.h b/engine.h index cbb860dc98..b588478fec 100644 --- a/engine.h +++ b/engine.h @@ -79,23 +79,56 @@ enum { RVI_WAGON = 2, }; +enum { + NUM_VEHICLE_TYPES = 6 +}; void AddTypeToEngines(void); void StartupEngines(void); - -extern byte _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO]; -enum { - CID_DEFAULT = 29, - CID_PURCHASE = 30, - NUM_CID = 31, +enum GlobalCargo { + GC_PASSENGERS = 0, + GC_COAL = 1, + GC_MAIL = 2, + GC_OIL = 3, + GC_LIVESTOCK = 4, + GC_GOODS = 5, + GC_GRAIN = 6, // GC_WHEAT / GC_MAIZE + GC_WOOD = 7, + GC_IRON_ORE = 8, + GC_STEEL = 9, + GC_VALUABLES = 10, // GC_GOLD / GC_DIAMONDS + GC_PAPER = 11, + GC_FOOD = 12, + GC_FRUIT = 13, + GC_COPPER_ORE = 14, + GC_WATER = 15, + GC_RUBBER = 16, + GC_SUGAR = 17, + GC_TOYS = 18, + GC_BATTERIES = 19, + GC_CANDY = 20, + GC_TOFFEE = 21, + GC_COLA = 22, + GC_COTTON_CANDY = 23, + GC_BUBBLES = 24, + GC_PLASTIC = 25, + GC_FIZZY_DRINKS = 26, + GC_PAPER_TEMP = 27, + GC_UNDEFINED = 28, // undefined; unused slot in arctic climate + GC_DEFAULT = 29, + GC_PURCHASE = 30, + GC_INVALID = 255, + NUM_GLOBAL_CID = 31 }; -extern byte _local_cargo_id_ctype[NUM_CID]; -extern byte _local_cargo_id_landscape[NUM_CID]; -extern uint32 _engine_refit_masks[256]; +VARDEF const uint32 _default_refitmasks[NUM_VEHICLE_TYPES]; +VARDEF const CargoID _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO]; +VARDEF const uint32 _landscape_global_cargo_mask[NUM_LANDSCAPE]; +VARDEF const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID]; -extern byte _engine_original_sprites[256]; +VARDEF uint32 _engine_refit_masks[256]; +VARDEF byte _engine_original_sprites[256]; void SetWagonOverrideSprites(byte engine, struct SpriteGroup *group, byte *train_id, int trains); void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group); // loaded is in percents, overriding_engine 0xffff is none diff --git a/lang/english.txt b/lang/english.txt index c15eb9b78e..892c6fae1c 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -2445,6 +2445,7 @@ STR_882C_BUILT_VALUE :{LTBLUE}{STRING STR_882D_VALUE :{LTBLUE}{STRING}{BLACK} Value: {LTBLUE}{CURRENCY} STR_882E :{WHITE}{STRING} STR_882F_LOADING_UNLOADING :{LTBLUE}Loading / Unloading +STR_TRAIN_MUST_BE_STOPPED :{WHITE}Train must be stopped inside depot STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT :{WHITE}Can't send train to depot... STR_8831_NO_MORE_SPACE_FOR_ORDERS :{WHITE}No more space for orders STR_8832_TOO_MANY_ORDERS :{WHITE}Too many orders diff --git a/newgrf.c b/newgrf.c index 30c5a8f064..3607326aad 100644 --- a/newgrf.c +++ b/newgrf.c @@ -1450,8 +1450,7 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len) return; } - if (ctype == 0xFF) - ctype = CID_PURCHASE; + if (ctype == GC_INVALID) ctype = GC_PURCHASE; if (wagover) { // TODO: No multiple cargo types per vehicle yet. --pasky @@ -1482,7 +1481,7 @@ static void NewVehicle_SpriteGroupMapping(byte *buf, int len) // TODO: No multiple cargo types per vehicle yet. --pasky SetWagonOverrideSprites(engine, &_cur_grffile->spritegroups[groupid], last_engines, last_engines_count); } else { - SetCustomEngineSprites(engine, CID_DEFAULT, &_cur_grffile->spritegroups[groupid]); + SetCustomEngineSprites(engine, GC_DEFAULT, &_cur_grffile->spritegroups[groupid]); last_engines[i] = engine; } } diff --git a/ship_cmd.c b/ship_cmd.c index 24d21fc472..b8190d6b7f 100644 --- a/ship_cmd.c +++ b/ship_cmd.c @@ -1067,36 +1067,39 @@ int32 CmdChangeShipServiceInt(int x, int y, uint32 flags, uint32 p1, uint32 p2) return 0; } - -// p1 = vehicle -// p2 = new cargo (0xFF) -// p2 = skip check for stopped in hanger (0x0100) +/** Refits a ship to the specified cargo type. + * @param x,y unused + * @param p1 vehicle ID of the ship to refit + * @param p2 various bitstuffed elements + * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) + * - p2 = (bit 8) - skip check for stopped in depot, used by autoreplace (p2 & 0x100) + * @todo p2 bit8 check NEEDS TO GO + */ int32 CmdRefitShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) { Vehicle *v; int32 cost; - byte SkipStoppedInDepotCheck = (p2 & 0x100) >> 8; //excludes the cargo value - - p2 = p2 & 0xFF; + CargoID new_cid = p2 & 0xFF; //gets the cargo number + bool SkipStoppedInDepotCheck = !!HASBIT(p2, 8); // XXX - needs to go, yes? if (!IsVehicleIndex(p1)) return CMD_ERROR; v = GetVehicle(p1); - if (v->type != VEH_Ship || !CheckOwnership(v->owner)) - return CMD_ERROR; - - if (!( SkipStoppedInDepotCheck )) { - if (!IsTileDepotType(v->tile, TRANSPORT_WATER) || - !(v->vehstatus&VS_STOPPED) || - v->u.ship.state != 0x80) + if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR; + if (!SkipStoppedInDepotCheck) { + if (!IsTileDepotType(v->tile, TRANSPORT_WATER) || !(v->vehstatus&VS_STOPPED) || v->u.ship.state != 0x80) return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN); - } + } + + /* Check cargo */ + if (!ShipVehInfo(v->engine_type)->refittable) return CMD_ERROR; + if (new_cid > NUM_CARGO || !CanRefitTo(v, new_cid)) return CMD_ERROR; SET_EXPENSES_TYPE(EXPENSES_SHIP_RUN); cost = 0; - if (IS_HUMAN_PLAYER(v->owner) && (byte)p2 != v->cargo_type) { + if (IS_HUMAN_PLAYER(v->owner) && new_cid != v->cargo_type) { cost = _price.ship_base >> 7; } @@ -1105,12 +1108,10 @@ int32 CmdRefitShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) //it will be checked if the cargo is valid in CmdRenewVehicle if (!(SkipStoppedInDepotCheck)) v->cargo_count = 0; - v->cargo_type = (byte)p2; + v->cargo_type = new_cid; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); } return cost; } - - diff --git a/ship_gui.c b/ship_gui.c index c0cf59e103..1e1428de28 100644 --- a/ship_gui.c +++ b/ship_gui.c @@ -45,23 +45,11 @@ static void DrawShipImage(const Vehicle *v, int x, int y, VehicleID selection) } } -const byte _ship_refit_types[4][16] = { - {CT_MAIL, CT_COAL, CT_LIVESTOCK, CT_GOODS, CT_GRAIN, CT_WOOD, CT_IRON_ORE, CT_STEEL, CT_VALUABLES, 255}, - {CT_MAIL, CT_COAL, CT_LIVESTOCK, CT_GOODS, CT_GRAIN, CT_WOOD, CT_PAPER, CT_FOOD, CT_VALUABLES, 255}, - {CT_MAIL, CT_FRUIT, CT_GOODS, CT_COPPER_ORE, CT_GRAIN, CT_WOOD, CT_WATER, CT_VALUABLES, 255}, - {CT_MAIL, CT_SUGAR, CT_TOYS, CT_CANDY, CT_COLA, CT_COTTON_CANDY, CT_BUBBLES, CT_TOFFEE, CT_BATTERIES, CT_PLASTIC, CT_FIZZY_DRINKS, 255}, -}; - static void ShipRefitWndProc(Window *w, WindowEvent *e) { - switch(e->event) { + switch (e->event) { case WE_PAINT: { - Vehicle *v = GetVehicle(w->window_number); - const byte *b; - int sel; - int x,y; - byte color; - int cargo; + const Vehicle *v = GetVehicle(w->window_number); SetDParam(0, v->string_id); SetDParam(1, v->unitnumber); @@ -69,71 +57,32 @@ static void ShipRefitWndProc(Window *w, WindowEvent *e) DrawString(1, 15, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, 0); - cargo = -1; - x = 6; - y = 25; - sel = WP(w,refit_d).sel; + /* TODO: Support for custom GRFSpecial-specified refitting! --pasky */ + WP(w,refit_d).cargo = DrawVehicleRefitWindow(v, WP(w, refit_d).sel);; -#define show_cargo(ctype) { \ - color = 16; \ - if (sel == 0) { \ - cargo = ctype; \ - color = 12; \ - } \ - sel--; \ - DrawString(x, y, _cargoc.names_s[ctype], color); \ - y += 10; \ - } - - if (_engine_refit_masks[v->engine_type]) { - uint32 mask = _engine_refit_masks[v->engine_type]; - int cid = 0; - - for (; mask; mask >>= 1, cid++) { - if (!(mask & 1)) // not this cid - continue; - if (!(_local_cargo_id_landscape[cid] & (1 << _opt.landscape))) // not in this landscape - continue; - - show_cargo(_local_cargo_id_ctype[cid]); - } - - } else { // generic refit list - b = _ship_refit_types[_opt.landscape]; - do { - show_cargo(*b); - } while (*++b != 255); - } - -#undef show_cargo - - WP(w,refit_d).cargo = cargo; - - if (cargo != -1) { - int32 cost = DoCommandByTile(v->tile, v->index, cargo, 0, CMD_REFIT_SHIP); - if (cost != CMD_ERROR) { + if (WP(w,refit_d).cargo != CT_INVALID) { + int32 cost = DoCommandByTile(v->tile, v->index, WP(w,refit_d).cargo, DC_QUERY_COST, CMD_REFIT_SHIP); + if (!CmdFailed(cost)) { SetDParam(2, cost); - SetDParam(0, _cargoc.names_long_p[cargo]); + SetDParam(0, _cargoc.names_long_p[WP(w,refit_d).cargo]); SetDParam(1, v->cargo_cap); DrawString(1, 137, STR_9840_NEW_CAPACITY_COST_OF_REFIT, 0); } } - - break; - } + } break; case WE_CLICK: switch(e->click.widget) { case 2: { /* listbox */ - int y = e->click.pt.y - 25; - if (y >= 0) { - WP(w,refit_d).sel = y / 10; - SetWindowDirty(w); - } - } break; + int y = e->click.pt.y - 25; + if (y >= 0) { + WP(w,refit_d).sel = y / 10; + SetWindowDirty(w); + } + } break; case 4: /* refit button */ - if (WP(w,refit_d).cargo != 0xFF) { - Vehicle *v = GetVehicle(w->window_number); + if (WP(w,refit_d).cargo != CT_INVALID) { + const Vehicle *v = GetVehicle(w->window_number); if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo, NULL, CMD_REFIT_SHIP | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP))) DeleteWindow(w); } diff --git a/station.h b/station.h index 08c3e01c0d..608688fc73 100644 --- a/station.h +++ b/station.h @@ -248,7 +248,7 @@ typedef struct StationSpec { StationLayout **layouts; /* Sprite offsets for renderdata->seq->image. spritegroup[0] is default - * whilst spritegroup[1] is "CID_PURCHASE". */ + * whilst spritegroup[1] is "GC_PURCHASE". */ SpriteGroup spritegroup[2]; } StationSpec; diff --git a/train_cmd.c b/train_cmd.c index 255ede1742..95770e4429 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -1282,25 +1282,31 @@ int32 CmdForceTrainProceed(int x, int y, uint32 flags, uint32 p1, uint32 p2) return 0; } -// p1 = vehicle to refit -// p2 = new cargo (0xFF) -// p2 = skip check for stopped in hanger (0x0100) +/** Refits a train to the specified cargo type. + * @param x,y unused + * @param p1 vehicle ID of the train to refit + * @param p2 various bitstuffed elements + * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) + * - p2 = (bit 8) - skip check for stopped in depot, used by autoreplace (p2 & 0x100) + * @todo p2 bit8 check NEEDS TO GO + */ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) { Vehicle *v; int32 cost; uint num; - - byte SkipStoppedInDepotCheck = (p2 & 0x100) >> 8; - - p2 = p2 & 0xFF; + CargoID new_cid = p2 & 0xFF; //gets the cargo number + bool SkipStoppedInDepotCheck = !!HASBIT(p2, 8); // XXX - needs to go, yes? if (!IsVehicleIndex(p1)) return CMD_ERROR; v = GetVehicle(p1); - if (v->type != VEH_Train || !CheckOwnership(v->owner) || ((CheckTrainStoppedInDepot(v) < 0) && !(SkipStoppedInDepotCheck))) - return CMD_ERROR; + if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; + if (!SkipStoppedInDepotCheck && CheckTrainStoppedInDepot(v) < 0) return_cmd_error(STR_TRAIN_MUST_BE_STOPPED); + + /* Check cargo */ + if (new_cid > NUM_CARGO) return CMD_ERROR; SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); @@ -1311,17 +1317,17 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) /* XXX: We also refit all the attached wagons en-masse if they * can be refitted. This is how TTDPatch does it. TODO: Have * some nice [Refit] button near each wagon. --pasky */ - if ((!(RailVehInfo(v->engine_type)->flags & RVI_WAGON) - || (_engine_refit_masks[v->engine_type] & (1 << p2))) - && (byte) p2 != v->cargo_type && v->cargo_cap != 0) { + if (!CanRefitTo(v, new_cid)) continue; + + if (new_cid != v->cargo_type && v->cargo_cap != 0) { cost += (_price.build_railvehicle >> 8); num += v->cargo_cap; if (flags & DC_EXEC) { - //autorefitted train cars wants to keep the cargo - //it will be checked if the cargo is valid in CmdReplaceVehicle + //autorefitted train cars wants to keep the cargo + //it will be checked if the cargo is valid in CmdReplaceVehicle if (!(SkipStoppedInDepotCheck)) v->cargo_count = 0; - v->cargo_type = (byte)p2; + v->cargo_type = new_cid; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); } } diff --git a/train_gui.c b/train_gui.c index c6bcf66cdd..3cc68811f7 100644 --- a/train_gui.c +++ b/train_gui.c @@ -662,23 +662,11 @@ void ShowTrainDepotWindow(uint tile) } } -const byte _rail_vehicle_refit_types[4][16] = { - { 0,1,2,4,5,6,7,8,9,10,0xFF }, // normal - { 0,1,4,5,6,7,9,11,10,0xFF }, // arctic - { 0,4,5,8,6,7,9,10,0xFF }, // desert - { 0,1,3,5,7,8,9,6,4,10,11,0xFF }// candy -}; - static void RailVehicleRefitWndProc(Window *w, WindowEvent *e) { - switch(e->event) { + switch (e->event) { case WE_PAINT: { - Vehicle *v = GetVehicle(w->window_number); - const byte *b; - int sel; - int x,y; - byte color; - int cargo; + const Vehicle *v = GetVehicle(w->window_number); SetDParam(0, v->string_id); SetDParam(1, v->unitnumber); @@ -686,57 +674,19 @@ static void RailVehicleRefitWndProc(Window *w, WindowEvent *e) DrawString(1, 15, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, 0); - cargo = -1; - x = 6; - y = 25; - sel = WP(w,refit_d).sel; + /* TODO: Support for custom GRFSpecial-specified refitting! --pasky */ + WP(w,refit_d).cargo = DrawVehicleRefitWindow(v, WP(w, refit_d).sel); -#define show_cargo(ctype) { \ - color = 16; \ - if (sel == 0) { \ - cargo = ctype; \ - color = 12; \ - } \ - sel--; \ - DrawString(x, y, _cargoc.names_s[ctype], color); \ - y += 10; \ - } - - if (_engine_refit_masks[v->engine_type]) { - uint32 mask = _engine_refit_masks[v->engine_type]; - int cid = 0; - - for (; mask; mask >>= 1, cid++) { - if (!(mask & 1)) // not this cid - continue; - if (!(_local_cargo_id_landscape[cid] & (1 << _opt.landscape))) // not in this landscape - continue; - - show_cargo(_local_cargo_id_ctype[cid]); - } - - } else { // generic refit list - b = _rail_vehicle_refit_types[_opt.landscape]; - do { - show_cargo(*b); - } while (*++b != 255); - } - -#undef show_cargo - - WP(w,refit_d).cargo = cargo; - - if (cargo != -1) { - int32 cost = DoCommandByTile(v->tile, v->index, cargo, 0, CMD_REFIT_RAIL_VEHICLE); - if (cost != CMD_ERROR) { + if (WP(w,refit_d).cargo != CT_INVALID) { + int32 cost = DoCommandByTile(v->tile, v->index, WP(w,refit_d).cargo, DC_QUERY_COST, CMD_REFIT_RAIL_VEHICLE); + if (!CmdFailed(cost)) { SetDParam(2, cost); - SetDParam(0, _cargoc.names_long_p[cargo]); + SetDParam(0, _cargoc.names_long_p[WP(w,refit_d).cargo]); SetDParam(1, _returned_refit_amount); DrawString(1, 137, STR_9840_NEW_CAPACITY_COST_OF_REFIT, 0); } } - break; - } + } break; case WE_CLICK: switch(e->click.widget) { @@ -748,8 +698,8 @@ static void RailVehicleRefitWndProc(Window *w, WindowEvent *e) } } break; case 4: /* refit button */ - if (WP(w,refit_d).cargo != 0xFF) { - Vehicle *v = GetVehicle(w->window_number); + if (WP(w,refit_d).cargo != CT_INVALID) { + const Vehicle *v = GetVehicle(w->window_number); if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo, NULL, CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE))) DeleteWindow(w); } @@ -809,9 +759,9 @@ static Widget _train_view_widgets[] = { static void TrainViewWndProc(Window *w, WindowEvent *e) { - switch(e->event) { + switch (e->event) { case WE_PAINT: { - Vehicle *v, *u; + const Vehicle *v, *u; StringID str; v = GetVehicle(w->window_number); @@ -820,22 +770,16 @@ static void TrainViewWndProc(Window *w, WindowEvent *e) SETBIT(w->disabled_state, 12); - /* See if any carriage can be refitted */ + /* See if any vehicle can be refitted */ for ( u = v; u != NULL; u = u->next) { - if (_engine_refit_masks[u->engine_type] != 0) { + if (_engine_refit_masks[u->engine_type] != 0 || + (!(RailVehInfo(v->engine_type)->flags & RVI_WAGON) && v->cargo_cap != 0)) { CLRBIT(w->disabled_state, 12); /* We have a refittable carriage, bail out */ break; } } - /* Above code doesn't seem to handle non-newgrf engines, do it separately - TODO: handle engines which are NOT the head of the train, but don't break wagons */ - if (v->cargo_cap != 0) { - /* we can refit this engine */ - CLRBIT(w->disabled_state, 12); - } - /* draw widgets & caption */ SetDParam(0, v->string_id); SetDParam(1, v->unitnumber); diff --git a/ttd.h b/ttd.h index 3c9e4ae2b5..0181987793 100644 --- a/ttd.h +++ b/ttd.h @@ -67,6 +67,7 @@ typedef struct DrawPixelInfo DrawPixelInfo; typedef uint16 VehicleID; typedef byte PlayerID; typedef byte OrderID; +typedef byte CargoID; typedef uint16 StringID; typedef uint16 SpriteID; typedef uint32 PalSpriteID; diff --git a/vehicle.c b/vehicle.c index 025bb0679f..a705ed93bb 100644 --- a/vehicle.c +++ b/vehicle.c @@ -501,6 +501,28 @@ bool CanFillVehicle(Vehicle *v) return false; } +/** Check if a given vehicle (type) can be refitted to a given cargo + * @param *v vehicle to check + * @param cid_to check refit to this cargo-type + * @return true if it is possible, false otherwise + */ +bool CanRefitTo(const Vehicle *v, CargoID cid_to) +{ + CargoID cid = _global_cargo_id[_opt_ptr->landscape][cid_to]; + + if (cid == GC_INVALID) return false; + + if (_engine_refit_masks[v->engine_type]) { + if (!HASBIT(_engine_refit_masks[v->engine_type], cid)) return false; + } else { + /* If we are talking about normal vehicles (no newgrf), you can only refit engines */ + if (v->type == VEH_Train && (RailVehInfo(v->engine_type)->flags & RVI_WAGON)) return false; + if (!HASBIT(_default_refitmasks[v->type - VEH_Train], cid)) return false; + } + + return true; +} + static void DoDrawVehicle(Vehicle *v) { uint32 image = v->cur_image; diff --git a/vehicle.h b/vehicle.h index a86a1ce378..f0699b5a6e 100644 --- a/vehicle.h +++ b/vehicle.h @@ -262,6 +262,7 @@ Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z); void InitializeTrains(void); bool CanFillVehicle(Vehicle *v); +bool CanRefitTo(const Vehicle *v, CargoID cid_to); void ViewportAddVehicles(DrawPixelInfo *dpi); diff --git a/vehicle_gui.c b/vehicle_gui.c index 5832498e88..c83f287ec0 100644 --- a/vehicle_gui.c +++ b/vehicle_gui.c @@ -165,6 +165,42 @@ void DrawVehicleProfitButton(Vehicle *v, int x, int y) DrawSprite(SPR_BLOT | ormod, x, y); } +/** Draw the list of available refit options. + * Draw the list and highlight the selected refit option (if any) + * @param *v vehicle(type) to get the refit-options of + * @param sel selected refit cargo-type in the window + * @return the cargo type that is hightlighted, CT_INVALID if none + */ +CargoID DrawVehicleRefitWindow(const Vehicle *v, int sel) +{ + uint32 cmask; + CargoID cid, cargo = CT_INVALID; + int y = 25; +#define show_cargo(ctype) { \ + byte colour = 16; \ + if (sel == 0) { \ + cargo = ctype; \ + colour = 12; \ +} \ + sel--; \ + DrawString(6, y, _cargoc.names_s[ctype], colour); \ + y += 10; \ +} + + /* Check if engine has custom refit or normal ones, and get its bitmasked value. + * Now just and it with the bitmasked available cargo on the current landscape, and + * where the bits are set: those are available */ + cmask = (_engine_refit_masks[v->engine_type] != 0) ? _engine_refit_masks[v->engine_type] : _default_refitmasks[v->type - VEH_Train]; + cmask &= _landscape_global_cargo_mask[_opt_ptr->landscape]; + + /* Check which cargo has been selected from the refit window and draw list */ + for (cid = 0; cmask != 0; cmask >>= 1, cid++) { + if (HASBIT(cmask, 0)) // vehicle is refittable to this cargo + show_cargo(_local_cargo_id_ctype[cid]); + } + return cargo; +} + /************ Sorter functions *****************/ int CDECL GeneralOwnerSorter(const void *a, const void *b) { diff --git a/vehicle_gui.h b/vehicle_gui.h index 1af4e0abae..a4e010c696 100644 --- a/vehicle_gui.h +++ b/vehicle_gui.h @@ -6,6 +6,7 @@ struct vehiclelist_d; void DrawVehicleProfitButton(Vehicle *v, int x, int y); +CargoID DrawVehicleRefitWindow(const Vehicle *v, int sel); void InitializeVehiclesGuiList(void); /* sorter stuff */