Skip to content

Added confirmation dialog to vehicle/wagon deletion, but only for vehicles that currently have cargo aboard#3323

Merged
LeftofZen merged 19 commits intoOpenLoco:masterfrom
luciditee:cargo-delete-confirmation
Oct 14, 2025
Merged

Added confirmation dialog to vehicle/wagon deletion, but only for vehicles that currently have cargo aboard#3323
LeftofZen merged 19 commits intoOpenLoco:masterfrom
luciditee:cargo-delete-confirmation

Conversation

@luciditee
Copy link
Copy Markdown
Contributor

@luciditee luciditee commented Oct 12, 2025

A few nights ago I lost a particularly grueling scenario playthrough due to not realizing that deleting a wagon off a train actually clears all cargo values from the vehicle. This inspired me to add a confirmation dialog to the specific case of deleting part of a vehicle that has cargo already loaded onboard.

  • It would be trivial to get cargo values to persist before and after the delete operation, but this is not how vanilla Locomotion behaves and I wasn't sure how the team would feel about altering non-bug behavior in vanilla, so I opted for the confirm dialog.
  • There is not currently a way to directly pass state from a GameCommand to the calling context. It would theoretically be possible to do via GameState, but I didn't feel comfortable modifying the GameState struct since it appears that the size of the GameState struct matters enough for there to be an assertion about it. For this reason I added a new struct--TempState--which can be used for ephemeral values that should not replicate over the network or survive a save/load operation.
    • This allows us to pass state out of a GameCommand context. It can be removed or replaced with something better later.
    • It currently automatically clears itself anytime a scenario or savegame is loaded.
  • In the vehicle UI, click and drag a wagon on a train that has cargo aboard to the delete button.
    • If the train has cargo aboard, you will see a confirmation dialog asking you to confirm the operation. If you accept, the wagon will be deleted and you will be refunded for its value. If you decline, nothing will happen.
    • If the train has no cargo aboard, you will not be given a confirmation dialog and the wagon will be deleted uneventfully.
  • Deleting the entire train by clicking the delete icon without clicking-and-dragging a vehicle to it will also delete the vehicle without confirmation, mirroring the behavior of vanilla.
  • This procedure only occurs for vehicles belonging to the local player, e.g. owner == CompanyManager::getControllingId().
  • A utility function, hasAnyCargo(), was added to VehicleHead to simplify the predicate in VehicleSell.cpp.
  • String constants in the headers + the strings in en-GB.yml were updated to add the confirmation text.

I've attached a scenario containing a test case set up so that you can try deleting a wagon from the train, which has cargo aboard, and notice the confirmation dialog. Once you delete a wagon, the cargo values are reset to 0 and if you attempt to delete another wagon, it will do so without confirming because the train has no cargo aboard. I have attached it to this PR (zipped because GitHub doesn't understand the file format): SellConfirmTest.zip

I also uploaded a video to the OpenLoco Discord showing this feature in action (this was too big for me to embed here on GitHub): https://cdn.discordapp.com/attachments/689445672394555507/1426766722118979644/WagonDeleteTest_1.mp4?ex=68ec6b8e&is=68eb1a0e&hm=bc59c97378e5c3c62877e7203310998953bd300dc3617e0228025750c620b5f9& (if you are uncomfortable clicking the link, you can find it in #openloco-talk on the Discord).

EDIT: Omitted a word by mistake.

- If the user attempts to delete a wagon from a train which has cargo
  aboard in any wagon, ordinarily this would delete all cargo from the
  train due to how the vehicle is removed and replaced from the game
  board
- With this commit, players are prompted to confirm their intention to
  delete the wagon by informing them the action will clear ALL cargo
  from the train, not just what's aboard the wagon being deleted
- If no cargo exists on the train, no confirmation dialog is given, and
  the behavior is as before
- If the entire train is deleted, it behaves as before with no
  confirmation given
- ONLY deleting a single wagon from a train which has cargo somewhere
  onboard generates a dialog
- To implement this, a global, temporary state-tracking struct was added to
  coexist alongside GameState and is cleared on map/scenario load. This
  allows for passing state out of GameCommands and can eventually be
  replaced with something better when the GameCommand system is
  overhauled
- Small utility functions (Vehicle::hasAnyCargo()) added for
  implementation
- Strings in en-GB.yml and header files updated to reflect changes.
Copy link
Copy Markdown
Member

@AaronVanGeffen AaronVanGeffen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a nice QoL feature, but the implementation needs a bit of work. To be honest, I think it's more complex than it needs to be.

You've added a nice helper function, VehicleHead::hasAnyCargo. I think this can and should be called directly from the Vehicle window, before even invoking any game command. Only after the confirmation prompt has been shown (or indeed wasn't necessary) should the VehicleSell command be invoked.

By leaving this all in the UI, there is no need for modifications to any game commands, nor any need for the TempState you've introduced.

Does this make sense?

- removed TempState implementation
- reverted changes to VehicleSell command
- migrated check entirely to Ui/Vehicle.cpp
- extracted new functionality to separate method (checkDeletion) to
  declutter the scrollDragEnd switch block
@luciditee
Copy link
Copy Markdown
Contributor Author

By leaving this all in the UI, there is no need for modifications to any game commands, nor any need for the TempState you've introduced.

Does this make sense?

Loud and clear--and you're right, it's a lot simpler to just do it in the UI. This actually is how it started, but I was hopelessly distracted by the lack of ability to read a return code from the GameCommand--it didn't occur to me that it would be seen as acceptable to do all the casting and checking for nullptr, etc. within the UI itself--and the result is what you see in the first commit on this branch.

Regardless of any excuse on my part, I've simplified it down to be concentrated in VehicleSell.cpp, and kept the hasAnyCargo() function intact. It now functions identically to before, without the need for clunky state machines.

Copy link
Copy Markdown
Contributor Author

@luciditee luciditee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed; see most recent comment.

@AaronVanGeffen AaronVanGeffen force-pushed the cargo-delete-confirmation branch from 9e4587a to 24dfd68 Compare October 13, 2025 10:44
@AaronVanGeffen AaronVanGeffen added this to the v25.10+ milestone Oct 13, 2025
Copy link
Copy Markdown
Contributor

@LeftofZen LeftofZen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The naming of some of the strings and methods is off. You're selling a vehicle component, not deleting a vehicle.
  • The problem still exists for vehicles with reconfigurable cargo - you can change the cargo configuration and it deletes the cargo with no warning. I would probably add that check in this PR too.

@AaronVanGeffen
Copy link
Copy Markdown
Member

The naming of some of the strings and methods is off. You're selling a vehicle component, not deleting a vehicle.

Hmm, you make a good point, but at the same time... it is a trash can you're dragging the component to. I can see the confusion. 'Selling' is probably better, though, yeah.

The problem still exists for vehicles with reconfigurable cargo - you can change the cargo configuration and it deletes the cargo with no warning. I would probably add that check in this PR too.

That's a different check that would require a different prompt, right? Agreed it would be good to add it there, too, though.

@LeftofZen
Copy link
Copy Markdown
Contributor

That's a different check that would require a different prompt, right? Agreed it would be good to add it there, too, though.

Good point, you'd need to add some extra strings for the message in that context. I still think it can be done in this PR though as extra strings + the code change for the additional check should be minimal

luciditee and others added 4 commits October 13, 2025 11:40
Co-authored-by: Benjamin Sutas <bigbadbennybrother@gmail.com>
Co-authored-by: Benjamin Sutas <bigbadbennybrother@gmail.com>
- Users now receive a prompt when asked to refit a vehicle in
  the same fashion as when they alter a component
- Updated string handles in StringIds.h and added necessary strings
  into en-GB.yml
@luciditee
Copy link
Copy Markdown
Contributor Author

Had a little trouble there with clang-format showing whitespace that the IDE wasn't showing, but it should be good to go now. Players now get a warning when refitting a vehicle that has cargo already aboard.

Any other recommendations?

OpenLoco_RxYXFDQcGq

Co-authored-by: Benjamin Sutas <bigbadbennybrother@gmail.com>
@luciditee luciditee requested a review from LeftofZen October 13, 2025 22:39
@LeftofZen LeftofZen merged commit 052761d into OpenLoco:master Oct 14, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants