diff --git a/src/fios.h b/src/fios.h index e22e14c0b5..b17ab8e4ce 100644 --- a/src/fios.h +++ b/src/fios.h @@ -48,6 +48,9 @@ struct LoadCheckData { struct LoggedAction *gamelog_action; ///< Gamelog actions uint gamelog_actions; ///< Number of gamelog actions + bool want_debug_log_data = false; + std::string debug_log_data; + LoadCheckData() : error_data(nullptr), grfconfig(nullptr), grf_compatibility(GLC_NOT_FOUND), gamelog_action(nullptr), gamelog_actions(0) { diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index fadcb9377c..7e58102420 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -67,6 +67,8 @@ void LoadCheckData::Clear() this->gamelog_actions = 0; ClearGRFConfigList(&this->grfconfig); + + this->debug_log_data.clear(); } /** Load game/scenario with optional content download */ diff --git a/src/gfx.cpp b/src/gfx.cpp index 5010874810..8cdfaba8df 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -36,6 +36,8 @@ byte _support8bpp; CursorVars _cursor; bool _ctrl_pressed; ///< Is Ctrl pressed? bool _shift_pressed; ///< Is Shift pressed? +bool _invert_ctrl; +bool _invert_shift; byte _fast_forward; bool _left_button_down; ///< Is left mouse button pressed? bool _left_button_clicked; ///< Is left mouse button clicked? diff --git a/src/gfx_func.h b/src/gfx_func.h index 75a8459aff..4e12a4d946 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -56,6 +56,8 @@ extern byte _support8bpp; extern CursorVars _cursor; extern bool _ctrl_pressed; ///< Is Ctrl pressed? extern bool _shift_pressed; ///< Is Shift pressed? +extern bool _invert_ctrl; +extern bool _invert_shift; extern byte _fast_forward; extern bool _left_button_down; @@ -73,6 +75,7 @@ extern Palette _cur_palette; ///< Current palette void HandleKeypress(uint keycode, WChar key); void HandleTextInput(const char *str, bool marked = false, const char *caret = nullptr, const char *insert_location = nullptr, const char *replacement_end = nullptr); void HandleCtrlChanged(); +void HandleShiftChanged(); void HandleMouseEvents(); void UpdateWindows(); diff --git a/src/gui.h b/src/gui.h index 39e80a5b66..3bb78c4078 100644 --- a/src/gui.h +++ b/src/gui.h @@ -59,6 +59,8 @@ void ShowEstimatedCostOrIncome(Money cost, int x, int y); void ShowExtraViewPortWindow(TileIndex tile = INVALID_TILE); void ShowExtraViewPortWindowForTileUnderCursor(); +void ShowModifierKeyToggleWindow(); + /* bridge_gui.cpp */ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, byte bridge_type); diff --git a/src/lang/english.txt b/src/lang/english.txt index 1a69627f75..05541fca3b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -490,6 +490,7 @@ STR_ABOUT_MENU_ZOOMIN_SCREENSHOT :Fully zoomed in STR_ABOUT_MENU_DEFAULTZOOM_SCREENSHOT :Default zoom screenshot STR_ABOUT_MENU_GIANT_SCREENSHOT :Whole map screenshot STR_ABOUT_MENU_SHOW_FRAMERATE :Show frame rate +STR_ABOUT_MENU_SHOW_TOGGLE_MODIFIER_KEYS :Modifier key window STR_ABOUT_MENU_ABOUT_OPENTTD :About 'OpenTTD' STR_ABOUT_MENU_SPRITE_ALIGNER :Sprite aligner STR_ABOUT_MENU_TOGGLE_BOUNDING_BOXES :Toggle bounding boxes @@ -6186,3 +6187,9 @@ STR_SCHDISPATCH_SUMMARY_L2 :{BLACK}This sch STR_SCHDISPATCH_SUMMARY_L3 :{BLACK}Maximum delay of {STRING3} is allowed before the slot is skipped. STR_SCHDISPATCH_SUMMARY_NOT_ENABLED :{BLACK}This schedule is not active. +# Modifier key toggle window +STR_MODIFIER_KEY_TOGGLE_CAPTION :{WHITE}Modifier keys +STR_SHIFT_KEY_NAME :{BLACK}Shift +STR_CTRL_KEY_NAME :{BLACK}Ctrl +STR_MODIFIER_TOGGLE_SHIFT_TOOLTIP :{BLACK}Click to invert state of Shift key +STR_MODIFIER_TOGGLE_CTRL_TOOLTIP :{BLACK}Click to invert state of Ctrl key diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 563b5c9b08..5526bf48b6 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1339,3 +1339,88 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback new QueryWindow(&_query_desc, caption, message, parent, callback); } + +static const NWidgetPart _modifier_key_toggle_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MODIFIER_KEY_TOGGLE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MKT_SHIFT), SetMinimalSize(78, 12), SetFill(1, 0), + SetDataTip(STR_SHIFT_KEY_NAME, STR_MODIFIER_TOGGLE_SHIFT_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MKT_CTRL), SetMinimalSize(78, 12), SetFill(1, 0), + SetDataTip(STR_CTRL_KEY_NAME, STR_MODIFIER_TOGGLE_CTRL_TOOLTIP), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), + EndContainer(), +}; + +struct ModifierKeyToggleWindow : Window { + ModifierKeyToggleWindow(WindowDesc *desc, WindowNumber window_number) : + Window(desc) + { + this->InitNested(window_number); + this->UpdateButtons(); + } + + ~ModifierKeyToggleWindow() + { + _invert_shift = false; + _invert_ctrl = false; + } + + void UpdateButtons() + { + this->SetWidgetLoweredState(WID_MKT_SHIFT, _shift_pressed); + this->SetWidgetLoweredState(WID_MKT_CTRL, _ctrl_pressed); + this->SetDirty(); + } + + EventState OnCTRLStateChange() override + { + this->UpdateButtons(); + return ES_NOT_HANDLED; + } + + void OnShiftStateChange() override + { + this->UpdateButtons(); + } + + void OnClick(Point pt, int widget, int click_count) override + { + switch (widget) { + case WID_MKT_SHIFT: + _invert_shift = !_invert_shift; + UpdateButtons(); + break; + + case WID_MKT_CTRL: + _invert_ctrl = !_invert_ctrl; + UpdateButtons(); + break; + } + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + this->UpdateButtons(); + } +}; + +static WindowDesc _modifier_key_toggle_desc( + WDP_AUTO, "modifier_key_toggle", 0, 0, + WC_MODIFIER_KEY_TOGGLE, WC_NONE, + WDF_NO_FOCUS, + _modifier_key_toggle_widgets, lengthof(_modifier_key_toggle_widgets) +); + +void ShowModifierKeyToggleWindow() +{ + AllocateWindowDescFront(&_modifier_key_toggle_desc, 0); +} diff --git a/src/openttd.cpp b/src/openttd.cpp index 04672c3fe8..60b50d6c4c 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -281,7 +281,7 @@ static void WriteSavegameInfo(const char *name) GamelogInfo(_load_check_data.gamelog_action, _load_check_data.gamelog_actions, &last_ottd_rev, &ever_modified, &removed_newgrfs); - char buf[8192]; + char buf[65536]; char *p = buf; p += seprintf(p, lastof(buf), "Name: %s\n", name); const char *type = ""; @@ -323,6 +323,40 @@ static void WriteSavegameInfo(const char *name) #endif } +static void WriteSavegameDebugData(const char *name) +{ + char *buf = MallocT(4096); + char *buflast = buf + 4095; + char *p = buf; + auto bump_size = [&]() { + size_t offset = p - buf; + size_t new_size = buflast - buf + 1 + 4096; + buf = ReallocT(buf, new_size); + buflast = buf + new_size - 1; + p = buf + offset; + }; + p += seprintf(p, buflast, "Name: %s\n", name); + if (_load_check_data.debug_log_data.size()) { + p += seprintf(p, buflast, "%u bytes of debug data in savegame\n", (uint) _load_check_data.debug_log_data.size()); + std::string buffer = _load_check_data.debug_log_data; + ProcessLineByLine(const_cast(buffer.data()), [&](const char *line) { + if (buflast - p <= 1024) bump_size(); + p += seprintf(p, buflast, "> %s\n", line); + }); + } else { + p += seprintf(p, buflast, "No debug data in savegame\n"); + } + + /* ShowInfo put output to stderr, but version information should go + * to stdout; this is the only exception */ +#if !defined(_WIN32) + printf("%s\n", buf); +#else + ShowInfo(buf); +#endif + free(buf); +} + /** * Extract the resolution from the given string and store @@ -612,6 +646,7 @@ static const OptionData _options[] = { GETOPT_SHORT_VALUE('c'), GETOPT_SHORT_NOVAL('x'), GETOPT_SHORT_VALUE('q'), + GETOPT_SHORT_VALUE('K'), GETOPT_SHORT_NOVAL('h'), GETOPT_SHORT_VALUE('J'), GETOPT_END() @@ -730,7 +765,8 @@ int openttd_main(int argc, char *argv[]) scanner->generation_seed = InteractiveRandom(); } break; - case 'q': { + case 'q': + case 'K': { DeterminePaths(argv[0]); if (StrEmpty(mgo.opt)) { ret = 1; @@ -742,10 +778,12 @@ int openttd_main(int argc, char *argv[]) FiosGetSavegameListCallback(SLO_LOAD, mgo.opt, strrchr(mgo.opt, '.'), title, lastof(title)); _load_check_data.Clear(); + if (i == 'K') _load_check_data.want_debug_log_data = true; SaveOrLoadResult res = SaveOrLoad(mgo.opt, SLO_CHECK, DFT_GAME_FILE, SAVE_DIR, false); if (res != SL_OK || _load_check_data.HasErrors()) { fprintf(stderr, "Failed to open savegame\n"); if (_load_check_data.HasErrors()) { + InitializeLanguagePacks(); char buf[256]; SetDParamStr(0, _load_check_data.error_data); GetString(buf, _load_check_data.error, lastof(buf)); @@ -754,7 +792,11 @@ int openttd_main(int argc, char *argv[]) goto exit_noshutdown; } - WriteSavegameInfo(title); + if (i == 'q') { + WriteSavegameInfo(title); + } else { + WriteSavegameDebugData(title); + } goto exit_noshutdown; } @@ -1528,7 +1570,12 @@ void CheckCaches(bool force_check, std::function log) } if (veh_cache[length].cached_max_speed != u->vcache.cached_max_speed || veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period || veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect || HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) { - CCLOG("vehicle cache mismatch: type %i, vehicle %i, company %i, unit number %i, wagon %i", (int)v->type, v->index, (int)v->owner, v->unitnumber, length); + CCLOG("vehicle cache mismatch: %c%c%c%c, type %i, vehicle %i, company %i, unit number %i, wagon %i", + veh_cache[length].cached_max_speed != u->vcache.cached_max_speed ? 'm' : '-', + veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period ? 'c' : '-', + veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect ? 'v' : '-', + HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 'l' : '-', + (int)v->type, v->index, (int)v->owner, v->unitnumber, length); } if (u->IsGroundVehicle() && (HasBit(u->GetGroundVehicleFlags(), GVF_GOINGUP_BIT) || HasBit(u->GetGroundVehicleFlags(), GVF_GOINGDOWN_BIT)) && u->GetGroundVehicleCache()->cached_slope_resistance && HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST)) { CCLOG("VCF_GV_ZERO_SLOPE_RESIST set incorrectly (2): type %i, vehicle %i, company %i, unit number %i, wagon %i", (int)v->type, v->index, (int)v->owner, v->unitnumber, length); @@ -1642,8 +1689,17 @@ void CheckCaches(bool force_check, std::function log) if (_order_destination_refcount_map_valid) { btree::btree_map saved_order_destination_refcount_map = std::move(_order_destination_refcount_map); + for (auto iter = saved_order_destination_refcount_map.begin(); iter != saved_order_destination_refcount_map.end();) { + if (iter->second == 0) { + iter = saved_order_destination_refcount_map.erase(iter); + } else { + ++iter; + } + } IntialiseOrderDestinationRefcountMap(); if (saved_order_destination_refcount_map != _order_destination_refcount_map) CCLOG("Order destination refcount map mismatch"); + } else { + CCLOG("Order destination refcount map not valid"); } #undef CCLOG diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 05c0e03f2c..b3f1326e54 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -58,6 +58,8 @@ INSTANTIATE_POOL_METHODS(OrderList) btree::btree_map _order_destination_refcount_map; bool _order_destination_refcount_map_valid = false; +CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID sel_ord, const Order &new_order, bool allow_load_by_cargo_type); + void IntialiseOrderDestinationRefcountMap() { ClearOrderDestinationRefcountMap(); @@ -942,7 +944,10 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 VehicleOrderID sel_ord = GB(p1, 20, 8); Order new_order(p2); - Vehicle *v = Vehicle::GetIfValid(veh); + return CmdInsertOrderIntl(flags, Vehicle::GetIfValid(veh), sel_ord, new_order, false); +} + +CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID sel_ord, const Order &new_order, bool allow_load_by_cargo_type) { if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); @@ -972,10 +977,16 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 /* Filter invalid load/unload types. */ switch (new_order.GetLoadType()) { case OLF_LOAD_IF_POSSIBLE: case OLFB_FULL_LOAD: case OLF_FULL_LOAD_ANY: case OLFB_NO_LOAD: break; + case OLFB_CARGO_TYPE_LOAD: + if (allow_load_by_cargo_type) break; + return CMD_ERROR; default: return CMD_ERROR; } switch (new_order.GetUnloadType()) { case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break; + case OUFB_CARGO_TYPE_UNLOAD: + if (allow_load_by_cargo_type) break; + return CMD_ERROR; default: return CMD_ERROR; } @@ -1928,6 +1939,17 @@ static void CheckAdvanceVehicleOrdersAfterClone(Vehicle *v, DoCommandFlag flags) DoCommand(v->tile, v->index, skip_to, flags, CMD_SKIP_TO_ORDER); } +static bool ShouldResetOrderIndicesOnOrderCopy(const Vehicle *src, const Vehicle *dst) +{ + const int num_orders = src->GetNumOrders(); + if (dst->GetNumOrders() != num_orders) return true; + + for (int i = 0; i < num_orders; i++) { + if (!src->GetOrder(i)->Equals(*dst->GetOrder(i))) return true; + } + return false; +} + /** * Clone/share/copy an order-list of another vehicle. * @param tile unused @@ -1999,9 +2021,9 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (flags & DC_EXEC) { /* If the destination vehicle had a OrderList, destroy it. - * We only reset the order indices, if the new orders are obviously different. + * We reset the order indices, if the new orders are different. * (We mainly do this to keep the order indices valid and in range.) */ - DeleteVehicleOrders(dst, false, dst->GetNumOrders() != src->GetNumOrders()); + DeleteVehicleOrders(dst, false, ShouldResetOrderIndicesOnOrderCopy(src, dst)); dst->orders.list = src->orders.list; @@ -2086,9 +2108,9 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 Order **order_dst; /* If the destination vehicle had an order list, destroy the chain but keep the OrderList. - * We only reset the order indices, if the new orders are obviously different. + * We only the order indices, if the new orders are different. * (We mainly do this to keep the order indices valid and in range.) */ - DeleteVehicleOrders(dst, true, dst->GetNumOrders() != src->GetNumOrders()); + DeleteVehicleOrders(dst, true, ShouldResetOrderIndicesOnOrderCopy(src, dst)); order_dst = &first; FOR_VEHICLE_ORDERS(src, order) { @@ -2856,14 +2878,20 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u Order new_order; new_order.AssignOrder(*order); new_order.SetDestination(to_dest); + const bool wait_fixed = new_order.IsWaitFixed(); + const bool wait_timetabled = wait_fixed && new_order.IsWaitTimetabled(); new_order.SetWaitTimetabled(false); new_order.SetTravelTimetabled(false); - if (DoCommand(0, v->index | ((index + 1) << 20), new_order.Pack(), flags, CMD_INSERT_ORDER).Succeeded()) { + if (CmdInsertOrderIntl(flags, v, index + 1, new_order, true).Succeeded()) { DoCommand(0, v->index, index, flags, CMD_DELETE_ORDER); order = v->orders.list->GetOrderAt(index); order->SetRefit(new_order.GetRefitCargo()); order->SetMaxSpeed(new_order.GetMaxSpeed()); + if (wait_fixed) { + extern void SetOrderFixedWaitTime(Vehicle *v, VehicleOrderID order_number, uint32 wait_time, bool wait_timetabled); + SetOrderFixedWaitTime(v, index, new_order.GetWaitTime(), wait_timetabled); + } changed = true; } diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index fcd31e7094..91c79989ce 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -24,6 +24,7 @@ #include "../../openttd.h" #include "../../screenshot.h" #include "../../debug.h" +#include "../../settings_type.h" #if defined(WITH_DEMANGLE) #include #endif @@ -483,6 +484,8 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c /* virtual */ int CrashLogWindows::WriteCrashDump(char *filename, const char *filename_last) const { + if (_settings_client.gui.developer == 0) return 0; + int ret = 0; HMODULE dbghelp = LoadLibrary(_T("dbghelp.dll")); if (dbghelp != nullptr) { @@ -624,7 +627,7 @@ static bool _expanded; static const TCHAR _crash_desc[] = _T("A serious fault condition occurred in the game. The game will shut down.\n") - _T("Please send the crash information and the crash.dmp file (if any) to the patchpack developer.\n") + _T("Please send the crash information (log files and crash saves, if any) to the patchpack developer.\n") _T("This will greatly help debugging. The correct place to do this is https://www.tt-forums.net/viewtopic.php?f=33&t=73469") _T(" or https://github.com/JGRennison/OpenTTD-patches\n") _T("The information contained in the report is displayed below.\n") @@ -689,7 +692,7 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA TCHAR *text = AllocaM(TCHAR, len); _sntprintf(text, len, _crash_desc, OTTD2FS(CrashLogWindows::current->crashlog_filename)); - if (OTTD2FS(CrashLogWindows::current->crashdump_filename)[0] != _T('\0')) { + if (_settings_client.gui.developer > 0 && OTTD2FS(CrashLogWindows::current->crashdump_filename)[0] != _T('\0')) { _tcscat(text, _T("\n")); _tcscat(text, OTTD2FS(CrashLogWindows::current->crashdump_filename)); } diff --git a/src/saveload/debug_sl.cpp b/src/saveload/debug_sl.cpp index e4c163495d..5a41516066 100644 --- a/src/saveload/debug_sl.cpp +++ b/src/saveload/debug_sl.cpp @@ -14,6 +14,7 @@ #include "../debug.h" #include "saveload.h" #include "saveload_buffer.h" +#include "../fios.h" #include "../safeguards.h" @@ -37,6 +38,19 @@ static void Load_DBGL() } } +static void Check_DBGL() +{ + if (!_load_check_data.want_debug_log_data) { + SlSkipBytes(SlGetFieldLength()); + return; + } + size_t length = SlGetFieldLength(); + if (length) { + _load_check_data.debug_log_data.resize(length); + ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast(const_cast(_load_check_data.debug_log_data.data())), length); + } +} + extern const ChunkHandler _debug_chunk_handlers[] = { - { 'DBGL', Save_DBGL, Load_DBGL, nullptr, nullptr, CH_RIFF | CH_LAST}, + { 'DBGL', Save_DBGL, Load_DBGL, nullptr, Check_DBGL, CH_RIFF | CH_LAST}, }; diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 6cbdf757ef..1aebf14ed4 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -96,6 +96,14 @@ class NIHVehicle : public NIHelper { b += seprintf(b, lastof(buffer), " Flags: "); b = v->DumpVehicleFlags(b, lastof(buffer)); print(buffer); + if (v->IsPrimaryVehicle()) { + seprintf(buffer, lastof(buffer), " Order indices: real: %u, implicit: %u, tt: %u", + v->cur_real_order_index, v->cur_implicit_order_index, v->cur_timetable_order_index); + print(buffer); + } + seprintf(buffer, lastof(buffer), " V Cache: max speed: %u, cargo age period: %u, vis effect: %u", + v->vcache.cached_max_speed, v->vcache.cached_cargo_age_period, v->vcache.cached_vis_effect); + print(buffer); if (v->IsGroundVehicle()) { const GroundVehicleCache &gvc = *(v->GetGroundVehicleCache()); seprintf(buffer, lastof(buffer), " GV Cache: weight: %u, slope res: %u, max TE: %u, axle res: %u", diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 00c89cc025..91da70aaff 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -997,3 +997,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); } } + +void SetOrderFixedWaitTime(Vehicle *v, VehicleOrderID order_number, uint32 wait_time, bool wait_timetabled) { + ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME, wait_timetabled, true); + ChangeTimetable(v, order_number, 1, MTF_SET_WAIT_FIXED, false, true); +} diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index f56f74d213..e950ce4245 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -1079,7 +1079,7 @@ static CallBackFunction PlaceLandBlockInfo() static CallBackFunction ToolbarHelpClick(Window *w) { - PopupMainToolbMenu(w, _game_mode == GM_EDITOR ? (int)WID_TE_HELP : (int)WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 13 : 10); + PopupMainToolbMenu(w, _game_mode == GM_EDITOR ? (int)WID_TE_HELP : (int)WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 14 : 11); return CBF_NONE; } @@ -1182,10 +1182,11 @@ static CallBackFunction MenuClickHelp(int index) case 6: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break; case 7: MenuClickLargeWorldScreenshot(SC_WORLD); break; case 8: ShowFramerateWindow(); break; - case 9: ShowAboutWindow(); break; - case 10: ShowSpriteAlignerWindow(); break; - case 11: ToggleBoundingBoxes(); break; - case 12: ToggleDirtyBlocks(); break; + case 9: ShowModifierKeyToggleWindow(); break; + case 10: ShowAboutWindow(); break; + case 11: ShowSpriteAlignerWindow(); break; + case 12: ToggleBoundingBoxes(); break; + case 13: ToggleDirtyBlocks(); break; } return CBF_NONE; } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index e80a27af0e..d8fe3079eb 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1355,6 +1355,7 @@ static void NormaliseTrainHead(Train *head) /* Tell the 'world' the train changed. */ head->ConsistChanged(CCF_ARRANGE); UpdateTrainGroupID(head); + SetBit(head->flags, VRF_CONSIST_SPEED_REDUCTION); /* Not a front engine, i.e. a free wagon chain. No need to do more. */ if (!head->IsFrontEngine()) return; diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp index 3d4aea8f11..38a72ba1f9 100644 --- a/src/video/allegro_v.cpp +++ b/src/video/allegro_v.cpp @@ -499,9 +499,10 @@ void VideoDriver_Allegro::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG); - _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG); + _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG) != _invert_ctrl; + _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG) != _invert_shift; /* determine which directional keys are down */ _dirkeys = @@ -511,6 +512,7 @@ void VideoDriver_Allegro::MainLoop() (key[KEY_DOWN] ? 8 : 0); if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); GameLoop(); diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm index 9af2fa5c4c..0e067840a1 100644 --- a/src/video/cocoa/event.mm +++ b/src/video/cocoa/event.mm @@ -695,11 +695,13 @@ void QZ_GameLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(_current_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)); - _shift_pressed = !!(_current_mods & NSShiftKeyMask); + _ctrl_pressed = !!(_current_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != _invert_ctrl; + _shift_pressed = !!(_current_mods & NSShiftKeyMask) != _invert_shift; if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); GameLoop(); diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index fdaac36605..983bcd923f 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -728,9 +728,10 @@ void VideoDriver_SDL::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(mod & KMOD_CTRL); - _shift_pressed = !!(mod & KMOD_SHIFT); + _ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl; + _shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift; /* determine which directional keys are down */ _dirkeys = @@ -746,6 +747,7 @@ void VideoDriver_SDL::MainLoop() (keys[SDLK_DOWN] ? 8 : 0); #endif if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); /* The gameloop is the part that can run asynchronously. The rest * except sleeping can't. */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 387b32b002..a0bb6bd755 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1243,9 +1243,10 @@ void VideoDriver_Win32::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; - _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; + _ctrl_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_CONTROL) < 0) != _invert_ctrl; + _shift_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) != _invert_shift; /* determine which directional keys are down */ if (_wnd.has_focus) { @@ -1259,6 +1260,7 @@ void VideoDriver_Win32::MainLoop() } if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ GdiFlush(); diff --git a/src/widgets/misc_widget.h b/src/widgets/misc_widget.h index fb7940722e..c139e04d49 100644 --- a/src/widgets/misc_widget.h +++ b/src/widgets/misc_widget.h @@ -57,4 +57,10 @@ enum TextfileWidgets { WID_TF_HSCROLLBAR, ///< Horizontal scrollbar to scroll through the textfile left-to-right. }; +/** Widgets of the #TextfileWindow class. */ +enum ModifierKeyToggleWidgets { + WID_MKT_SHIFT, + WID_MKT_CTRL, +}; + #endif /* WIDGETS_MISC_WIDGET_H */ diff --git a/src/window.cpp b/src/window.cpp index c8dee0379e..e4d43847f0 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2743,6 +2743,17 @@ void HandleCtrlChanged() } } +/** + * State of SHIFT key has changed + */ +void HandleShiftChanged() +{ + Window *w; + FOR_ALL_WINDOWS_FROM_FRONT(w) { + w->OnShiftStateChange(); + } +} + /** * Insert a text string at the cursor position into the edit box widget. * @param wid Edit box widget. diff --git a/src/window_gui.h b/src/window_gui.h index 6c3638de20..636c06e3fd 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -667,6 +667,10 @@ public: */ virtual EventState OnCTRLStateChange() { return ES_NOT_HANDLED; } + /** + * The state of the shift key has changed + */ + virtual void OnShiftStateChange() {} /** * A click with the left mouse button has been made on the window. diff --git a/src/window_type.h b/src/window_type.h index 96743dee32..75bceaace0 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -755,6 +755,11 @@ enum WindowClass { WC_BUILD_VIRTUAL_TRAIN, WC_CREATE_TEMPLATE, + /** + * Modifier key toggle window. + */ + WC_MODIFIER_KEY_TOGGLE, + WC_INVALID = 0xFFFF, ///< Invalid window. };