diff --git a/command.c b/command.c index df2230f5fd..bd88aafd2b 100644 --- a/command.c +++ b/command.c @@ -160,6 +160,8 @@ DEF_COMMAND(CmdBuildLock); DEF_COMMAND(CmdStartScenario); +DEF_COMMAND(CmdBuildManySignals); + /* The master command table */ static CommandProc * const _command_proc_table[] = { CmdBuildRailroadTrack, /* 0 */ @@ -294,8 +296,8 @@ static CommandProc * const _command_proc_table[] = { CmdRefitRailVehicle, /* 106 */ CmdRestoreOrderIndex, /* 107 */ CmdBuildLock, /* 108 */ - CmdStartScenario /* 109 */ - + CmdStartScenario, /* 109 */ + CmdBuildManySignals, /* 110 */ //CmdDestroyIndustry, /* 109 */ }; diff --git a/command.h b/command.h index b2e3306226..49ba78ebf8 100644 --- a/command.h +++ b/command.h @@ -142,6 +142,7 @@ enum { CMD_BUILD_LOCK = 108, CMD_START_SCENARIO = 109, + CMD_BUILD_MANY_SIGNALS = 110, //CMD_DESTROY_INDUSTRY = 109, }; diff --git a/lang/english.txt b/lang/english.txt index 3571feaa4a..443294c3d9 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -999,6 +999,7 @@ STR_CONFIG_PATCHES_SERVINT_SHIPS_DISABLED :{LTBLUE}Default service interval fo STR_CONFIG_PATCHES_COLORED_NEWS_DATE :{LTBLUE}Coloured news appears in: {ORANGE}{STRING} STR_CONFIG_PATCHES_STARTING_DATE :{LTBLUE}Starting date: {ORANGE}{STRING} STR_CONFIG_PATCHES_SMOOTH_ECONOMY :{LTBLUE}Enable smooth economy (more, smaller changes) +STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY :{LTBLUE}When dragging place signals every: {ORANGE}{STRING} tile(s) STR_CONFIG_PATCHES_GUI :{BLACK}Interface STR_CONFIG_PATCHES_CONSTRUCTION :{BLACK}Construction diff --git a/rail_cmd.c b/rail_cmd.c index dd6fd90a92..cdb8ad6581 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -521,8 +521,8 @@ static const struct { /* Build either NE or NW sequence of tracks. - * p1 0:15 - start pt X - * p1 16:31 - start pt y + * p1 0:15 - end pt X + * p1 16:31 - end pt y * * p2 0:3 - rail type * p2 4:7 - rail direction @@ -538,7 +538,7 @@ int32 CmdBuildRailroadTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) SndPlayTileFx(0x1E, TILE_FROM_XY(x,y)); - /* unpack start point */ + /* unpack end point */ sx = (p1 & 0xFFFF) & ~0xF; sy = (p1 >> 16) & ~0xF; @@ -837,7 +837,11 @@ int32 CmdRenameCheckpoint(int x, int y, uint32 flags, uint32 p1, uint32 p2) } -/* build signals, p1 = track */ +/* build signals, alternate between double/single, signal/semaphore, pre/exit/combo -signals + p1 = (lower 3 bytes) - track-orientation + p1 = (byte 4) - semaphores/signals + p2 = used for CmdBuildManySignals() to copy style first signal + */ int32 CmdBuildSignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) { uint tile; @@ -845,8 +849,7 @@ int32 CmdBuildSignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) int32 cost; int track = p1 & 0x7; - assert(track >= 0 && track < 6); - assert(p2 == 0); + assert(track >= 0 && track < 6); // only 6 possible track-combinations SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); @@ -855,12 +858,12 @@ int32 CmdBuildSignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (!EnsureNoVehicle(tile)) return CMD_ERROR; - _error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK; - // must be railway, and not a depot, and it must have a track in the suggested position. if (!IS_TILETYPE(tile, MP_RAILWAY) || (m5=_map5[tile], m5&0x80) || !HASBIT(m5, track)) return CMD_ERROR; + _error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK; + // check rail combination { byte m = m5 & RAIL_BIT_MASK; @@ -873,40 +876,54 @@ int32 CmdBuildSignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (!CheckTileOwnership(tile)) return CMD_ERROR; - // If it had signals previously it's no cost to build. + // If it had signals previously it is free to change orientation/pre-exit-combo signals cost = 0; if (!(m5 & RAIL_TYPE_SIGNALS)) { cost = _price.build_signals; + // if converting signals<->semaphores, charge the player for it + } else if (p2 && ((HASBIT(p1, 3) && !HASBIT(_map3_hi[tile], 2)) || (!HASBIT(p1, 3) && HASBIT(_map3_hi[tile], 2)) ) ) { + cost += _price.build_signals + _price.remove_signals; } if (flags & DC_EXEC) { - if (!(m5 & RAIL_TYPE_SIGNALS)) { + if (!(m5 & RAIL_TYPE_SIGNALS)) { // if there are no signals yet present on the track _map5[tile] |= RAIL_TYPE_SIGNALS; // change into signals _map2[tile] |= 0xF0; // all signals are on _map3_lo[tile] &= ~0xF0; // no signals built by default _map3_hi[tile] = (p1 & 8) ? 4 : 0;// initial presignal state, semaphores depend on ctrl key - goto ignore_presig; + if (!p2) + goto ignore_presig; } - if (!(p1 & 8)) { - byte a,b,c,d; -ignore_presig: - a = _signals_table[track]; // signal for this track in one direction - b = _signals_table[track + 8]; // signal for this track in the other direction - c = a | b; - d = _map3_lo[tile] & c; // mask of built signals. it only affects &0xF0 - - // Alternate between a|b, b, a - if ( d != 0 && d != a) { - c = (d==c)?b:a; - } + if (!p2) { // not called from CmdBuildManySignals + if (!HASBIT(p1, 3)) { // not CTRL pressed + byte a,b,c,d; + ignore_presig: + a = _signals_table[track]; // signal for this track in one direction + b = _signals_table[track + 8]; // signal for this track in the other direction + c = a | b; + d = _map3_lo[tile] & c; // mask of built signals. it only affects &0xF0 + + // Alternate between a|b, b, a + if ( d != 0 && d != a) { + c = (d==c)?b:a; + } - _map3_lo[tile] = (_map3_lo[tile]&~(a|b)) | c; + _map3_lo[tile] = (_map3_lo[tile]&~(a|b)) | c; + } else // CTRL pressed + _map3_hi[tile] = (_map3_hi[tile] & ~3) | ((_map3_hi[tile] + 1) & 3); } else { - // toggle between the signal types. Using low 2 bits of map3_hi. - _map3_hi[tile] = (_map3_hi[tile] & ~3) | ((_map3_hi[tile] + 1) & 3); + /* If CmdBuildManySignals is called with copying signals, just copy the style of the first signal + * given as parameter by CmdBuildManySignals */ + switch (track) { + case 2: case 4: _map3_lo[tile] = (p2&0xC0) | _map3_lo[tile]&~0xC0; break; + case 3: case 5: _map3_lo[tile] = (p2&0x30) | _map3_lo[tile]&~0x30; break; + default : _map3_lo[tile] = (p2&0xF0) | _map3_lo[tile]&0xF; + } + // convert between signal<->semaphores when dragging + HASBIT(p1, 3) ? SETBIT(_map3_hi[tile], 2) : CLRBIT(_map3_hi[tile], 2); } MarkTileDirtyByTile(tile); @@ -916,6 +933,108 @@ ignore_presig: return cost; } +/* Build many signals by dragging: AutoSignals + x,y= start tile + p1 = end tile + p2 = (byte 0) - 0 = build, 1 = remove signals + p2 = (byte 3) - 0 = signals, 1 = semaphores + p2 = (byte 7-4) - track-orientation + p2 = (byte 8-) - track style + */ +int32 CmdBuildManySignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + int ex, ey, railbit; + bool error = true; + TileIndex tile = TILE_FROM_XY(x, y); + int32 ret, total_cost, signal_ctr; + byte m5, semaphores = (HASBIT(p2, 3)) ? 8 : 0; + int mode = (p2 >> 4)&0xF; + // for vertical/horizontal tracks, double the given signals density + // since the original amount will be too dense (shorter tracks) + byte signal_density = (mode == 1 || mode == 2) ? _patches.drag_signals_density : _patches.drag_signals_density * 2; + byte signals = p2 >> 8; + mode = p2 & 0x1; // build/remove signals + + /* unpack end tile */ + ex = GET_TILE_X(p1)*16; + ey = GET_TILE_Y(p1)*16; + + railbit = _railbit.initial[((p2 >> 4)&0xF) + (x > ex ? 4 : 0) + (y > ey ? 8 : 0)]; + + // copy the signal-style of the first rail-piece if existing + m5 = _map5[tile]; + if (!(m5 & RAIL_TYPE_SPECIAL) && (m5 & RAIL_BIT_MASK) && (m5 & RAIL_TYPE_SIGNALS)) { + if (m5 & 0x3) // X,Y direction tracks + signals = _map3_lo[tile]&0xC0; + else { + /* W-E or N-S direction, only copy the side which was chosen, leave + * the other side alone */ + switch (signals) { + case 0x20: case 8: /* east corner (N-S), south corner (W-E) */ + if (_map3_lo[tile]&0x30) + signals = _map3_lo[tile]&0x30; + else + signals = 0x30 | _map3_lo[tile]&0xC0; + break; + case 0x10: case 4: /* west corner (N-S), north corner (W-E) */ + if (_map3_lo[tile]&0xC0) + signals = _map3_lo[tile]&0xC0; + else + signals = 0xC0 | _map3_lo[tile]&0x30; + break; + } + } + + semaphores = (_map3_hi[tile] & ~3) ? 8 : 0; // copy signal/semaphores style (independent of CTRL) + } else { // no signals exist, drag a two-way signal stretch + switch (signals) { + case 0x20: case 8: /* east corner (N-S), south corner (W-E) */ + signals = 0x30; break; + case 0x10: case 4: /* west corner (N-S), north corner (W-E) */ + signals = 0xC0; + } + } + + /* signal_density_ctr - amount of tiles already processed + * signals_density - patch setting to put signal on every Nth tile (double space on |, -- tracks) + ********** + * railbit - direction of autorail + * semaphores - semaphores or signals + * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way) + and convert all others to semaphore/signal + * mode - 1 remove signals, 0 build signals */ + signal_ctr = total_cost = 0; + for(;;) { + // only build/remove signals with the specified density + if ((signal_ctr % signal_density) == 0 ) { + ret = DoCommand(x, y, (railbit & 7) | semaphores, signals, flags, (mode == 1) ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS); + + /* Abort placement for any other error then NOT_SUITEABLE_TRACK + * This includes vehicles on track, competitor's tracks, etc. */ + if (ret == CMD_ERROR) { + if (_error_message != STR_1005_NO_SUITABLE_RAILROAD_TRACK && mode != 1) { + return CMD_ERROR; + } + } else { + error = false; + total_cost += ret; + } + } + + if (ex == x && ey == y) // reached end of drag + break; + + x += _railbit.xinc[railbit]; + y += _railbit.yinc[railbit]; + signal_ctr++; + + // toggle railbit for the diagonal tiles (|, -- tracks) + if (railbit & 0x6) railbit ^= 1; + } + + return (error) ? CMD_ERROR : total_cost; +} + /* Remove signals * p1 = unused * p2 = unused @@ -952,6 +1071,7 @@ int32 CmdRemoveSignals(int x, int y, uint32 flags, uint32 p1, uint32 p2) byte bits = _map5[tile]; _map5[tile] &= ~RAIL_TYPE_SIGNALS; _map2[tile] &= ~0xF0; + CLRBIT(_map3_hi[tile], 2); // remove any possible semaphores /* TTDBUG: this code contains a bug, if a tile contains 2 signals * on separate tracks, it won't work properly for the 2nd track */ diff --git a/rail_gui.c b/rail_gui.c index ddbbd26b8e..a2e830ec59 100644 --- a/rail_gui.c +++ b/rail_gui.c @@ -140,7 +140,7 @@ static void PlaceRail_Station(uint tile) } } -static void PlaceRail_Signals(uint tile) +static void GenericPlaceSignals(uint tile) { uint trackstat; int i; @@ -199,6 +199,11 @@ static void PlaceRail_ConvertRail(uint tile) VpStartPlaceSizing(tile, VPM_X_AND_Y | (1<<4)); } +static void PlaceRail_AutoSignals(uint tile) +{ + VpStartPlaceSizing(tile, VPM_SIGNALDIRS); +} + static void BuildRailClick_AutoRail(Window *w) { HandlePlacePushButton(w, 3, _cur_railtype + SPR_OPENTTD_BASE + 4, 1, PlaceRail_AutoRail); @@ -255,9 +260,9 @@ static void BuildRailClick_Station(Window *w) if (HandlePlacePushButton(w, 12, 0x514, 1, PlaceRail_Station)) ShowStationBuilder(); } -static void BuildRailClick_Signals(Window *w) +static void BuildRailClick_AutoSignals(Window *w) { - HandlePlacePushButton(w, 13, ANIMCURSOR_BUILDSIGNALS, 1, PlaceRail_Signals); + HandlePlacePushButton(w, 13, ANIMCURSOR_BUILDSIGNALS , 1, PlaceRail_AutoSignals); } static void BuildRailClick_Bridge(Window *w) @@ -508,6 +513,42 @@ static void HandleAutodirPlacement() } } +static void HandleAutoSignalPlacement() +{ + TileHighlightData *thd = &_thd; + int mode; + uint trackstat = 0; + + int dx = thd->selstart.x - (thd->selend.x&~0xF); + int dy = thd->selstart.y - (thd->selend.y&~0xF); + + if (dx == 0 && dy == 0 ) // 1x1 tile signals + GenericPlaceSignals(TILE_FROM_XY(thd->selend.x, thd->selend.y)); + else { // signals have been dragged + if (thd->drawstyle == HT_RECT) { // X,Y direction + if (dx == 0) + mode = VPM_FIX_X; + else if (dy == 0) + mode = VPM_FIX_Y; + + trackstat = 0xC0; + } else { // W-E or N-S direction + mode = thd->drawstyle & 1 ? 0 : 3; + + if (dx == dy || abs(dx - dy) == 16) // North<->South track | + trackstat = (thd->drawstyle & 1) ? 0x20 : 0x10; + else if (dx == -dy || abs(dx + dy) == 16) // East<->West track -- + trackstat = (thd->drawstyle & 1) ? 4 : 8; + } + + DoCommandP(TILE_FROM_XY(thd->selstart.x, thd->selstart.y), TILE_FROM_XY(thd->selend.x, thd->selend.y), + (mode << 4) | (_remove_button_clicked + (_ctrl_pressed ? 8 : 0)) | (trackstat << 8), + CcPlaySound1E, + (_remove_button_clicked ? CMD_BUILD_MANY_SIGNALS | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1013_CAN_T_REMOVE_SIGNALS_FROM) : + CMD_BUILD_MANY_SIGNALS | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1010_CAN_T_BUILD_SIGNALS_HERE) ) ); + } +} + static OnButtonClick * const _build_railroad_button_proc[] = { BuildRailClick_AutoRail, BuildRailClick_N, @@ -519,7 +560,7 @@ static OnButtonClick * const _build_railroad_button_proc[] = { BuildRailClick_Raise, BuildRailClick_Depot, BuildRailClick_Station, - BuildRailClick_Signals, + BuildRailClick_AutoSignals, BuildRailClick_Bridge, BuildRailClick_Tunnel, BuildRailClick_Remove, @@ -603,6 +644,8 @@ static void BuildRailToolbWndProc(Window *w, WindowEvent *e) if (_ctrl_pressed) _remove_button_clicked = true; HandleAutodirPlacement(); _remove_button_clicked = old; + } else if (e->place.userdata == VPM_SIGNALDIRS) { + HandleAutoSignalPlacement(); } else if (e->place.userdata == VPM_X_AND_Y) { DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA)); } else if (e->place.userdata == (VPM_X_AND_Y | (1<<4))) { diff --git a/settings.c b/settings.c index d09431384a..8000f6523b 100644 --- a/settings.c +++ b/settings.c @@ -873,6 +873,8 @@ static const SettingDesc patch_settings[] = { {"wait_oneway_signal", SDT_UINT8, (void*)15, (void*)offsetof(Patches, wait_oneway_signal)}, {"wait_twoway_signal", SDT_UINT8, (void*)41, (void*)offsetof(Patches, wait_twoway_signal)}, + {"drag_signals_density", SDT_UINT8, (void*)4, (void*)offsetof(Patches, drag_signals_density)}, + {NULL} }; diff --git a/settings_gui.c b/settings_gui.c index a68c7af813..60310fa932 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -700,6 +700,7 @@ static const PatchEntry _patches_construction[] = { {PE_BOOL, 0, STR_CONFIG_PATCHES_SIGNALSIDE, &_patches.signal_side}, {PE_BOOL, 0, STR_CONFIG_PATCHES_BUILD_IN_PAUSE, &_patches.build_in_pause}, {PE_BOOL, 0, STR_CONFIG_PATCHES_SMALL_AIRPORTS, &_patches.always_small_airport}, + {PE_UINT8,0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, &_patches.drag_signals_density, 1, 20, 1}, }; diff --git a/variables.h b/variables.h index 11e1a87304..fcb7f1736e 100644 --- a/variables.h +++ b/variables.h @@ -152,6 +152,7 @@ typedef struct Patches { byte wait_oneway_signal; //waitingtime in days before a oneway signal byte wait_twoway_signal; //waitingtime in days before a twoway signal + byte drag_signals_density; // many signals density } Patches; VARDEF Patches _patches; diff --git a/viewport.c b/viewport.c index 3254b754f5..e6ea33bd42 100644 --- a/viewport.c +++ b/viewport.c @@ -1910,7 +1910,7 @@ void VpSelectTilesWithMethod(int x, int y, int method) } // allow drag in any rail direction - if (method == VPM_RAILDIRS) { + if (method == VPM_RAILDIRS || method == VPM_SIGNALDIRS) { CalcRaildirsDrawstyle(thd, x, y); return; } diff --git a/viewport.h b/viewport.h index e2501d2cab..7ebb816f6e 100644 --- a/viewport.h +++ b/viewport.h @@ -53,6 +53,7 @@ enum { VPM_RAILDIRS = 3, VPM_X_AND_Y = 4, VPM_X_AND_Y_LIMITED = 5, + VPM_SIGNALDIRS = 6, }; void VpSelectTilesWithMethod(int x, int y, int method);