Author: sveinung Date: Mon Aug 10 16:06:25 2015 New Revision: 29437 URL: http://svn.gna.org/viewcvs/freeciv?rev=29437&view=rev Log: Don't break unique unit rules via unit transfer
See bug #23758 Modified: branches/S2_5/common/unittype.c branches/S2_5/common/unittype.h branches/S2_5/server/citytools.c branches/S2_5/server/diplomats.c branches/S2_5/server/unithand.c branches/S2_5/server/unittools.c Modified: branches/S2_5/common/unittype.c URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/common/unittype.c?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/common/unittype.c (original) +++ branches/S2_5/common/unittype.c Mon Aug 10 16:06:25 2015 @@ -620,6 +620,31 @@ } /************************************************************************** + Returns TRUE iff the unit type is unique and the player already has one. +**************************************************************************/ +bool utype_player_already_has_this_unique(const struct player *pplayer, + const struct unit_type *putype) +{ + if (!utype_has_flag(putype, UTYF_UNIQUE)) { + /* This isn't a unique unit type. */ + return FALSE; + } + + unit_list_iterate(pplayer->units, existing_unit) { + if (putype == unit_type(existing_unit)) { + /* FIXME: This could be slow if we have lots of units. We could + * consider keeping an array of unittypes updated with this info + * instead. */ + + return TRUE; + } + } unit_list_iterate_end; + + /* The player doesn't already have one. */ + return FALSE; +} + +/************************************************************************** Whether player can build given unit somewhere, ignoring whether unit is obsolete and assuming the player has a coastal city. @@ -682,17 +707,11 @@ return FALSE; } } - - } - if (utype_has_flag(punittype, UTYF_UNIQUE)) { - /* FIXME: This could be slow if we have lots of units. We could - * consider keeping an array of unittypes updated with this info - * instead. */ - unit_list_iterate(p->units, punit) { - if (unit_type(punit) == punittype) { - return FALSE; - } - } unit_list_iterate_end; + } + + if (utype_player_already_has_this_unique(p, punittype)) { + /* A player can only have one unit of each unique unit type. */ + return FALSE; } /* If the unit has a building requirement, we check to see if the player Modified: branches/S2_5/common/unittype.h URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/common/unittype.h?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/common/unittype.h (original) +++ branches/S2_5/common/unittype.h Mon Aug 10 16:06:25 2015 @@ -378,6 +378,8 @@ TYPED_LIST_ITERATE(struct combat_bonus, bonuslist, pbonus) #define combat_bonus_list_iterate_end LIST_ITERATE_END +BV_DEFINE(bv_unit_types, U_LAST); + struct veteran_level { struct name_translation name; /* level/rank name */ int power_fact; /* combat/work speed/diplomatic power factor (in %) */ @@ -584,6 +586,9 @@ const struct unit_type *from, const struct unit_type *to); +bool utype_player_already_has_this_unique(const struct player *pplayer, + const struct unit_type *putype); + bool can_player_build_unit_direct(const struct player *p, const struct unit_type *punittype); bool can_player_build_unit_later(const struct player *p, Modified: branches/S2_5/server/citytools.c URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/server/citytools.c?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/server/citytools.c (original) +++ branches/S2_5/server/citytools.c Mon Aug 10 16:06:25 2015 @@ -598,6 +598,24 @@ } else { struct tile *utile = unit_tile(punit); struct city *in_city = tile_city(utile); + + if (utype_player_already_has_this_unique(to_player, + unit_type(punit))) { + /* This is a unique unit that to_player already has. A transfer would + * break the rule that a player only may have one unit of each unique + * unit type. */ + + log_debug("%s already have a %s. Can't transfer from %s", + nation_rule_name(nation_of_player(to_player)), + unit_rule_name(punit), + nation_rule_name(nation_of_player(from_player))); + + /* TODO: What should be done when the unit is a game loss unit? Maybe + * it should be bounced rather than killed? */ + wipe_unit(punit, ULR_CITY_LOST, NULL); + + return; + } if (in_city) { log_verbose("Transferred %s in %s from %s to %s", Modified: branches/S2_5/server/diplomats.c URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/server/diplomats.c?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/server/diplomats.c (original) +++ branches/S2_5/server/diplomats.c Mon Aug 10 16:06:25 2015 @@ -432,6 +432,15 @@ uplayer = unit_owner(pvictim); /* We might make it allowable in peace with a loss of reputation */ if (!uplayer || pplayers_allied(pplayer, uplayer)) { + return; + } + + /* Sanity check: The victim isn't a unique unit the actor player already + * has. */ + if (utype_player_already_has_this_unique(pplayer, + unit_type(pvictim))) { + log_debug("bribe-unit: already got unique unit"); + return; } Modified: branches/S2_5/server/unithand.c URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/server/unithand.c?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/server/unithand.c (original) +++ branches/S2_5/server/unithand.c Mon Aug 10 16:06:25 2015 @@ -366,6 +366,9 @@ if (old_owner != new_owner) { struct city *pcity = tile_city(punit->tile); + + fc_assert(!utype_player_already_has_this_unique(new_owner, + unit_type(punit))); vision_clear_sight(punit->server.vision); vision_free(punit->server.vision); @@ -1475,10 +1478,44 @@ if (unit_has_type_flag(punit, UTYF_CAPTURER) && pcity == NULL) { bool capture_possible = TRUE; - + bv_unit_types unique_on_tile; + + BV_CLR_ALL(unique_on_tile); unit_list_iterate(pdesttile->units, to_capture) { if (!unit_has_type_flag(to_capture, UTYF_CAPTURABLE)) { capture_possible = FALSE; + break; + } + + /* Sanity check: make sure that the capture won't result in the actor + * ending up with more than one unit of each unique unit type. */ + + /* Check what the player already has. */ + if (utype_player_already_has_this_unique(pplayer, + unit_type(to_capture))) { + /* The player already has a unit of this kind. */ + capture_possible = FALSE; + } + + if (utype_has_flag(unit_type(to_capture), UTYF_UNIQUE)) { + /* The type of the units at the tile must also be checked. Two allied + * players can both have their unique unit at the same tile. + * Capturing them both would give the actor two units of a kind that + * is supposed to be unique. */ + + if (BV_ISSET(unique_on_tile, utype_index(unit_type(to_capture)))) { + /* There is another unit of the same kind at this tile. */ + capture_possible = FALSE; + } else { + /* Remember the unit type in case another unit of the same kind is + * encountered later. */ + BV_SET(unique_on_tile, utype_index(unit_type(to_capture))); + } + } + + if (!capture_possible) { + log_debug("capture units: already got unique unit"); + break; } } unit_list_iterate_end; Modified: branches/S2_5/server/unittools.c URL: http://svn.gna.org/viewcvs/freeciv/branches/S2_5/server/unittools.c?rev=29437&r1=29436&r2=29437&view=diff ============================================================================== --- branches/S2_5/server/unittools.c (original) +++ branches/S2_5/server/unittools.c Mon Aug 10 16:06:25 2015 @@ -1962,6 +1962,9 @@ int homecity, enum unit_loss_reason reason) { struct unit *gained_unit; + + fc_assert(!utype_player_already_has_this_unique(pplayer, + unit_type(punit))); /* Convert the unit to your cause. Fog is lifted in the create algorithm. */ gained_unit = create_unit_full(pplayer, unit_tile(punit), _______________________________________________ Freeciv-commits mailing list Freeciv-commits@gna.org https://mail.gna.org/listinfo/freeciv-commits