<URL: http://bugs.freeciv.org/Ticket/Display.html?id=34717 >

2007/9/4 Marko Lindqvist:
> On 28/01/07, Daniel Markstedt wrote:
>>
>> On 1/28/07, Per Inge Mathisen wrote:
>> > >
>> > >> > ~Event upon the death of a unique unit.
>> > >> > ~Event upon the conquest of a specific city.
>
>  This alpha level patch implements two city events:
>  "city_lost"           - city has been transferred from player to player
>  "city_destroyed" - city has been destroyed

 Updated against svn.


 - ML

diff -Nurd -X.diff_ignore freeciv/server/citytools.c freeciv/server/citytools.c
--- freeciv/server/citytools.c  2008-06-18 17:45:27.000000000 +0300
+++ freeciv/server/citytools.c  2008-06-22 02:27:42.000000000 +0300
@@ -1323,6 +1323,8 @@
    * the city will be destroyed.
    */
   if (pcity->size <= 1) {
+    int saved_id = pcity->id;
+
     notify_player(pplayer, pcity->tile, E_UNIT_WIN_ATT,
                  _("You destroy %s completely."),
                  city_name(pcity));
@@ -1330,7 +1332,16 @@
                     _("%s has been destroyed by %s."), 
                     city_name(pcity),
                     player_name(pplayer));
-    remove_city(pcity);
+    script_signal_emit("city_destroyed", 3,
+                       API_TYPE_CITY, pcity,
+                       API_TYPE_PLAYER, cplayer,
+                       API_TYPE_PLAYER, pplayer);
+
+    /* We cant't be sure of city existence after running some script */
+    if (city_exist(saved_id)) {
+      remove_city(pcity);
+    }
+
     if (do_civil_war) {
       civil_war(cplayer);
     }
@@ -1417,12 +1428,18 @@
     }
   } players_iterate_end;
 
-  city_reduce_size(pcity, 1);
+  assert(pcity->size > 1); /* reduce size should not destroy this city */
+  city_reduce_size(pcity, 1, pplayer);
   send_player_info(pplayer, pplayer); /* Update techs */
 
   if (do_civil_war) {
     civil_war(cplayer);
   }
+
+  script_signal_emit("city_lost", 3,
+                     API_TYPE_CITY, pcity,
+                     API_TYPE_PLAYER, cplayer,
+                     API_TYPE_PLAYER, pplayer);
 }
 
 /**************************************************************************
diff -Nurd -X.diff_ignore freeciv/server/cityturn.c freeciv/server/cityturn.c
--- freeciv/server/cityturn.c   2008-05-13 13:26:10.000000000 +0300
+++ freeciv/server/cityturn.c   2008-06-22 02:05:33.000000000 +0300
@@ -479,7 +479,8 @@
   Reduce the city size.  Return TRUE if the city survives the population
   loss.
 **************************************************************************/
-bool city_reduce_size(struct city *pcity, int pop_loss)
+bool city_reduce_size(struct city *pcity, int pop_loss,
+                      struct player *destroyer)
 {
   int loss_remain;
   int i;
@@ -489,6 +490,12 @@
   }
 
   if (pcity->size <= pop_loss) {
+
+    script_signal_emit("city_destroyed", 3,
+                       API_TYPE_CITY, pcity,
+                       API_TYPE_PLAYER, pcity->owner,
+                       API_TYPE_PLAYER, destroyer);
+
     remove_city(pcity);
     return FALSE;
   }
@@ -688,7 +695,10 @@
     }
     return TRUE;
   } else if (size < pcity->size) {
-    return city_reduce_size(pcity, pcity->size - size);
+    /* We assume that city_change_size() is never called because
+     * of enemy actions. If that changes, enemy must be passed
+     * to city_reduce_size() */
+    return city_reduce_size(pcity, pcity->size - size, NULL);
   } else {
     return TRUE;
   }
@@ -739,7 +749,7 @@
     }
     pcity->food_stock = (city_granary_size(pcity->size - 1)
                         * granary_savings(pcity)) / 100;
-    city_reduce_size(pcity, 1);
+    city_reduce_size(pcity, 1, NULL);
   }
 }
 
@@ -1269,7 +1279,7 @@
                         "upkeep %s!"),
                         city_name(pcity),
                         unit_name_translation(punit));
-       if (!city_reduce_size(pcity, 1)) {
+       if (!city_reduce_size(pcity, 1, NULL)) {
          return FALSE;
        }
 
@@ -1487,7 +1497,7 @@
        rearrange the worker to take into account the extra resources
        (food) needed. */
     if (pop_cost > 0) {
-      city_reduce_size(pcity, pop_cost);
+      city_reduce_size(pcity, pop_cost, NULL);
     }
 
     /* to eliminate micromanagement, we only subtract the unit's
diff -Nurd -X.diff_ignore freeciv/server/cityturn.h freeciv/server/cityturn.h
--- freeciv/server/cityturn.h   2008-03-12 21:57:04.000000000 +0200
+++ freeciv/server/cityturn.h   2008-06-22 02:07:44.000000000 +0300
@@ -32,7 +32,8 @@
                            const struct cm_result *const cmr);
 
 bool city_change_size(struct city *pcity, int new_size);
-bool city_reduce_size(struct city *pcity, int pop_loss);
+bool city_reduce_size(struct city *pcity, int pop_loss,
+                      struct player *destroyer);
 void city_repair_size(struct city *pcity, int change);
 
 void send_global_city_turn_notifications(struct conn_list *dest);
diff -Nurd -X.diff_ignore freeciv/server/diplomats.c freeciv/server/diplomats.c
--- freeciv/server/diplomats.c  2008-06-18 17:45:27.000000000 +0300
+++ freeciv/server/diplomats.c  2008-06-22 02:05:33.000000000 +0300
@@ -106,7 +106,7 @@
   freelog (LOG_DEBUG, "poison: succeeded");
 
   /* Poison people! */
-  city_reduce_size(pcity, 1);
+  city_reduce_size(pcity, 1, pplayer);
 
   /* Notify everybody involved. */
   notify_player(pplayer, pcity->tile, E_MY_DIPLOMAT_POISON,
@@ -661,7 +661,7 @@
 
   /* City loses some population. */
   if (pcity->size > 1) {
-    city_reduce_size(pcity, 1);
+    city_reduce_size(pcity, 1, pplayer);
   }
 
   /* This costs! */
diff -Nurd -X.diff_ignore freeciv/server/plrhand.c freeciv/server/plrhand.c
--- freeciv/server/plrhand.c    2008-05-13 13:26:10.000000000 +0300
+++ freeciv/server/plrhand.c    2008-06-22 02:05:33.000000000 +0300
@@ -1640,6 +1640,12 @@
   int i, j;
   struct player *cplayer;
 
+  /* It is possible that this function gets called after pplayer
+   * died. Player pointers are safe even after death. */
+  if (!pplayer->is_alive) {
+    return;
+  }
+
   if (player_count() >= MAX_NUM_PLAYERS) {
     /* No space to make additional player */
     freelog(LOG_NORMAL,
diff -Nurd -X.diff_ignore freeciv/server/scripting/script_signal.c 
freeciv/server/scripting/script_signal.c
--- freeciv/server/scripting/script_signal.c    2007-09-02 17:21:02.000000000 
+0300
+++ freeciv/server/scripting/script_signal.c    2008-06-22 02:05:33.000000000 
+0300
@@ -151,6 +151,12 @@
                       3, API_TYPE_TECH_TYPE, API_TYPE_PLAYER,
                       API_TYPE_STRING);
 
+  /* First player is city owner, second is enemy. */
+  script_signal_create("city_destroyed",
+                       3, API_TYPE_CITY, API_TYPE_PLAYER, API_TYPE_PLAYER);
+  script_signal_create("city_lost",
+                       3, API_TYPE_CITY, API_TYPE_PLAYER, API_TYPE_PLAYER);
+
   script_signal_create("hut_enter", 1, API_TYPE_UNIT);
 }
 
diff -Nurd -X.diff_ignore freeciv/server/unithand.c freeciv/server/unithand.c
--- freeciv/server/unithand.c   2008-04-23 00:56:39.000000000 +0300
+++ freeciv/server/unithand.c   2008-06-22 02:05:33.000000000 +0300
@@ -851,7 +851,7 @@
       && pcity->size > 1
       && get_city_bonus(pcity, EFT_UNIT_NO_LOSE_POP) == 0
       && kills_citizen_after_attack(punit)) {
-    city_reduce_size(pcity,1);
+    city_reduce_size(pcity, 1, pplayer);
     city_refresh(pcity);
     send_city_info(NULL, pcity);
   }
@@ -946,7 +946,7 @@
       && pcity->size > 1
       && get_city_bonus(pcity, EFT_UNIT_NO_LOSE_POP) == 0
       && kills_citizen_after_attack(punit)) {
-    city_reduce_size(pcity,1);
+    city_reduce_size(pcity, 1, pplayer);
     city_refresh(pcity);
     send_city_info(NULL, pcity);
   }
diff -Nurd -X.diff_ignore freeciv/server/unittools.c freeciv/server/unittools.c
--- freeciv/server/unittools.c  2008-06-18 17:45:27.000000000 +0300
+++ freeciv/server/unittools.c  2008-06-22 02:34:17.000000000 +0300
@@ -2028,7 +2028,7 @@
                    city_name(pcity));
     }
 
-    city_reduce_size(pcity, pcity->size / 2);
+    city_reduce_size(pcity, pcity->size / 2, pplayer);
   }
 
   if (!is_ocean_tile(ptile) && myrand(2) == 1) {
@@ -2533,70 +2533,103 @@
 {
   struct city *fromcity = tile_city(src_tile);
   struct city *tocity = tile_city(dst_tile);
-  struct city *homecity = NULL;
-  struct player *pplayer = unit_owner(punit);
-  bool refresh_homecity = FALSE;
-
-  if (0 != punit->homecity) {
-    homecity = game_find_city_by_number(punit->homecity);
-  }
+  struct city *homecity_start_pos = NULL;
+  struct city *homecity_end_pos = NULL;
+  int homecity_id_start_pos = punit->homecity;
+  int homecity_id_end_pos = punit->homecity;
+  struct player *pplayer_start_pos = unit_owner(punit);
+  struct player *pplayer_end_pos = pplayer_start_pos;
+  struct unit_type *type_start_pos = unit_type(punit);
+  struct unit_type *type_end_pos = type_start_pos;
+  bool refresh_homecity_start_pos = FALSE;
+  bool refresh_homecity_end_pos = FALSE;
+  int saved_id = punit->id;
+  bool unit_died = FALSE;
 
   if (tocity) {
     unit_enter_city(punit, tocity, passenger);
+
+    if(!unit_alive(saved_id)) {
+      /* Unit died inside unit_enter_city().
+       * This means that some cleanup has already taken place when it was
+       * removed from game. */
+      unit_died = TRUE;
+    } else {
+      /* In case script has changed something about unit */
+      pplayer_end_pos = unit_owner(punit);
+      type_end_pos = unit_type(punit);
+      homecity_id_end_pos = punit->homecity;
+    }
   }
 
-  /* We only do this for non-AI players to now make sure the AI turns
+  if (homecity_id_start_pos != 0) {
+    homecity_start_pos = game_find_city_by_number(homecity_id_start_pos);
+  }
+  if (homecity_id_start_pos != homecity_id_end_pos) {
+    homecity_end_pos = game_find_city_by_number(homecity_id_end_pos);
+  } else {
+    homecity_end_pos = homecity_start_pos;
+  }
+
+  /* We only do refreshes for non-AI players to now make sure the AI turns
      doesn't take too long. Perhaps we should make a special refresh_city
-     functions that only refreshed happiness. */
-  if (!pplayer->ai.control) {
-    /* might have changed owners or may be destroyed */
-    tocity = tile_city(dst_tile);
+     functions that only refreshed happines. */
 
-    if (tocity) { /* entering a city */
-      if (city_owner(tocity) == unit_owner(punit)) {
-       if (tocity != homecity) {
-         city_refresh(tocity);
-         send_city_info(pplayer, tocity);
-       }
-      }
-      if (homecity) {
-        refresh_homecity = TRUE;
-      }
-    }
+  /* might have changed owners or may be destroyed */
+  tocity = tile_city(dst_tile);
 
-    if (fromcity) { /* leaving a city */
-      if (homecity) {
-       refresh_homecity = TRUE;
-      }
-      if (fromcity != homecity && city_owner(fromcity) == unit_owner(punit)) {
-       city_refresh(fromcity);
-       send_city_info(pplayer, fromcity);
+  if (tocity) { /* entering a city */
+    if (tocity->owner == pplayer_end_pos) {
+      if (tocity != homecity_end_pos && !pplayer_end_pos->ai.control) {
+        city_refresh(tocity);
+        send_city_info(pplayer_end_pos, tocity);
       }
     }
+    if (homecity_start_pos) {
+      refresh_homecity_start_pos = TRUE;
+    }
+  }
 
-    /* entering/leaving a fortress or friendly territory */
-    if (homecity) {
-      if ((game.info.happyborders > 0 && tile_owner(src_tile) != 
tile_owner(dst_tile))
-          ||
-         (tile_has_base_flag_for_unit(dst_tile,
-                                       unit_type(punit),
-                                       BF_NOT_AGGRESSIVE)
-          && is_friendly_city_near(unit_owner(punit), dst_tile))
-         ||
-          (tile_has_base_flag_for_unit(src_tile,
-                                       unit_type(punit),
-                                       BF_NOT_AGGRESSIVE)
-          && is_friendly_city_near(unit_owner(punit), src_tile))) {
-        refresh_homecity = TRUE;
-      }
+  if (fromcity) { /* leaving a city */
+    if (homecity_start_pos) {
+      refresh_homecity_start_pos = TRUE;
     }
-    
-    if (refresh_homecity) {
-      city_refresh(homecity);
-      send_city_info(pplayer, homecity);
+    if (fromcity != homecity_start_pos
+        && fromcity->owner == pplayer_start_pos
+        && !pplayer_start_pos->ai.control) {
+      city_refresh(fromcity);
+      send_city_info(pplayer_start_pos, fromcity);
     }
   }
 
+  /* entering/leaving a fortress or friendly territory */
+  if (homecity_start_pos || homecity_end_pos) {
+    if ((game.info.happyborders > 0 && tile_owner(src_tile) != 
tile_owner(dst_tile))
+        || (tile_has_base_flag_for_unit(dst_tile,
+                                        type_end_pos,
+                                        BF_NOT_AGGRESSIVE)
+            && is_friendly_city_near(pplayer_end_pos, dst_tile))
+        || (tile_has_base_flag_for_unit(src_tile,
+                                        type_start_pos,
+                                        BF_NOT_AGGRESSIVE)
+            && is_friendly_city_near(pplayer_start_pos, src_tile))) {
+      refresh_homecity_start_pos = TRUE;
+      refresh_homecity_end_pos = TRUE;
+    }
+  }
+
+  if (refresh_homecity_start_pos && !pplayer_start_pos->ai.control) {
+    city_refresh(homecity_start_pos);
+    send_city_info(pplayer_start_pos, homecity_start_pos);
+  }
+  if (refresh_homecity_end_pos
+      && (!refresh_homecity_start_pos
+          || homecity_start_pos != homecity_end_pos)
+      && !pplayer_end_pos->ai.control) {
+    city_refresh(homecity_end_pos);
+    send_city_info(pplayer_end_pos, homecity_end_pos);
+  }
+
   city_map_update_tile_now(dst_tile);
   sync_cities();
 }
@@ -2654,6 +2687,7 @@
   struct vision *old_vision = punit->server.vision;
   struct vision *new_vision;
   int saved_id = punit->id;
+  bool unit_lives;
     
   conn_list_do_buffer(pplayer->connections);
 
@@ -2673,6 +2707,9 @@
 
     /* Insert them again. */
     unit_list_iterate(cargo_units, pcargo) {
+      /* FIXME: Some script may have killed unit while it has been
+       *        in cargo_units list, but it has not been unlinked
+       *        from this list. pcargo may be invalid pointer. */
       struct vision *old_vision = pcargo->server.vision;
       struct vision *new_vision = vision_new(unit_owner(pcargo), pdesttile);
 
@@ -2702,6 +2739,8 @@
     unit_list_free(cargo_units);
   }
 
+  unit_lives = unit_alive(saved_id);
+
   /* We first unfog the destination, then move the unit and send the
      move, and then fog the old territory. This means that the player
      gets a chance to see the newly explored territory while the
@@ -2709,38 +2748,40 @@
      move */
 
   /* Enhance vision if unit steps into a fortress */
-  new_vision = vision_new(unit_owner(punit), pdesttile);
-  punit->server.vision = new_vision;
-  vision_layer_iterate(v) {
-    vision_change_sight(new_vision, v,
-                       get_unit_vision_at(punit, pdesttile, v));
-  } vision_layer_iterate_end;
+  if (unit_lives) {
+    new_vision = vision_new(unit_owner(punit), pdesttile);
+    punit->server.vision = new_vision;
+    vision_layer_iterate(v) {
+      vision_change_sight(new_vision, v,
+                          get_unit_vision_at(punit, pdesttile, v));
+    } vision_layer_iterate_end;
 
-  ASSERT_VISION(new_vision);
+    ASSERT_VISION(new_vision);
 
-  /* Claim ownership of fortress? */
-  if (tile_has_base_flag_for_unit(pdesttile, unit_type(punit),
-                                  BF_CLAIM_TERRITORY)
-      && (!tile_owner(pdesttile) || pplayers_at_war(tile_owner(pdesttile), 
pplayer))) {
-    map_claim_ownership(pdesttile, pplayer, pdesttile);
-    map_claim_border(pdesttile, pplayer);
-    city_thaw_workers_queue();
-    city_refresh_queue_processing();
-  }
+    /* Claim ownership of fortress? */
+    if (tile_has_base_flag_for_unit(pdesttile, unit_type(punit),
+                                    BF_CLAIM_TERRITORY)
+        && (!tile_owner(pdesttile) || pplayers_at_war(tile_owner(pdesttile), 
pplayer))) {
+      map_claim_ownership(pdesttile, pplayer, pdesttile);
+      map_claim_border(pdesttile, pplayer);
+      city_thaw_workers_queue();
+      city_refresh_queue_processing();
+    }
 
-  unit_list_unlink(psrctile->units, punit);
-  punit->tile = pdesttile;
-  punit->moved = TRUE;
-  if (punit->transported_by != -1) {
-    ptransporter = game_find_unit_by_number(punit->transported_by);
-    pull_unit_from_transporter(punit, ptransporter);
-  }
-  punit->moves_left = MAX(0, punit->moves_left - move_cost);
-  if (punit->moves_left == 0) {
-    punit->done_moving = TRUE;
+    unit_list_unlink(psrctile->units, punit);
+    punit->tile = pdesttile;
+    punit->moved = TRUE;
+    if (punit->transported_by != -1) {
+      ptransporter = game_find_unit_by_number(punit->transported_by);
+      pull_unit_from_transporter(punit, ptransporter);
+    }
+    punit->moves_left = MAX(0, punit->moves_left - move_cost);
+    if (punit->moves_left == 0) {
+      punit->done_moving = TRUE;
+    }
+    unit_list_prepend(pdesttile->units, punit);
+    check_unit_activity(punit);
   }
-  unit_list_prepend(pdesttile->units, punit);
-  check_unit_activity(punit);
 
   /*
    * Transporter info should be send first becouse this allow us get right
@@ -2754,48 +2795,50 @@
   if (ptransporter) {
     send_unit_info(NULL, ptransporter);
   }
-  
+
   /* Send updated information to anyone watching.  If the unit moves
    * in or out of a city we update the 'occupied' field.  Note there may
    * be cities at both src and dest under some rulesets.
    *   If unit is about to take over enemy city, unit is seen by
    * those players seeing inside cities of old city owner. After city
    * has been transferred, updated info is sent by unit_enter_city() */
-  send_unit_info_to_onlookers(NULL, punit, psrctile, FALSE);
+  if (unit_lives) {
+    send_unit_info_to_onlookers(NULL, punit, psrctile, FALSE);
     
-  /* Special checks for ground units in the ocean. */
-  if (!can_unit_survive_at_tile(punit, pdesttile)) {
-    ptransporter = find_transporter_for_unit(punit);
-    if (ptransporter) {
-      put_unit_onto_transporter(punit, ptransporter);
-    }
+    /* Special checks for ground units in the ocean. */
+    if (!can_unit_survive_at_tile(punit, pdesttile)) {
+      ptransporter = find_transporter_for_unit(punit);
+      if (ptransporter) {
+        put_unit_onto_transporter(punit, ptransporter);
+      }
 
-    /* Set activity to sentry if boarding a ship. */
-    if (ptransporter && !pplayer->ai.control && !unit_has_orders(punit)
-       && !can_unit_exist_at_tile(punit, pdesttile)) {
-      set_unit_activity(punit, ACTIVITY_SENTRY);
-    }
+      /* Set activity to sentry if boarding a ship. */
+      if (ptransporter && !pplayer->ai.control && !unit_has_orders(punit)
+          && !can_unit_exist_at_tile(punit, pdesttile)) {
+        set_unit_activity(punit, ACTIVITY_SENTRY);
+      }
 
-    /*
-     * Transporter info should be send first becouse this allow us get right
-     * update_menu effect in client side.
-     */
+      /*
+       * Transporter info should be send first because this allow us get right
+       * update_menu effect in client side.
+       */
     
-    /*
-     * Send updated information to anyone watching that transporter has cargo.
-     */
-    if (ptransporter) {
-      send_unit_info(NULL, ptransporter);
-    }
+      /*
+       * Send updated information to anyone watching that transporter has 
cargo.
+       */
+      if (ptransporter) {
+        send_unit_info(NULL, ptransporter);
+      }
 
-    /*
-     * Send updated information to anyone watching that unit is on transport.
-     * All players without shared vison with owner player get
-     * REMOVE_UNIT package.
-     */
-    send_unit_info_to_onlookers(NULL, punit, punit->tile, TRUE);
+      /*
+       * Send updated information to anyone watching that unit is on transport.
+       * All players without shared vison with owner player get
+       * REMOVE_UNIT package.
+       */
+      send_unit_info_to_onlookers(NULL, punit, punit->tile, TRUE);
+    }
   }
-  
+
   if ((pcity = tile_city(psrctile))) {
     refresh_dumb_city(pcity);
   }
@@ -2822,27 +2865,38 @@
     } players_iterate_end;
   } square_iterate_end;
 
-  unit_move_consequences(punit, psrctile, pdesttile, FALSE);
+  if (unit_lives) {
+    unit_move_consequences(punit, psrctile, pdesttile, FALSE);
 
-  /* FIXME: Should signal emit be after sentried units have been
-   *        waken up in case script causes unit death? */
-  script_signal_emit("unit_moved", 3,
-                    API_TYPE_UNIT, punit,
-                    API_TYPE_TILE, psrctile,
-                    API_TYPE_TILE, pdesttile);
-  if (!unit_alive(saved_id)) {
-    /* Script caused unit to die */
-    return FALSE;
+    /* FIXME: Should signal emit be after sentried units have been
+     *        waken up in case script causes unit death? */
+    script_signal_emit("unit_moved", 3,
+                       API_TYPE_UNIT, punit,
+                       API_TYPE_TILE, psrctile,
+                       API_TYPE_TILE, pdesttile);
+    unit_lives = unit_alive(saved_id);
+
+    if (!unit_lives) {
+      /* Script caused unit to die */
+      return FALSE;
+    }
   }
-  wakeup_neighbor_sentries(punit);
-  if (!unit_survive_autoattack(punit)) {
-    conn_list_do_unbuffer(pplayer->connections);
-    return FALSE;
+
+  if (unit_lives) {
+    wakeup_neighbor_sentries(punit);
+    unit_lives = unit_survive_autoattack(punit);
+  }
+
+  if (unit_lives) {
+    maybe_make_contact(pdesttile, unit_owner(punit));
   }
-  maybe_make_contact(pdesttile, unit_owner(punit));
 
   conn_list_do_unbuffer(pplayer->connections);
 
+  if (!unit_lives) {
+    return FALSE;
+  }
+
   if (game.info.timeout != 0 && game.timeoutaddenemymove > 0) {
     bool new_information_for_enemy = FALSE;
 
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to