Author: sveinung
Date: Thu Dec  3 12:43:14 2015
New Revision: 30852

URL: http://svn.gna.org/viewcvs/freeciv?rev=30852&view=rev
Log:
Fix unit removal collateral damage negative gold

A player's gold is supposed to be positive during the turn. The gold upkeep
payment starts with paying the unit gold upkeep. If the gold becomes
negative a list of units with gold upkeep is generated. A unit on the list
is removed ("sold") and its gold upkeep returned until the amount of gold
becomes acceptable ("balanced").

An action auto performer rule caused by missing upkeep can kill other units
as collateral damage. (Example: "Explode Nuclear") A unit killed as
collateral damage can be on the list of units that are missing upkeep.

Pay back the gold upkeep of collateral damage units the game considers to
sell to balance the player's gold. This avoids the collateral damage
causing negative gold.

Store the payed gold upkeep for a unit so the correct gold upkeep is payed
back. This avoids a reduction of the current amount of upkeep causing
negative gold.

See bug #24132

Modified:
    trunk/common/unit.c
    trunk/common/unit.h
    trunk/server/cityturn.c

Modified: trunk/common/unit.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/common/unit.c?rev=30852&r1=30851&r2=30852&view=diff
==============================================================================
--- trunk/common/unit.c (original)
+++ trunk/common/unit.c Thu Dec  3 12:43:14 2015
@@ -1758,6 +1758,9 @@
     punit->server.dying = FALSE;
 
     punit->server.removal_callback = NULL;
+
+    memset(punit->server.upkeep_payed, 0,
+           O_LAST * sizeof(*punit->server.upkeep_payed));
 
     punit->server.ord_map = 0;
     punit->server.ord_city = 0;

Modified: trunk/common/unit.h
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/common/unit.h?rev=30852&r1=30851&r2=30852&view=diff
==============================================================================
--- trunk/common/unit.h (original)
+++ trunk/common/unit.h Thu Dec  3 12:43:14 2015
@@ -215,6 +215,9 @@
 
       /* Call back to run on unit removal. */
       void (*removal_callback)(struct unit *punit);
+
+      /* The upkeep that actually was payed. */
+      int upkeep_payed[O_LAST];
     } server;
   };
 };

Modified: trunk/server/cityturn.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/server/cityturn.c?rev=30852&r1=30851&r2=30852&view=diff
==============================================================================
--- trunk/server/cityturn.c     (original)
+++ trunk/server/cityturn.c     Thu Dec  3 12:43:14 2015
@@ -2492,8 +2492,19 @@
 **************************************************************************/
 static void uk_rem_gold_callback(struct unit *punit)
 {
+  int gold_upkeep;
+
   /* Remove the unit from uk_rem_gold. */
   unit_list_remove(uk_rem_gold, punit);
+
+  gold_upkeep = punit->server.upkeep_payed[O_GOLD];
+
+  /* All units in uk_rem_gold should have gold upkeep! */
+  fc_assert_ret_msg(gold_upkeep > 0, "%s has %d gold upkeep",
+                    unit_rule_name(punit), gold_upkeep);
+
+  /* Get the upkeep gold back. */
+  unit_owner(punit)->economic.gold += gold_upkeep;
 }
 
 /**************************************************************************
@@ -2538,7 +2549,7 @@
                                      struct unit_list *punitlist)
 {
   struct unit *punit;
-  int gold_upkeep, r;
+  int r;
   struct unit_list *cargo;
 
   fc_assert_ret_val(pplayer != NULL, FALSE);
@@ -2575,11 +2586,6 @@
 
   unit_list_destroy(cargo);
 
-  gold_upkeep = punit->upkeep[O_GOLD];
-
-  /* All units in punitlist should have gold upkeep! */
-  fc_assert_ret_val(gold_upkeep > 0, NULL);
-
   {
     const char *punit_link = unit_tile_link(punit);
     const char *punit_logname = unit_name_translation(punit);
@@ -2588,15 +2594,15 @@
     if (upkeep_kill_unit(punit, O_GOLD, ULR_SOLD,
                          game.info.muuk_gold_wipe)) {
       unit_list_remove(punitlist, punit);
+
+      /* The gold was payed back when the unit removal made
+       * uk_rem_gold_callback() run as the unit's removal call back. */
 
       notify_player(pplayer, utile, E_UNIT_LOST_MISC, ftc_server,
                     _("Not enough gold. %s disbanded."),
                     punit_link);
       log_debug("%s: unit sold (%s)", player_name(pplayer),
                 punit_logname);
-
-      /* Get the upkeep gold back. */
-      pplayer->economic.gold += gold_upkeep;
     } else {
       /* Not able to get rid of punit */
       return NULL;
@@ -3047,6 +3053,11 @@
     pplayer->economic.gold += pcity->prod[O_GOLD];
     pplayer->economic.gold -= city_total_impr_gold_upkeep(pcity);
     pplayer->economic.gold -= city_total_unit_gold_upkeep(pcity);
+
+    /* Remember how much gold upkeep each unit was payed. */
+    unit_list_iterate(pcity->units_supported, punit) {
+      punit->server.upkeep_payed[O_GOLD] = punit->upkeep[O_GOLD];
+    } unit_list_iterate_end;
 
     if (pplayer->economic.gold < 0) {
       /* Not enough gold - we have to sell some buildings, and if that


_______________________________________________
Freeciv-commits mailing list
Freeciv-commits@gna.org
https://mail.gna.org/listinfo/freeciv-commits

Reply via email to