From 6eda745f7ebb52227dc4e1f3a501786409e57984 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sat, 26 Nov 2022 15:40:52 +0200 Subject: [PATCH 41/41] lua: Check legality of unit_move() and unit_teleport() Reported by ihnatus See osdn #44769 Signed-off-by: Marko Lindqvist --- common/movement.c | 98 +++++++++++++++++++++++++++++- common/movement.h | 9 +++ server/scripting/api_server_edit.c | 16 ++++- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/common/movement.c b/common/movement.c index 8a00f0b881..e8abdaac3c 100644 --- a/common/movement.c +++ b/common/movement.c @@ -578,7 +578,7 @@ bool unit_can_move_to_tile(const struct civ_map *nmap, /************************************************************************//** Returns whether the unit can move from its current tile to the - destination tile. An enumerated value is returned indication the error + destination tile. An enumerated value is returned indication the error or success status. The unit can move if: @@ -726,6 +726,102 @@ unit_move_to_tile_test(const struct civ_map *nmap, return MR_OK; } +/************************************************************************//** + Returns whether the unit can move from its current tile to the + destination tile. An enumerated value is returned indication the error + or success status. + + The unit can teleport if: + 1) There are no non-allied units on the target tile. + 2) Animals cannot move out from home terrains + 3) Unit can move to a tile where it can't survive on its own if there + is free transport capacity. + 4) There are no peaceful but non-allied units on the target tile. + 5) There is not a non-allied city on the target tile when + enter_enemy_city is false. When enter_enemy_city is true a non + peaceful city is also accepted. + 6) Triremes cannot move out of sight from land. + 7) It is not the territory of a player we are at peace with. +****************************************************************************/ +enum unit_move_result +unit_teleport_to_tile_test(const struct civ_map *nmap, + const struct unit *punit, + enum unit_activity activity, + const struct tile *src_tile, + const struct tile *dst_tile, bool igzoc, + bool enter_transport, struct unit *embark_to, + bool enter_enemy_city) +{ + struct city *pcity; + const struct unit_type *punittype = unit_type_get(punit); + const struct player *puowner = unit_owner(punit); + + /* 1) */ + if (is_non_allied_unit_tile(dst_tile, puowner)) { + /* You can't move onto a tile with non-allied units on it (try + * attacking instead). */ + return MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_UNIT; + } + + /* 2) */ + if (puowner->ai_common.barbarian_type == ANIMAL_BARBARIAN + && dst_tile->terrain->animal != punittype) { + return MR_ANIMAL_DISALLOWED; + } + + /* 3) */ + if (embark_to != NULL) { + if (!could_unit_load(punit, embark_to)) { + return MR_NO_TRANSPORTER_CAPACITY; + } + } else if (!(can_exist_at_tile(nmap, punittype, dst_tile) + || (enter_transport + && unit_could_load_at(punit, dst_tile)))) { + return MR_NO_TRANSPORTER_CAPACITY; + } + + /* 4) */ + if (is_non_attack_unit_tile(dst_tile, puowner)) { + /* You can't move into a non-allied tile. + * + * FIXME: this should never happen since it should be caught by check + * #1. */ + return MR_NO_WAR; + } + + /* 5) */ + if ((pcity = tile_city(dst_tile))) { + if (enter_enemy_city) { + if (pplayers_non_attack(city_owner(pcity), puowner)) { + /* You can't move into an empty city of a civilization you're at + * peace with - you must first either declare war or make an + * alliance. */ + return MR_NO_WAR; + } + } else { + if (!pplayers_allied(city_owner(pcity), puowner)) { + /* You can't move into an empty city of a civilization you're not + * allied to. Assume that the player tried to attack. */ + return MR_NO_WAR; + } + } + } + + /* 6) */ + if (utype_has_flag(punittype, UTYF_COAST_STRICT) + && !pcity && !is_safe_ocean(&(wld.map), dst_tile)) { + return MR_TRIREME; + } + + /* 7) */ + if (!utype_has_flag(punittype, UTYF_CIVILIAN) + && !player_can_invade_tile(puowner, dst_tile)) { + return MR_PEACE; + } + + return MR_OK; +} + /************************************************************************//** Return true iff transporter has ability to transport transported. ****************************************************************************/ diff --git a/common/movement.h b/common/movement.h index 3daf8e62ce..6aae1bafe3 100644 --- a/common/movement.h +++ b/common/movement.h @@ -121,6 +121,15 @@ unit_move_to_tile_test(const struct civ_map *nmap, bool igzoc, bool enter_transport, struct unit *embark_to, bool enter_enemy_city); +enum unit_move_result +unit_teleport_to_tile_test(const struct civ_map *nmap, + const struct unit *punit, + enum unit_activity activity, + const struct tile *src_tile, + const struct tile *dst_tile, + bool igzoc, + bool enter_transport, struct unit *embark_to, + bool enter_enemy_city); bool can_unit_transport(const struct unit *transporter, const struct unit *transported); bool can_unit_type_transport(const struct unit_type *transporter, const struct unit_class *transported); diff --git a/server/scripting/api_server_edit.c b/server/scripting/api_server_edit.c index 5993e675c4..49dcff5a6f 100644 --- a/server/scripting/api_server_edit.c +++ b/server/scripting/api_server_edit.c @@ -209,7 +209,6 @@ bool api_edit_unit_teleport(lua_State *L, Unit *punit, Tile *dest, bool enter_hut, bool frighten_hut) { bool alive; - struct city *pcity; LUASCRIPT_CHECK_STATE(L, FALSE); LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit, FALSE); @@ -224,7 +223,12 @@ bool api_edit_unit_teleport(lua_State *L, Unit *punit, Tile *dest, return TRUE; } - pcity = tile_city(dest); + if (unit_teleport_to_tile_test(&(wld.map), punit, ACTIVITY_IDLE, + unit_tile(punit), dest, TRUE, + FALSE, embark_to, TRUE) != MR_OK) { + /* Can't teleport to target. Return that unit is still alive. */ + return TRUE; + } /* Teleport first so destination is revealed even if unit dies */ alive = unit_move(punit, dest, 0, @@ -233,6 +237,7 @@ bool api_edit_unit_teleport(lua_State *L, Unit *punit, Tile *dest, enter_hut, frighten_hut); if (alive) { struct player *owner = unit_owner(punit); + struct city *pcity = tile_city(dest); if (!can_unit_exist_at_tile(&(wld.map), punit, dest) && !unit_transported(punit)) { @@ -1032,6 +1037,13 @@ bool api_edit_unit_move(lua_State *L, Unit *punit, Tile *ptile, return TRUE; } + if (unit_move_to_tile_test(&(wld.map), punit, ACTIVITY_IDLE, + unit_tile(punit), ptile, TRUE, + FALSE, embark_to, TRUE) != MR_OK) { + /* Can't move to target. Return that unit is still alive. */ + return TRUE; + } + return unit_move(punit, ptile, movecost, embark_to, embark_to != NULL, conquer_city, conquer_extra, -- 2.35.1