From 2959673a01aaa8c01d59f80818efb1885c9d43b2 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Tue, 11 Jul 2023 05:15:02 +0300 Subject: [PATCH 28/28] Bounce cargo when transport lost due to terrain change If neither transport nor cargo can remain on the changed terraain, and transport itself cannot even bounce, try to bounce cargo itself. Reported by Alexandro Ignatiev See osdn #46277 Signed-off-by: Marko Lindqvist --- server/maphand.c | 88 ++++++++++++++++++++++++++++++---------------- server/unittools.c | 15 +++++--- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/server/maphand.c b/server/maphand.c index e0984bb500..c0ccb9f515 100644 --- a/server/maphand.c +++ b/server/maphand.c @@ -1790,6 +1790,57 @@ static void ocean_to_land_fix_rivers(struct tile *ptile) } cardinal_adjc_iterate_end; } +/**********************************************************************//** + Bounce one unit from tile on terrain change. +**************************************************************************/ +static void terrain_change_bounce_single_unit(struct unit *punit, + struct tile *from) +{ + bool unit_alive = TRUE; + + /* Look for a nearby safe tile */ + adjc_iterate(&(wld.map), from, ptile2) { + if (can_unit_exist_at_tile(&(wld.map), punit, ptile2) + && !is_non_allied_unit_tile(ptile2, unit_owner(punit)) + && !is_non_allied_city_tile(ptile2, unit_owner(punit))) { + log_verbose("Moved %s %s due to changing terrain at (%d,%d).", + nation_rule_name(nation_of_unit(punit)), + unit_rule_name(punit), TILE_XY(unit_tile(punit))); + notify_player(unit_owner(punit), unit_tile(punit), + E_UNIT_RELOCATED, ftc_server, + _("Moved your %s due to changing terrain."), + unit_link(punit)); + + /* TODO: should a unit be able to bounce to a transport like is + * done below? What if the unit can't legally enter the transport, + * say because the transport is Unreachable and the unit doesn't + * have it in its embarks field or because "Transport Embark" + * isn't enabled? Kept like it was to preserve the old rules for + * now. -- Sveinung */ + unit_alive = unit_move(punit, ptile2, 0, + NULL, TRUE, FALSE, FALSE, FALSE, FALSE); + if (unit_alive && punit->activity == ACTIVITY_SENTRY) { + unit_activity_handling(punit, ACTIVITY_IDLE); + } + break; + } + } adjc_iterate_end; + + if (unit_alive && unit_tile(punit) == from) { + /* If we get here we could not move punit. */ + + /* Try to bounce transported units. */ + if (0 < get_transporter_occupancy(punit)) { + struct unit_list *pcargo_units; + + pcargo_units = unit_transport_cargo(punit); + unit_list_iterate_safe(pcargo_units, pcargo) { + terrain_change_bounce_single_unit(pcargo, from); + } unit_list_iterate_safe_end; + } + } +} + /**********************************************************************//** Helper function for bounce_units_on_terrain_change() that checks units on a single tile. @@ -1797,39 +1848,14 @@ static void ocean_to_land_fix_rivers(struct tile *ptile) static void check_units_single_tile(struct tile *ptile) { unit_list_iterate_safe(ptile->units, punit) { - bool unit_alive = TRUE; + int id = punit->id; - if (unit_tile(punit) == ptile - && !unit_transported(punit) + /* Top-level transports only. Each handle their own cargo */ + if (!unit_transported(punit) && !can_unit_exist_at_tile(&(wld.map), punit, ptile)) { - /* look for a nearby safe tile */ - adjc_iterate(&(wld.map), ptile, ptile2) { - if (can_unit_exist_at_tile(&(wld.map), punit, ptile2) - && !is_non_allied_unit_tile(ptile2, unit_owner(punit)) - && !is_non_allied_city_tile(ptile2, unit_owner(punit))) { - log_verbose("Moved %s %s due to changing terrain at (%d,%d).", - nation_rule_name(nation_of_unit(punit)), - unit_rule_name(punit), TILE_XY(unit_tile(punit))); - notify_player(unit_owner(punit), unit_tile(punit), - E_UNIT_RELOCATED, ftc_server, - _("Moved your %s due to changing terrain."), - unit_link(punit)); - /* TODO: should a unit be able to bounce to a transport like is - * done below? What if the unit can't legally enter the transport, - * say because the transport is Unreachable and the unit doesn't - * have it in its embarks field or because "Transport Embark" - * isn't enabled? Kept like it was to preserve the old rules for - * now. -- Sveinung */ - unit_alive = unit_move(punit, ptile2, 0, - NULL, TRUE, FALSE, FALSE, FALSE, FALSE); - if (unit_alive && punit->activity == ACTIVITY_SENTRY) { - unit_activity_handling(punit, ACTIVITY_IDLE); - } - break; - } - } adjc_iterate_end; - if (unit_alive && unit_tile(punit) == ptile) { - /* If we get here we could not move punit. */ + terrain_change_bounce_single_unit(punit, ptile); + + if (unit_is_alive(id) && unit_tile(punit) == ptile) { log_verbose("Disbanded %s %s due to changing land " " to sea at (%d, %d).", nation_rule_name(nation_of_unit(punit)), diff --git a/server/unittools.c b/server/unittools.c index a43153735c..4dbcb39b5e 100644 --- a/server/unittools.c +++ b/server/unittools.c @@ -1245,7 +1245,6 @@ void bounce_unit(struct unit *punit, bool verbose) { struct player *pplayer; struct tile *punit_tile; - struct unit_list *pcargo_units; int count = 0; /* I assume that there are no topologies that have more than @@ -1285,6 +1284,7 @@ void bounce_unit(struct unit *punit, bool verbose) _("Moved your %s."), unit_link(punit)); } + /* TODO: should a unit be able to bounce to a transport like is done * below? What if the unit can't legally enter the transport, say * because the transport is Unreachable and the unit doesn't have it in @@ -1297,6 +1297,8 @@ void bounce_unit(struct unit *punit, bool verbose) /* Didn't find a place to bounce the unit, going to disband it. * Try to bounce transported units. */ if (0 < get_transporter_occupancy(punit)) { + struct unit_list *pcargo_units; + pcargo_units = unit_transport_cargo(punit); unit_list_iterate(pcargo_units, pcargo) { bounce_unit(pcargo, verbose); @@ -1961,6 +1963,7 @@ static void wipe_unit_full(struct unit *punit, bool transported, struct unit_list *unsaved = unit_list_new(); struct unit *ptrans = unit_transport_get(punit); struct city *pexclcity; + struct civ_map *nmap = &(wld.map); if (killer != NULL && (game.info.gameloss_style & GAMELOSS_STYLE_LOOT) @@ -1997,16 +2000,16 @@ static void wipe_unit_full(struct unit *punit, bool transported, if (!can_unit_unload(pcargo, punit)) { unit_list_prepend(helpless, pcargo); } else { - if (!can_unit_exist_at_tile(&(wld.map), pcargo, ptile)) { + if (!can_unit_exist_at_tile(nmap, pcargo, ptile)) { unit_list_prepend(imperiled, pcargo); } else { - /* These units do not need to be saved. */ + /* These units do not need to be saved. */ healthy = TRUE; } } - /* Could use unit_transport_unload_send here, but that would - * call send_unit_info for the transporter unnecessarily. + /* Could use unit_transport_unload_send() here, but that would + * call send_unit_info() for the transporter unnecessarily. * Note that this means that unit might to get seen briefly * by clients other than owner's, for example as a result of * update of homecity common to this cargo and some other @@ -2193,6 +2196,7 @@ static bool try_to_save_unit(struct unit *punit, const struct unit_type *pttype, } } } + /* The unit could not use transport on the tile, and could not teleport. */ return FALSE; } @@ -2260,6 +2264,7 @@ struct unit *unit_change_owner(struct unit *punit, struct player *pplayer, /* Destroyed by a script */ return NULL; } + return gained_unit; /* Returns the replacement. */ } -- 2.40.1