From feb953162c457ad1b029c7fb9d977dabfc12b30e Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Tue, 11 Jul 2023 05:38:16 +0300 Subject: [PATCH 16/16] 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 | 73 +++++++++++++++++++++++++++++++--------------- server/unittools.c | 11 ++++--- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/server/maphand.c b/server/maphand.c index 5616d29b21..fdf14d5b8d 100644 --- a/server/maphand.c +++ b/server/maphand.c @@ -1723,6 +1723,50 @@ 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(from, ptile2) { + if (can_unit_exist_at_tile(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)); + + unit_alive = unit_move(punit, ptile2, 0, NULL, 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. @@ -1730,31 +1774,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(punit, ptile)) { - /* look for a nearby safe tile */ - adjc_iterate(ptile, ptile2) { - if (can_unit_exist_at_tile(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)); - unit_alive = unit_move(punit, ptile2, 0, NULL, 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) { + terrain_change_bounce_single_unit(punit, ptile); + + if (unit_is_alive(id) && unit_tile(punit) == ptile) { /* If we get here we could not move punit. */ log_verbose("Disbanded %s %s due to changing land " " to sea at (%d, %d).", diff --git a/server/unittools.c b/server/unittools.c index 9331edb32d..1c9770680e 100644 --- a/server/unittools.c +++ b/server/unittools.c @@ -1205,7 +1205,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 @@ -1252,6 +1251,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); @@ -1984,13 +1985,13 @@ static void wipe_unit_full(struct unit *punit, bool transported, if (!can_unit_exist_at_tile(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 @@ -2175,6 +2176,7 @@ static bool try_to_save_unit(struct unit *punit, struct unit_type *pttype, } } } + /* The unit could not use transport on the tile, and could not teleport. */ return FALSE; } @@ -2230,6 +2232,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