Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2005:
[Freeciv-Dev] (PR#13773) Improved cursors
Home

[Freeciv-Dev] (PR#13773) Improved cursors

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: andrearo@xxxxxxxxxxxx
Subject: [Freeciv-Dev] (PR#13773) Improved cursors
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 10 Dec 2005 10:21:35 -0800
Reply-to: bugs@xxxxxxxxxxx

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

The patch should be fixed up and committed.

The only parts I think need fixing are those in common/.  I don't
understand these changes, and I suspect they are wrong.  For instance a
query on the goto status should go not through common/movement.c, it
should go through client/goto.c.  And the diplomat change looks wrong to
me.  But as I said I don't understand them.

Here's an updated patch.  I updated the patch to apply to current cvs. 
I also fixed up cursors.spec - mostly by converting it from latin1 into
UTF-8 (all non-ascii text must be in utf-8; type "file cursors.spec" to
see the current format and use the iconv program to convert as needed).

-jason

Index: data/misc/cursors.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: data/misc/cursors.spec
===================================================================
--- data/misc/cursors.spec      (revision 11332)
+++ data/misc/cursors.spec      (working copy)
@@ -7,7 +7,7 @@
 [info]
 
 artists = "
-    ???
+    Andreas Røsdal <andrearo@xxxxxxxxxxxx>
 "
 
 [file]
@@ -15,15 +15,53 @@
 
 [grid_main]
 
-x_top_left = 0
-y_top_left = 0
-dx = 27
-dy = 27
-pixel_border=0
+x_top_left = 1
+y_top_left = 1
+dx = 32
+dy = 32
+pixel_border = 1
 
 tiles = { "row", "column", "tag", "hot_x", "hot_y"
-       0, 0, "cursor.goto", 25, 1
-       0, 1, "cursor.patrol", 13, 13
-       0, 2, "cursor.paradrop", 13, 25
-       0, 3, "cursor.nuke", 13, 13
+       0, 0, "cursor.goto0", 16, 16
+       1, 0, "cursor.goto1", 16, 16
+       2, 0, "cursor.goto2", 16, 16
+       3, 0, "cursor.goto3", 16, 16
+       4, 0, "cursor.goto4", 16, 16
+       5, 0, "cursor.goto5", 16, 16
+       0, 1, "cursor.patrol0", 16, 16
+       1, 1, "cursor.patrol1", 16, 16
+       2, 1, "cursor.patrol2", 16, 16
+       3, 1, "cursor.patrol3", 16, 16
+       4, 1, "cursor.patrol4", 16, 16
+       5, 1, "cursor.patrol5", 16, 16
+       0, 2, "cursor.paradrop0", 16, 16
+       1, 2, "cursor.paradrop1", 16, 16
+       2, 2, "cursor.paradrop2", 16, 16
+       3, 2, "cursor.paradrop3", 16, 16
+       4, 2, "cursor.paradrop4", 16, 16
+       5, 2, "cursor.paradrop5", 16, 16
+       0, 3, "cursor.nuke0", 16, 16
+       1, 3, "cursor.nuke1", 16, 16
+       2, 3, "cursor.nuke2", 16, 16
+       3, 3, "cursor.nuke3", 16, 16
+       4, 3, "cursor.nuke4", 16, 16
+       5, 3, "cursor.nuke5", 16, 16
+       0, 4, "cursor.select0", 16, 16
+       1, 4, "cursor.select1", 16, 16
+       2, 4, "cursor.select2", 16, 16
+       3, 4, "cursor.select3", 16, 16
+       4, 4, "cursor.select4", 16, 16
+       5, 4, "cursor.select5", 16, 16
+       0, 5, "cursor.invalid0", 16, 16
+       1, 5, "cursor.invalid1", 16, 16
+       2, 5, "cursor.invalid2", 16, 16
+       3, 5, "cursor.invalid3", 16, 16
+       4, 5, "cursor.invalid4", 16, 16
+       5, 5, "cursor.invalid5", 16, 16
+       0, 6, "cursor.attack0", 16, 16
+       1, 6, "cursor.attack1", 16, 16
+       2, 6, "cursor.attack2", 16, 16
+       3, 6, "cursor.attack3", 16, 16
+       4, 6, "cursor.attack4", 16, 16
+       5, 6, "cursor.attack5", 16, 16
 }
Index: common/unit.c
===================================================================
--- common/unit.c       (revision 11332)
+++ common/unit.c       (working copy)
@@ -83,8 +83,7 @@
   }
 
   if (pcity) {
-    if (pcity->owner != pdiplomat->owner
-       && real_map_distance(pdiplomat->tile, pcity->tile) <= 1) {
+    if (pcity->owner != pdiplomat->owner) {
       if(action==DIPLOMAT_SABOTAGE)
        return pplayers_at_war(unit_owner(pdiplomat), city_owner(pcity));
       if(action==DIPLOMAT_MOVE)
Index: common/movement.c
===================================================================
--- common/movement.c   (revision 11332)
+++ common/movement.c   (working copy)
@@ -313,17 +313,16 @@
 
   The unit can move if:
     1) The unit is idle or on server goto.
-    2) The target location is next to the unit.
-    3) There are no non-allied units on the target tile.
-    4) A ground unit can only move to ocean squares if there
+    2) There are no non-allied units on the target tile.
+    3) A ground unit can only move to ocean squares if there
        is a transporter with free capacity.
-    5) Marines are the only units that can attack from a ocean square.
-    6) Naval units can only be moved to ocean squares or city squares.
-    7) There are no peaceful but un-allied units on the target tile.
-    8) There is not a peaceful but un-allied city on the target tile.
-    9) There is no non-allied unit blocking (zoc) [or igzoc is true].
+    4) Marines are the only units that can attack from a ocean square.
+    5) Naval units can only be moved to ocean squares or city squares.
+    6) There are no peaceful but un-allied units on the target tile.
+    7) There is not a peaceful but un-allied city on the target tile.
+    8) There is no non-allied unit blocking (zoc) [or igzoc is true].
 **************************************************************************/
-enum unit_move_result test_unit_move_to_tile(const struct unit_type *punittype,
+enum unit_move_result test_unit_goto_tile(const struct unit_type *punittype,
                                             const struct player *unit_owner,
                                             enum unit_activity activity,
                                             const struct tile *src_tile,
@@ -341,12 +340,6 @@
   }
 
   /* 2) */
-  if (!is_tiles_adjacent(src_tile, dst_tile)) {
-    /* Of course you can only move to adjacent positions. */
-    return MR_BAD_DESTINATION;
-  }
-
-  /* 3) */
   if (is_non_allied_unit_tile(dst_tile, unit_owner)) {
     /* You can't move onto a tile with non-allied units on it (try
      * attacking instead). */
@@ -354,7 +347,7 @@
   }
 
   if (punittype->move_type == LAND_MOVING) {
-    /* 4) */
+    /* 3) */
     if (is_ocean(dst_tile->terrain) &&
        ground_unit_transporter_capacity(dst_tile, unit_owner) <= 0) {
       /* Ground units can't move onto ocean tiles unless there's enough
@@ -364,7 +357,7 @@
 
     /* Moving from ocean */
     if (is_ocean(src_tile->terrain)) {
-      /* 5) */
+      /* 4) */
       if (!unit_type_flag(punittype, F_MARINES)
          && is_enemy_city_tile(dst_tile, unit_owner)) {
        /* Most ground units can't move into cities from ships.  (Note this
@@ -374,7 +367,7 @@
       }
     }
   } else if (punittype->move_type == SEA_MOVING) {
-    /* 6) */
+    /* 5) */
     if (!is_ocean(dst_tile->terrain)
        && dst_tile->terrain != T_UNKNOWN
        && (!is_allied_city_tile(dst_tile, unit_owner)
@@ -384,11 +377,11 @@
        * The check for T_UNKNOWN here is probably unnecessary.  Since the
        * dst_tile is adjacent to the src_tile it must be known to punit's
        * owner, even at the client side. */
-      return MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_CITY;
+      return MR_BAD_MAP_POSITION;
     }
   }
 
-  /* 7) */
+  /* 6) */
   if (is_non_attack_unit_tile(dst_tile, unit_owner)) {
     /* You can't move into a non-allied tile.
      *
@@ -397,7 +390,7 @@
     return MR_NO_WAR;
   }
 
-  /* 8) */
+  /* 7) */
   pcity = dst_tile->city;
   if (pcity && pplayers_non_attack(city_owner(pcity), unit_owner)) {
     /* You can't move into an empty city of a civilization you're at
@@ -405,7 +398,7 @@
     return MR_NO_WAR;
   }
 
-  /* 9) */
+  /* 8) */
   zoc = igzoc
     || can_step_taken_wrt_to_zoc(punittype, unit_owner, src_tile, dst_tile);
   if (!zoc) {
@@ -417,6 +410,39 @@
 }
 
 /**************************************************************************
+  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 move if:
+    1) The unit is idle or on server goto.
+    2) The target location is next to the unit.
+    3) There are no non-allied units on the target tile.
+    4) A ground unit can only move to ocean squares if there
+       is a transporter with free capacity.
+    5) Marines are the only units that can attack from a ocean square.
+    6) Naval units can only be moved to ocean squares or city squares.
+    7) There are no peaceful but un-allied units on the target tile.
+    8) There is not a peaceful but un-allied city on the target tile.
+    9) There is no non-allied unit blocking (zoc) [or igzoc is true].
+**************************************************************************/
+enum unit_move_result test_unit_move_to_tile(const struct unit_type *punittype,
+                                            const struct player *unit_owner,
+                                            enum unit_activity activity,
+                                            const struct tile *src_tile,
+                                            const struct tile *dst_tile,
+                                            bool igzoc)
+{
+  if (!is_tiles_adjacent(src_tile, dst_tile)) {
+    /* Of course you can only move to adjacent positions. */
+    return MR_BAD_DESTINATION;
+  } else {
+    return test_unit_goto_tile(punittype, unit_owner, activity,
+                              src_tile, dst_tile, igzoc);
+  }
+}
+
+/**************************************************************************
   Return true iff transporter has ability to transport transported.
 **************************************************************************/
 bool can_unit_transport(const struct unit *transporter,
Index: common/movement.h
===================================================================
--- common/movement.h   (revision 11332)
+++ common/movement.h   (working copy)
@@ -48,6 +48,12 @@
                                             const struct tile *src_tile,
                                             const struct tile *dst_tile,
                                             bool igzoc);
+enum unit_move_result test_unit_goto_tile(const struct unit_type *punittype,
+                                            const struct player *unit_owner,
+                                            enum unit_activity activity,
+                                            const struct tile *src_tile,
+                                            const struct tile *dst_tile,
+                                            bool igzoc);
 bool can_unit_transport(const struct unit *transporter, const struct unit 
*transported);
 
 #endif  /* FC__MOVEMENT_H */
Index: client/control.c
===================================================================
--- client/control.c    (revision 11332)
+++ client/control.c    (working copy)
@@ -31,6 +31,7 @@
 #include "climap.h"
 #include "climisc.h"
 #include "clinet.h"
+#include "combat.h"
 #include "dialogs_g.h"
 #include "goto.h"
 #include "gui_main_g.h"
@@ -57,9 +58,12 @@
 /* These should be set via set_hover_state() */
 int hover_unit = 0; /* id of unit hover_state applies to */
 enum cursor_hover_state hover_state = HOVER_NONE;
+enum cursor_action_state action_state = CURSOR_ACTION_DEFAULT;
 enum unit_activity connect_activity;
 enum unit_orders goto_last_order; /* Last order for goto */
 
+struct tile *hover_tile = NULL;
+
 /* units involved in current combat */
 static struct unit *punit_attacking = NULL;
 static struct unit *punit_defending = NULL;
@@ -682,7 +686,7 @@
 
   if (hover_state != HOVER_GOTO) {
     set_hover_state(punit, HOVER_GOTO, ACTIVITY_LAST, last_order);
-    update_unit_info_label(punit);
+    handle_mouse_cursor(NULL);
     enter_goto_state(punit);
     create_line_at_mouse_pos();
   } else {
@@ -692,6 +696,99 @@
 }
 
 /**************************************************************************
+  Determines which mouse cursor should be used, according to hover_state,
+  and the information gathered from the tile which is under the mouse 
+  cursor (ptile).
+**************************************************************************/
+void handle_mouse_cursor(struct tile *ptile)
+{
+  struct unit *punit = NULL;
+  struct city *pcity = NULL;
+  struct unit *active_unit = get_unit_in_focus();
+
+  if (!ptile) {
+    if (hover_tile) {
+      /* hover_tile is the tile which is currently under the mouse cursor. */
+      ptile = hover_tile;
+    } else {
+      return;
+    }
+  }
+
+  punit = find_visible_unit(ptile);
+  pcity = ptile ? ptile->city : NULL;
+
+  if (hover_state == HOVER_NONE) {
+    if (punit && game.player_ptr == punit->owner) {
+      /* Set mouse cursor to select a unit.  */
+      action_state = CURSOR_ACTION_SELECT;
+    } else if (pcity && can_player_see_city_internals(game.player_ptr, pcity)) 
{
+      /* Set mouse cursor to select a city. */
+      action_state = CURSOR_ACTION_SELECT;
+    } else {
+      /* Set default mouse cursor, because nothing selectable found. */
+      action_state = CURSOR_ACTION_DEFAULT;
+    }
+
+  } else if (hover_state == HOVER_GOTO) {
+    /* Determine if the goto is valid, invalid or will attack. */
+    enum unit_move_result result = test_unit_goto_tile(active_unit->type, 
+                                         unit_owner(active_unit),
+                                          ACTIVITY_IDLE, active_unit->tile,
+                                          ptile, TRUE);
+    struct city *hcity = find_city_by_id(active_unit->homecity);
+
+    if (result == MR_OK
+        || result == MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_UNIT
+        || result == MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_CITY) {
+      if (is_attack_unit(active_unit) 
+         && can_unit_attack_tile(active_unit, ptile)) {
+        /* Goto results in military attack. */
+        action_state = CURSOR_ACTION_ATTACK;
+      } else if (is_diplomat_unit(active_unit) 
+                && is_diplomat_action_available(active_unit,
+                                             DIPLOMAT_ANY_ACTION, ptile)) {
+        /* Goto results in diplomatic action. */
+        action_state = CURSOR_ACTION_ATTACK;
+      } else {
+        /* Goto is valid. */
+        action_state = CURSOR_ACTION_GOTO;
+      }
+    } else if (is_diplomat_unit(active_unit) 
+              && is_diplomat_action_available(active_unit,
+                                              DIPLOMAT_ANY_ACTION, ptile)) {
+      /* Goto results in diplomatic action. */
+      action_state = CURSOR_ACTION_ATTACK;
+    } else if (is_enemy_city_tile(ptile, unit_owner(active_unit))
+              && is_attack_unit(active_unit) 
+              && can_unit_attack_tile(active_unit, ptile)) {
+      /* Goto results in attack of enemy city. */
+      action_state = CURSOR_ACTION_ATTACK;
+    } else if (unit_flag(active_unit, F_TRADE_ROUTE) && pcity
+               && can_cities_trade(hcity, pcity)
+              && can_establish_trade_route(hcity, pcity)) {
+      /* Goto can establish trade route with city. */
+      action_state = CURSOR_ACTION_GOTO;
+    } else { 
+      /* Invalid goto. */
+      action_state = CURSOR_ACTION_INVALID;
+    }
+  } else if (hover_state == HOVER_PATROL || hover_state == HOVER_CONNECT) {
+    enum unit_move_result result = test_unit_goto_tile(active_unit->type, 
+                                                      unit_owner(active_unit),
+                                                      ACTIVITY_IDLE, 
active_unit->tile,
+                                                      ptile, TRUE); 
+    if (result == MR_OK) {
+      action_state = CURSOR_ACTION_GOTO;
+    } else {
+      action_state = CURSOR_ACTION_INVALID;
+    }
+  } 
+
+  update_unit_info_label(active_unit);
+}
+
+/**************************************************************************
   Return TRUE if there are any units doing the activity on the tile.
 **************************************************************************/
 static bool is_activity_on_tile(struct tile *ptile,
Index: client/gui-gtk-2.0/mapview.c
===================================================================
--- client/gui-gtk-2.0/mapview.c        (revision 11332)
+++ client/gui-gtk-2.0/mapview.c        (working copy)
@@ -55,8 +55,8 @@
 #include "mapview.h"
 
 static GtkObject *map_hadj, *map_vadj;
+static int cursor_timer_id = 0, cursor_type = -1, cursor_frame = 0;
 
-
 /**************************************************************************
   If do_restore is FALSE it will invert the turn done button style. If
   called regularly from a timer this will give a blinking turn done
@@ -157,6 +157,45 @@
 }
 
 /**************************************************************************
+  This function is used to animate the mouse cursor. 
+**************************************************************************/
+static gint anim_cursor_cb(gpointer data)
+{
+  if (!cursor_timer_id) {
+    return FALSE;
+  }
+
+  cursor_frame++;
+  if (cursor_frame == NUM_CURSOR_FRAMES) {
+    cursor_frame = 0;
+  }
+
+  if (cursor_type == CURSOR_DEFAULT) {
+    gdk_window_set_cursor(root_window, NULL);
+    cursor_timer_id = 0;
+    return FALSE; 
+  }
+
+  gdk_window_set_cursor(root_window,
+                fc_cursors[cursor_type * NUM_CURSOR_FRAMES + cursor_frame]);
+  handle_mouse_cursor(NULL);
+  return TRUE;
+}
+
+/**************************************************************************
+  This function will change the current mouse cursor.
+**************************************************************************/
+
+static void modify_mouse_cursor(enum cursor_type new_cursor_type) {
+
+  cursor_type = new_cursor_type;
+  if (!cursor_timer_id) {
+    cursor_timer_id = gtk_timeout_add(CURSOR_INTERVAL, anim_cursor_cb, NULL);
+  }
+
+}
+
+/**************************************************************************
   Update the information label which gives info on the current unit and the
   square under the current unit, for specified unit.  Note that in practice
   punit is always the focus unit.
@@ -168,6 +207,7 @@
 **************************************************************************/
 void update_unit_info_label(struct unit *punit)
 {
+  enum cursor_type mouse_cursor_type = CURSOR_DEFAULT;
   GtkWidget *label;
 
   label = gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame));
@@ -177,31 +217,53 @@
   gtk_label_set_text(GTK_LABEL(unit_info_label),
                     get_unit_info_label_text2(punit));
 
-  if(punit) {
-    if (hover_unit != punit->id)
-      set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-
-    switch (hover_state) {
-    case HOVER_NONE:
-      gdk_window_set_cursor (root_window, NULL);
-      break;
-    case HOVER_PATROL:
-      gdk_window_set_cursor(root_window, fc_cursors[CURSOR_PATROL]);
-      break;
-    case HOVER_GOTO:
-    case HOVER_CONNECT:
-      gdk_window_set_cursor(root_window, fc_cursors[CURSOR_GOTO]);
-      break;
-    case HOVER_NUKE:
-      gdk_window_set_cursor(root_window, fc_cursors[CURSOR_NUKE]);
-      break;
-    case HOVER_PARADROP:
-      gdk_window_set_cursor(root_window, fc_cursors[CURSOR_PARADROP]);
-      break;
+  switch (hover_state) {
+  case HOVER_NONE:
+    if (action_state == CURSOR_ACTION_SELECT) {
+      mouse_cursor_type = CURSOR_SELECT;
+    } else if (action_state == CURSOR_ACTION_PARATROOPER) {
+      mouse_cursor_type = CURSOR_PARADROP;
+    } else if (action_state == CURSOR_ACTION_NUKE) {
+      mouse_cursor_type = CURSOR_NUKE;
+    } else {
+      mouse_cursor_type = CURSOR_DEFAULT;
     }
-  } else {
-    gdk_window_set_cursor(root_window, NULL);
+    break;
+  case HOVER_PATROL:
+    if (action_state == CURSOR_ACTION_INVALID) {
+      mouse_cursor_type = CURSOR_INVALID;
+    } else {
+      mouse_cursor_type = CURSOR_PATROL;
+    }
+    break;
+  case HOVER_GOTO:
+    if (action_state == CURSOR_ACTION_GOTO) {
+      mouse_cursor_type = CURSOR_GOTO;
+    } else if (action_state == CURSOR_ACTION_DEFAULT) {
+      mouse_cursor_type = CURSOR_DEFAULT;
+    } else if (action_state == CURSOR_ACTION_ATTACK) {
+      mouse_cursor_type = CURSOR_ATTACK;
+    } else {
+      mouse_cursor_type = CURSOR_INVALID;
+    }
+    break;
+  case HOVER_CONNECT:
+    if (action_state == CURSOR_ACTION_INVALID) {
+      mouse_cursor_type = CURSOR_INVALID;
+    } else {
+      mouse_cursor_type = CURSOR_GOTO;
+    }
+    break;
+  case HOVER_NUKE:
+    mouse_cursor_type = CURSOR_NUKE;
+    break;
+  case HOVER_PARADROP:
+    mouse_cursor_type = CURSOR_PARADROP;
+    break;
   }
+
+  modify_mouse_cursor(mouse_cursor_type);
+
   update_unit_pix_label(punit);
 }
 
Index: client/gui-gtk-2.0/mapview.h
===================================================================
--- client/gui-gtk-2.0/mapview.h        (revision 11332)
+++ client/gui-gtk-2.0/mapview.h        (working copy)
@@ -28,6 +28,8 @@
 
 GdkPixbuf *get_thumb_pixbuf(int onoff);
 
+#define CURSOR_INTERVAL 200
+
 gboolean overview_canvas_expose(GtkWidget *w, GdkEventExpose *ev, gpointer 
data);
 gboolean map_canvas_expose(GtkWidget *w, GdkEventExpose *ev, gpointer data);
 gboolean map_canvas_configure(GtkWidget *w, GdkEventConfigure *ev,
Index: client/gui-gtk-2.0/gui_main.c
===================================================================
--- client/gui-gtk-2.0/gui_main.c       (revision 11332)
+++ client/gui-gtk-2.0/gui_main.c       (working copy)
@@ -1003,6 +1003,9 @@
   g_signal_connect(map_canvas, "motion_notify_event",
                    G_CALLBACK(move_mapcanvas), NULL);
 
+  g_signal_connect(toplevel, "enter_notify_event",
+                   G_CALLBACK(leave_mapcanvas), NULL);
+
   g_signal_connect(map_canvas, "button_press_event",
                    G_CALLBACK(butt_down_mapcanvas), NULL);
 
Index: client/gui-gtk-2.0/graphics.c
===================================================================
--- client/gui-gtk-2.0/graphics.c       (revision 11332)
+++ client/gui-gtk-2.0/graphics.c       (working copy)
@@ -44,7 +44,7 @@
 struct sprite *intro_gfx_sprite;
 struct sprite *radar_gfx_sprite;
 
-GdkCursor *fc_cursors[CURSOR_LAST];
+GdkCursor *fc_cursors[CURSOR_LAST * NUM_CURSOR_FRAMES];
 
 /***************************************************************************
 ...
@@ -88,14 +88,18 @@
 {
   enum cursor_type cursor;
   GdkDisplay *display = gdk_display_get_default();
+  int frame, i;
 
   for (cursor = 0; cursor < CURSOR_LAST; cursor++) {
-    int hot_x, hot_y;
-    struct sprite *sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y);
-    GdkPixbuf *pixbuf = sprite_get_pixbuf(sprite);
+    for (frame = 0; frame < NUM_CURSOR_FRAMES; frame++) {
+      int hot_x, hot_y;
+      struct sprite *sprite = get_cursor_sprite(tileset, cursor, &hot_x, 
&hot_y, frame);
+      GdkPixbuf *pixbuf = sprite_get_pixbuf(sprite);
+      i = cursor * NUM_CURSOR_FRAMES + frame;
 
-    fc_cursors[cursor] = gdk_cursor_new_from_pixbuf(display, pixbuf,
-                                                hot_x, hot_y);
+      fc_cursors[i] = gdk_cursor_new_from_pixbuf(display, pixbuf,
+                                                hot_x, hot_y);
+    }
   }
 }
 
Index: client/gui-gtk-2.0/mapctrl.c
===================================================================
--- client/gui-gtk-2.0/mapctrl.c        (revision 11332)
+++ client/gui-gtk-2.0/mapctrl.c        (working copy)
@@ -363,10 +363,13 @@
 }
 
 /**************************************************************************
-...
+  Triggered by the mouse moving on the mapcanvas, this function will
+  update the mouse cursor and goto lines. 
 **************************************************************************/
 gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
 {
+  struct tile *ptile = NULL;
+
   if (!GTK_WIDGET_HAS_FOCUS(map_canvas)) {
     gtk_widget_grab_focus(map_canvas);
   }
@@ -376,10 +379,26 @@
   if (keyboardless_goto_button_down && hover_state == HOVER_NONE) {
     maybe_activate_keyboardless_goto(ev->x, ev->y);
   }
+  ptile = canvas_pos_to_tile(ev->x, ev->y);
+  handle_mouse_cursor(ptile);
+  hover_tile = ptile;
+
   return TRUE;
 }
 
 /**************************************************************************
+  This function will reset the mouse cursor if it leaves the map.
+**************************************************************************/
+gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event)
+{
+  struct unit *active_unit = get_unit_in_focus();
+
+  action_state = CURSOR_ACTION_DEFAULT;
+  update_unit_info_label(active_unit); 
+  return TRUE;
+}
+
+/**************************************************************************
 ...
 **************************************************************************/
 gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
Index: client/gui-gtk-2.0/graphics.h
===================================================================
--- client/gui-gtk-2.0/graphics.h       (revision 11332)
+++ client/gui-gtk-2.0/graphics.h       (working copy)
@@ -28,7 +28,7 @@
 
 /* This name is to avoid a naming conflict with a global 'cursors'
  * variable in GTK+-2.6.  See PR#12459. */
-extern GdkCursor *fc_cursors[CURSOR_LAST];
+extern GdkCursor *fc_cursors[CURSOR_LAST * NUM_CURSOR_FRAMES];
 
 void gtk_draw_shadowed_string(GdkDrawable *drawable,
                              GdkGC *black_gc,
Index: client/gui-gtk-2.0/mapctrl.h
===================================================================
--- client/gui-gtk-2.0/mapctrl.h        (revision 11332)
+++ client/gui-gtk-2.0/mapctrl.h        (working copy)
@@ -25,6 +25,7 @@
 gboolean butt_down_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data);
 gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEventButton *ev, gpointer 
data);
 gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data);
+gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event);
 gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data);
 
 void center_on_unit(void);
Index: client/control.h
===================================================================
--- client/control.h    (revision 11332)
+++ client/control.h    (working copy)
@@ -24,6 +24,16 @@
   HOVER_PATROL
 };
 
+enum cursor_action_state {
+  CURSOR_ACTION_DEFAULT,
+  CURSOR_ACTION_GOTO,
+  CURSOR_ACTION_SELECT,
+  CURSOR_ACTION_INVALID,
+  CURSOR_ACTION_ATTACK,
+  CURSOR_ACTION_NUKE,
+  CURSOR_ACTION_PARATROOPER
+};
+
 /* Selecting unit from a stack without popup. */
 enum quickselect_type {
   SELECT_POPUP = 0, SELECT_SEA, SELECT_LAND
@@ -34,10 +44,13 @@
 
 extern int hover_unit; /* unit hover_state applies to */
 extern enum cursor_hover_state hover_state;
+extern enum cursor_action_state action_state;
 extern enum unit_activity connect_activity;
 extern enum unit_orders goto_last_order;
 extern bool non_ai_unit_focus;
 
+extern struct tile *hover_tile;
+
 bool can_unit_do_connect(struct unit *punit, enum unit_activity activity);
 
 void do_move_unit(struct unit *punit, struct unit *target_unit);
@@ -48,6 +61,7 @@
 void do_unit_connect(struct unit *punit, struct tile *ptile,
                     enum unit_activity activity);
 void do_map_click(struct tile *ptile, enum quickselect_type qtype);
+void handle_mouse_cursor(struct tile *ptile);
 
 void set_hover_state(struct unit *punit, enum cursor_hover_state state,
                     enum unit_activity connect_activity,
Index: client/gui-xaw/graphics.c
===================================================================
--- client/gui-xaw/graphics.c   (revision 11332)
+++ client/gui-xaw/graphics.c   (working copy)
@@ -237,7 +237,7 @@
   XQueryColor(display, cmap, &black);
 
   for (cursor = 0; cursor < CURSOR_LAST; cursor++) {
-    sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y);
+    sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y, 0);
 
     /* FIXME: this is entirely wrong.  It should be rewritten using
      * XcursorImageLoadCursor.  See gdkcursor-x11.c in the GTK sources for
Index: client/gui-win32/mapview.c
===================================================================
--- client/gui-win32/mapview.c  (revision 11332)
+++ client/gui-win32/mapview.c  (working copy)
@@ -58,6 +58,8 @@
 
 static HBITMAP intro_gfx;
 
+static int cursor_type = -1;
+
 extern void do_mainwin_layout();
 
 extern int seconds_to_turndone;   
@@ -66,7 +68,21 @@
 static void draw_rates(HDC hdc);
 
 /**************************************************************************
+  This function is used to animate the mouse cursor. 
+**************************************************************************/
+void anim_cursor(float time)
+{
+  int cursor_frame = (int)(time * 15.0f) % NUM_CURSOR_FRAMES;
 
+  if (cursor_type == CURSOR_DEFAULT) {
+    SetCursor (LoadCursor(NULL, IDC_ARROW));
+  } else {
+    SetCursor(cursors[cursor_type * NUM_CURSOR_FRAMES + cursor_frame]);
+  }
+}
+
+/**************************************************************************
+
 **************************************************************************/
 void map_expose(int x, int y, int width, int height)
 {
@@ -182,32 +198,52 @@
   SetWindowText(unit_info_frame, get_unit_info_label_text1(punit));
   SetWindowText(unit_info_label, get_unit_info_label_text2(punit));
 
-  if(punit) {
-    if (hover_unit != punit->id)
-      set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-    switch (hover_state) {
-      case HOVER_NONE:
-       SetCursor (LoadCursor(NULL, IDC_ARROW));
-       break;
-      case HOVER_PATROL:
-       SetCursor (cursors[CURSOR_PATROL]);
-       break;
-      case HOVER_GOTO:
-      case HOVER_CONNECT:
-       SetCursor (cursors[CURSOR_GOTO]);
-       break;
-      case HOVER_NUKE:
-       SetCursor (cursors[CURSOR_NUKE]);
-       break;
-      case HOVER_PARADROP:
-       SetCursor (cursors[CURSOR_PARADROP]);
-       break;
-    }
-  } else {
-    SetCursor (LoadCursor(NULL, IDC_ARROW));
+  switch (hover_state) {
+    case HOVER_NONE:
+      if (action_state == CURSOR_ACTION_SELECT) {
+        cursor_type = CURSOR_SELECT;
+      } else if (action_state == CURSOR_ACTION_PARATROOPER) {
+        cursor_type = CURSOR_PARADROP;
+      } else if (action_state == CURSOR_ACTION_NUKE) {
+        cursor_type = CURSOR_NUKE;
+      } else {
+        cursor_type = CURSOR_DEFAULT;
+      }
+      break;
+    case HOVER_PATROL:
+      if (action_state == CURSOR_ACTION_INVALID) {
+        cursor_type = CURSOR_INVALID;
+      } else {
+        cursor_type = CURSOR_PATROL;
+      }
+      break;
+    case HOVER_GOTO:
+      if (action_state == CURSOR_ACTION_GOTO) {
+        cursor_type = CURSOR_GOTO;
+      } else if (action_state == CURSOR_ACTION_DEFAULT) {
+        cursor_type = CURSOR_DEFAULT;
+      } else if (action_state == CURSOR_ACTION_ATTACK) {
+        cursor_type = CURSOR_ATTACK;
+      } else {
+        cursor_type = CURSOR_INVALID;
+      }
+      break;
+    case HOVER_CONNECT:
+      if (action_state == CURSOR_ACTION_INVALID) {
+        cursor_type = CURSOR_INVALID;
+      } else {
+        cursor_type = CURSOR_GOTO;
+      }
+      break;
+    case HOVER_NUKE:
+      cursor_type = CURSOR_NUKE;
+      break;
+    case HOVER_PARADROP:
+      cursor_type = CURSOR_PARADROP;
+      break;
   }
 
-    do_mainwin_layout();
+  do_mainwin_layout();
 }
 
 /**************************************************************************
Index: client/gui-win32/gui_main.c
===================================================================
--- client/gui-win32/gui_main.c (revision 11332)
+++ client/gui-win32/gui_main.c (working copy)
@@ -711,6 +711,8 @@
   return TRUE;
 }
 
+extern void anim_cursor(float time);
+
 /**************************************************************************
 
 **************************************************************************/
@@ -722,6 +724,7 @@
   bool quit = FALSE;
   bool idle;
   struct timer *callback_timer;
+  struct timer *anim_timer;
   float callback_seconds = 0;
 
   freecivhinst = GetModuleHandle(NULL); /* There is no WinMain! */
@@ -754,6 +757,7 @@
   callbacks = callback_list_new();
 
   callback_timer = new_timer_start(TIMER_USER, TIMER_ACTIVE);
+  anim_timer = new_timer_start(TIMER_USER, TIMER_ACTIVE);
 
   while (!quit) {
 
@@ -785,12 +789,15 @@
       }
     }
 
-    /* If nothing happened in the three blocks above, call an idle function */
+    /* If nothing happened in the three blocks above, call an idle function
+     * and do animations */
     if (idle && callbacks && callback_list_size(callbacks) > 0) {
       struct callback *cb = callback_list_get(callbacks, 0);
       callback_list_unlink(callbacks, cb);
       (cb->callback)(cb->data);
       free(cb);
+
+      anim_cursor(read_timer_seconds(anim_timer));
     }
 
     /* If we're idle, give up the CPU. */
@@ -799,6 +806,7 @@
     }
   }
 
+  free_timer(anim_timer);
   free_timer(callback_timer);
   callback_list_unlink_all(callbacks);
   free(callbacks);
Index: client/gui-win32/graphics.c
===================================================================
--- client/gui-win32/graphics.c (revision 11332)
+++ client/gui-win32/graphics.c (working copy)
@@ -56,7 +56,7 @@
 
 extern HINSTANCE freecivhinst;
 
-HCURSOR cursors[CURSOR_LAST];
+HCURSOR cursors[CURSOR_LAST * NUM_CURSOR_FRAMES];
 
 struct sprite *intro_gfx_sprite = NULL;
 struct sprite *radar_gfx_sprite = NULL;
@@ -95,6 +95,7 @@
 {
   enum cursor_type cursor;
   ICONINFO ii;
+  int frame, i;
 
   /* For some reason win32 lets you enter a cursor size, which
    * only works as long as it's this size. */
@@ -112,8 +113,12 @@
     int hot_x, hot_y;
     int x, y;
     int minwidth, minheight;
-    struct sprite *sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y);
 
+    for (frame = 0; frame < NUM_CURSOR_FRAMES; frame++) {
+
+    struct sprite *sprite = get_cursor_sprite(tileset, cursor, 
+                                             &hot_x, &hot_y, frame);
+
     ii.xHotspot = MIN(hot_x, width);
     ii.yHotspot = MIN(hot_y, height);
 
@@ -148,10 +153,14 @@
     ii.hbmMask = BITMAP2HBITMAP(and_bmp);
     ii.hbmColor = BITMAP2HBITMAP(xor_bmp);
 
-    cursors[cursor] = CreateIconIndirect(&ii);
+    i = cursor * NUM_CURSOR_FRAMES + frame;
 
+    cursors[i] = CreateIconIndirect(&ii);
+
     DeleteObject(ii.hbmMask);
     DeleteObject(ii.hbmColor);
+
+    }
   }
 
   bmp_free(xor_bmp);
Index: client/gui-win32/mapctrl.c
===================================================================
--- client/gui-win32/mapctrl.c  (revision 11332)
+++ client/gui-win32/mapctrl.c  (working copy)
@@ -60,7 +60,13 @@
 *************************************************************************/
 void map_handle_move(int window_x, int window_y)
 {
+  struct tile *ptile = NULL;
+
   update_line(window_x, window_y);
+
+  ptile = canvas_pos_to_tile(window_x, window_y);
+  handle_mouse_cursor(ptile);
+  hover_tile = ptile;
 }
 
 /*************************************************************************
Index: client/tilespec.c
===================================================================
--- client/tilespec.c   (revision 11332)
+++ client/tilespec.c   (working copy)
@@ -157,7 +157,7 @@
   struct sprite *spaceship[SPACESHIP_COUNT];
   struct {
     int hot_x, hot_y;
-    struct sprite *icon;
+    struct sprite *frame[NUM_CURSOR_FRAMES];
   } cursor[CURSOR_LAST];
   struct {
     struct sprite
@@ -1965,7 +1965,7 @@
   char buffer[512];
   const char dir_char[] = "nsew";
   const int W = t->normal_tile_width, H = t->normal_tile_height;
-  int i, j;
+  int i, j, f;
   
   assert(t->sprite_hash != NULL);
 
@@ -2006,14 +2006,18 @@
   }
 
   for (i = 0; i < CURSOR_LAST; i++) {
-    const char *names[CURSOR_LAST] = {"goto", "patrol", "paradrop", "nuke"};
-    struct small_sprite *ss;
+    for (f = 0; f < NUM_CURSOR_FRAMES; f++) {
+      const char *names[CURSOR_LAST] =
+               {"goto", "patrol", "paradrop", "nuke", "select", 
+               "invalid", "attack"};
+      struct small_sprite *ss;
 
-    my_snprintf(buffer, sizeof(buffer), "cursor.%s", names[i]);
-    SET_SPRITE(cursor[i].icon, buffer);
-    ss = hash_lookup_data(t->sprite_hash, buffer);
-    t->sprites.cursor[i].hot_x = ss->hot_x;
-    t->sprites.cursor[i].hot_y = ss->hot_y;
+      my_snprintf(buffer, sizeof(buffer), "cursor.%s%d", names[i], f);
+      SET_SPRITE(cursor[i].frame[f], buffer);
+      ss = hash_lookup_data(t->sprite_hash, buffer);
+      t->sprites.cursor[i].hot_x = ss->hot_x;
+      t->sprites.cursor[i].hot_y = ss->hot_y;
+    }
   }
 
   for (i = 0; i < ICON_COUNT; i++) {
@@ -4528,15 +4532,16 @@
 /**************************************************************************
   Returns a sprite for the given cursor.  The "hot" coordinates (the
   active coordinates of the mouse relative to the sprite) are placed int
-  (*hot_x, *hot_y).
+  (*hot_x, *hot_y). 
+  A cursor can consist of several frames to be used for animation.
 **************************************************************************/
 struct sprite *get_cursor_sprite(const struct tileset *t,
                                 enum cursor_type cursor,
-                                int *hot_x, int *hot_y)
+                                int *hot_x, int *hot_y, int frame)
 {
   *hot_x = t->sprites.cursor[cursor].hot_x;
   *hot_y = t->sprites.cursor[cursor].hot_y;
-  return t->sprites.cursor[cursor].icon;
+  return t->sprites.cursor[cursor].frame[frame];
 }
 
 /****************************************************************************
Index: client/tilespec.h
===================================================================
--- client/tilespec.h   (revision 11332)
+++ client/tilespec.h   (working copy)
@@ -148,9 +148,15 @@
   CURSOR_PATROL,
   CURSOR_PARADROP,
   CURSOR_NUKE,
-  CURSOR_LAST
+  CURSOR_SELECT,
+  CURSOR_INVALID,
+  CURSOR_ATTACK,
+  CURSOR_LAST,
+  CURSOR_DEFAULT,
 };
 
+#define NUM_CURSOR_FRAMES 6
+
 enum indicator_type {
   INDICATOR_BULB,
   INDICATOR_WARMING,
@@ -209,7 +215,7 @@
 struct sprite *get_nuke_explode_sprite(const struct tileset *t);
 struct sprite *get_cursor_sprite(const struct tileset *t,
                                 enum cursor_type cursor,
-                                int *hot_x, int *hot_y);
+                                int *hot_x, int *hot_y, int frame);
 const struct citybar_sprites *get_citybar_sprites(const struct tileset *t);
 struct sprite *get_icon_sprite(const struct tileset *t, enum icon_type icon);
 struct sprite *get_attention_crosshair_sprite(const struct tileset *t);

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#13773) Improved cursors, Jason Short <=