Squashed commit of the following:

commit b17f39a2016dc11a6a9815f398d690d82a6a59aa
Merge: 67b3190 3bb7c47
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Feb 12 19:44:34 2016 +0100

    Merge branch 'merge/trunk27506' into dev

commit 3bb7c4768580198b7316bfeebc4b870d355439e8
Merge: 14929fe 9db36bd
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Feb 12 19:43:53 2016 +0100

    Merge remote-tracking branch 'openttd/master' into merge/trunk27506

commit 14929fe3536e2aa5b4d6a43d0d55043da7a2f252
Merge: af15609 4b8c698
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Feb 10 22:14:25 2016 +0100

    Merge branch 'master' into merge/trunk27506

commit 67b319060b4b88b72c94b0e0c2c9fdcf1c2fd95d
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sat Feb 28 20:17:13 2015 +0100

    removed 2 unused function calls

commit af15609c938eb388dd507b16fb7b6d547c54c2da
Merge: 5465c88 b251ba3
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sat Feb 28 15:12:33 2015 +0100

    Merge branch 'trunk' into merge_trunk

commit 5465c88c8016c5e7910570ab5795222e8348c703
Author: me <streen01@gmx.de>
Date:   Sat Feb 28 10:59:41 2015 +0100

    regenerated MSVS project files

    forgot to do this, they still retained the old filenames

commit 0391455e29c5ed794fcd0f58c63ff98dc52685ac
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Feb 26 16:53:05 2015 +0100

    removed the patch files from this repo again

    that was a rather dum idea, it made the difference patch between
    branches trunk and tbtr huge.
    the patch files are now being tracked again in the supplimentary repo
    'tbtr_proj', that will keep this fork clean and creating diff-patches
    will be much easier

commit 8395d40386c8d620c90fb4be66cf6679408ac975
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Feb 26 16:27:40 2015 +0100

    fix for reported bug by DC-1: crash in station gui

    the template gui item was added to the drop-down list that was also
    shown in a station gui, but there was no action present when this item
    was selected in a station gui. per default the game would commit suicide
    by called NOT_REACHED() at the default case of the according
    switch-statement.

commit 833873245d33bd77105a82a584d9bec2362419bc
Merge: 39596be 8688c95
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Feb 26 15:08:53 2015 +0100

    Merge branch 'fix_disableTemplateOrderCheck' into tbtr

commit 8688c95a01ed5933a35a08597bbf45ff148f5a67
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Feb 26 15:06:25 2015 +0100

    added fix by DC-1

    don't check the orders list of a virtual vehicle

commit 39596beff9a815a0f9b2ea3abe5d82c3ec5933e7
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 22 10:47:58 2015 +0100

    added history of patches for the mod

commit b3ae74ac4e9143202a1fda1333a91c3716ebb21e
Merge: 9a601a1 ee756e1
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 22 10:03:04 2015 +0100

    Merge branch 'tbtr' into merge_tbtr

commit ee756e1c2229534f1cc05edb97269b0c83ddde66
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Feb 17 22:25:50 2015 +0100

    removed nonsensical comments + disabled code

commit e7d37f0500c56c84a36ce8b93eafb31f800e1086
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Feb 17 21:30:38 2015 +0100

    added some missing renames in includes

commit 63c2b13766b077e4f2923f321e95d53356dee2db
Merge: e92e6ba 9752606
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Feb 17 21:22:11 2015 +0100

    Merge branch 'feat_renameFiles' into merge_renameFiles

    Conflicts:
    	src/tbtr_template_gui_create_virtualtrain.cpp

commit 975260643d212f8cac72485f2011011210622849
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Mon Feb 9 22:11:18 2015 +0100

    replaced source file prefix: aaa -> tbtr

commit e92e6ba7089564886d17dd5c1fd8d85ea0ca4ac7
Merge: 62d2f80 ac16eab
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 15:02:19 2015 +0100

    Merge branch 'rm_TODOs' into dev

    Conflicts:
    	src/aaa_template_gui_main.cpp

commit 62d2f809edf170cfbeb0599822c4c3d4f9a1fefe
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 14:59:36 2015 +0100

    i++ -> ++i

commit ac16eabc082f62b9fe2ef6c11a314f8e9a28c26b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 14:34:36 2015 +0100

    rm'ed TODOs

commit 22f642f32265882b8f99b409b517823991c08101
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 14:17:49 2015 +0100

    rm TODO

    yes, depends on the selected template because the button "Start
    replacing" means, to start the replacement for the currently selected
    group and template (create a templatereplacement object for this
    combination)

commit 60d8192838e340a3cf6899979361c997df73b716
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 14:17:26 2015 +0100

    rm'ed TODO: included task in TODO-list

commit 39e42674ac9f5ad5dd056b613e80ef4e754c1153
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Feb 8 11:19:36 2015 +0100

    changed window class in use: WC_NONE -> WC_TEMPLATE_GUI_MAIN

commit cadfac96e21aeb862b75e0454197ddce89fb728c
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Mon Jan 26 23:18:29 2015 +0100

    removed a weird call to deleteAllTmplReplacements

    was a TODO task, it was set to delete all template replacements
    belonging to group with id -1, which does not exist, ever

commit dc1058464c29f61b6197dec556ec468d1ff38451
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 25 23:27:03 2015 +0100

    removed some TODOs

commit 7afeb17db512600424039099a0f4bd78882fcd8e
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 25 11:35:47 2015 +0100

    removed all MYGUI comments

    tried to replace them with useful comments where necessary
    added a few new TODOs here and there

commit 6b9453224a77811062254e6bce7dac4074b829a8
Merge: 292a5aa 687bc4c
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 18 20:47:06 2015 +0100

    Merge branch 'fix_compiler_warnings' into dev

commit 687bc4c34fbb9ddeaf15b4857b235a9709dd85be
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 18 20:43:26 2015 +0100

    fixed all remaining warnings

commit ada08d7097772e325b7852fd058d8bad7036ae4d
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 18 18:25:45 2015 +0100

    removed testing code that produced a warning

commit f3b1568384f36998aeb1fa51c1fab4cfb96c7f93
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 18 00:07:34 2015 +0100

    removed unused variable REPLACEMENT_IN_PROGRESS

commit 5aa9098880070cfaa3d2815f445497b2886933f9
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 18 00:02:43 2015 +0100

    removed variable 'mode' from ClickedOnVehicle()

    member function of class TemplateCreateWindow
    in the depot gui the mode variable is used to decide whether a vehicle
    is started or dragged or ...

    here, we only drag so the mode is never used

commit 292a5aa9dba9cf1d0003e84055fb95357f922454
Merge: 8f6df8c 2bb12bc
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Jan 14 23:41:29 2015 +0100

    Merge branch 'feat_mergePatch0.4c' into dev

commit 2bb12bcf283cccc8869bf537b79b22f479cb7203
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Jan 14 23:32:04 2015 +0100

    added vi's .swp files to .gitignore

commit aecf6f549b32f92342f8e0b65158bebef6270537
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Jan 13 20:15:25 2015 +0100

    corrected UpdateViewport code

    was VehicleUpdateViewport(Vehicle*, bool) before
    is  Vehicle::UpdateViewport(bool, bool) now

commit ae199283fd5ac0199cef1c4c980561122d030199
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Mon Jan 12 22:34:22 2015 +0100

    updated code for EngineNumberSorter

commit 9735035c6dd4ded9bb76958722dc25e26ced5f05
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 11 18:36:17 2015 +0100

    removed unused parameter 'part_of_chain'

    from cargo movement code

commit b8b86e1f2592288ddcfb46a0a5d81c3257da60d3
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Jan 4 21:44:17 2015 +0100

    Reimplemented moving of cargo

    - uses the new shift function
    - manages to spread the old cargo of replaced vehicles from a chain across the memebers of the newly constructed chain

    some TODOs are left within the code and some testing needs to be done,
    how this behaves when there is more than one vehicle being replaced

commit 0d76e1bfe10ef207ac5e4018976e9fba0b0bb25e
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sat Jan 3 01:05:54 2015 +0100

    fixed saveload code for TemplateVehicle

commit ba0ea6975f48fe38c2b5376ebc83c23d6bb6151c
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 11:32:23 2015 +0100

    final changes for the merge

    - removed the WDF_UNLICK_BUTTON
    - updated ctor calls to Window()
    - disabled the engine number sorter

commit 9cc213335046b3febfe6649fde40b00e1bb43d5b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 11:29:03 2015 +0100

    disabled cargo movement during templrpl

    need to reimplement this step since the cargo is now moved
    packet-by-packet and not as a complete list from a vehicle onto another
    vehicle anymore

commit 39743806d0156f8547670c525af0e59083dbcd49
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 11:16:54 2015 +0100

    replaced cargo function 'Count'

    - not available anymore: VehicleCargoList::Count()
    - using StoredCount() for now, should check if this is the correct count

commit 9b240bbf9b2ee5659bbcb518e9e2767103861254
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 01:27:56 2015 +0100

    final corrections for template_gui_create_virtualtrain

commit cf0d48d8fa052ff521e1fac0ec75d75107c9b76e
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 01:20:30 2015 +0100

    disbabled usage of not-anymore-existing newgrf_engine.h::ListPositionOfEngine

commit 81da16b7f0c3ea2417b24707329d1d971a67e82e
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 01:09:53 2015 +0100

    fixed typo in value WID_BV_SORT_ASSENDING_DESCENDING

commit c8f81a5c3df5ccf4858bda64a53979af510ccd87
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 01:09:25 2015 +0100

    create_virtual_train: uint GetEngineListHeight not static

commit bd29d99f80bd824e28104f3bc839fc2a5abdd297
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 00:57:25 2015 +0100

    template_gui_create: static WindowDesc not const

commit edee9c1c544845459102328209b98d424cfd3248
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Fri Jan 2 00:44:50 2015 +0100

    updated call to Window::FinishInitNested

commit 25fc3cb7ed6db15f42bd3fdff9506621fbba3d72
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Jan 1 23:56:48 2015 +0100

    updated ctor calls for classes derived from Window

    - first param in the constructor used to be const WindowDesc*, now it is
      WindowDesc*

commit 54d710170f1ce9cf5539cd525744ca61f4089e7b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Thu Jan 1 02:50:29 2015 +0100

    updated constr calls to WindowDesc::WindowDesc

    need a const char* at 2nd pos now

commit 7c954141f00666dec4c9559019a1a4af3b452372
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 02:30:02 2014 +0100

    applied patch vehicle.cpp

commit aa12720049a3dfb1c2e02d453813bd567b67ff60
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 02:22:54 2014 +0100

    applied patch vehicle_gui.cpp (failed hunks 2,4,5/6)

commit 2b8e70f15478072264f1e063418f8de0744a98e1
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 02:13:29 2014 +0100

    applied patch train_cmd.cpp (failed hunk 1/8)

commit 47499523bf1ed0cce5fdf6cc2a7102e571dcb07d
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 02:07:00 2014 +0100

    applied patch newgrf_engine.cpp

commit 7a40c62a7b5ab8059981270252a7def69eacb7d7
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 02:02:52 2014 +0100

    applied patch vehicle_cmd.cpp (failed hunks 2,3/3)

commit 277839abd8cb7eb277e4ed6cb72e0f3da5b7e479
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 01:56:35 2014 +0100

    applied patch saveload.h (failed hunk 1/1)

commit 7b64c87ad3dede6442a88dafa5aa8a6a3e0db812
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 01:53:56 2014 +0100

    applied patch group_gui.cpp (failed hunks 2,3/4)

commit 8075261c526004e21534fa0ab80429132d5f634b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 01:46:24 2014 +0100

    applied patch vehiclelist.cpp (failed hunk 1/2)

commit b1c197c0f38e45fb50dad7f7e33f1438b150a34f
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Wed Dec 31 01:42:04 2014 +0100

    applied patch train.h (failed hunk 1/3)

commit 81bfa209e92fa74387420cc85851767d2737c1b0
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch viewport.cpp.patch (file src/viewport.cpp)

commit 5c083054544eabac9260a75033198c665b169215
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch train_gui.cpp.patch (file src/train_gui.cpp)

commit 3c3534621c6b37530035faadfa092d70fed724c9
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch source.list.patch (file source.list)

commit 6bbb071431882d4bab43023f7194f96c824e78e5
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch saveload_internal.h.patch (file src/saveload/saveload_internal.h)

commit 158640eb786cc7867c9e689eb8a92a209e528a83
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch saveload.cpp.patch (file src/saveload/saveload.cpp)

commit e171ad716c126e98bc045f5ce574ac6161f3ab4f
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch openttd_vs90.vcproj.patch (file projects/openttd_vs90.vcproj)

commit b77486d89c12a80f73f088759da760abd0af7f49
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch openttd_vs80.vcproj.patch (file projects/openttd_vs80.vcproj)

commit 57f9c52fc580da51e20bd40f116fe66c9a0f3669
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch openttd_vs100.vcxproj.patch (file projects/openttd_vs100.vcxproj)

commit bda1f739a415600a7f522b1c7f9ca53fa7713ed3
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch openttd_vs100.vcxproj.filters.patch (file projects/openttd_vs100.vcxproj.filters)

commit ed96771b03e726e5cb56cac9a8328c3a1e63856b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch newgrf_spritegroup.cpp.patch (file src/newgrf_spritegroup.cpp)

commit 3df57e0d855fef5f54be4fd8d25e231a7eb3c3f1
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:10 2014 +0100

    applied patch group_cmd.cpp.patch (file src/group_cmd.cpp)

commit da31ca4b67d6993f127c6cecac717eb286ead4e6
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch english.txt.patch (file src/lang/english.txt)

commit ddc0af7139fccbae4060c70440f17c763e3bba96
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch depot_gui.cpp.patch (file src/depot_gui.cpp)

commit 88aca9db192c6a2b92185f56505caf8b91d23ab4
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch autoreplace_cmd.cpp.patch (file src/autoreplace_cmd.cpp)

commit 45ca80f7c9847ac3afe181a0badeb12bbbd5ed0d
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch articulated_vehicles.cpp.patch (file src/articulated_vehicles.cpp)

commit 44bd0bf2e77f366b61f96b0f4ca564f2e2e5814a
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch afterload.cpp.patch (file src/saveload/afterload.cpp)

commit 679f9b327f9d3f3bec327ae0266f289981972c85
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_veh_sl.cpp.patch (file src/saveload/aaa_template_veh_sl.cpp)

commit ebcec221ec7c1988e85ba458283ff362e034e6d5
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_vehicle.h.patch (file src/aaa_template_vehicle.h)

commit ad690e74b95d2aa07157b73834eef672c63ef901
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_vehicle_func.h.patch (file src/aaa_template_vehicle_func.h)

commit 5982153c369432fc694daaa91a06dfefeeb29485
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_vehicle_func.cpp.patch (file src/aaa_template_vehicle_func.cpp)

commit 773f889e165b013de96736fa380d5ab5c311b3dd
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_vehicle.cpp.patch (file src/aaa_template_vehicle.cpp)

commit 03af781d69a09863d3b76ee4911e5eecd90a7cf5
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_replacement_sl.cpp.patch (file src/saveload/aaa_template_replacement_sl.cpp)

commit ab6cb0562fd390d551670dbc27e0c3c94c8554db
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_gui_replaceall.cpp.patch (file src/aaa_template_gui_replaceall.cpp)

commit d88452a6195c55e39bceb3ea7689fc546c4eee6a
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:43:09 2014 +0100

    applied patch aaa_template_gui_main.cpp.patch (file src/aaa_template_gui_main.cpp)

commit ab6ac687f355d400ad9ebc154e75477671a8e0fa
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:42:21 2014 +0100

    applied patch aaa_template_gui_create_virtualtrain.cpp.patch (file src/aaa_template_gui_create_virtualtrain.cpp)

commit 288d14b9b145cb045b6a287d23cf3be4f2712ede
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:38:12 2014 +0100

    applied patch aaa_template_gui_create.cpp.patch (file src/aaa_template_gui_create.cpp)

commit 5342db70e07fb7c1f3c41654abd2c6a4c51472c4
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Tue Dec 30 01:18:32 2014 +0100

    applied aaa_* header files

commit 6f14e94a0ad715a33a2653cf6c12e1c2981ace8d
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 17:29:55 2014 +0100

    applied vehicle_base.h.patch

commit b76a5ce921fab5d81b60755ce66db71e38664e9b
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 17:29:28 2014 +0100

    applied window_type.h.patch

commit d33d738c7e3477de3f12affcc74c88194a61c442
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 17:28:30 2014 +0100

    applied newgrf_engine.h.patch

commit 931fd1143706bc76aa145e1430645cb4496f9f4a
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 17:27:21 2014 +0100

    applied vehicle_gui.h.patch

commit f6c4ab089dad5a4a01401e18cffa8f20e02f733e
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 17:00:52 2014 +0100

    applied vehicle_gui_base.h.patch

commit 5f7378136758fcc4987791d264856169950cbfe2
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 12:34:23 2014 +0100

    applied build_vehicle_widget.h.patch

commit 5c6fc73847a2f34573260721bb68c7b552d546bc
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 12:01:10 2014 +0100

    applied autoreplace_func.h

commit 7636f27011841d01e5f954c855dfa0cf1859e0e0
Author: Thomas Schmidt <streen01@gmx.de>
Date:   Sun Dec 28 12:01:00 2014 +0100

    applied newgrf.h

Remove some spurious whitespace changes, update projects files.
pull/6/merge
Jonathan G Rennison 8 years ago
parent 9db36bd87a
commit 769462f537

@ -291,6 +291,18 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_replaceall.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp" />
<ClInclude Include="..\src\tbtr_template_gui_main.h" />
<ClInclude Include="..\src\tbtr_template_gui_create.h" />
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h" />
<ClInclude Include="..\src\tbtr_template_gui_replaceall.h" />
<ClInclude Include="..\src\tbtr_template_vehicle.h" />
<ClInclude Include="..\src\tbtr_template_vehicle_func.h" />
<ClCompile Include="..\src\airport.cpp" />
<ClCompile Include="..\src\animated_tile.cpp" />
<ClCompile Include="..\src\articulated_vehicles.cpp" />
@ -869,6 +881,8 @@
<ClCompile Include="..\src\saveload\town_sl.cpp" />
<ClCompile Include="..\src\saveload\vehicle_sl.cpp" />
<ClCompile Include="..\src\saveload\waypoint_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp" />
<ClInclude Include="..\src\table\airport_defaults.h" />
<ClInclude Include="..\src\table\airport_movement.h" />
<ClInclude Include="..\src\table\airporttile_ids.h" />

@ -102,6 +102,42 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_replaceall.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClInclude Include="..\src\tbtr_template_gui_main.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_replaceall.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle_func.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClCompile Include="..\src\airport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -1836,6 +1872,12 @@
<ClCompile Include="..\src\saveload\waypoint_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClInclude Include="..\src\table\airport_defaults.h">
<Filter>Tables</Filter>
</ClInclude>

@ -308,6 +308,18 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp" />
<ClCompile Include="..\src\tbtr_template_gui_replaceall.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle.cpp" />
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp" />
<ClInclude Include="..\src\tbtr_template_gui_main.h" />
<ClInclude Include="..\src\tbtr_template_gui_create.h" />
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h" />
<ClInclude Include="..\src\tbtr_template_gui_replaceall.h" />
<ClInclude Include="..\src\tbtr_template_vehicle.h" />
<ClInclude Include="..\src\tbtr_template_vehicle_func.h" />
<ClCompile Include="..\src\airport.cpp" />
<ClCompile Include="..\src\animated_tile.cpp" />
<ClCompile Include="..\src\articulated_vehicles.cpp" />
@ -886,6 +898,8 @@
<ClCompile Include="..\src\saveload\town_sl.cpp" />
<ClCompile Include="..\src\saveload\vehicle_sl.cpp" />
<ClCompile Include="..\src\saveload\waypoint_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp" />
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp" />
<ClInclude Include="..\src\table\airport_defaults.h" />
<ClInclude Include="..\src\table\airport_movement.h" />
<ClInclude Include="..\src\table\airporttile_ids.h" />

@ -102,6 +102,42 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\tbtr_template_gui_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_create_virtualtrain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_gui_replaceall.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\tbtr_template_vehicle_func.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClInclude Include="..\src\tbtr_template_gui_main.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_create_virtualtrain.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_gui_replaceall.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\src\tbtr_template_vehicle_func.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClCompile Include="..\src\airport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -1836,6 +1872,12 @@
<ClCompile Include="..\src\saveload\waypoint_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_replacement_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClCompile Include="..\src\saveload\tbtr_template_veh_sl.cpp">
<Filter>Save/Load handlers</Filter>
</ClCompile>
<ClInclude Include="..\src\table\airport_defaults.h">
<Filter>Tables</Filter>
</ClInclude>

@ -434,6 +434,54 @@
<Filter
Name="Source Files"
>
<File
RelativePath=".\..\src\tbtr_template_gui_main.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_replaceall.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_main.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_replaceall.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.h"
>
</File>
<File
RelativePath=".\..\src\airport.cpp"
>
@ -2770,6 +2818,14 @@
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_replacement_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_veh_sl.cpp"
>
</File>
</Filter>
<Filter
Name="Tables"

@ -431,6 +431,54 @@
<Filter
Name="Source Files"
>
<File
RelativePath=".\..\src\tbtr_template_gui_main.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_replaceall.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.cpp"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_main.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_create_virtualtrain.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_gui_replaceall.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle.h"
>
</File>
<File
RelativePath=".\..\src\tbtr_template_vehicle_func.h"
>
</File>
<File
RelativePath=".\..\src\airport.cpp"
>
@ -2767,6 +2815,14 @@
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_replacement_sl.cpp"
>
</File>
<File
RelativePath=".\..\src\saveload\tbtr_template_veh_sl.cpp"
>
</File>
</Filter>
<Filter
Name="Tables"

@ -1,4 +1,17 @@
# Source Files
tbtr_template_gui_main.cpp
tbtr_template_gui_create.cpp
tbtr_template_gui_create_virtualtrain.cpp
tbtr_template_gui_replaceall.cpp
tbtr_template_vehicle.cpp
tbtr_template_vehicle_func.cpp
tbtr_template_gui_main.h
tbtr_template_gui_create.h
tbtr_template_gui_create_virtualtrain.h
tbtr_template_gui_replaceall.h
tbtr_template_vehicle.h
tbtr_template_vehicle_func.h
airport.cpp
animated_tile.cpp
articulated_vehicles.cpp
@ -633,6 +646,8 @@ saveload/subsidy_sl.cpp
saveload/town_sl.cpp
saveload/vehicle_sl.cpp
saveload/waypoint_sl.cpp
saveload/tbtr_template_replacement_sl.cpp
saveload/tbtr_template_veh_sl.cpp
# Tables
table/airport_defaults.h

@ -347,7 +347,7 @@ static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after,
* @param new_head The new head of the completely replaced vehicle chain
* @param flags the command flags to use
*/
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
{
CommandCost cost = CommandCost();

@ -99,4 +99,6 @@ static inline CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company);
CommandCost CopyHeadSpecificThings(Vehicle*, Vehicle*, DoCommandFlag);
#endif /* AUTOREPLACE_FUNC_H */

@ -21,6 +21,7 @@
#include "company_func.h"
#include "core/pool_func.hpp"
#include "order_backup.h"
#include "tbtr_template_vehicle.h"
#include "table/strings.h"
@ -137,6 +138,9 @@ void GroupStatistics::Clear()
*/
/* static */ void GroupStatistics::CountVehicle(const Vehicle *v, int delta)
{
/* make virtual trains group-neutral */
if ( HasBit(v->subtype, GVSF_VIRTUAL) ) return;
assert(delta == 1 || delta == -1);
GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
@ -341,6 +345,9 @@ CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
VehicleType vt = g->vehicle_type;
/* Delete all template replacements using the just deleted group */
deleteIllegalTemplateReplacements(g->index);
/* Delete the Replace Vehicle Windows */
DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
delete g;

@ -34,6 +34,8 @@
static const int LEVEL_WIDTH = 10; ///< Indenting width of a sub-group in pixels
#include "tbtr_template_gui_main.h"
typedef GUIList<const Group*> GUIGroupList;
static const NWidgetPart _nested_group_widgets[] = {
@ -643,6 +645,7 @@ public:
case WID_GL_DELETE_GROUP: { // Delete the selected group
this->group_confirm = this->vli.index;
ShowQuery(STR_QUERY_GROUP_DELETE_CAPTION, STR_GROUP_DELETE_QUERY_TEXT, this, DeleteGroupCallback);
InvalidateWindowData(WC_TEMPLATEGUI_MAIN, 0, 0, 0);
break;
}
@ -762,6 +765,7 @@ public:
virtual void OnQueryTextFinished(char *str)
{
if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str);
InvalidateWindowData(WC_TEMPLATEGUI_MAIN, 0, 0, 0);
this->group_rename = INVALID_GROUP;
}
@ -782,6 +786,10 @@ public:
assert(this->vehicles.Length() != 0);
switch (index) {
case ADI_TEMPLATE_REPLACE: // TemplateReplace Window
if ( vli.vtype == VEH_TRAIN )
ShowTemplateReplaceWindow(this->unitnumber_digits, this->resize.step_height);
break;
case ADI_REPLACE: // Replace window
ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
break;

@ -4968,3 +4968,62 @@ STR_PLANE :{BLACK}{PLANE}
STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
STR_TMPL_RPL_TITLE :{WHITE}Template Replacement
STR_TMPL_TEMPLATE_REPLACEMENT :Template Replacement
STR_TMPL_TRAINS_IN_GROUP :{BLACK}Trains in group
STR_TMPL_AVAILABLE_TEMPLATES :{BLACK}Available Templates
STR_TMPL_DEFINE_TEMPLATE :{BLACK}New
STR_TMPL_EDIT_TEMPLATE :{BLACK}Edit
STR_TMPL_CREATE_CLONE_VEH :{BLACK}Clone
STR_TMPL_DELETE_TEMPLATE :{BLACK}Delete
STR_TMPL_RPL_ALL_TMPL :{BLACK}Replace All Templates
STR_TMPL_NEW_VEHICLE :{BLACK}New Vehicle
STR_TMPL_CONFIRM :{BLACK}Ok
STR_TMPL_CANCEL :{BLACK}Cancel
STR_TMPL_NEW :{BLACK}New Template Vehicle
STR_TMPL_REFIT :{BLACK}Refit
STR_TMPL_GROUP_INFO :{BLACK}Group Info: {ORANGE}
STR_TMPL_TEMPLATE_INFO :{BLACK}Template Info: {ORANGE}
STR_TMPL_RPL_START :{BLACK}Start replacing
STR_TMPL_RPL_STOP :{BLACK}Stop replacing
STR_TMPL_TRAIN_OVR_VALUE :{TINY_FONT}{BLACK}Train Value: {CURRENCY_SHORT}
STR_TMPL_TEMPLATE_OVR_VALUE :{TINY_FONT}{BLACK}Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_nogold :{TINY_FONT}{BLACK}Buying Cost: {CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_nogoldandcurrency :{TINY_FONT}{BLACK}Buying Cost:
STR_TMPL_TEMPLATE_OVR_VALUE_notinyfont :{BLACK}Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_TEMPLATE_OVR_VALUE_notinyfontandblack :Buying Cost: {GOLD}{CURRENCY_LONG}
STR_TMPL_WARNING_FREE_WAGON :{RED}Free Chain: not runnable!
STR_TMPL_TEST :{ORANGE}Test String: {RAW_STRING} {RAW_STRING}
STR_TMPL_GROUP_USES_TEMPLATE :{BLACK}Template in use: {NUM}
STR_TMP_TEMPLATE_IN_USE :Template is in use
STR_TMPL_GROUP_NUM_TRAINS :{BLACK}{NUM}
STR_TMPL_CREATEGUI_TITLE :{WHITE}Create/Edit Template Vehicle
STR_TMPL_MAINGUI_DEFINEDGROUPS :{BLACK}Defined Groups for Company
STR_TMPL_TMPLRPL_EX_DIFF_RAILTYPE :Uses Template of different rail type
STR_TMPL_SET_USEDEPOT :{BLACK}Use vehicles in depot
STR_TMPL_SET_USEDEPOT_TIP :{BLACK}Use vehicles inside the depot that are in a neutral and idle state to compose trains on template replacement in order to reduce buying costs
STR_TMPL_SET_KEEPREMAINDERS :{BLACK}Keep remainders
STR_TMPL_SET_KEEPREMAINDERS_TIP :{BLACK}After finishing template replacement keep all remaining vehicles from the old train in a neutral and idle state for later use
STR_TMPL_SET_REFIT :{BLACK}Use Refit
STR_TMPL_SET_REFIT_TIP :{BLACK}If set, the train will use exactly the cargo refit specified by the template. If not every wagon that is to be newly bought or retrieved from the depot, will *attempt* to be refitted as the old one was. Standard refit if this is impossible.
STR_TMPL_CONFIG_USEDEPOT :use depot
STR_TMPL_CONFIG_KEEPREMAINDERS :keep rem
STR_TMPL_CONFIG_REFIT :refit
STR_TMPL_NUM_TRAINS_NEED_RPL :# trains to replace:
STR_TMPL_CARGO_SUMMARY :{CARGO_LONG}
STR_TMPL_CARGO_SUMMARY_MULTI :{CARGO_LONG} (x{NUM})
STR_TMPL_RPLALLGUI_TITLE :{WHITE}Replace all Templace Vehicles
STR_TMPL_RPLALLGUI_INSET_TOP :{BLACK}Choose Vehicle Type and Replacement
STR_TMPL_RPLALLGUI_INSET_TOP_1 :{BLACK}Template Engines
STR_TMPL_RPLALLGUI_INSET_TOP_2 :{BLACK}Buyable Engines
STR_TMPL_RPLALLGUI_INSET_BOTTOM :{BLACK}Current Template List (updated only after replacement)
STR_TMPL_RPLALLGUI_BUTTON_RPLALL :{BLACK}Replace All
STR_TMPL_RPLALLGUI_BUTTON_APPLY :{BLACK}Apply
STR_TMPL_RPLALLGUI_BUTTON_CANCEL :{BLACK}Cancel
STR_TMPL_RPLALLGUI_USE_TIP :{BLACK}Select a vehicle type from each list and press 'Replace All'. If you are happy with the result displayed in the template list, press 'Apply' to actually apply these changes.

@ -196,4 +196,6 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile);
StringID MapGRFStringID(uint32 grfid, StringID str);
void ShowNewGRFError();
struct TemplateVehicle;
#endif /* NEWGRF_H */

@ -1771,7 +1771,8 @@ void CheckOrders(const Vehicle *v)
if (v->FirstShared() != v) return;
/* Only check every 20 days, so that we don't flood the message log */
if (v->owner == _local_company && v->day_counter % 20 == 0) {
/* The check is skipped entirely in case the current vehicle is virtual (a.k.a a 'template train') */
if (v->owner == _local_company && v->day_counter % 20 == 0 && !HasBit(v->subtype, GVSF_VIRTUAL) ) {
const Order *order;
StringID message = INVALID_STRING_ID;

@ -775,6 +775,9 @@ bool AfterLoadGame()
/* Update all vehicles */
AfterLoadVehicles(true);
/* Update template vehicles */
AfterLoadTemplateVehicles();
/* Make sure there is an AI attached to an AI company */
{
Company *c;

@ -44,6 +44,8 @@
#include "../fios.h"
#include "../error.h"
#include "../tbtr_template_vehicle.h"
#include "table/strings.h"
#include "saveload_internal.h"
@ -447,6 +449,8 @@ extern const ChunkHandler _linkgraph_chunk_handlers[];
extern const ChunkHandler _airport_chunk_handlers[];
extern const ChunkHandler _object_chunk_handlers[];
extern const ChunkHandler _persistent_storage_chunk_handlers[];
extern const ChunkHandler _template_replacement_chunk_handlers[];
extern const ChunkHandler _template_vehicle_chunk_handlers[];
/** Array of all chunks in a savegame, \c NULL terminated. */
static const ChunkHandler * const _chunk_handlers[] = {
@ -483,6 +487,8 @@ static const ChunkHandler * const _chunk_handlers[] = {
_airport_chunk_handlers,
_object_chunk_handlers,
_persistent_storage_chunk_handlers,
_template_replacement_chunk_handlers,
_template_vehicle_chunk_handlers,
NULL,
};
@ -1243,6 +1249,7 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
switch (rt) {
case REF_VEHICLE_OLD: // Old vehicles we save as new ones
case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
case REF_TEMPLATE_VEHICLE: return ((const TemplateVehicle*)obj)->index + 1;
case REF_STATION: return ((const Station*)obj)->index + 1;
case REF_TOWN: return ((const Town*)obj)->index + 1;
case REF_ORDER: return ((const Order*)obj)->index + 1;
@ -1302,6 +1309,10 @@ static void *IntToReference(size_t index, SLRefType rt)
if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
SlErrorCorrupt("Referencing invalid Vehicle");
case REF_TEMPLATE_VEHICLE:
if (TemplateVehicle::IsValidID(index)) return TemplateVehicle::Get(index);
SlErrorCorrupt("Referencing invalid TemplateVehicle");
case REF_STATION:
if (Station::IsValidID(index)) return Station::Get(index);
SlErrorCorrupt("Referencing invalid Station");

@ -85,6 +85,7 @@ enum SLRefType {
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage.
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle
};
/** Highest possible savegame version. */

@ -28,6 +28,7 @@ const SaveLoad *GetBaseStationDescription();
void AfterLoadVehicles(bool part_of_load);
void FixupTrainLengths();
void AfterLoadTemplateVehicles();
void AfterLoadStations();
void AfterLoadRoadStops();
void AfterLoadLabelMaps();

@ -0,0 +1,35 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "saveload.h"
static const SaveLoad _template_replacement_desc[] = {
SLE_VAR(TemplateReplacement, sel_template, SLE_UINT16),
SLE_VAR(TemplateReplacement, group, SLE_UINT16),
SLE_END()
};
static void Save_TMPL_RPLS()
{
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
SlSetArrayIndex(tr->index);
SlObject(tr, _template_replacement_desc);
}
}
static void Load_TMPL_RPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateReplacement *tr = new (index) TemplateReplacement();
SlObject(tr, _template_replacement_desc);
}
}
extern const ChunkHandler _template_replacement_chunk_handlers[] = {
{'TRPL', Save_TMPL_RPLS, Load_TMPL_RPLS, NULL, NULL, CH_ARRAY | CH_LAST},
};

@ -0,0 +1,99 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "saveload.h"
const SaveLoad* GTD() {
static const SaveLoad _template_veh_desc[] = {
SLE_REF(TemplateVehicle, next, REF_TEMPLATE_VEHICLE),
SLE_VAR(TemplateVehicle, reuse_depot_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, keep_remaining_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, refit_as_template, SLE_UINT8),
SLE_VAR(TemplateVehicle, owner, SLE_UINT32),
SLE_VAR(TemplateVehicle, owner_b, SLE_UINT8),
SLE_VAR(TemplateVehicle, engine_type, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_type, SLE_UINT8),
SLE_VAR(TemplateVehicle, cargo_cap, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, railtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, index, SLE_UINT32),
SLE_VAR(TemplateVehicle, real_consist_length, SLE_UINT16),
SLE_VAR(TemplateVehicle, max_speed, SLE_UINT16),
SLE_VAR(TemplateVehicle, power, SLE_UINT32),
SLE_VAR(TemplateVehicle, weight, SLE_UINT32),
SLE_VAR(TemplateVehicle, max_te, SLE_UINT32),
SLE_VAR(TemplateVehicle, spritenum, SLE_UINT8),
SLE_VAR(TemplateVehicle, cur_image, SLE_UINT32),
SLE_VAR(TemplateVehicle, image_width, SLE_UINT32),
SLE_END()
};
static const SaveLoad * const _ret[] = {
_template_veh_desc,
};
return _ret[0];
}
static void Save_TMPLS()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
SlSetArrayIndex(tv->index);
SlObject(tv, GTD());
}
}
static void Load_TMPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateVehicle *tv = new (index) TemplateVehicle(); //TODO:check with veh sl code
SlObject(tv, GTD());
}
}
static void Ptrs_TMPLS()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
SlObject(tv, GTD());
}
}
void AfterLoadTemplateVehicles()
{
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
/* Reinstate the previous pointer */
if (tv->next != NULL) tv->next->previous = tv;
tv->first =NULL;
}
FOR_ALL_TEMPLATES(tv) {
/* Fill the first pointers */
if (tv->previous == NULL) {
for (TemplateVehicle *u = tv; u != NULL; u = u->Next()) {
u->first = tv;
}
}
}
}
extern const ChunkHandler _template_vehicle_chunk_handlers[] = {
{'TMPL', Save_TMPLS, Load_TMPLS, Ptrs_TMPLS, NULL, CH_ARRAY | CH_LAST},
};

@ -0,0 +1,459 @@
#include "stdafx.h"
#include "gfx_func.h"
#include "direction_type.h"
#include "strings_func.h"
#include "window_func.h"
#include "company_func.h"
#include "window_gui.h"
#include "settings_func.h"
#include "core/geometry_func.hpp"
#include "table/sprites.h"
#include "table/strings.h"
#include "viewport_func.h"
#include "window_func.h"
#include "gui.h"
#include "textbuf_gui.h"
#include "command_func.h"
#include "depot_base.h"
#include "vehicle_gui.h"
#include "spritecache.h"
#include "strings_func.h"
#include "window_func.h"
#include "vehicle_func.h"
#include "company_func.h"
#include "tilehighlight_func.h"
#include "window_gui.h"
#include "vehiclelist.h"
#include "order_backup.h"
#include "group.h"
#include "company_base.h"
#include "tbtr_template_gui_create.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include "train.h"
class TemplateReplaceWindow;
// some space in front of the virtual train in the matrix
uint16 TRAIN_FRONT_SPACE = 16;
enum TemplateReplaceWindowWidgets {
TCW_CAPTION,
TCW_MATRIX_NEW_TMPL,
TCW_INFO_PANEL,
TCW_SCROLLBAR_NEW_TMPL,
TCW_SELL_TMPL,
TCW_NEW,
TCW_OK,
TCW_CANCEL,
TCW_REFIT,
TCW_CLONE,
};
static const NWidgetPart _widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, TCW_CAPTION), SetDataTip(STR_TMPL_CREATEGUI_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
NWidget(WWT_MATRIX, COLOUR_GREY, TCW_MATRIX_NEW_TMPL), SetMinimalSize(216, 60), SetFill(1, 0), SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 0), SetScrollbar(TCW_SCROLLBAR_NEW_TMPL),
NWidget(WWT_PANEL, COLOUR_GREY, TCW_INFO_PANEL), SetMinimalSize(216,80), SetResize(1,1), EndContainer(),
NWidget(NWID_HSCROLLBAR, COLOUR_GREY, TCW_SCROLLBAR_NEW_TMPL), SetResize(1,0),
EndContainer(),
NWidget(WWT_IMGBTN, COLOUR_GREY, TCW_SELL_TMPL), SetDataTip(0x0, STR_NULL), SetMinimalSize(23,23), SetResize(0, 1), SetFill(0, 1),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_OK), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CONFIRM, STR_TMPL_CONFIRM),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_NEW), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_NEW, STR_TMPL_NEW),
NWidget(WWT_TEXTBTN, COLOUR_GREY, TCW_CLONE), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_TMPL_CREATE_CLONE_VEH),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_REFIT), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_REFIT, STR_TMPL_REFIT),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TCW_CANCEL), SetMinimalSize(52, 12), SetResize(1,0), SetDataTip(STR_TMPL_CANCEL, STR_TMPL_CANCEL),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
static WindowDesc _template_create_window_desc(
WDP_AUTO, // window position
"template create window", // const char* ini_key
456, 100, // window size
WC_CREATE_TEMPLATE, // window class
WC_TEMPLATEGUI_MAIN, // parent window class
WDF_CONSTRUCTION, // window flags
_widgets, lengthof(_widgets) // widgets + num widgets
);
static void TrainDepotMoveVehicle(const Vehicle *wagon, VehicleID sel, const Vehicle *head)
{
const Vehicle *v = Vehicle::Get(sel);
if (v == wagon) return;
if (wagon == NULL) {
if (head != NULL) wagon = head->Last();
} else {
wagon = wagon->Previous();
if (wagon == NULL) return;
}
if (wagon == v) return;
CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (_ctrl_pressed ? 1:0)<<20 | (1<<21) | v->index, wagon == NULL ? INVALID_VEHICLE : wagon->index, 0);
}
class TemplateCreateWindow : public Window {
private:
Scrollbar *hscroll;
int line_height;
Train* virtual_train;
bool editMode;
bool *noticeParent;
bool *createWindowOpen; /// used to notify main window of progress (dummy way of disabling 'delete' while editing a template)
bool virtualTrainChangedNotice;
VehicleID sel;
VehicleID vehicle_over;
TemplateVehicle *editTemplate;
public:
TemplateCreateWindow(WindowDesc* _wdesc, TemplateVehicle *to_edit, bool *notice, bool *windowOpen, int step_h) : Window(_wdesc)
{
this->line_height = step_h;
this->CreateNestedTree(_wdesc);
this->hscroll = this->GetScrollbar(TCW_SCROLLBAR_NEW_TMPL);
this->FinishInitNested(VEH_TRAIN);
/* a sprite */
this->GetWidget<NWidgetCore>(TCW_SELL_TMPL)->widget_data = SPR_SELL_TRAIN;
this->owner = _local_company;
noticeParent = notice;
createWindowOpen = windowOpen;
virtualTrainChangedNotice = false;
this->editTemplate = to_edit;
if ( to_edit ) editMode = true;
else editMode = false;
this->sel = INVALID_VEHICLE;
this->vehicle_over = INVALID_VEHICLE;
this->virtual_train = VirtualTrainFromTemplateVehicle(to_edit);
this->resize.step_height = 1;
}
~TemplateCreateWindow()
{
if ( virtual_train )
delete virtual_train;
SetWindowClassesDirty(WC_TRAINS_LIST);
/* more cleanup */
*createWindowOpen = false;
DeleteWindowById(WC_BUILD_VIRTUAL_TRAIN, this->window_number);
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case TCW_MATRIX_NEW_TMPL:
size->height = 20;
break;
}
}
virtual void OnResize()
{
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(TCW_MATRIX_NEW_TMPL);
this->hscroll->SetCapacity(nwi->current_x);
nwi->widget_data = (this->hscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
virtualTrainChangedNotice = true;
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch(widget) {
case TCW_MATRIX_NEW_TMPL: {
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(TCW_MATRIX_NEW_TMPL);
ClickedOnVehiclePanel(pt.x - nwi->pos_x-TRAIN_FRONT_SPACE, pt.y - nwi->pos_y);
break;
}
case TCW_NEW: {
ShowBuildVirtualTrainWindow(&virtual_train, &virtualTrainChangedNotice);
break;
}
case TCW_CLONE: {
this->SetWidgetDirty(TCW_CLONE);
this->ToggleWidgetLoweredState(TCW_CLONE);
if (this->IsWidgetLowered(TCW_CLONE)) {
static const CursorID clone_icon = SPR_CURSOR_CLONE_TRAIN;
SetObjectToPlaceWnd(clone_icon, PAL_NONE, HT_VEHICLE, this);
} else {
ResetObjectToPlace();
}
break;
}
case TCW_OK: {
TemplateVehicle *tv = NULL;
if ( editMode ) tv = DeleteTemplateVehicle(editTemplate);
editTemplate = TemplateVehicleFromVirtualTrain(virtual_train);
if ( tv ) *noticeParent = true;
delete this;
break;
}
case TCW_CANCEL: {
delete this;
break;
}
case TCW_REFIT: {
ShowVehicleRefitWindow(virtual_train, INVALID_VEH_ORDER_ID, this, false, true);
break;
}
}
}
virtual bool OnVehicleSelect(const Vehicle *v)
{
// throw away the current virtual train
if ( virtual_train )
delete this->virtual_train;
// create a new one
this->virtual_train = CloneVirtualTrainFromTrain((const Train*)v);
this->ToggleWidgetLoweredState(TCW_CLONE);
ResetObjectToPlace();
this->SetDirty();
return true;
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch(widget) {
case TCW_MATRIX_NEW_TMPL: {
if ( this->virtual_train ) {
DrawTrainImage(virtual_train, r.left+TRAIN_FRONT_SPACE, r.right, r.top+2, this->sel, EIT_PURCHASE, this->hscroll->GetPosition(), this->vehicle_over);
SetDParam(0, CeilDiv(virtual_train->gcache.cached_total_length * 10, TILE_SIZE));
SetDParam(1, 1);
DrawString(r.left, r.right, r.top, STR_TINY_BLACK_DECIMAL, TC_BLACK, SA_RIGHT);
}
break;
}
case TCW_INFO_PANEL: {
if ( this->virtual_train ) {
/* Draw vehicle performance info */
const GroundVehicleCache *gcache = this->virtual_train->GetGroundVehicleCache();
SetDParam(2, this->virtual_train->GetDisplayMaxSpeed());
SetDParam(1, gcache->cached_power);
SetDParam(0, gcache->cached_weight);
SetDParam(3, gcache->cached_max_te / 1000);
DrawString(r.left+8, r.right, r.top+4, STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE);
/* Draw cargo summary */
CargoArray cargo_caps;
for ( const Train *tmp=this->virtual_train; tmp; tmp=tmp->Next() )
cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
int y = r.top+24;
for (CargoID i = 0; i < NUM_CARGO; ++i) {
if ( cargo_caps[i] > 0 ) {
SetDParam(0, i);
SetDParam(1, cargo_caps[i]);
SetDParam(2, _settings_game.vehicle.freight_trains);
DrawString(r.left+8, r.right, y, STR_TMPL_CARGO_SUMMARY, TC_WHITE, SA_LEFT);
y += this->line_height/2;
}
}
}
break;
}
default:
break;
}
}
virtual void OnTick()
{
if ( virtualTrainChangedNotice ) {
this->SetDirty();
virtualTrainChangedNotice = false;
}
}
virtual void OnDragDrop(Point pt, int widget)
{
switch (widget) {
case TCW_MATRIX_NEW_TMPL: {
const Vehicle *v = NULL;
VehicleID sel;
if ( virtual_train ) sel = virtual_train->index;
else sel = INVALID_VEHICLE;
this->SetDirty();
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(TCW_MATRIX_NEW_TMPL);
GetDepotVehiclePtData gdvp = { NULL, NULL };
if (this->GetVehicleFromDepotWndPt(pt.x - nwi->pos_x, pt.y - nwi->pos_y, &v, &gdvp) == MODE_DRAG_VEHICLE && sel != INVALID_VEHICLE) {
if (gdvp.wagon == NULL || gdvp.wagon->index != sel) {
this->vehicle_over = INVALID_VEHICLE;
TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head);
virtual_train = virtual_train->First();
}
}
break;
}
case TCW_SELL_TMPL: {
if (this->IsWidgetDisabled(widget)) return;
if (this->sel == INVALID_VEHICLE) return;
virtual_train = DeleteVirtualTrain(virtual_train, Train::Get(this->sel));
this->sel = INVALID_VEHICLE;
this->SetDirty();
break;
}
default:
this->sel = INVALID_VEHICLE;
this->SetDirty();
}
_cursor.vehchain = false;
this->sel = INVALID_VEHICLE;
this->SetDirty();
}
virtual void OnMouseDrag(Point pt, int widget)
{
if (this->sel == INVALID_VEHICLE) return;
/* A rail vehicle is dragged.. */
if (widget != TCW_MATRIX_NEW_TMPL) { // ..outside of the depot matrix.
if (this->vehicle_over != INVALID_VEHICLE) {
this->vehicle_over = INVALID_VEHICLE;
this->SetWidgetDirty(TCW_MATRIX_NEW_TMPL);
}
return;
}
NWidgetBase *matrix = this->GetWidget<NWidgetBase>(widget);
const Vehicle *v = NULL;
GetDepotVehiclePtData gdvp = {NULL, NULL};
if (this->GetVehicleFromDepotWndPt(pt.x - matrix->pos_x, pt.y - matrix->pos_y, &v, &gdvp) != MODE_DRAG_VEHICLE) return;
VehicleID new_vehicle_over = INVALID_VEHICLE;
if (gdvp.head != NULL) {
if (gdvp.wagon == NULL && gdvp.head->Last()->index != this->sel) { // ..at the end of the train.
/* NOTE: As a wagon can't be moved at the begin of a train, head index isn't used to mark a drag-and-drop
* destination inside a train. This head index is then used to indicate that a wagon is inserted at
* the end of the train.
*/
new_vehicle_over = gdvp.head->index;
} else if (gdvp.wagon != NULL && gdvp.head != gdvp.wagon &&
gdvp.wagon->index != this->sel &&
gdvp.wagon->Previous()->index != this->sel) { // ..over an existing wagon.
new_vehicle_over = gdvp.wagon->index;
}
}
if (this->vehicle_over == new_vehicle_over) return;
this->vehicle_over = new_vehicle_over;
this->SetWidgetDirty(widget);
}
virtual void OnPaint()
{
uint max_width = 32;
uint width = 0;
if ( virtual_train )
for (Train *v = virtual_train; v != NULL; v = v->Next())
width += v->GetDisplayImageWidth();
max_width = max(max_width, width);
this->hscroll->SetCount(max_width+25);
this->DrawWidgets();
}
struct GetDepotVehiclePtData {
const Vehicle *head;
const Vehicle *wagon;
};
enum DepotGUIAction {
MODE_ERROR,
MODE_DRAG_VEHICLE,
MODE_SHOW_VEHICLE,
MODE_START_STOP,
};
uint count_width;
uint header_width;
DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, const Vehicle **veh, GetDepotVehiclePtData *d) const
{
const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(TCW_MATRIX_NEW_TMPL);
/* In case of RTL the widgets are swapped as a whole */
if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x;
uint xm = x;
bool wagon = false;
x += this->hscroll->GetPosition();
const Train *v = virtual_train;
d->head = d->wagon = v;
if (xm <= this->header_width) {
if (wagon) return MODE_ERROR;
return MODE_SHOW_VEHICLE;
}
/* Account for the header */
x -= this->header_width;
/* find the vehicle in this row that was clicked */
for (; v != NULL; v = v->Next()) {
x -= v->GetDisplayImageWidth();
if (x < 0) break;
}
d->wagon = (v != NULL ? v->GetFirstEnginePart() : NULL);
return MODE_DRAG_VEHICLE;
}
void ClickedOnVehiclePanel(int x, int y)
{
GetDepotVehiclePtData gdvp = { NULL, NULL };
const Vehicle *v = NULL;
this->GetVehicleFromDepotWndPt(x, y, &v, &gdvp);
v = gdvp.wagon;
if (v != NULL && VehicleClicked(v)) return;
VehicleID sel = this->sel;
if (sel != INVALID_VEHICLE) {
this->sel = INVALID_VEHICLE;
} else if (v != NULL) {
int image = v->GetImage(_current_text_dir == TD_RTL ? DIR_E : DIR_W, EIT_PURCHASE);
SetObjectToPlaceWnd(image, GetVehiclePalette(v), HT_DRAG, this);
this->sel = v->index;
this->SetDirty();
_cursor.short_vehicle_offset = v->IsGroundVehicle() ? 16 - v->GetGroundVehicleCache()->cached_veh_length * 2 : 0;
_cursor.vehchain = _ctrl_pressed;
}
}
};
void ShowTemplateCreateWindow(TemplateVehicle *to_edit, bool *noticeParent, bool *createWindowOpen, int step_h)
{
if ( BringWindowToFrontById(WC_CREATE_TEMPLATE, VEH_TRAIN) != NULL ) return;
new TemplateCreateWindow(&_template_create_window_desc, to_edit, noticeParent, createWindowOpen, step_h);
}

@ -0,0 +1,11 @@
// template creation gui
#ifndef TEMPLATE_GUI_CREATE
#define TEMPLATE_GUI_CREATE
#include "tbtr_template_vehicle.h"
#include "tbtr_template_gui_create_virtualtrain.h"
void ShowTemplateCreateWindow(TemplateVehicle*, bool*, bool*, int);
#endif

@ -0,0 +1,829 @@
/* $Id: build_vehicle_gui.cpp 23792 2012-01-12 19:23:00Z yexo $ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file build_vehicle_gui.cpp GUI for building vehicles. */
#include "stdafx.h"
#include "engine_base.h"
#include "engine_func.h"
#include "station_base.h"
#include "articulated_vehicles.h"
#include "textbuf_gui.h"
#include "command_func.h"
#include "company_func.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "newgrf_text.h"
#include "group.h"
#include "string_func.h"
#include "strings_func.h"
#include "window_func.h"
#include "date_func.h"
#include "vehicle_func.h"
#include "widgets/dropdown_func.h"
#include "engine_gui.h"
#include "cargotype.h"
#include "core/geometry_func.hpp"
#include "widgets/build_vehicle_widget.h"
#include "table/strings.h"
#include "tbtr_template_gui_create_virtualtrain.h"
#include "vehicle_gui.h"
static const NWidgetPart _nested_build_vehicle_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0),
NWidget(NWID_SPACER), SetFill(1, 1),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
EndContainer(),
EndContainer(),
EndContainer(),
/* Vehicle list. */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetDataTip(0x101, STR_NULL), SetScrollbar(WID_BV_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BV_SCROLLBAR),
EndContainer(),
/* Panel with details. */
NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
/* Build/rename buttons, resize button. */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_TMPL_CONFIRM, STR_TMPL_CONFIRM),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
/** Special cargo filter criteria */
static const CargoID CF_ANY = CT_NO_REFIT; ///< Show all vehicles independent of carried cargo (i.e. no filtering)
static const CargoID CF_NONE = CT_INVALID; ///< Show only vehicles which do not carry cargo (e.g. train engines)
static bool _internal_sort_order; ///< false = descending, true = ascending
static byte _last_sort_criteria[] = {0, 0, 0, 0};
static bool _last_sort_order[] = {false, false, false, false};
static CargoID _last_filter_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_ANY};
/**
* Determines order of engines by engineID
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
{
int r = Engine::Get(*a)->list_position - Engine::Get(*b)->list_position;
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by introduction date
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineIntroDateSorter(const EngineID *a, const EngineID *b)
{
const int va = Engine::Get(*a)->intro_date;
const int vb = Engine::Get(*b)->intro_date;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by name
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineNameSorter(const EngineID *a, const EngineID *b)
{
static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
static char last_name[2][64] = { "\0", "\0" };
const EngineID va = *a;
const EngineID vb = *b;
if (va != last_engine[0]) {
last_engine[0] = va;
SetDParam(0, va);
GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
}
if (vb != last_engine[1]) {
last_engine[1] = vb;
SetDParam(0, vb);
GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
}
int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by reliability
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineReliabilitySorter(const EngineID *a, const EngineID *b)
{
const int va = Engine::Get(*a)->reliability;
const int vb = Engine::Get(*b)->reliability;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by purchase cost
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineCostSorter(const EngineID *a, const EngineID *b)
{
Money va = Engine::Get(*a)->GetCost();
Money vb = Engine::Get(*b)->GetCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by speed
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineSpeedSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetDisplayMaxSpeed();
int vb = Engine::Get(*b)->GetDisplayMaxSpeed();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by power
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EnginePowerSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetPower();
int vb = Engine::Get(*b)->GetPower();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by tractive effort
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineTractiveEffortSorter(const EngineID *a, const EngineID *b)
{
int va = Engine::Get(*a)->GetDisplayMaxTractiveEffort();
int vb = Engine::Get(*b)->GetDisplayMaxTractiveEffort();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by running costs
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EngineRunningCostSorter(const EngineID *a, const EngineID *b)
{
Money va = Engine::Get(*a)->GetRunningCost();
Money vb = Engine::Get(*b)->GetRunningCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of engines by running costs
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL EnginePowerVsRunningCostSorter(const EngineID *a, const EngineID *b)
{
const Engine *e_a = Engine::Get(*a);
const Engine *e_b = Engine::Get(*b);
/* Here we are using a few tricks to get the right sort.
* We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
* we will actually calculate cunning cost/power (to make it more than 1).
* Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
* Another thing is that both power and running costs should be doubled for multiheaded engines.
* Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */
Money va = (e_a->GetRunningCost()) / max(1U, (uint)e_a->GetPower());
Money vb = (e_b->GetRunningCost()) / max(1U, (uint)e_b->GetPower());
int r = ClampToI32(vb - va);
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/* Train sorting functions */
/**
* Determines order of train engines by capacity
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL TrainEngineCapacitySorter(const EngineID *a, const EngineID *b)
{
const RailVehicleInfo *rvi_a = RailVehInfo(*a);
const RailVehicleInfo *rvi_b = RailVehInfo(*b);
int va = GetTotalCapacityOfArticulatedParts(*a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
int vb = GetTotalCapacityOfArticulatedParts(*b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/**
* Determines order of train engines by engine / wagon
* @param *a first engine to compare
* @param *b second engine to compare
* @return for descending order: returns < 0 if a < b and > 0 for a > b. Vice versa for ascending order and 0 for equal
*/
static int CDECL TrainEnginesThenWagonsSorter(const EngineID *a, const EngineID *b)
{
int val_a = (RailVehInfo(*a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int val_b = (RailVehInfo(*b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int r = val_a - val_b;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return _internal_sort_order ? -r : r;
}
/** Sort functions for the vehicle sort criteria, for each vehicle type. */
static EngList_SortTypeFunction * const _sorter[][11] = {{
/* Trains */
&EngineNumberSorter,
&EngineCostSorter,
&EngineSpeedSorter,
&EnginePowerSorter,
&EngineTractiveEffortSorter,
&EngineIntroDateSorter,
&EngineNameSorter,
&EngineRunningCostSorter,
&EnginePowerVsRunningCostSorter,
&EngineReliabilitySorter,
&TrainEngineCapacitySorter,
}};
static const StringID _sort_listing[][12] = {{
/* Trains */
STR_SORT_BY_ENGINE_ID,
STR_SORT_BY_COST,
STR_SORT_BY_MAX_SPEED,
STR_SORT_BY_POWER,
STR_SORT_BY_TRACTIVE_EFFORT,
STR_SORT_BY_INTRO_DATE,
STR_SORT_BY_NAME,
STR_SORT_BY_RUNNING_COST,
STR_SORT_BY_POWER_VS_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
INVALID_STRING_ID
}};
/** Cargo filter functions */
static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
{
if (cid == CF_ANY) return true;
uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true);
return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
}
static GUIEngineList::FilterFunction * const _filter_funcs[] = {
&CargoFilter,
};
/**
* Engine drawing loop
* @param type Type of vehicle (VEH_*)
* @param l The left most location of the list
* @param r The right most location of the list
* @param y The top most location of the list
* @param eng_list What engines to draw
* @param min where to start in the list
* @param max where in the list to end
* @param selected_id what engine to highlight as selected, if any
* @param show_count Whether to show the amount of engines or not
* @param selected_group the group to list the engines of
*/
static void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
{
static const int sprite_widths[] = { 60, 60, 76, 67 };
static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
/* Obligatory sanity checks! */
assert((uint)type < lengthof(sprite_widths));
assert_compile(lengthof(sprite_y_offsets) == lengthof(sprite_widths));
assert(max <= eng_list->Length());
bool rtl = _current_text_dir == TD_RTL;
int step_size = GetEngineListHeight(type);
int sprite_width = sprite_widths[type];
int sprite_x = (rtl ? r - sprite_width / 2 : l + sprite_width / 2) - 1;
int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
int text_left = l + (rtl ? WD_FRAMERECT_LEFT : sprite_width);
int text_right = r - (rtl ? sprite_width : WD_FRAMERECT_RIGHT);
int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
int small_text_y_offset = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
for (; min < max; min++, y += step_size) {
const EngineID engine = (*eng_list)[min];
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
SetDParam(0, engine);
DrawString(text_left, text_right, y + normal_text_y_offset, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK);
DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
if (show_count) {
SetDParam(0, num_engines);
DrawString(text_left, text_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT);
}
}
}
struct BuildVirtualTrainWindow : Window {
VehicleType vehicle_type;
union {
RailTypeByte railtype;
RoadTypes roadtypes;
} filter;
bool descending_sort_order;
byte sort_criteria;
bool listview_mode;
EngineID sel_engine;
EngineID rename_engine;
GUIEngineList eng_list;
CargoID cargo_filter[NUM_CARGO + 2]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE
StringID cargo_filter_texts[NUM_CARGO + 3]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
byte cargo_filter_criteria; ///< Selected cargo filter
int details_height; ///< Minimal needed height of the details panels (found so far).
Scrollbar *vscroll;
Train **virtual_train; ///< the virtual train that is currently being created
bool *noticeParent;
BuildVirtualTrainWindow(WindowDesc *desc, Train **vt, bool *notice) : Window(desc)
{
this->vehicle_type = VEH_TRAIN;
this->window_number = 0;//tile == INVALID_TILE ? (int)type : tile;
this->sel_engine = INVALID_ENGINE;
this->sort_criteria = _last_sort_criteria[VEH_TRAIN];
this->descending_sort_order = _last_sort_order[VEH_TRAIN];
this->filter.railtype = RAILTYPE_END;
this->listview_mode = (this->window_number <= VEH_END);
this->CreateNestedTree(desc);
this->vscroll = this->GetScrollbar(WID_BV_SCROLLBAR);
NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
widget = this->GetWidget<NWidgetCore>(WID_BV_RENAME);
widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + VEH_TRAIN;
widget->tool_tip = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + VEH_TRAIN;
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
this->FinishInitNested(VEH_TRAIN);
this->owner = _local_company;
this->eng_list.ForceRebuild();
this->GenerateBuildList();
if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
this->virtual_train = vt;
this->noticeParent = notice;
}
/** Populate the filter list and set the cargo filter criteria. */
void SetCargoFilterArray()
{
uint filter_items = 0;
/* Add item for disabling filtering. */
this->cargo_filter[filter_items] = CF_ANY;
this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
filter_items++;
/* Add item for vehicles not carrying anything, e.g. train engines.
* This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
if (this->vehicle_type == VEH_TRAIN) {
this->cargo_filter[filter_items] = CF_NONE;
this->cargo_filter_texts[filter_items] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE;
filter_items++;
}
/* Collect available cargo types for filtering. */
const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
this->cargo_filter[filter_items] = cs->Index();
this->cargo_filter_texts[filter_items] = cs->name;
filter_items++;
}
/* Terminate the filter list. */
this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
/* If not found, the cargo criteria will be set to all cargoes. */
this->cargo_filter_criteria = 0;
/* Find the last cargo filter criteria. */
for (uint i = 0; i < filter_items; ++i) {
if (this->cargo_filter[i] == _last_filter_criteria[this->vehicle_type]) {
this->cargo_filter_criteria = i;
break;
}
}
this->eng_list.SetFilterFuncs(_filter_funcs);
this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
}
void OnInit()
{
this->SetCargoFilterArray();
}
/** Filter the engine list against the currently selected cargo filter */
void FilterEngineList()
{
this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
this->sel_engine = INVALID_ENGINE;
} else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
this->sel_engine = this->eng_list[0];
}
}
/** Filter a single engine */
bool FilterSingleEngine(EngineID eid)
{
CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
}
/* Figure out what train EngineIDs to put in the list */
void GenerateBuildTrainList()
{
EngineID sel_id = INVALID_ENGINE;
int num_engines = 0;
int num_wagons = 0;
this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
this->eng_list.Clear();
/* Make list of all available train engines and wagons.
* Also check to see if the previously selected engine is still available,
* and if not, reset selection to INVALID_ENGINE. This could be the case
* when engines become obsolete and are removed */
const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail;
if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
/* Filter now! So num_engines and num_wagons is valid */
if (!FilterSingleEngine(eid)) continue;
*this->eng_list.Append() = eid;
if (rvi->railveh_type != RAILVEH_WAGON) {
num_engines++;
} else {
num_wagons++;
}
if (eid == this->sel_engine) sel_id = eid;
}
this->sel_engine = sel_id;
/* make engines first, and then wagons, sorted by ListPositionOfEngine() */
_internal_sort_order = false;
EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
/* and then sort engines */
_internal_sort_order = this->descending_sort_order;
EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
/* and finally sort wagons */
EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
}
/* Generate the list of vehicles */
void GenerateBuildList()
{
if (!this->eng_list.NeedRebuild()) return;
this->GenerateBuildTrainList();
this->eng_list.Compact();
this->eng_list.RebuildDone();
return; // trains should not reach the last sorting
this->FilterEngineList();
_internal_sort_order = this->descending_sort_order;
EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
this->eng_list.Compact();
this->eng_list.RebuildDone();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch (widget) {
case WID_BV_SORT_ASCENDING_DESCENDING:
this->descending_sort_order ^= true;
_last_sort_order[this->vehicle_type] = this->descending_sort_order;
this->eng_list.ForceRebuild();
this->SetDirty();
break;
case WID_BV_LIST: {
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
size_t num_items = this->eng_list.Length();
this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
this->SetDirty();
if (click_count > 1 && !this->listview_mode) this->OnClick(pt, WID_BV_BUILD, 1);
break;
}
case WID_BV_SORT_DROPDOWN: { // Select sorting criteria dropdown menu
uint32 hidden_mask = 0;
/* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
if (this->vehicle_type == VEH_ROAD &&
_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
SetBit(hidden_mask, 3); // power
SetBit(hidden_mask, 4); // tractive effort
SetBit(hidden_mask, 8); // power by running costs
}
/* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
if (this->vehicle_type == VEH_TRAIN &&
_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
SetBit(hidden_mask, 4); // tractive effort
}
ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, WID_BV_SORT_DROPDOWN, 0, hidden_mask);
break;
}
case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_BV_CARGO_FILTER_DROPDOWN, 0, 0);
break;
case WID_BV_BUILD: {
EngineID sel_eng = this->sel_engine;
if (sel_eng != INVALID_ENGINE) {
Train *tmp = CmdBuildVirtualRailVehicle(sel_eng);
if (tmp) AddVirtualEngine(tmp);
}
break;
}
}
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
if (!gui_scope) return;
/* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
if (this->vehicle_type == VEH_ROAD &&
_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL &&
this->sort_criteria > 7) {
this->sort_criteria = 0;
_last_sort_criteria[VEH_ROAD] = 0;
}
this->eng_list.ForceRebuild();
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
case WID_BV_CAPTION:
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
SetDParam(0, rti->strings.build_caption);
} else {
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
}
break;
case WID_BV_SORT_DROPDOWN:
SetDParam(0, _sort_listing[this->vehicle_type][this->sort_criteria]);
break;
case WID_BV_CARGO_FILTER_DROPDOWN:
SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
}
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case WID_BV_LIST:
resize->height = GetEngineListHeight(this->vehicle_type);
size->height = 3 * resize->height;
break;
case WID_BV_PANEL:
size->height = this->details_height;
break;
case WID_BV_SORT_ASCENDING_DESCENDING: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + WD_CLOSEBOX_WIDTH * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
break;
}
}
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case WID_BV_LIST:
DrawEngineList(this->vehicle_type, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, &this->eng_list, this->vscroll->GetPosition(), min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->eng_list.Length()), this->sel_engine, false, DEFAULT_GROUP);
break;
case WID_BV_SORT_ASCENDING_DESCENDING:
this->DrawSortButtonState(WID_BV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
break;
}
}
virtual void OnPaint()
{
this->GenerateBuildList();
this->vscroll->SetCount(this->eng_list.Length());
this->DrawWidgets();
if (!this->IsShaded()) {
int needed_height = this->details_height;
/* Draw details panels. */
if (this->sel_engine != INVALID_ENGINE) {
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_BV_PANEL);
int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine);
needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
}
if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
int resize = needed_height - this->details_height;
this->details_height = needed_height;
this->ReInit(0, resize);
return;
}
}
}
virtual void OnQueryTextFinished(char *str)
{
if (str == NULL) return;
DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), NULL, str);
}
virtual void OnDropdownSelect(int widget, int index)
{
switch (widget) {
case WID_BV_SORT_DROPDOWN:
if (this->sort_criteria != index) {
this->sort_criteria = index;
_last_sort_criteria[this->vehicle_type] = this->sort_criteria;
this->eng_list.ForceRebuild();
}
break;
case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
if (this->cargo_filter_criteria != index) {
this->cargo_filter_criteria = index;
_last_filter_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
/* deactivate filter if criteria is 'Show All', activate it otherwise */
this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
this->eng_list.ForceRebuild();
}
break;
}
this->SetDirty();
}
virtual void OnResize()
{
this->vscroll->SetCapacityFromWidget(this, WID_BV_LIST);
this->GetWidget<NWidgetCore>(WID_BV_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
void AddVirtualEngine(Train *toadd)
{
if ( !*virtual_train ) {
*virtual_train = toadd;
}
else {
VehicleID target = (*(this->virtual_train))->GetLastUnit()->index;
CommandCost movec;
movec = CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (1<<21) | toadd->index, target, 0);
}
*noticeParent = true;
}
};
static WindowDesc _build_vehicle_desc(
WDP_AUTO, // window position
"template create virtual train",// const char* ini_key
240, 268, // window size
WC_BUILD_VIRTUAL_TRAIN, // window class
WC_CREATE_TEMPLATE, // parent window class
WDF_CONSTRUCTION, // window flags
_nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets) // widgets + num widgets
);
void ShowBuildVirtualTrainWindow(Train **vt, bool *noticeParent)
{
// '0' as in VEH_TRAIN = Tile=0
assert(IsCompanyBuildableVehicleType(VEH_TRAIN));
DeleteWindowById(WC_BUILD_VEHICLE, 0);
new BuildVirtualTrainWindow(&_build_vehicle_desc, vt, noticeParent);
}

@ -0,0 +1,8 @@
#ifndef BUILD_VIRTUAL_TRAIN_GUI
#define BUILD_VIRTUAL_TRAIN_GUI
#include "train.h"
void ShowBuildVirtualTrainWindow(Train**, bool*);
#endif

@ -0,0 +1,692 @@
// mygui.c
//#include "tbtr_mygui.h"
#include <iostream>
#include <stdio.h>
#include "stdafx.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "group.h"
#include "rail.h"
#include "strings_func.h"
#include "window_func.h"
#include "autoreplace_func.h"
#include "company_func.h"
#include "engine_base.h"
#include "window_gui.h"
#include "viewport_func.h"
#include "tilehighlight_func.h"
#include "engine_gui.h"
#include "settings_func.h"
#include "core/geometry_func.hpp"
#include "rail_gui.h"
#include "table/sprites.h"
#include "table/strings.h"
// test creating pool -> creating vehicles
#include "core/pool_func.hpp"
#include "vehicle_gui_base.h"
#include "vehicle_base.h"
#include "train.h"
#include "vehicle_func.h"
#include "gfx_type.h"
#include "engine_func.h"
// drawing the vehicle length based on occupied tiles
#include "spritecache.h"
#include "tbtr_template_gui_main.h"
#include "tbtr_template_gui_create.h"
#include "tbtr_template_vehicle.h"
//#include "tbtr_template_vehicle_func.h"
typedef GUIList<const Group*> GUIGroupList;
enum TemplateReplaceWindowWidgets {
TRW_CAPTION,
TRW_WIDGET_INSET_GROUPS,
TRW_WIDGET_TOP_MATRIX,
TRW_WIDGET_TOP_SCROLLBAR,
TRW_WIDGET_INSET_TEMPLATES,
TRW_WIDGET_BOTTOM_MATRIX,
TRW_WIDGET_BOTTOM_SCROLLBAR,
TRW_WIDGET_TMPL_INFO_INSET,
TRW_WIDGET_TMPL_INFO_PANEL,
TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP,
TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT,
TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL,
TRW_WIDGET_TMPL_BUTTONS_DEFINE,
TRW_WIDGET_TMPL_BUTTONS_EDIT,
TRW_WIDGET_TMPL_BUTTONS_CLONE,
TRW_WIDGET_TMPL_BUTTONS_DELETE,
TRW_WIDGET_TMPL_BUTTONS_RPLALL,
TRW_WIDGET_TMPL_BUTTON_FLUFF,
TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL,
TRW_WIDGET_TITLE_INFO_GROUP,
TRW_WIDGET_TITLE_INFO_TEMPLATE,
TRW_WIDGET_INFO_GROUP,
TRW_WIDGET_INFO_TEMPLATE,
TRW_WIDGET_TMPL_BUTTONS_SPACER,
TRW_WIDGET_START,
TRW_WIDGET_TRAIN_FLUFF_LEFT,
TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
TRW_WIDGET_TRAIN_FLUFF_RIGHT,
TRW_WIDGET_STOP,
TRW_WIDGET_SEL_TMPL_DISPLAY_CREATE,
};
static const NWidgetPart _widgets[] = {
// Title bar
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, TRW_CAPTION), SetDataTip(STR_TMPL_RPL_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
//Top Matrix
NWidget(NWID_VERTICAL),
NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_INSET_GROUPS), SetMinimalSize(216,12), SetDataTip(STR_TMPL_MAINGUI_DEFINEDGROUPS, STR_TMPL_MAINGUI_DEFINEDGROUPS), SetResize(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_TOP_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 0), SetScrollbar(TRW_WIDGET_TOP_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_TOP_SCROLLBAR),
EndContainer(),
EndContainer(),
// Template Display
NWidget(NWID_VERTICAL),
NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_INSET_TEMPLATES), SetMinimalSize(216,12), SetDataTip(STR_TMPL_AVAILABLE_TEMPLATES, STR_TMPL_AVAILABLE_TEMPLATES), SetResize(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_BOTTOM_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_BOTTOM_SCROLLBAR),
EndContainer(),
EndContainer(),
// Info Area
NWidget(NWID_VERTICAL),
NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_INSET), SetMinimalSize(216,12), SetResize(1,0), SetDataTip(STR_TMPL_AVAILABLE_TEMPLATES, STR_TMPL_AVAILABLE_TEMPLATES), EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_PANEL), SetMinimalSize(216,50), SetResize(1,0), EndContainer(),
EndContainer(),
// Control Area
NWidget(NWID_VERTICAL),
// Spacing
NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF), SetMinimalSize(139, 12), SetResize(1,0), EndContainer(),
// Config buttons
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_USEDEPOT, STR_TMPL_SET_USEDEPOT_TIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_KEEPREMAINDERS, STR_TMPL_SET_KEEPREMAINDERS_TIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_REFIT, STR_TMPL_SET_REFIT_TIP),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL), SetMinimalSize(12,12), SetResize(1,0), EndContainer(),
EndContainer(),
// Edit buttons
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DEFINE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DEFINE_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_EDIT_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CLONE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DELETE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DELETE_TEMPLATE, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_RPLALL), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_RPL_ALL_TMPL, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL), SetMinimalSize(50,12), SetResize(1,0), EndContainer(),
EndContainer(),
EndContainer(),
// Start/Stop buttons
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_START), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_START, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_LEFT), SetMinimalSize(15, 12), EndContainer(),
NWidget(WWT_DROPDOWN, COLOUR_GREY, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_RIGHT), SetMinimalSize(16, 12), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_STOP), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_STOP, STR_REPLACE_REMOVE_WAGON_HELP),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
static WindowDesc _replace_rail_vehicle_desc(
WDP_AUTO,
"template replace window",
456, 156,
WC_TEMPLATEGUI_MAIN,
WC_NONE, // parent window class
WDF_CONSTRUCTION,
_widgets, lengthof(_widgets)
);
class TemplateReplaceWindow : public Window {
private:
GUIGroupList groups; ///< List of groups
byte unitnumber_digits;
short line_height;
short matrixContentLeftMargin;
int details_height; ///< Minimal needed height of the details panels (found so far).
RailType sel_railtype; ///< Type of rail tracks selected.
Scrollbar *vscroll[2];
// listing/sorting continued
GUITemplateList templates;
GUITemplateList::SortFunction **template_sorter_funcs;
short selected_template_index;
short selected_group_index;
bool templateNotice;
bool editInProgress;
public:
TemplateReplaceWindow(WindowDesc *wdesc, byte dig, int step_h) : Window(wdesc)
{
// listing/sorting
templates.SetSortFuncs(this->template_sorter_funcs);
// From BaseVehicleListWindow
this->unitnumber_digits = dig;
this->sel_railtype = RAILTYPE_BEGIN;
this->details_height = 10 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
this->line_height = step_h;
this->CreateNestedTree(wdesc);
this->vscroll[0] = this->GetScrollbar(TRW_WIDGET_TOP_SCROLLBAR);
this->vscroll[1] = this->GetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR);
this->vscroll[0]->SetStepSize(step_h / 2);
this->vscroll[1]->SetStepSize(step_h);
this->FinishInitNested(VEH_TRAIN);
this->owner = _local_company;
this->groups.ForceRebuild();
this->groups.NeedResort();
this->BuildGroupList(_local_company);
this->groups.Sort(&GroupNameSorter);
this->matrixContentLeftMargin = 40;
this->selected_template_index = -1;
this->selected_group_index = -1;
this->templateNotice = false;
this->editInProgress = false;
this->templates.ForceRebuild();
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
}
~TemplateReplaceWindow() {
DeleteWindowById(WC_CREATE_TEMPLATE, this->window_number);
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case TRW_WIDGET_TOP_MATRIX:
resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP) / 2;
size->height = 8 * resize->height;
break;
case TRW_WIDGET_BOTTOM_MATRIX:
resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP);
size->height = 4 * resize->height;
break;
case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: {
Dimension d = {0, 0};
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
const RailtypeInfo *rti = GetRailTypeInfo(rt);
// Skip rail type if it has no label
if (rti->label == 0) continue;
d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
}
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
break;
}
}
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
case TRW_CAPTION:
SetDParam(0, STR_TMPL_RPL_TITLE);
break;
}
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case TRW_WIDGET_TOP_MATRIX: {
DrawAllGroupsFunction(this->line_height, r);
break;
}
case TRW_WIDGET_BOTTOM_MATRIX: {
DrawTemplateList(this->line_height, r);
break;
}
case TRW_WIDGET_TMPL_INFO_PANEL: {
DrawTemplateInfo(this->line_height, r);
break;
}
}
}
virtual void OnPaint()
{
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->BuildGroupList(_local_company);
this->groups.Sort(&GroupNameSorter);
if ( templateNotice ) {
BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype);
templateNotice = false;
this->SetDirty();
}
/* sets the colour of that art thing */
this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
/* Show the selected railtype in the pulldown menu */
this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN)->widget_data = GetRailTypeInfo(sel_railtype)->strings.replace_text;
this->DrawWidgets();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
if ( this->editInProgress ) return;
switch (widget) {
case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE: {
if ( this->selected_template_index >= 0 ) {
TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
sel->ToggleReuseDepotVehicles();
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP: {
if ( this->selected_template_index >= 0 ) {
TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
sel->ToggleKeepRemainingVehicles();
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT: {
if ( this->selected_template_index >= 0 ) {
TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
sel->ToggleRefitAsTemplate();
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_DEFINE:
ShowTemplateCreateWindow(0, &templateNotice, &editInProgress, this->line_height);
break;
case TRW_WIDGET_TMPL_BUTTONS_EDIT: {
if ( this->selected_template_index >= 0 ) {
editInProgress = true;
TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
ShowTemplateCreateWindow(sel, &templateNotice, &editInProgress, this->line_height);
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_CLONE: {
this->SetWidgetDirty(TRW_WIDGET_TMPL_BUTTONS_CLONE);
this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE);
if (this->IsWidgetLowered(TRW_WIDGET_TMPL_BUTTONS_CLONE)) {
static const CursorID clone_icon = SPR_CURSOR_CLONE_TRAIN;
SetObjectToPlaceWnd(clone_icon, PAL_NONE, HT_VEHICLE, this);
} else {
ResetObjectToPlace();
}
break;
}
case TRW_WIDGET_TMPL_BUTTONS_DELETE:
if ( selected_template_index >= 0 && !editInProgress ) {
// identify template to delete
TemplateVehicle *del = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
// remove a corresponding template replacement if existing
TemplateReplacement *tr = GetTemplateReplacementByTemplateID(del->index);
if ( tr ) {
delete tr;
}
delete del;
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
selected_template_index = -1;
}
break;
case TRW_WIDGET_TMPL_BUTTONS_RPLALL: {
ShowTemplateReplaceAllGui();
break;
}
case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
ShowDropDownList(this, GetRailTypeDropDownList(true), sel_railtype, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN);
break;
case TRW_WIDGET_TOP_MATRIX: {
uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_TOP_MATRIX]->pos_y) / (this->line_height/2) ) + this->vscroll[0]->GetPosition();
if ( newindex == this->selected_group_index || newindex >= this->groups.Length() ) {
this->selected_group_index = -1;
}
else if ( newindex < this->groups.Length() ) {
this->selected_group_index = newindex;
}
break;
}
case TRW_WIDGET_BOTTOM_MATRIX: {
uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_BOTTOM_MATRIX]->pos_y) / this->line_height) + this->vscroll[1]->GetPosition();
if ( newindex == this->selected_template_index || newindex >= templates.Length() ) {
this->selected_template_index = -1;
}
else if ( newindex < templates.Length() ) {
this->selected_template_index = newindex;
}
break;
}
case TRW_WIDGET_START: {
if ( this->selected_template_index >= 0 && this->selected_group_index >= 0) {
uint32 tv_index = ((this->templates)[selected_template_index])->index;
int current_group_index = (this->groups)[this->selected_group_index]->index;
IssueTemplateReplacement(current_group_index, tv_index);
}
break;
}
case TRW_WIDGET_STOP:
if ( this->selected_group_index == -1 )
return;
int current_group_index = (this->groups)[this->selected_group_index]->index;
TemplateReplacement *tr = GetTemplateReplacementByGroupID(current_group_index);
if ( tr )
delete tr;
break;
}
this->SetDirty();
}
virtual bool OnVehicleSelect(const Vehicle *v)
{
// create a new template from the clicked vehicle
TemplateVehicle *tv = CloneTemplateVehicleFromTrain((const Train*)v);
if ( !tv ) return false;
BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype);
this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE);
ResetObjectToPlace();
this->SetDirty();
return true;
}
virtual void OnDropdownSelect(int widget, int index)
{
RailType temp = (RailType)index;
if (temp == this->sel_railtype) return; // we didn't select a new one. No need to change anything
this->sel_railtype = temp;
/* Reset scrollbar positions */
this->vscroll[0]->SetPosition(0);
this->vscroll[1]->SetPosition(0);
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->SetDirty();
}
virtual void OnResize()
{
/* Top Matrix */
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(TRW_WIDGET_TOP_MATRIX);
this->vscroll[0]->SetCapacityFromWidget(this, TRW_WIDGET_TOP_MATRIX);
nwi->widget_data = (this->vscroll[0]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
/* Bottom Matrix */
NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(TRW_WIDGET_BOTTOM_MATRIX);
this->vscroll[1]->SetCapacityFromWidget(this, TRW_WIDGET_BOTTOM_MATRIX);
nwi2->widget_data = (this->vscroll[1]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
virtual void OnTick()
{
if ( templateNotice ) {
BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
this->SetDirty();
templateNotice = false;
}
}
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
this->groups.ForceRebuild();
this->templates.ForceRebuild();
}
/** For a given group (id) find the template that is issued for template replacement for this group and return this template's index
* from the gui list */
short FindTemplateIndexForGroup(short gid) const
{
TemplateReplacement *tr = GetTemplateReplacementByGroupID(gid);
if ( !tr )
return -1;
for ( uint32 i=0; i<this->templates.Length(); ++i )
if ( templates[i]->index == tr->sel_template )
return i;
return -1;
}
/** Sort the groups by their name */
static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b)
{
static const Group *last_group[2] = { NULL, NULL };
static char last_name[2][64] = { "", "" };
if (*a != last_group[0]) {
last_group[0] = *a;
SetDParam(0, (*a)->index);
GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
}
if (*b != last_group[1]) {
last_group[1] = *b;
SetDParam(0, (*b)->index);
GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
}
int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
if (r == 0) return (*a)->index - (*b)->index;
return r;
}
void BuildGroupList(Owner owner)
{
if (!this->groups.NeedRebuild()) {
return;
}
this->groups.Clear();
const Group *g;
FOR_ALL_GROUPS(g) {
if (g->owner == owner ) {
*this->groups.Append() = g;
}
}
this->groups.Compact();
this->groups.RebuildDone();
this->vscroll[0]->SetCount(groups.Length());
}
void DrawAllGroupsFunction(int line_height, const Rect &r) const
{
int left = r.left + WD_MATRIX_LEFT;
int right = r.right - WD_MATRIX_RIGHT;
int y = r.top;
int max = min(this->vscroll[0]->GetPosition() + this->vscroll[0]->GetCapacity(), this->groups.Length());
/* Then treat all groups defined by/for the current company */
for ( int i=this->vscroll[0]->GetPosition(); i<max; ++i ) {
const Group *g = (this->groups)[i];
short g_id = g->index;
/* Fill the background of the current cell in a darker tone for the currently selected template */
if ( this->selected_group_index == i ) {
GfxFillRect(left, y, right, y+(this->line_height)/2, _colour_gradient[COLOUR_GREY][3]);
}
SetDParam(0, g_id);
StringID str = STR_GROUP_NAME;
DrawString(left+30, right, y+2, str, TC_BLACK);
/* Draw the template in use for this group, if there is one */
short template_in_use = FindTemplateIndexForGroup(g_id);
if ( template_in_use >= 0 ) {
SetDParam(0, template_in_use);
DrawString ( left, right, y+2, STR_TMPL_GROUP_USES_TEMPLATE, TC_BLACK, SA_HOR_CENTER);
}
/* If there isn't a template applied from the current group, check if there is one for another rail type */
else if ( GetTemplateReplacementByGroupID(g_id) ) {
DrawString ( left, right, y+2, STR_TMPL_TMPLRPL_EX_DIFF_RAILTYPE, TC_SILVER, SA_HOR_CENTER);
}
/* Draw the number of trains that still need to be treated by the currently selected template replacement */
TemplateReplacement *tr = GetTemplateReplacementByGroupID(g_id);
if ( tr ) {
TemplateVehicle *tv = TemplateVehicle::Get(tr->sel_template);
int num_trains = NumTrainsNeedTemplateReplacement(g_id, tv);
// Draw text
TextColour color = TC_GREY;
if ( num_trains ) color = TC_BLACK;
DrawString(left, right-16, y+2, STR_TMPL_NUM_TRAINS_NEED_RPL, color, SA_RIGHT);
// Draw number
if ( num_trains ) color = TC_ORANGE;
else color = TC_GREY;
SetDParam(0, num_trains);
DrawString(left, right-4, y+2, STR_JUST_INT, color, SA_RIGHT);
}
y+=line_height / 2;
}
}
void DrawTemplateList(int line_height, const Rect &r) const
{
int left = r.left;
int right = r.right;
int y = r.top;
Scrollbar *draw_vscroll = vscroll[1];
uint max = min(draw_vscroll->GetPosition() + draw_vscroll->GetCapacity(), this->templates.Length());
const TemplateVehicle *v;
for ( uint i = draw_vscroll->GetPosition(); i < max; ++i) {
v = (this->templates)[i];
/* Fill the background of the current cell in a darker tone for the currently selected template */
if ( this->selected_template_index == (int32)i ) {
GfxFillRect(left, y, right, y+this->line_height, _colour_gradient[COLOUR_GREY][3]);
}
/* Draw a notification string for chains that are not runnable */
if ( v->IsFreeWagonChain() ) {
DrawString(left, right-2, y+line_height-FONT_HEIGHT_SMALL-WD_FRAMERECT_BOTTOM - 2, STR_TMPL_WARNING_FREE_WAGON, TC_RED, SA_RIGHT);
}
/* Draw the template's length in tile-units */
SetDParam(0, v->GetRealLength());
SetDParam(1, 1);
DrawString(left, right-4, y+2, STR_TINY_BLACK_DECIMAL, TC_BLACK, SA_RIGHT);
/* Draw the template */
DrawTemplate(v, left+50, right, y);
/* Buying cost */
SetDParam(0, CalculateOverallTemplateCost(v));
DrawString(left+35, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMPL_TEMPLATE_OVR_VALUE_notinyfont, TC_BLUE, SA_LEFT);
/* Index of current template vehicle in the list of all templates for its company */
SetDParam(0, i);
DrawString(left+5, left+25, y + line_height/2, STR_BLACK_INT, TC_BLACK, SA_RIGHT);
/* Draw whether the current template is in use by any group */
if ( v->NumGroupsUsingTemplate() > 0 ) {
DrawString(left+200, right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 2, STR_TMP_TEMPLATE_IN_USE, TC_GREEN, SA_LEFT);
}
/* Draw information about template configuration settings */
TextColour color;
if ( v->IsSetReuseDepotVehicles() ) color = TC_LIGHT_BLUE;
else color = TC_GREY;
DrawString(left+200, right, y+2, STR_TMPL_CONFIG_USEDEPOT, color, SA_LEFT);
if ( v->IsSetKeepRemainingVehicles() ) color = TC_LIGHT_BLUE;
else color = TC_GREY;
DrawString(left+275, right, y+2, STR_TMPL_CONFIG_KEEPREMAINDERS, color, SA_LEFT);
if ( v->IsSetRefitAsTemplate() ) color = TC_LIGHT_BLUE;
else color = TC_GREY;
DrawString(left+350, right, y+2, STR_TMPL_CONFIG_REFIT, color, SA_LEFT);
y += line_height;
}
}
void DrawTemplateInfo(int line_height, const Rect &r) const
{
if ( this->selected_template_index == -1 || (short)this->templates.Length() <= this->selected_template_index )
return;
const TemplateVehicle *tmp = this->templates[this->selected_template_index];
/* Draw vehicle performance info */
SetDParam(2, tmp->max_speed);
SetDParam(1, tmp->power);
SetDParam(0, tmp->weight);
SetDParam(3, tmp->max_te);
DrawString(r.left+8, r.right, r.top+4, STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE);
/* Draw cargo summary */
short top = r.top + 24;
short left = r.left + 8;
short count_rows = 0;
short max_rows = 2;
CargoArray cargo_caps;
for ( ; tmp; tmp=tmp->Next() )
cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
int y = top;
for (CargoID i = 0; i < NUM_CARGO; ++i) {
if ( cargo_caps[i] > 0 ) {
count_rows++;
SetDParam(0, i);
SetDParam(1, cargo_caps[i]);
SetDParam(2, _settings_game.vehicle.freight_trains);
DrawString(left, r.right, y, FreightWagonMult(i) > 1 ? STR_TMPL_CARGO_SUMMARY_MULTI : STR_TMPL_CARGO_SUMMARY, TC_WHITE, SA_LEFT);
y += this->line_height/2;
if ( count_rows % max_rows == 0 ) {
y = top;
left += 150;
}
}
}
}
};
void ShowTemplateReplaceWindow(byte dig, int step_h)
{
new TemplateReplaceWindow(&_replace_rail_vehicle_desc, dig, step_h);
}

@ -0,0 +1,20 @@
// _template_gui_main.h
#ifndef TEMPLATE_GUI_H
#define TEMPLATE_GUI_H
#include "engine_type.h"
#include "group_type.h"
#include "vehicle_type.h"
#include "string_func.h"
#include "strings_func.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include "tbtr_template_gui_replaceall.h"
typedef GUIList<const Group*> GUIGroupList;
void ShowTemplateReplaceWindow(byte, int);
#endif

@ -0,0 +1,517 @@
// replace all gui impl
#include "tbtr_template_gui_replaceall.h"
#include <stdio.h>
/*
* A wrapper which contains a virtual train and additional info of the template vehicle it is replacing
* We will restore this additional info when creating a new template from the changed virtual train
*/
struct VirtTrainInfo {
// the virtual train
Train *vt;
// additional info from the template
VehicleID original_index;
bool reuse_depot_vehicles,
keep_remaining_vehicles,
refit_as_template;
CargoID cargo_type;
byte cargo_subtype;
// a fancy constructor
VirtTrainInfo(Train *t) { this->vt = t; }
};
typedef AutoFreeSmallVector<VirtTrainInfo*, 64> VirtTrainList;
enum Widgets {
RPLALL_GUI_CAPTION,
RPLALL_GUI_INSET_1,
RPLALL_GUI_INSET_1_1,
RPLALL_GUI_INSET_1_2,
RPLALL_GUI_MATRIX_TOPLEFT,
RPLALL_GUI_MATRIX_TOPRIGHT,
RPLALL_GUI_SCROLL_TL,
RPLALL_GUI_SCROLL_TR,
RPLALL_GUI_INSET_2,
RPLALL_GUI_MATRIX_BOTTOM,
RPLALL_GUI_SCROLL_BO,
RPLALL_GUI_INSET_3,
RPLALL_GUI_BUTTON_RPLALL,
RPLALL_GUI_PANEL_BUTTONFLUFF_1,
RPLALL_GUI_PANEL_BUTTONFLUFF_2,
RPLALL_GUI_BUTTON_APPLY,
RPLALL_GUI_PANEL_BUTTONFLUFF_3,
RPLALL_GUI_BUTTON_CANCEL,
RPLALL_GUI_PANEL_RESIZEFLUFF
};
static const NWidgetPart widgets[] = {
// title bar
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, RPLALL_GUI_CAPTION), SetDataTip(STR_TMPL_RPLALLGUI_TITLE, STR_TMPL_RPLALLGUI_TITLE),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
// top matrices
NWidget(WWT_INSET, COLOUR_GREY, RPLALL_GUI_INSET_1), SetMinimalSize(100,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_INSET_TOP, STR_TMPL_RPLALLGUI_INSET_TOP), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
NWidget(WWT_INSET, COLOUR_GREY, RPLALL_GUI_INSET_1_1), SetMinimalSize(100,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_INSET_TOP_1, STR_TMPL_RPLALLGUI_INSET_TOP_1), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, RPLALL_GUI_MATRIX_TOPLEFT), SetMinimalSize(100, 16), SetFill(1, 1), SetResize(1, 1), SetScrollbar(RPLALL_GUI_SCROLL_TL),// SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, RPLALL_GUI_SCROLL_TL),
EndContainer(),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(WWT_INSET, COLOUR_GREY, RPLALL_GUI_INSET_1_2), SetMinimalSize(100,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_INSET_TOP_2, STR_TMPL_RPLALLGUI_INSET_TOP_2), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, RPLALL_GUI_MATRIX_TOPRIGHT), SetMinimalSize(100, 16), SetFill(1, 1), SetResize(1, 1), SetScrollbar(RPLALL_GUI_SCROLL_TR),// SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, RPLALL_GUI_SCROLL_TR),
EndContainer(),
EndContainer(),
EndContainer(),
// bottom matrix
NWidget(WWT_INSET, COLOUR_GREY, RPLALL_GUI_INSET_2), SetMinimalSize(200,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_INSET_BOTTOM, STR_TMPL_RPLALLGUI_INSET_BOTTOM), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, RPLALL_GUI_MATRIX_BOTTOM), SetMinimalSize(200, 16), SetFill(1, 1), SetResize(1, 1), SetScrollbar(RPLALL_GUI_SCROLL_BO),// SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, RPLALL_GUI_SCROLL_BO),
EndContainer(),
// control area
NWidget(WWT_INSET, COLOUR_GREY, RPLALL_GUI_INSET_3), SetMinimalSize(200,12), SetResize(1,0), EndContainer(),// SetDataTip(STR_TMPL_MAINGUI_DEFINEDGROUPS, STR_TMPL_MAINGUI_DEFINEDGROUPS),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_GREY, RPLALL_GUI_PANEL_BUTTONFLUFF_1), SetMinimalSize(75,12), SetResize(1,0), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, RPLALL_GUI_BUTTON_RPLALL), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_RPLALLGUI_BUTTON_RPLALL, STR_TMPL_RPLALLGUI_BUTTON_RPLALL),
NWidget(WWT_PANEL, COLOUR_GREY, RPLALL_GUI_PANEL_BUTTONFLUFF_2), SetMinimalSize(75,12), SetResize(1,0), EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, RPLALL_GUI_BUTTON_APPLY), SetMinimalSize(75,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_BUTTON_APPLY, STR_TMPL_RPLALLGUI_BUTTON_APPLY),
NWidget(WWT_PANEL, COLOUR_GREY, RPLALL_GUI_PANEL_BUTTONFLUFF_3), SetMinimalSize(150,12), SetResize(0,0), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, RPLALL_GUI_BUTTON_CANCEL), SetMinimalSize(75,12), SetResize(1,0), SetDataTip(STR_TMPL_RPLALLGUI_BUTTON_CANCEL, STR_TMPL_RPLALLGUI_BUTTON_CANCEL),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_GREY, RPLALL_GUI_PANEL_RESIZEFLUFF), SetMinimalSize(100,12), SetResize(1,0), EndContainer(),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
static WindowDesc _template_replace_replaceall_desc(
WDP_AUTO,
"template replace window",
400, 200,
WC_TEMPLATEGUI_RPLALL, WC_NONE,
WDF_CONSTRUCTION,
widgets, lengthof(widgets)
);
static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
{
int r = Engine::Get(*a)->list_position - Engine::Get(*b)->list_position;
return r;
}
static int CDECL TrainEnginesThenWagonsSorter(const EngineID *a, const EngineID *b)
{
int val_a = (RailVehInfo(*a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int val_b = (RailVehInfo(*b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int r = val_a - val_b;
/* Use EngineID to sort instead since we want consistent sorting */
if (r == 0) return EngineNumberSorter(a, b);
return r;
}
class TemplateReplacementReplaceAllWindow : public Window {
private:
uint16 line_height;
Scrollbar *vscroll_tl,
*vscroll_tr,
*vscroll_bo;
GUIEngineList *engines_left,
*engines_right;
short selected_left,
selected_right;
VirtTrainList *virtualTrains;
public:
TemplateReplacementReplaceAllWindow(WindowDesc *wdesc) : Window(wdesc)
{
this->CreateNestedTree(wdesc);
this->vscroll_tl = this->GetScrollbar(RPLALL_GUI_SCROLL_TL);
this->vscroll_tr = this->GetScrollbar(RPLALL_GUI_SCROLL_TR);
this->vscroll_bo = this->GetScrollbar(RPLALL_GUI_SCROLL_BO);
this->vscroll_tl->SetStepSize(16);
this->vscroll_tr->SetStepSize(16);
this->vscroll_bo->SetStepSize(16);
this->FinishInitNested(VEH_TRAIN);
this->owner = _local_company;
engines_left = new GUIEngineList();
engines_right = new GUIEngineList();
virtualTrains = new VirtTrainList();
this->GenerateBuyableEnginesList();
this->GenerateIncludedTemplateList();
this->line_height = 16;
this->selected_left = -1;
this->selected_right = -1;
}
~TemplateReplacementReplaceAllWindow()
{
for ( uint i=0; i<this->virtualTrains->Length(); ++i )
delete (*this->virtualTrains)[i]->vt;
SetWindowClassesDirty(WC_TEMPLATEGUI_MAIN);
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch ( widget ) {
case RPLALL_GUI_MATRIX_TOPLEFT:
case RPLALL_GUI_MATRIX_TOPRIGHT:
case RPLALL_GUI_MATRIX_BOTTOM: {
resize->height = 16;
size->height = 16;
break;
}
}
}
virtual void OnPaint()
{
this->GetWidget<NWidgetCore>(RPLALL_GUI_PANEL_BUTTONFLUFF_3)->colour = _company_colours[_local_company];
this->DrawWidgets();
}
virtual void OnResize()
{
NWidgetCore *nwi_tl = this->GetWidget<NWidgetCore>(RPLALL_GUI_MATRIX_TOPLEFT);
this->vscroll_tl->SetCapacityFromWidget(this, RPLALL_GUI_MATRIX_TOPLEFT);
nwi_tl->widget_data = (this->vscroll_tl->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
NWidgetCore *nwi_tr = this->GetWidget<NWidgetCore>(RPLALL_GUI_MATRIX_TOPRIGHT);
this->vscroll_tr->SetCapacityFromWidget(this, RPLALL_GUI_MATRIX_TOPRIGHT);
nwi_tr->widget_data = (this->vscroll_tr->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
NWidgetCore *nwi_bo = this->GetWidget<NWidgetCore>(RPLALL_GUI_MATRIX_BOTTOM);
this->vscroll_bo->SetCapacityFromWidget(this, RPLALL_GUI_MATRIX_BOTTOM);
nwi_bo->widget_data = (this->vscroll_bo->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case RPLALL_GUI_MATRIX_TOPLEFT: {
this->DrawEngineList(r, true);
break;
}
case RPLALL_GUI_MATRIX_TOPRIGHT: {
this->DrawEngineList(r, false);
break;
}
case RPLALL_GUI_MATRIX_BOTTOM: {
this->DrawVirtualTrains(r);
break;
}
}
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch(widget) {
case RPLALL_GUI_MATRIX_TOPLEFT: {
uint16 newindex = (uint16)((pt.y - this->nested_array[RPLALL_GUI_MATRIX_TOPLEFT]->pos_y) / this->line_height) + this->vscroll_tl->GetPosition();
if ( newindex >= this->engines_left->Length() || newindex==this->selected_left )
this->selected_left = -1;
else
this->selected_left = newindex;
this->SetDirty();
break;
}
case RPLALL_GUI_MATRIX_TOPRIGHT: {
uint16 newindex = (uint16)((pt.y - this->nested_array[RPLALL_GUI_MATRIX_TOPRIGHT]->pos_y) / this->line_height) + this->vscroll_tr->GetPosition();
if ( newindex > this->engines_right->Length() || newindex==this->selected_right )
this->selected_right = -1;
else
this->selected_right = newindex;
this->SetDirty();
break;
}
case RPLALL_GUI_BUTTON_RPLALL: {
this->ReplaceAll();
break;
}
case RPLALL_GUI_BUTTON_APPLY: {
// check if we actually did anything so far, if not, applying is forbidden
if ( this->virtualTrains->Length() == 0 )
return;
// first delete all current templates
this->DeleteAllTemplateTrains();
// then build a new list from the current virtual trains
for ( uint i=0; i<this->virtualTrains->Length(); ++i ) {
// the relevant info struct
VirtTrainInfo *vti = (*this->virtualTrains)[i];
// setup template from contained train
Train *t = vti->vt;
TemplateVehicle *tv = TemplateVehicleFromVirtualTrain(t);
// restore template specific stuff
tv->reuse_depot_vehicles = vti->reuse_depot_vehicles;
tv->keep_remaining_vehicles = vti->keep_remaining_vehicles;
tv->refit_as_template = vti->refit_as_template;
tv->cargo_type = vti->cargo_type;
tv->cargo_subtype = vti->cargo_subtype;
// use the original_index information to repoint the relevant TemplateReplacement if existing
TemplateReplacement *tr = GetTemplateReplacementByTemplateID(vti->original_index);
if ( tr )
tr->sel_template = tv->index;
}
// then close this window and return to parent
delete this;
break;
}
case RPLALL_GUI_BUTTON_CANCEL: {
delete this;
break;
}
}
}
bool HasTemplateWithEngine(EngineID eid) const
{
const TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if ( tv->Prev() || tv->owner != _local_company ) continue;
for ( const TemplateVehicle *tmp=tv; tmp; tmp=tmp->GetNextUnit() ) {
if ( tmp->engine_type == eid )
return true;
}
}
return false;
}
void GenerateVirtualTrains()
{
this->virtualTrains->Clear();
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if ( !tv->Prev() && tv->owner==this->owner ) {
// setup template train
Train *newtrain = VirtualTrainFromTemplateVehicle(tv);
VirtTrainInfo *vti = new VirtTrainInfo(newtrain);
// store template specific stuff
vti->original_index = tv->index;
vti->reuse_depot_vehicles = tv->reuse_depot_vehicles;
vti->keep_remaining_vehicles = tv->keep_remaining_vehicles;
vti->refit_as_template = tv->refit_as_template;
vti->cargo_type = tv->cargo_type;
vti->cargo_subtype = tv->cargo_subtype;
// add new info struct
*this->virtualTrains->Append() = vti;
}
}
this->vscroll_bo->SetCount(this->virtualTrains->Length());
}
void DeleteAllTemplateTrains()
{
TemplateVehicle *tv, *tmp;
FOR_ALL_TEMPLATES(tv) {
tmp = tv;
if ( tmp->Prev()==0 && tmp->owner==this->owner )
delete tmp;
}
}
void GenerateIncludedTemplateList()
{
int num_engines = 0;
int num_wagons = 0;
this->engines_left->Clear();
const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
EngineID eid = e->index;
const RailVehicleInfo*rvi = &e->u.rail;
if ( !HasTemplateWithEngine(eid) ) continue;
*this->engines_left->Append() = eid;
if (rvi->railveh_type != RAILVEH_WAGON) {
num_engines++;
} else {
num_wagons++;
}
}
this->vscroll_tl->SetCount(this->engines_left->Length());
}
bool VirtualTrainHasEngineID(EngineID eid)
{
for ( uint i=0; i<this->virtualTrains->Length(); ++i ) {
const Train *tmp = (*this->virtualTrains)[i]->vt;
for ( ; tmp; tmp=tmp->Next() )
if ( tmp->engine_type == eid )
return true;
}
return false;
}
// after 'replace all' we need to replace the currently used templates as well
void RebuildIncludedTemplateList() {
// first remove all engine ids
for ( uint i=0; i<this->engines_left->Length(); ++i ) {
EngineID entry = (*this->engines_left)[i];
if ( !VirtualTrainHasEngineID(entry) )
this->engines_left->Erase(&((*this->engines_left)[i]));
}
}
void ReplaceAll()
{
if ( this->selected_left==-1 || this->selected_right==-1 )
return;
EngineID eid_orig = (*this->engines_left)[this->selected_left];
EngineID eid_repl = (*this->engines_right)[this->selected_right];
if ( eid_orig == eid_repl )
return;
if ( this->virtualTrains->Length() == 0 )
this->GenerateVirtualTrains();
for ( uint i=0; i<this->virtualTrains->Length(); ++i ) {
Train *tmp = (*this->virtualTrains)[i]->vt;
while ( tmp ) {
if ( tmp->engine_type == eid_orig ) {
// build a new virtual rail vehicle and test for success
Train *nt = CmdBuildVirtualRailVehicle(eid_repl);
if ( !nt ) continue;
// include the (probably) new engine into the 'included'-list
this->engines_left->Include( nt->engine_type );
// advance the tmp pointer in the chain, otherwise it would get deleted later on
Train *to_del = tmp;
tmp = tmp->GetNextUnit();
// first move the new virtual rail vehicle behind to_del
CommandCost move = CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, nt->index|(1<<21), to_del->index, 0);
// then move to_del away from the chain and delete it
move = CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, to_del->index|(1<<21), INVALID_VEHICLE, 0);
(*this->virtualTrains)[i]->vt = nt->First();
delete to_del;
} else {
tmp = tmp->GetNextUnit();
}
}
}
this->selected_left = -1;
// rebuild the left engines list as some engines might not be there anymore
this->RebuildIncludedTemplateList();
this->SetDirty();
}
void GenerateBuyableEnginesList()
{
int num_engines = 0;
int num_wagons = 0;
this->engines_right->Clear();
const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
*this->engines_right->Append() = eid;
if (rvi->railveh_type != RAILVEH_WAGON) {
num_engines++;
} else {
num_wagons++;
}
}
/* make engines first, and then wagons, sorted by ListPositionOfEngine() */
EngList_Sort(this->engines_right, TrainEnginesThenWagonsSorter);
this->vscroll_tr->SetCount(this->engines_right->Length());
}
void DrawEngineList(const Rect &r, bool left) const//, GUIEngineList el, Scrollbar* sb) const
{
uint16 y = r.top;
uint32 eid;
Scrollbar *sb;
const GUIEngineList *el;
if ( left ) {
sb = this->vscroll_tl;
el = this->engines_left;
} else {
sb = this->vscroll_tr;
el = this->engines_right;
}
int maximum = min((int)sb->GetCapacity(), (int)el->Length()) + sb->GetPosition();
for ( int i=sb->GetPosition(); i<maximum; ++i ) {
eid = (*el)[i];
/* Draw a grey background rectangle if the current line is the selected one */
if ( (left && this->selected_left == i) || (!left && this->selected_right == i) )
GfxFillRect(r.left, y, r.right, y+this->line_height, _colour_gradient[COLOUR_GREY][3]);
/* Draw a description string of the current engine */
SetDParam(0, eid);
DrawString(r.left+100, r.right, y+4, STR_ENGINE_NAME, TC_BLACK);
/* Draw the engine */
DrawVehicleEngine( r.left, r.right, r.left+29, y+8, eid, GetEnginePalette(eid, _local_company), EIT_PURCHASE );
y += this->line_height;
}
}
void DrawVirtualTrains(const Rect &r) const
{
uint16 y = r.top;
uint16 max = min(virtualTrains->Length(), this->vscroll_bo->GetCapacity());
for ( uint16 i=vscroll_bo->GetPosition(); i<max+vscroll_bo->GetPosition(); ++i ) {
/* Draw a virtual train*/
DrawTrainImage( (*this->virtualTrains)[i]->vt, r.left+32, r.right, y, INVALID_VEHICLE, EIT_PURCHASE, 0, -1 );
y+= this->line_height;
}
}
};
void ShowTemplateReplaceAllGui()
{
new TemplateReplacementReplaceAllWindow(&_template_replace_replaceall_desc);
}

@ -0,0 +1,27 @@
#ifndef TMPL_RPLALL_GUI
#define TMPL_RPLALL_GUI
#include "stdafx.h"
#include "window_gui.h"
#include "window_func.h"
#include "company_func.h"
#include "engine_base.h"
#include "engine_func.h"
#include "engine_gui.h"
#include "train.h"
#include "strings_func.h"
#include "vehicle_base.h"
#include "vehicle_func.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include "core/math_func.hpp"
#include "table/strings.h"
void ShowTemplateReplaceAllGui();
#endif

@ -0,0 +1,227 @@
#include "stdafx.h"
#include "company_func.h"
#include "train.h"
#include "command_func.h"
#include "engine_func.h"
#include "vehicle_func.h"
#include "autoreplace_func.h"
#include "autoreplace_gui.h"
#include "group.h"
#include "articulated_vehicles.h"
#include "core/random_func.hpp"
#include "core/pool_type.hpp"
#include "engine_type.h"
#include "group_type.h"
#include "core/pool_func.hpp"
#include "table/strings.h"
#include "newgrf.h"
#include "vehicle_type.h"
#include "vehicle_base.h"
#include "vehicle_func.h"
#include "table/train_cmd.h"
#include "tbtr_template_vehicle.h"
// since doing stuff with sprites
#include "newgrf_spritegroup.h"
#include "newgrf_engine.h"
#include "newgrf_cargo.h"
TemplatePool _template_pool("TemplatePool");
INSTANTIATE_POOL_METHODS(Template)
TemplateReplacementPool _template_replacement_pool("TemplateReplacementPool");
INSTANTIATE_POOL_METHODS(TemplateReplacement)
TemplateVehicle::TemplateVehicle(VehicleType ty, EngineID eid, byte subtypeflag, Owner current_owner)
{
this->type = ty;
this->engine_type = eid;
this->reuse_depot_vehicles = true;
this->keep_remaining_vehicles = true;
this->first = this;
this->next = 0x0;
this->previous = 0x0;
this->owner_b = _current_company;
this->cur_image = SPR_IMG_QUERY;
this->owner = current_owner;
this->real_consist_length = 0;
}
TemplateVehicle::~TemplateVehicle() {
TemplateVehicle *v = this->Next();
this->SetNext(NULL);
delete v;
}
/** getting */
void TemplateVehicle::SetNext(TemplateVehicle *v) { this->next = v; }
void TemplateVehicle::SetPrev(TemplateVehicle *v) { this->previous = v; }
void TemplateVehicle::SetFirst(TemplateVehicle *v) { this->first = v; }
TemplateVehicle* TemplateVehicle::GetNextUnit() const
{
TemplateVehicle *tv = this->Next();
while ( tv && HasBit(tv->subtype, GVSF_ARTICULATED_PART) ) tv = tv->Next();
if ( tv && HasBit(tv->subtype, GVSF_MULTIHEADED) && !HasBit(tv->subtype, GVSF_ENGINE) ) tv = tv->Next();
return tv;
}
TemplateVehicle* TemplateVehicle::GetPrevUnit()
{
TemplateVehicle *tv = this->Prev();
while ( tv && HasBit(tv->subtype, GVSF_ARTICULATED_PART|GVSF_ENGINE) ) tv = tv->Prev();
if ( tv && HasBit(tv->subtype, GVSF_MULTIHEADED|GVSF_ENGINE) ) tv = tv->Prev();
return tv;
}
/** setting */
void appendTemplateVehicle(TemplateVehicle *orig, TemplateVehicle *newv)
{
if ( !orig ) return;
while ( orig->Next() ) orig=orig->Next();
orig->SetNext(newv);
newv->SetPrev(orig);
newv->SetFirst(orig->First());
}
void insertTemplateVehicle(TemplateVehicle *orig, TemplateVehicle *newv, TemplateVehicle *insert_after)
{
if ( !orig || !insert_after ) return;
TemplateVehicle *insert_before = insert_after->Next();
insert_after->SetNext(newv);
insert_before->SetPrev(newv);
newv->SetPrev(insert_after);
newv->SetNext(insert_before);
newv->SetFirst(insert_after);
}
/** Length()
* @return: length of vehicle, including current part
*/
int TemplateVehicle::Length() const
{
int l=1;
const TemplateVehicle *tmp=this;
while ( tmp->Next() ) { tmp=tmp->Next(); l++; }
return l;
}
TemplateReplacement* GetTemplateReplacementByGroupID(GroupID gid)
{
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->Group() == gid )
return tr;
}
return 0;
}
TemplateReplacement* GetTemplateReplacementByTemplateID(TemplateID tid) {
TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->Template() == tid )
return tr;
}
return 0;
}
bool IssueTemplateReplacement(GroupID gid, TemplateID tid) {
TemplateReplacement *tr = GetTemplateReplacementByGroupID(gid);
if ( tr ) {
/* Then set the new TemplateVehicle and return */
tr->SetTemplate(tid);
return true;
}
else if ( TemplateReplacement::CanAllocateItem() ) {
tr = new TemplateReplacement(gid, tid);
return true;
}
else return false;
}
short TemplateVehicle::NumGroupsUsingTemplate() const
{
short amount = 0;
const TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->sel_template == this->index )
amount++;
}
return amount;
}
short TemplateVehicle::CountEnginesInChain()
{
TemplateVehicle *tv = this->first;
short count = 0;
for ( ; tv; tv=tv->GetNextUnit() )
if ( HasBit(tv->subtype, GVSF_ENGINE ) )
count++;
return count;
}
short deleteIllegalTemplateReplacements(GroupID g_id)
{
short del_amount = 0;
const TemplateReplacement *tr;
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->group == g_id ) {
delete tr;
del_amount++;
}
}
return del_amount;
}

@ -0,0 +1,195 @@
#ifndef TEMPLATE_VEH_H
#define TEMPLATE_VEH_H
#include "company_func.h"
#include "vehicle_type.h"
#include "vehicle_base.h"
#include "vehicle_func.h"
#include "articulated_vehicles.h"
#include "newgrf_callbacks.h"
#include "newgrf_engine.h"
#include "newgrf_spritegroup.h"
#include "engine_base.h"
#include "engine_type.h"
#include "engine_func.h"
#include "sortlist_type.h"
#define FOR_ALL_TEMPLATES_FROM(var, start) FOR_ALL_ITEMS_FROM(TemplateVehicle, template_index, var, start)
#define FOR_ALL_TEMPLATES(var) FOR_ALL_TEMPLATES_FROM(var, 0)
#define FOR_ALL_TEMPLATE_REPLACEMENTS_FROM(var, start) FOR_ALL_ITEMS_FROM(TemplateReplacement, template_replacement_index, var, start)
#define FOR_ALL_TEMPLATE_REPLACEMENTS(var) FOR_ALL_TEMPLATE_REPLACEMENTS_FROM(var, 0)
struct TemplateVehicle;
struct TemplateReplacement;
CommandCost CmdBuildTemplateVehicle(uint i, DoCommandFlag flags, uint p1, uint p2, char const* text);
CommandCost CmdTemplateReplaceVehicle(uint i, DoCommandFlag flags, uint p1, uint p2, char const* text);
typedef uint16 TemplateID;
static const uint16 CONSIST_HEAD = 0x0;
static const uint16 CONSIST_TAIL = 0xffff;
/** A pool allowing to store up to ~64k templates */
typedef Pool<TemplateVehicle, TemplateID, 512, 0x10000> TemplatePool;
extern TemplatePool _template_pool;
/// listing/sorting templates
typedef GUIList<const TemplateVehicle*> GUITemplateList;
struct TemplateVehicle : TemplatePool::PoolItem<&_template_pool>, BaseVehicle {
private:
TemplateVehicle *next; ///< pointer to the next vehicle in the chain
TemplateVehicle *previous; ///< NOSAVE: pointer to the previous vehicle in the chain
TemplateVehicle *first; ///< NOSAVE: pointer to the first vehicle in the chain
public:
friend const SaveLoad* GTD();
friend void AfterLoadTemplateVehicles();
// Template usage configuration
bool reuse_depot_vehicles;
bool keep_remaining_vehicles;
bool refit_as_template;
// Things derived from a virtual train
TemplateVehicle *other_multiheaded_part; ///< Multiheaded Engine support
Money value; ///< Value of the vehicle
Owner owner;
OwnerByte owner_b;
EngineID engine_type; ///< The type of engine used for this vehicle.
CargoID cargo_type; ///< type of cargo this vehicle is carrying
uint16 cargo_cap; ///< total capacity
byte cargo_subtype;
byte subtype;
RailTypeByte railtype;
VehicleID index;
uint16 real_consist_length;
uint16 max_speed;
uint32 power;
uint32 weight;
uint32 max_te;
byte spritenum;
SpriteID cur_image;
uint32 image_width;
const SpriteGroup *sgroup;
TemplateVehicle(VehicleType type=VEH_INVALID, EngineID e=INVALID_ENGINE, byte B=0, Owner=_local_company);
TemplateVehicle(EngineID, RailVehicleInfo*);
TemplateVehicle(EngineID eid) {
next=0;
previous=0;
first=this;
engine_type=eid;
this->reuse_depot_vehicles = true;
this->keep_remaining_vehicles = true;
this->refit_as_template = true;
}
~TemplateVehicle();
inline TemplateVehicle* Next() const { return this->next; }
inline TemplateVehicle* Prev() const { return this->previous; }
inline TemplateVehicle* First() const { return this->first; }
void SetNext(TemplateVehicle*);
void SetPrev(TemplateVehicle*);
void SetFirst(TemplateVehicle*);
TemplateVehicle* GetNextUnit() const;
TemplateVehicle* GetPrevUnit();
bool IsSetReuseDepotVehicles() const { return this->reuse_depot_vehicles; }
bool IsSetKeepRemainingVehicles() const { return this->keep_remaining_vehicles; }
bool IsSetRefitAsTemplate() const { return this->refit_as_template; }
void ToggleReuseDepotVehicles() { this->reuse_depot_vehicles = !this->reuse_depot_vehicles; }
void ToggleKeepRemainingVehicles() { this->keep_remaining_vehicles = !this->keep_remaining_vehicles; }
void ToggleRefitAsTemplate() { this->refit_as_template = !this->refit_as_template; }
bool IsPrimaryVehicle() const { return this->IsFrontEngine(); }
inline bool IsFrontEngine() const { return HasBit(this->subtype, GVSF_FRONT); }
inline bool HasArticulatedPart() const { return this->Next() != NULL && this->Next()->IsArticulatedPart(); }
inline bool IsArticulatedPart() const { return HasBit(this->subtype, GVSF_ARTICULATED_PART); }
inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
inline bool IsFreeWagonChain() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
// since CmdBuildTemplateVehicle(...)
inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
inline uint16 GetRealLength() const { return this->real_consist_length; }
inline void SetRealLength(uint16 len) { this->real_consist_length = len; }
int Length() const;
SpriteID GetImage(Direction) const;
//int GetDisplayImageWidth(Point *offset = NULL) const;
SpriteID GetSpriteID() const;
short NumGroupsUsingTemplate() const;
short CountEnginesInChain();
};
void appendTemplateVehicle(TemplateVehicle*, TemplateVehicle*);
void insertTemplateVehicle(TemplateVehicle*, TemplateVehicle*, TemplateVehicle*);
void NeutralizeVehicleStatus(Train*);
void SplitVehicleRemainders(Train*);
// TemplateReplacement stuff
typedef Pool<TemplateReplacement, uint16, 16, 1024> TemplateReplacementPool;
extern TemplateReplacementPool _template_replacement_pool;
struct TemplateReplacement : TemplateReplacementPool::PoolItem<&_template_replacement_pool> {
GroupID group;
TemplateID sel_template;
TemplateReplacement(GroupID gid, TemplateID tid) { this->group=gid; this->sel_template=tid; }
TemplateReplacement() {}
~TemplateReplacement() {}
inline GroupID Group() { return this->group; }
inline GroupID Template() { return this->sel_template; }
inline void SetGroup(GroupID gid) { this->group = gid; }
inline void SetTemplate(TemplateID tid) { this->sel_template = tid; }
inline TemplateID GetTemplateVehicleID() { return sel_template; }
inline const TemplateVehicle* GetTemplateVehicle() {
const TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if ( tv->index == this->sel_template )
return tv;
}
return NULL;
}
};
TemplateReplacement* GetTemplateReplacementByGroupID(GroupID);
TemplateReplacement* GetTemplateReplacementByTemplateID(TemplateID);
bool IssueTemplateReplacement(GroupID, TemplateID);
short deleteIllegalTemplateReplacements(GroupID);
#endif /* TEMPLATE_VEH_H */

@ -0,0 +1,773 @@
// template_vehicle_func.cpp
#include "stdafx.h"
#include "window_gui.h"
#include "gfx_func.h"
#include "window_func.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "train.h"
#include "strings_func.h"
#include "vehicle_func.h"
#include "core/geometry_type.hpp"
#include "debug.h"
#include "table/sprites.h"
#include "table/strings.h"
#include "cargoaction.h"
#include "train.h"
#include "company_func.h"
#include "newgrf.h"
#include "spritecache.h"
#include "articulated_vehicles.h"
#include "autoreplace_func.h"
#include "depot_base.h"
#include "tbtr_template_vehicle.h"
#include "tbtr_template_vehicle_func.h"
#include <map>
#include <stdio.h>
Vehicle *vhead, *vtmp;
static const uint MAX_ARTICULATED_PARTS = 100;
// debugging printing functions for convenience, usually called from gdb
void pat() {
TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if ( tv->Prev() ) continue;
ptv(tv);
printf("__________\n");
}
}
void pav() {
Train *t;
FOR_ALL_TRAINS(t) {
if ( t->Previous() ) continue;
pvt(t);
printf("__________\n");
}
}
void ptv(TemplateVehicle* tv) {
if (!tv) return;
while (tv->Next() ) {
printf("eid:%3d st:%2d tv:%x next:%x cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype);
tv = tv->Next();
}
printf("eid:%3d st:%2d tv:%x next:%x cargo: %d cargo_sub: %d\n", tv->engine_type, tv->subtype, tv, tv->Next(), tv->cargo_type, tv->cargo_subtype);
}
void pvt (const Train *printme) {
for ( const Train *tmp = printme; tmp; tmp=tmp->Next() ) {
if ( tmp->index <= 0 ) {
printf("train has weird index: %d %d %x\n", tmp->index, tmp->engine_type, (__int64)tmp);
return;
}
printf("eid:%3d index:%2d subtype:%2d vehstat: %d cargo_t: %d cargo_sub: %d ref:%x\n", tmp->engine_type, tmp->index, tmp->subtype, tmp->vehstatus, tmp->cargo_type, tmp->cargo_subtype, tmp);
}
}
void BuildTemplateGuiList(GUITemplateList *list, Scrollbar *vscroll, Owner oid, RailType railtype)
{
list->Clear();
const TemplateVehicle *tv;
FOR_ALL_TEMPLATES(tv) {
if (tv->owner == oid && (tv->IsPrimaryVehicle() || tv->IsFreeWagonChain()) && TemplateVehicleContainsEngineOfRailtype(tv, railtype))
*list->Append() = tv;
}
list->RebuildDone();
if (vscroll) vscroll->SetCount(list->Length());
}
Money CalculateOverallTemplateCost(const TemplateVehicle *tv)
{
Money val = 0;
for (; tv; tv = tv->Next())
val += (Engine::Get(tv->engine_type))->GetCost();
return val;
}
void DrawTemplate(const TemplateVehicle *tv, int left, int right, int y)
{
if ( !tv ) return;
const TemplateVehicle *t = tv;
int offset=left;
while (t) {
PaletteID pal = GetEnginePalette(t->engine_type, _current_company);
DrawSprite(t->cur_image, pal, offset, y+12);
offset += t->image_width;
t = t->Next();
}
}
// copy important stuff from the virtual vehicle to the template
inline void SetupTemplateVehicleFromVirtual(TemplateVehicle *tmp, TemplateVehicle *prev, Train *virt)
{
if (prev) {
prev->SetNext(tmp);
tmp->SetPrev(prev);
tmp->SetFirst(prev->First());
}
tmp->railtype = virt->railtype;
tmp->owner = virt->owner;
tmp->value = virt->value;
// set the subtype but also clear the virtual flag while doing it
tmp->subtype = virt->subtype & ~(1 << GVSF_VIRTUAL);
// set the cargo type and capacity
tmp->cargo_type = virt->cargo_type;
tmp->cargo_subtype = virt->cargo_subtype;
tmp->cargo_cap = virt->cargo_cap;
const GroundVehicleCache *gcache = virt->GetGroundVehicleCache();
tmp->max_speed = virt->GetDisplayMaxSpeed();
tmp->power = gcache->cached_power;
tmp->weight = gcache->cached_weight;
tmp->max_te = gcache->cached_max_te / 1000;
tmp->spritenum = virt->spritenum;
tmp->cur_image = virt->GetImage(DIR_W, EIT_PURCHASE);
Point *p = new Point();
tmp->image_width = virt->GetDisplayImageWidth(p);
}
// create a new virtual train as clone of a real train
Train* CloneVirtualTrainFromTrain(const Train *clicked)
{
if ( !clicked ) return 0;
CommandCost c;
Train *tmp, *head, *tail;
head = CmdBuildVirtualRailVehicle(clicked->engine_type);
if ( !head ) return 0;
tail = head;
clicked = clicked->GetNextUnit();
while ( clicked ) {
tmp = CmdBuildVirtualRailVehicle(clicked->engine_type);
if ( tmp ) {
tmp->cargo_type = clicked->cargo_type;
tmp->cargo_subtype = clicked->cargo_subtype;
CmdMoveRailVehicle(0, DC_EXEC, (1<<21) | tmp->index, tail->index, 0);
tail = tmp;
}
clicked = clicked->GetNextUnit();
}
return head;
}
TemplateVehicle* CloneTemplateVehicleFromTrain(const Train *t)
{
Train *clicked = Train::Get(t->index);
if ( !clicked )
return 0;
Train *init_clicked = clicked;
int len = CountVehiclesInChain(clicked);
if ( !TemplateVehicle::CanAllocateItem(len) )
return 0;
TemplateVehicle *tmp, *prev=0;
for ( ; clicked; clicked=clicked->Next() ) {
tmp = new TemplateVehicle(clicked->engine_type);
SetupTemplateVehicleFromVirtual(tmp, prev, clicked);
prev = tmp;
}
tmp->First()->SetRealLength(CeilDiv(init_clicked->gcache.cached_total_length * 10, TILE_SIZE));
return tmp->First();
}
// create a full TemplateVehicle based train according to a virtual train
TemplateVehicle* TemplateVehicleFromVirtualTrain(Train *virt)
{
if ( !virt )
return 0;
Train *init_virt = virt;
int len = CountVehiclesInChain(virt);
if ( !TemplateVehicle::CanAllocateItem(len) )
return 0;
TemplateVehicle *tmp, *prev=0;
for ( ; virt; virt=virt->Next() ) {
tmp = new TemplateVehicle(virt->engine_type);
SetupTemplateVehicleFromVirtual(tmp, prev, virt);
prev = tmp;
}
tmp->First()->SetRealLength(CeilDiv(init_virt->gcache.cached_total_length * 10, TILE_SIZE));
return tmp->First();
}
// attempt to buy a train after a given template vehicle
// this might fail if the template e.g. deprecated and contains engines that are not sold anymore
Train* VirtualTrainFromTemplateVehicle(TemplateVehicle *tv)
{
if ( !tv ) return 0;
CommandCost c;
Train *tmp, *head, *tail;
head = CmdBuildVirtualRailVehicle(tv->engine_type);
if ( !head ) return 0;
tail = head;
tv = tv->GetNextUnit();
while ( tv ) {
tmp = CmdBuildVirtualRailVehicle(tv->engine_type);
if ( tmp ) {
tmp->cargo_type = tv->cargo_type;
tmp->cargo_subtype = tv->cargo_subtype;
CmdMoveRailVehicle(INVALID_TILE, DC_EXEC, (1<<21) | tmp->index, tail->index, 0);
tail = tmp;
}
tv = tv->GetNextUnit();
}
return head;
}
// return last in a chain (really last, so even a singular articulated part of a vehicle if the last one is artic)
inline TemplateVehicle* Last(TemplateVehicle *chain) {
if ( !chain ) return 0;
while ( chain->Next() ) chain = chain->Next();
return chain;
}
inline Train* Last(Train *chain) {
if ( !chain ) return 0;
while ( chain->GetNextUnit() ) chain = chain->GetNextUnit();
return chain;
}
// return: pointer to former vehicle
TemplateVehicle *DeleteTemplateVehicle(TemplateVehicle *todel)
{
if ( !todel )
return 0;
TemplateVehicle *cur = todel;
delete todel;
return cur;
}
// forward declaration, defined in train_cmd.cpp
CommandCost CmdSellRailWagon(DoCommandFlag, Vehicle*, uint16, uint32);
Train* DeleteVirtualTrain(Train *chain, Train *to_del) {
if ( chain != to_del ) {
CmdSellRailWagon(DC_EXEC, to_del, 0, 0);
return chain;
}
else {
chain = chain->GetNextUnit();
CmdSellRailWagon(DC_EXEC, to_del, 0, 0);
return chain;
}
}
// retrieve template vehicle from templatereplacement that belongs to the given group
TemplateVehicle* GetTemplateVehicleByGroupID(GroupID gid) {
TemplateReplacement *tr;
// first try to find a templatereplacement issued for the given groupid
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->Group() == gid )
return TemplateVehicle::GetIfValid(tr->Template()); // there can be only one
}
// if that didn't work, try to find a templatereplacement for ALL_GROUP
if ( gid != ALL_GROUP )
FOR_ALL_TEMPLATE_REPLACEMENTS(tr) {
if ( tr->Group() == ALL_GROUP )
return TemplateVehicle::GetIfValid(tr->Template());
}
// if all failed, just return null
return 0;
}
/**
* Check a template consist whether it contains any engine of the given railtype
*/
bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle *tv, RailType type)
{
/* For standard rail engines, allow only those */
if ( type == RAILTYPE_BEGIN || type == RAILTYPE_RAIL ) {
while ( tv ) {
if ( tv->railtype != type )
return false;
tv = tv->GetNextUnit();
}
return true;
}
/* For electrified rail engines, standard wagons or engines are allowed to be included */
while ( tv ) {
if ( tv->railtype == type )
return true;
tv = tv->GetNextUnit();
}
return false;
}
//helper
bool ChainContainsVehicle(Train *chain, Train *mem) {
for (; chain; chain=chain->Next())
if ( chain == mem )
return true;
return false;
}
// has O(n)
Train* ChainContainsEngine(EngineID eid, Train *chain) {
for (; chain; chain=chain->GetNextUnit())
if (chain->engine_type == eid)
return chain;
return 0;
}
// has O(n^2)
Train* DepotContainsEngine(TileIndex tile, EngineID eid, Train *not_in=0) {
Train *t;
FOR_ALL_TRAINS(t) {
// conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in'
// if 'not_in' is NULL, no check is needed
if ( t->tile==tile
// If the veh belongs to a chain, wagons will not return true on IsStoppedInDepot(), only primary vehicles will
// in case of t not a primary veh, we demand it to be a free wagon to consider it for replacement
&& ((t->IsPrimaryVehicle() && t->IsStoppedInDepot()) || t->IsFreeWagon())
&& t->engine_type==eid
&& (not_in==0 || ChainContainsVehicle(not_in, t)==0))
return t;
}
return 0;
}
void CopyStatus(Train *from, Train *to) {
DoCommand(to->tile, from->group_id, to->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
to->cargo_type = from->cargo_type;
to->cargo_subtype = from->cargo_subtype;
// swap names
char *tmp = to->name;
to->name = from->name;
from->name = tmp;
/*if ( !from->name || !to->name ) {
int tmpind = from->index;
from->index = to->index;
to->index = tmpind;
}*/
}
void NeutralizeStatus(Train *t) {
DoCommand(t->tile, DEFAULT_GROUP, t->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP);
t->name = 0;
}
bool TrainMatchesTemplate(const Train *t, TemplateVehicle *tv) {
while ( t && tv ) {
if ( t->engine_type != tv->engine_type )
return false;
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
if ( (t && !tv) || (!t && tv) )
return false;
return true;
}
bool TrainMatchesTemplateRefit(const Train *t, TemplateVehicle *tv)
{
if ( !tv->refit_as_template )
return true;
while ( t && tv ) {
if ( t->cargo_type != tv->cargo_type || t->cargo_subtype != tv->cargo_subtype )
return false;
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
return true;
}
void BreakUpRemainders(Train *t) {
while ( t ) {
Train *move;
if ( HasBit(t->subtype, GVSF_ENGINE) ) {
move = t;
t = t->Next();
DoCommand(move->tile, move->index, INVALID_VEHICLE, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
NeutralizeStatus( move );
}
else
t = t->Next();
}
}
short CountEnginesInChain(Train *t)
{
short count = 0;
for ( ; t; t=t->GetNextUnit() )
if ( HasBit(t->subtype, GVSF_ENGINE) )
count++;
return count;
}
int countOccurrencesInTrain(Train *t, EngineID eid) {
int count = 0;
Train *tmp = t;
for ( ; tmp; tmp=tmp->GetNextUnit() )
if ( tmp->engine_type == eid )
count++;
return count;
}
int countOccurrencesInTemplateVehicle(TemplateVehicle *contain, EngineID eid) {
int count = 0;
for ( ; contain; contain=contain->GetNextUnit() )
if ( contain->engine_type == eid )
count++;
return count;
}
int countOccurrencesInDepot(TileIndex tile, EngineID eid, Train *not_in=0) {
int count = 0;
Vehicle *v;
FOR_ALL_VEHICLES(v) {
// conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in'
// if 'not_in' is NULL, no check is needed
if ( v->tile==tile && v->IsStoppedInDepot() && v->engine_type==eid &&
(not_in==0 || ChainContainsVehicle(not_in, (Train*)v)==0))
count++;
}
return count;
}
// basically does the same steps as CmdTemplateReplaceVehicle but without actually moving things around
CommandCost CalculateTemplateReplacementCost(Train *incoming) {
TileIndex tile = incoming->tile;
TemplateVehicle *tv = GetTemplateVehicleByGroupID(incoming->group_id);
CommandCost estimate(EXPENSES_NEW_VEHICLES);
// count for each different eid in the incoming train
std::map<EngineID, short> unique_eids;
for ( TemplateVehicle *tmp=tv; tmp; tmp=tmp->GetNextUnit() )
unique_eids[tmp->engine_type]++;
std::map<EngineID, short>::iterator it = unique_eids.begin();
for ( ; it!=unique_eids.end(); it++ ) {
it->second -= countOccurrencesInTrain(incoming, it->first);
it->second -= countOccurrencesInDepot(incoming->tile, it->first, incoming);
if ( it->second < 0 ) it->second = 0;
}
// get overall buying cost
for ( it=unique_eids.begin(); it!=unique_eids.end(); it++ ) {
for ( int j=0; j<it->second; j++ ) {
estimate.AddCost(DoCommand(tile, it->first, 0, DC_NONE, CMD_BUILD_VEHICLE));
}
}
return estimate;
}
// make sure the real train wagon has the right cargo
void CopyWagonStatus(TemplateVehicle *from, Train *to) {
to->cargo_type = from->cargo_type;
to->cargo_subtype = from->cargo_subtype;
}
int NumTrainsNeedTemplateReplacement(GroupID g_id, TemplateVehicle *tv)
{
int count = 0;
if ( !tv ) return count;
const Train *t;
FOR_ALL_TRAINS(t) {
if ( t->IsPrimaryVehicle() && t->group_id == g_id && (!TrainMatchesTemplate(t, tv) || !TrainMatchesTemplateRefit(t, tv)) )
count++;
}
return count;
}
// refit each vehicle in t as is in tv, assume t and tv contain the same types of vehicles
static void RefitTrainFromTemplate(Train *t, TemplateVehicle *tv)
{
while ( t && tv ) {
// refit t as tv
uint32 cb = GetCmdRefitVeh(t);
DoCommandP(t->tile, t->index, tv->cargo_type | tv->cargo_subtype << 8 | 1 << 16 , cb);
// next
t = t->GetNextUnit();
tv = tv->GetNextUnit();
}
}
/** using cmdtemplatereplacevehicle as test-function (i.e. with flag DC_NONE) is not a good idea as that function relies on
* actually moving vehicles around to work properly.
* We do this worst-cast test instead.
*/
CommandCost TestBuyAllTemplateVehiclesInChain(TemplateVehicle *tv, TileIndex tile)
{
CommandCost cost(EXPENSES_NEW_VEHICLES);
for ( ; tv; tv=tv->GetNextUnit() )
cost.AddCost( DoCommand(tile, tv->engine_type, 0, DC_NONE, CMD_BUILD_VEHICLE) );
return cost;
}
/** Transfer as much cargo from a given (single train) vehicle onto a chain of vehicles.
* I.e., iterate over the chain from head to tail and use all available cargo capacity (w.r.t. cargo type of course)
* to store the cargo from the given single vehicle.
* @param old_veh: ptr to the single vehicle, which's cargo shall be moved
* @param new_head: ptr to the head of the chain, which shall obtain old_veh's cargo
* @return: amount of moved cargo TODO
*/
void TransferCargoForTrain(Train *old_veh, Train *new_head)
{
assert(new_head->IsPrimaryVehicle());
CargoID _cargo_type = old_veh->cargo_type;
byte _cargo_subtype = old_veh->cargo_subtype;
// how much cargo has to be moved (if possible)
uint remainingAmount = old_veh->cargo.TotalCount();
// each vehicle in the new chain shall be given as much of the old cargo as possible, until none is left
for (Train *tmp=new_head; tmp!=NULL && remainingAmount>0; tmp=tmp->GetNextUnit())
{
if (tmp->cargo_type == _cargo_type && tmp->cargo_subtype == _cargo_subtype)
{
// calculate the free space for new cargo on the current vehicle
uint curCap = tmp->cargo_cap - tmp->cargo.TotalCount();
uint moveAmount = std::min(remainingAmount, curCap);
// move (parts of) the old vehicle's cargo onto the current vehicle of the new chain
if (moveAmount > 0)
{
old_veh->cargo.Shift(moveAmount, &tmp->cargo);
remainingAmount -= moveAmount;
}
}
}
// TODO: needs to be implemented, too
// // from autoreplace_cmd.cpp : 121
/* Any left-overs will be thrown away, but not their feeder share. */
//if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
/* Update train weight etc., the old vehicle will be sold anyway */
new_head->ConsistChanged(ConsistChangeFlags::CCF_LOADUNLOAD);
}
// TODO: fit signature to regular cmd-structure
// do something with move_cost, it is not used right now
// if exec==DC_EXEC, test first and execute if sucessful
CommandCost CmdTemplateReplaceVehicle(Train *incoming, bool stayInDepot, DoCommandFlag flags) {
Train *new_chain=0,
*remainder_chain=0,
*tmp_chain=0;
TileIndex tile = incoming->tile;
TemplateVehicle *tv = GetTemplateVehicleByGroupID(incoming->group_id);
EngineID eid = tv->engine_type;
CommandCost buy(EXPENSES_NEW_VEHICLES);
CommandCost move_cost(EXPENSES_NEW_VEHICLES);
CommandCost tmp_result(EXPENSES_NEW_VEHICLES);
/* first some tests on necessity and sanity */
if ( !tv )
return buy;
bool need_replacement = !TrainMatchesTemplate(incoming, tv);
bool need_refit = !TrainMatchesTemplateRefit(incoming, tv);
bool use_refit = tv->refit_as_template;
CargoID store_refit_ct = CT_INVALID;
short store_refit_csubt = 0;
// if a train shall keep its old refit, store the refit setting of its first vehicle
if ( !use_refit ) {
for ( Train *getc=incoming; getc; getc=getc->GetNextUnit() )
if ( getc->cargo_type != CT_INVALID ) {
store_refit_ct = getc->cargo_type;
break;
}
}
// TODO: set result status to success/no success before returning
if ( !need_replacement ) {
if ( !need_refit || !use_refit ) {
/* before returning, release incoming train first if 2nd param says so */
if ( !stayInDepot ) incoming->vehstatus &= ~VS_STOPPED;
return buy;
}
} else {
CommandCost buyCost = TestBuyAllTemplateVehiclesInChain(tv, tile);
if ( !buyCost.Succeeded() || !CheckCompanyHasMoney(buyCost) ) {
if ( !stayInDepot ) incoming->vehstatus &= ~VS_STOPPED;
return buy;
}
}
/* define replacement behaviour */
bool reuseDepot = tv->IsSetReuseDepotVehicles();
bool keepRemainders = tv->IsSetKeepRemainingVehicles();
if ( need_replacement ) {
/// step 1: generate primary for newchain and generate remainder_chain
// 1. primary of incoming might already fit the template
// leave incoming's primary as is and move the rest to a free chain = remainder_chain
// 2. needed primary might be one of incoming's member vehicles
// 3. primary might be available as orphan vehicle in the depot
// 4. we need to buy a new engine for the primary
// all options other than 1. need to make sure to copy incoming's primary's status
if ( eid == incoming->engine_type ) { // 1
new_chain = incoming;
remainder_chain = incoming->GetNextUnit();
if ( remainder_chain )
move_cost.AddCost(CmdMoveRailVehicle(tile, flags, remainder_chain->index|(1<<20), INVALID_VEHICLE, 0));
}
else if ( (tmp_chain = ChainContainsEngine(eid, incoming)) && tmp_chain!=NULL ) { // 2
// new_chain is the needed engine, move it to an empty spot in the depot
new_chain = tmp_chain;
move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags,CMD_MOVE_RAIL_VEHICLE));
remainder_chain = incoming;
}
else if ( reuseDepot && (tmp_chain = DepotContainsEngine(tile, eid, incoming)) && tmp_chain!=NULL ) { // 3
new_chain = tmp_chain;
move_cost.AddCost(DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE));
remainder_chain = incoming;
}
else { // 4
tmp_result = DoCommand(tile, eid, 0, flags, CMD_BUILD_VEHICLE);
/* break up in case buying the vehicle didn't succeed */
if ( !tmp_result.Succeeded() )
return tmp_result;
buy.AddCost(tmp_result);
new_chain = Train::Get(_new_vehicle_id);
/* make sure the newly built engine is not attached to any free wagons inside the depot */
move_cost.AddCost ( DoCommand(tile, new_chain->index, INVALID_VEHICLE, flags, CMD_MOVE_RAIL_VEHICLE) );
/* prepare the remainder chain */
remainder_chain = incoming;
}
// If we bought a new engine or reused one from the depot, copy some parameters from the incoming primary engine
if ( incoming != new_chain && flags == DC_EXEC) {
CopyHeadSpecificThings(incoming, new_chain, flags);
NeutralizeStatus(incoming);
// additionally, if we don't want to use the template refit, refit as incoming
// the template refit will be set further down, if we use it at all
if ( !use_refit ) {
uint32 cb = GetCmdRefitVeh(new_chain);
DoCommandP(new_chain->tile, new_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 , cb);
}
}
/// step 2: fill up newchain according to the template
// foreach member of template (after primary):
// 1. needed engine might be within remainder_chain already
// 2. needed engine might be orphaned within the depot (copy status)
// 3. we need to buy (again) (copy status)
TemplateVehicle *cur_tmpl = tv->GetNextUnit();
Train *last_veh = new_chain;
while (cur_tmpl) {
// 1. engine contained in remainder chain
if ( (tmp_chain = ChainContainsEngine(cur_tmpl->engine_type, remainder_chain)) && tmp_chain!=NULL ) {
// advance remainder_chain (if necessary) to not lose track of it
if ( tmp_chain == remainder_chain )
remainder_chain = remainder_chain->GetNextUnit();
move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0));
}
// 2. engine contained somewhere else in the depot
else if ( reuseDepot && (tmp_chain = DepotContainsEngine(tile, cur_tmpl->engine_type, new_chain)) && tmp_chain!=NULL ) {
move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0));
}
// 3. must buy new engine
else {
tmp_result = DoCommand(tile, cur_tmpl->engine_type, 0, flags, CMD_BUILD_VEHICLE);
if ( !tmp_result.Succeeded() )
return tmp_result;
buy.AddCost(tmp_result);
tmp_chain = Train::Get(_new_vehicle_id);
move_cost.AddCost(CmdMoveRailVehicle(tile, flags, tmp_chain->index, last_veh->index, 0));
}
// TODO: is this enough ? might it be that we bought a new wagon here and it now has std refit ?
if ( need_refit && flags == DC_EXEC ) {
if ( use_refit ) {
uint32 cb = GetCmdRefitVeh(tmp_chain);
DoCommandP(tmp_chain->tile, tmp_chain->index, cur_tmpl->cargo_type | cur_tmpl->cargo_subtype << 8 | 1 << 16 , cb);
// old
// CopyWagonStatus(cur_tmpl, tmp_chain);
} else {
uint32 cb = GetCmdRefitVeh(tmp_chain);
DoCommandP(tmp_chain->tile, tmp_chain->index, store_refit_ct | store_refit_csubt << 8 | 1 << 16 , cb);
}
}
cur_tmpl = cur_tmpl->GetNextUnit();
last_veh = tmp_chain;
}
}
/* no replacement done */
else {
new_chain = incoming;
}
/// step 3: reorder and neutralize the remaining vehicles from incoming
// wagons remaining from remainder_chain should be filled up in as few freewagonchains as possible
// each locos might be left as singular in the depot
// neutralize each remaining engine's status
// refit, only if the template option is set so
if ( use_refit && (need_refit || need_replacement) ) {
RefitTrainFromTemplate(new_chain, tv);
}
if ( new_chain && remainder_chain )
for ( Train *ct=remainder_chain; ct; ct=ct->GetNextUnit() )
TransferCargoForTrain(ct, new_chain);
// point incoming to the newly created train so that starting/stopping from the calling function can be done
incoming = new_chain;
if ( !stayInDepot && flags == DC_EXEC )
new_chain->vehstatus &= ~VS_STOPPED;
if ( remainder_chain && keepRemainders && flags == DC_EXEC )
BreakUpRemainders(remainder_chain);
else if ( remainder_chain ) {
buy.AddCost(DoCommand(tile, remainder_chain->index | (1<<20), 0, flags, CMD_SELL_VEHICLE));
}
return buy;
}

@ -0,0 +1,66 @@
// template_vehicle_func.h
#ifndef TEMPLATE_VEHICLE_FUNC_H
#define TEMPLATE_VEHICLE_FUNC_H
#include "stdafx.h"
#include "window_gui.h"
#include "tbtr_template_vehicle.h"
//void DrawTemplateVehicle(TemplateVehicle*, int, const Rect&);
void DrawTemplateVehicle(const TemplateVehicle*, int, int, int, VehicleID, int, VehicleID);
void BuildTemplateGuiList(GUITemplateList*, Scrollbar*, Owner, RailType);
Money CalculateOverallTemplateCost(const TemplateVehicle*);
void DrawTemplateTrain(const TemplateVehicle*, int, int, int);
SpriteID GetSpriteID(EngineID, bool);
void DrawTemplate(const TemplateVehicle*, int, int, int);
int GetTemplateDisplayImageWidth(EngineID);
TemplateVehicle *CreateNewTemplateVehicle(EngineID);
void setupVirtTrain(const TemplateVehicle*, Train*);
TemplateVehicle* TemplateVehicleFromVirtualTrain(Train*);
Train* VirtualTrainFromTemplateVehicle(TemplateVehicle*);
inline TemplateVehicle* Last(TemplateVehicle*);
TemplateVehicle *DeleteTemplateVehicle(TemplateVehicle*);
Train* DeleteVirtualTrainPart(Train*, Train*);
Train* DeleteVirtualTrain(Train*, Train *);
CommandCost CmdTemplateReplaceVehicle(Train*, bool, DoCommandFlag);
void pat();
void pav();
void ptv(TemplateVehicle*);
void pvt(const Train*);
// for testing
TemplateVehicle* GetTemplateVehicleByGroupID(GroupID);
bool ChainContainsVehicle(Train*, Train*);
Train* ChainContainsEngine(EngineID, Train*);
Train* DepotContainsEngine(TileIndex, EngineID, Train*);
int NumTrainsNeedTemplateReplacement(GroupID, TemplateVehicle*);
CommandCost TestBuyAllTemplateVehiclesInChain(Train*);
CommandCost CalculateTemplateReplacementCost(Train*);
short CountEnginesInChain(Train*);
bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle*, RailType);
Train* CloneVirtualTrainFromTrain(const Train *);
TemplateVehicle* CloneTemplateVehicleFromTrain(const Train *);
void TransferCargoForTrain(Train*, Train*);
#endif

@ -123,6 +123,7 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
bool Tick();
void OnNewDay();
uint Crash(bool flooded = false);
Money CalculateCurrentOverallValue() const;
Trackdir GetVehicleTrackdir() const;
TileIndex GetOrderStationLocation(StationID station);
bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse);
@ -163,6 +164,15 @@ struct Train FINAL : public GroundVehicle<Train, VEH_TRAIN> {
return v;
}
/* Get the last vehicle of a chain
* @return pointer the last vehicle in a chain
*/
inline Train *GetLastUnit() {
Train *tmp = this;
while ( tmp->GetNextUnit() ) tmp = tmp->GetNextUnit();
return tmp;
}
/**
* Calculate the offset from this vehicle's center to the following center taking the vehicle lengths into account.
* @return Offset from center to center.
@ -336,6 +346,13 @@ protected: // These functions should not be called outside acceleration code.
}
};
CommandCost CmdMoveRailVehicle(TileIndex, DoCommandFlag , uint32, uint32, const char *);
CommandCost CmdMoveVirtualRailVehicle(TileIndex, DoCommandFlag, uint32, uint32, const char*);
Train* CmdBuildVirtualRailWagon(const Engine*);
Train* CmdBuildVirtualRailVehicle(EngineID);
#define FOR_ALL_TRAINS(var) FOR_ALL_VEHICLES_OF_TYPE(Train, var)
#endif /* TRAIN_H */

@ -41,6 +41,8 @@
#include "safeguards.h"
#include "engine_func.h"
static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
@ -259,7 +261,7 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
if (this->IsFrontEngine()) {
this->UpdateAcceleration();
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
if ( !HasBit(this->subtype, GVSF_VIRTUAL) ) SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
InvalidateWindowData(WC_VEHICLE_REFIT, this->index, VIWD_CONSIST_CHANGED);
InvalidateWindowData(WC_VEHICLE_ORDERS, this->index, VIWD_CONSIST_CHANGED);
InvalidateNewGRFInspectWindow(GSF_TRAINS, this->index);
@ -1158,6 +1160,7 @@ static void NormaliseTrainHead(Train *head)
* @param p1 various bitstuffed elements
* - p1 (bit 0 - 19) source vehicle index
* - p1 (bit 20) move all vehicles following the source vehicle
* - p1 (bit 21) this is a virtual vehicle (for creating TemplateVehicles)
* @param p2 what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
* @param text unused
* @return the cost of this operation or an error
@ -1222,10 +1225,14 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (!move_chain && dst != NULL && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
/* Check if all vehicles in the source train are stopped inside a depot. */
if (!src_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* Do this check only if the vehicle to be moved is non-virtual */
if ( !HasBit(p1, 21) )
if (!src_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* Check if all vehicles in the destination train are stopped inside a depot. */
if (dst_head != NULL && !dst_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* Do this check only if the destination vehicle is non-virtual */
if ( !HasBit(p1, 21) )
if (dst_head != NULL && !dst_head->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
/* First make a backup of the order of the trains. That way we can do
* whatever we want with the order and later on easily revert. */
@ -1334,8 +1341,11 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (dst_head != NULL) dst_head->First()->MarkDirty();
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* But only if the moved vehicle is not virtual */
if ( !HasBit(src->subtype, GVSF_VIRTUAL) ) {
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
}
} else {
/* We don't want to execute what we're just tried. */
RestoreTrainBackup(original_src);
@ -1418,8 +1428,11 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3
NormaliseTrainHead(new_head);
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* Unless its a virtual train */
if ( !HasBit(v->subtype, GVSF_VIRTUAL) ) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
}
/* Actually delete the sold 'goods' */
delete sell_head;
@ -3741,6 +3754,16 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse)
return true;
}
/* Calculate the summed up value of all parts of a train */
Money Train::CalculateCurrentOverallValue() const
{
Money ovr_value = 0;
const Train *v = this;
do {
ovr_value += v->value;
} while ( (v=v->GetNextVehicle()) != NULL );
return ovr_value;
}
static bool TrainLocoHandler(Train *v, bool mode)
{
@ -4035,3 +4058,158 @@ Trackdir Train::GetVehicleTrackdir() const
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
}
/* Get the pixel-width of the image that is used for the train vehicle
* @return: the image width number in pixel
*/
int GetDisplayImageWidth(Train *t, Point *offset)
{
int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
int vehicle_pitch = 0;
const Engine *e = Engine::Get(t->engine_type);
if (e->grf_prop.grffile != NULL && is_custom_sprite(e->u.rail.image_index)) {
reference_width = e->grf_prop.grffile->traininfo_vehicle_width;
vehicle_pitch = e->grf_prop.grffile->traininfo_vehicle_pitch;
}
if (offset != NULL) {
offset->x = reference_width / 2;
offset->y = vehicle_pitch;
}
//printf(" refwid:%d gdiw.cachedvehlen(%d):%d ", reference_width, this->engine_type, this->gcache.cached_veh_length);
return t->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH;
}
Train* CmdBuildVirtualRailWagon(const Engine *e)
{
const RailVehicleInfo *rvi = &e->u.rail;
Train *v = new Train();
v->x_pos = 0;
v->y_pos = 0;
v->spritenum = rvi->image_index;
v->engine_type = e->index;
v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
v->direction = DIR_W;
v->tile = 0;//INVALID_TILE;
v->owner = _current_company;
v->track = TRACK_BIT_DEPOT;
v->vehstatus = VS_HIDDEN | VS_DEFPAL;
v->SetWagon();
v->SetFreeWagon();
v->cargo_type = e->GetDefaultCargoType();
v->cargo_cap = rvi->capacity;
v->railtype = rvi->railtype;
v->build_year = _cur_year;
v->cur_image = SPR_IMG_QUERY;
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
AddArticulatedParts(v);
_new_vehicle_id = v->index;
// from revision r22xxx
// VehicleMove(v, false);
// new
v->UpdateViewport(true, false);
v->First()->ConsistChanged(ConsistChangeFlags::CCF_ARRANGE);
//UpdateTrainGroupID(v->First());
CheckConsistencyOfArticulatedVehicle(v);
/* The GVSF_VIRTUAL flag is used to prevent depot-tile sanity checks */
SetBit(v->subtype, GVSF_VIRTUAL);
// GroupStatistics::CountVehicle( v, -1 );
return v;
}
/**
* Build a railroad vehicle.
* @param tile tile of the depot where rail-vehicle is built.
* @param flags type of operation.
* @param e the engine to build.
* @param data bit 0 prevents any free cars from being added to the train.
* @param ret[out] the vehicle that has been built.
* @return the cost of this operation or an error.
*/
Train* CmdBuildVirtualRailVehicle(EngineID eid)
{
if ( !IsEngineBuildable(eid, VEH_TRAIN, _current_company) ) return 0;
const Engine* e = Engine::Get(eid);
const RailVehicleInfo *rvi = &e->u.rail;
int num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false);
if ( !Train::CanAllocateItem(num_vehicles) ) return 0;
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildVirtualRailWagon(e);
Train *v = new Train();
v->x_pos = 0;
v->y_pos = 0;
v->direction = DIR_W;
v->tile = 0;//INVALID_TILE;
v->owner = _current_company;
v->track = TRACK_BIT_DEPOT;
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
v->spritenum = rvi->image_index;
v->cargo_type = e->GetDefaultCargoType();
v->cargo_cap = rvi->capacity;
v->last_station_visited = INVALID_STATION;
v->engine_type = e->index;
v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
v->reliability = e->reliability;
v->reliability_spd_dec = e->reliability_spd_dec;
v->max_age = e->GetLifeLengthInDays();
v->railtype = rvi->railtype;
_new_vehicle_id = v->index;
v->cur_image = SPR_IMG_QUERY;
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
v->SetFrontEngine();
v->SetEngine();
// from revision r22xxx
// VehicleMove(v, false);
// new
v->UpdateViewport(true, false);
if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
AddRearEngineToMultiheadedTrain(v);
} else {
AddArticulatedParts(v);
}
v->ConsistChanged(ConsistChangeFlags::CCF_ARRANGE);
//UpdateTrainGroupID(v);
CheckConsistencyOfArticulatedVehicle(v);
SetBit(v->subtype, GVSF_VIRTUAL);
// GroupStatistics::CountVehicle( v, -1 );
return v;
}

@ -57,6 +57,8 @@
#include "safeguards.h"
#include "tbtr_template_vehicle_func.h"
#define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
VehicleID _new_vehicle_id;
@ -626,6 +628,13 @@ void ResetVehicleColourMap()
typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
static AutoreplaceMap _vehicles_to_autoreplace;
/**
* List of vehicles that are issued for template replacement this tick.
* Mapping is {vehicle : leave depot after replacement}
*/
typedef SmallMap<Train *, bool, 4> TemplateReplacementMap;
static TemplateReplacementMap _vehicles_to_templatereplace;
void InitializeVehicles()
{
_vehicles_to_autoreplace.Reset();
@ -828,14 +837,25 @@ Vehicle::~Vehicle()
*/
void VehicleEnteredDepotThisTick(Vehicle *v)
{
/* Vehicle should stop in the depot if it was in 'stopping' state */
_vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
/* Template Replacement Setup stuff */
bool stayInDepot = v->current_order.GetDepotActionType();
TemplateReplacement *tr = GetTemplateReplacementByGroupID(v->group_id);
if ( tr ) {
if ( stayInDepot ) _vehicles_to_templatereplace[(Train*)v] = true;
else _vehicles_to_templatereplace[(Train*)v] = false;
}
/* Moved the assignment for auto replacement here to prevent auto replacement
* from happening if template replacement is also scheduled */
else
/* Vehicle should stop in the depot if it was in 'stopping' state */
_vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
/* We ALWAYS set the stopped state. Even when the vehicle does not plan on
* stopping in the depot, so we stop it to ensure that it will not reserve
* the path out of the depot before we might autoreplace it to a different
* engine. The new engine would not own the reserved path we store that we
* stopped the vehicle, so autoreplace can start it again */
v->vehstatus |= VS_STOPPED;
}
@ -877,6 +897,7 @@ static void RunVehicleDayProc()
void CallVehicleTicks()
{
_vehicles_to_autoreplace.Clear();
_vehicles_to_templatereplace.Clear();
RunVehicleDayProc();
@ -953,6 +974,7 @@ void CallVehicleTicks()
}
}
/* do Auto Replacement */
Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
v = it->first;
@ -997,8 +1019,24 @@ void CallVehicleTicks()
SetDParam(1, error_message);
AddVehicleAdviceNewsItem(message, v->index);
}
cur_company.Restore();
/* do Template Replacement */
Backup<CompanyByte> tmpl_cur_company(_current_company, FILE_LINE);
for (TemplateReplacementMap::iterator it = _vehicles_to_templatereplace.Begin(); it != _vehicles_to_templatereplace.End(); it++) {
Train *t = it->first;
tmpl_cur_company.Change(t->owner);
bool stayInDepot = it->second;
it->first->vehstatus |= VS_STOPPED;
CmdTemplateReplaceVehicle(t, stayInDepot, DC_EXEC);
/* Redraw main gui for changed statistics */
SetWindowClassesDirty(WC_TEMPLATEGUI_MAIN);
}
tmpl_cur_company.Restore();
}
/**

@ -26,6 +26,8 @@
#include <list>
#include <map>
CommandCost CmdRefitVehicle(TileIndex, DoCommandFlag, uint32, uint32, const char*);
/** Vehicle status bits in #Vehicle::vehstatus. */
enum VehStatus {
VS_HIDDEN = 0x01, ///< Vehicle is not visible.
@ -115,6 +117,7 @@ enum GroundVehicleSubtypeFlags {
GVSF_ENGINE = 3, ///< Engine that can be front engine, but might be placed behind another engine (not used for road vehicles).
GVSF_FREE_WAGON = 4, ///< First in a wagon chain (in depot) (not used for road vehicles).
GVSF_MULTIHEADED = 5, ///< Engine is multiheaded (not used for road vehicles).
GVSF_VIRTUAL = 6, ///< Used for virtual trains during template design, it is needed to skip checks for tile or depot status
};
/** Cached often queried values common to all vehicles. */
@ -514,6 +517,7 @@ public:
Money GetDisplayProfitLastYear() const { return (this->profit_last_year >> 8); }
void SetNext(Vehicle *next);
inline void SetFirst(Vehicle *f) { this->first=f; }
/**
* Get the next vehicle of this vehicle.

@ -422,6 +422,7 @@ static CommandCost RefitVehicle(Vehicle *v, bool only_this, uint8 num_vehicles,
* @param p2 various bitstuffed elements
* - p2 = (bit 0-4) - New cargo type to refit to.
* - p2 = (bit 6) - Automatic refitting.
* - p2 = (bit 5) - Is a virtual train (used by template replacement to allow refitting without stopped-in-depot checks)
* - p2 = (bit 7) - Refit only this vehicle. Used only for cloning vehicles.
* - p2 = (bit 8-15) - New cargo subtype to refit to. 0xFF means to try keeping the same subtype according to GetBestFittingSubType().
* - p2 = (bit 16-23) - Number of vehicles to refit (not counting articulated parts). Zero means all vehicles.
@ -444,12 +445,17 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
if (ret.Failed()) return ret;
bool auto_refit = HasBit(p2, 6);
bool is_virtual_train = HasBit(p2, 5);
bool free_wagon = v->type == VEH_TRAIN && Train::From(front)->IsFreeWagon(); // used by autoreplace/renew
/* Don't allow shadows and such to be refitted. */
if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
/* Allow auto-refitting only during loading and normal refitting only in a depot. */
if ( ! is_virtual_train ) {
if (!free_wagon && (!auto_refit || !front->current_order.IsType(OT_LOADING)) && !front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);
}
if ((flags & DC_QUERY_COST) == 0 && // used by the refit GUI, including the order refit GUI.
!free_wagon && // used by autoreplace/renew
(!auto_refit || !front->current_order.IsType(OT_LOADING)) && // refit inside stations
@ -499,7 +505,9 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
InvalidateWindowData(WC_VEHICLE_DETAILS, front->index);
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
}
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
/* virtual vehicles get their cargo changed by the TemplateCreateWindow, so set this dirty instead of a depot window */
if ( HasBit(v->subtype, GVSF_VIRTUAL) ) SetWindowClassesDirty(WC_CREATE_TEMPLATE);
else SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
} else {
/* Always invalidate the cache; querycost might have filled it. */
v->InvalidateNewGRFCacheOfChain();

@ -37,11 +37,11 @@
#include "engine_func.h"
#include "station_base.h"
#include "tilehighlight_func.h"
#include "tbtr_template_gui_main.h"
#include "zoom_func.h"
#include "safeguards.h"
Sorting _sorting;
static GUIVehicleList::SortFunction VehicleNumberSorter;
@ -168,6 +168,7 @@ DropDownList *BaseVehicleListWindow::BuildActionDropdownList(bool show_autorepla
{
DropDownList *list = new DropDownList();
*list->Append() = new DropDownListStringItem(STR_TMPL_TEMPLATE_REPLACEMENT, ADI_TEMPLATE_REPLACE, false);
if (show_autoreplace) *list->Append() = new DropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE, false);
*list->Append() = new DropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE, false);
*list->Append() = new DropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT, false);
@ -400,6 +401,7 @@ struct RefitWindow : public Window {
VehicleID selected_vehicle; ///< First vehicle in the current selection.
uint8 num_vehicles; ///< Number of selected vehicles.
bool auto_refit; ///< Select cargo for auto-refitting.
bool is_virtual_train; ///< TemplateReplacement, whether the selected vehicle is virtual
/**
* Collects all (cargo, subcargo) refit options of a vehicle chain.
@ -581,11 +583,12 @@ struct RefitWindow : public Window {
return &l[this->sel[1]];
}
RefitWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window(desc)
RefitWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit, bool is_virtual) : Window(desc)
{
this->sel[0] = -1;
this->sel[1] = 0;
this->auto_refit = auto_refit;
this->is_virtual_train = is_virtual;
this->order = order;
this->CreateNestedTree();
@ -949,9 +952,9 @@ struct RefitWindow : public Window {
if (this->order == INVALID_VEH_ORDER_ID) {
bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16, GetCmdRefitVeh(v)) && delete_window) delete this;
if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16 | this->is_virtual_train << 5, GetCmdRefitVeh(v)) && delete_window) delete this;
} else {
if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->order << 16, CMD_ORDER_REFIT)) delete this;
if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16 | this->is_virtual_train << 5, CMD_ORDER_REFIT)) delete this;
}
}
break;
@ -1032,10 +1035,10 @@ static WindowDesc _vehicle_refit_desc(
* @param parent the parent window of the refit window
* @param auto_refit Choose cargo for auto-refitting
*/
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit, bool is_virtual_train)
{
DeleteWindowById(WC_VEHICLE_REFIT, v->index);
RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order, auto_refit);
RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order, auto_refit, is_virtual_train);
w->parent = parent;
}
@ -1663,6 +1666,9 @@ public:
case ADI_REPLACE: // Replace window
ShowReplaceGroupVehicleWindow(ALL_GROUP, this->vli.vtype);
break;
case ADI_TEMPLATE_REPLACE:
ShowTemplateReplaceWindow(this->unitnumber_digits, this->resize.step_height);
break;
case ADI_SERVICE: // Send for servicing
case ADI_DEPOT: // Send to Depots
DoCommandP(0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : (DepotCommand)0), this->window_number, GetCmdSendToDepot(this->vli.vtype));

@ -19,7 +19,7 @@
#include "engine_type.h"
#include "company_type.h"
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false);
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false, bool is_virtual_train = false);
/** The tabs in the train details window */
enum TrainDetailsWindowTabs {

@ -27,6 +27,7 @@ struct BaseVehicleListWindow : public Window {
VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show.
enum ActionDropdownItem {
ADI_TEMPLATE_REPLACE,
ADI_REPLACE,
ADI_SERVICE,
ADI_DEPOT,

@ -147,7 +147,7 @@ bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli
case VL_GROUP_LIST:
if (vli.index != ALL_GROUP) {
FOR_ALL_VEHICLES(v) {
if (v->type == vli.vtype && v->IsPrimaryVehicle() &&
if (!HasBit(v->subtype, GVSF_VIRTUAL) && v->type == vli.vtype && v->IsPrimaryVehicle() &&
v->owner == vli.company && GroupIsInGroup(v->group_id, vli.index)) {
*list->Append() = v;
}
@ -158,7 +158,7 @@ bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli
case VL_STANDARD:
FOR_ALL_VEHICLES(v) {
if (v->type == vli.vtype && v->owner == vli.company && v->IsPrimaryVehicle()) {
if (!HasBit(v->subtype, GVSF_VIRTUAL) && v->type == vli.vtype && v->owner == vli.company && v->IsPrimaryVehicle()) {
*list->Append() = v;
}
}

@ -2189,9 +2189,10 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y)
DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
if (IsCompanyBuildableVehicleType(v)) {
v = v->First();
WindowClass wc = _thd.GetCallbackWnd()->window_class;
if (_ctrl_pressed && v->owner == _local_company) {
StartStopVehicle(v, true);
} else {
} else if ( wc != WC_CREATE_TEMPLATE && wc != WC_TEMPLATEGUI_MAIN) {
ShowVehicleViewWindow(v);
}
}

@ -26,6 +26,7 @@ enum BuildVehicleWidgets {
WID_BV_SHOW_HIDE, ///< Button to hide or show the selected engine.
WID_BV_BUILD_SEL, ///< Build button.
WID_BV_RENAME, ///< Rename button.
BUILD_VEHICLE_WIDGET_BUILD, /// TODO: own
};
#endif /* WIDGETS_BUILD_VEHICLE_WIDGET_H */

@ -681,6 +681,12 @@ enum WindowClass {
*/
WC_SAVE_PRESET,
WC_TEMPLATEGUI_MAIN,
WC_TEMPLATEGUI_RPLALL,
WC_BUILD_VIRTUAL_TRAIN,
WC_CREATE_TEMPLATE,
WC_INVALID = 0xFFFF, ///< Invalid window.
};

Loading…
Cancel
Save