Complete.Org: Mailing Lists: Archives: freeciv-dev: February 2006:
[Freeciv-Dev] Re: (PR#13605) New civworld-in-client patch
Home

[Freeciv-Dev] Re: (PR#13605) New civworld-in-client patch

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#13605) New civworld-in-client patch
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 13 Feb 2006 00:29:26 -0800
Reply-to: bugs@xxxxxxxxxxx

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

Per I. Mathisen wrote:
> <URL: http://bugs.freeciv.org/Ticket/Display.html?id=13605 >
> 
> This is just updated to most recent svn. However, I would like suggest
> that we that one last look at this and then put it in the repository, as
> this is _the_ feature for 2.2 and everyone should ideally be helping out
> on it.

I agree.  However the patch is just too buggy to commit yet.  We should 
#if 0 out the broken parts and commit the rest.

Here is an updated version of the patch.  I made a lot of little fixes, 
but only 3 big ones:

- I think the server checking of parameters should work now.  I #if 0'd 
out some of the player code.

- The edit_mode variable was buggy in that it was maintained 
independently by server and client, but could be changed by multiple 
clients ending up with it being desynced.  In this patch I moved it into 
the game.info structure.

- Resources are no longer editable.  I fixed this at the server and 
network code but the client GUI still can't handle it.

Some notable bugs remain:

- When (global) observers use the editor there is an immediate client crash.
- Looking at the code I think there's a potential srv_main.c crash with 
editing observers.
- If you use the editor to create yourself a city the server crashes.

and I'm sure there are others.

-jason


Index: server/srv_main.c
===================================================================
--- server/srv_main.c   (revision 11583)
+++ server/srv_main.c   (working copy)
@@ -1068,6 +1068,7 @@
   pplayer->nturns_idle=0;
 
   if((!pplayer->is_alive || pconn->observer)
+     && pconn->access_level != ALLOW_HACK
      && !(type == PACKET_REPORT_REQ || type == PACKET_CONN_PONG)) {
     freelog(LOG_ERROR, _("Got a packet of type %d from a "
                         "dead or observer player"), type);
Index: server/cityturn.c
===================================================================
--- server/cityturn.c   (revision 11583)
+++ server/cityturn.c   (working copy)
@@ -523,6 +523,32 @@
   sync_cities();
 }
 
+/****************************************************************************
+  Change the city size.  Return TRUE if the city is still alive afterwards.
+****************************************************************************/
+bool city_change_size(struct city *pcity, int size)
+{
+  assert(size >= 0 && size <= MAX_CITY_SIZE);
+
+  if (size > pcity->size) {
+    while (size > pcity->size) {
+      const int old_size = pcity->size;
+
+      /* city_increase_size can silently fail. Don't get in an infinite
+       * loop. */
+      city_increase_size(pcity);
+      if (pcity->size <= old_size) {
+       return TRUE;
+      }
+    }
+    return TRUE;
+  } else if (size < pcity->size) {
+    return city_reduce_size(pcity, pcity->size - size);
+  } else {
+    return TRUE;
+  }
+}
+
 /**************************************************************************
   Check whether the population can be increased or
   if the city is unable to support a 'settler'...
Index: server/cityturn.h
===================================================================
--- server/cityturn.h   (revision 11583)
+++ server/cityturn.h   (working copy)
@@ -27,6 +27,7 @@
 void auto_arrange_workers(struct city *pcity); /* will arrange the workers */
 void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr);
 
+bool city_change_size(struct city *pcity, int new_size);
 bool city_reduce_size(struct city *pcity, int pop_loss);
 void send_global_city_turn_notifications(struct conn_list *dest);
 void send_city_turn_notifications(struct conn_list *dest, struct city *pcity);
Index: server/edithand.c
===================================================================
--- server/edithand.c   (revision 0)
+++ server/edithand.c   (revision 0)
@@ -0,0 +1,340 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2005 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "events.h"
+#include "fcintl.h"
+#include "log.h"
+#include "shared.h"
+#include "support.h"
+
+#include "game.h"
+#include "government.h"
+#include "map.h"
+#include "movement.h"
+#include "terrain.h"
+
+#include "citytools.h"
+#include "cityturn.h"
+#include "gamehand.h"
+#include "plrhand.h"
+#include "unittools.h"
+#include "hand_gen.h"
+#include "maphand.h"
+
+/****************************************************************************
+  Handles new tile information from the client, to make local edits to
+  the map.
+****************************************************************************/
+void handle_edit_mode(struct connection *pc, bool is_edit_mode)
+{
+  if (!game.info.is_edit_mode && is_edit_mode) {
+    /* Someone could be cheating! Warn people. */
+    notify_conn(NULL, NULL, E_SETTING,
+               _(" *** Server set to edit mode! *** "));
+  }
+  if (!EQ(game.info.is_edit_mode, is_edit_mode)) {
+    game.info.is_edit_mode = is_edit_mode;
+    send_game_info(NULL);
+  }
+}
+
+/****************************************************************************
+  Handles new tile information from the client, to make local edits to
+  the map.
+
+  TODO: Handle resources.
+****************************************************************************/
+void handle_edit_tile(struct connection *pc, int x, int y,
+                      Terrain_type_id terrain, Resource_type_id resource,
+                     bv_special special)
+{
+  struct tile *ptile = map_pos_to_tile(x, y);
+  struct terrain *pterrain = get_terrain(terrain), *old_terrain;
+  struct resource *presource = get_resource(resource);
+
+  if (pc->access_level != ALLOW_HACK || !game.info.is_edit_mode
+      || !ptile || !pterrain) {
+    return;
+  }
+
+  old_terrain = ptile->terrain;
+
+  specials_iterate(s) {
+    if (contains_special(special, s) && !tile_has_special(ptile, s)) {
+      tile_add_special(ptile, s);
+    } else if (!contains_special(special, s) && tile_has_special(ptile, s)) {
+      tile_remove_special(ptile, s);
+    }
+  } specials_iterate_end;
+
+  tile_set_resource(ptile, presource); /* May be NULL. */
+
+  tile_change_terrain(ptile, pterrain);
+
+  /* Handle global side effects. */
+  check_terrain_change(ptile, old_terrain);
+
+  /* update playertiles and send updates to the clients */
+  update_tile_knowledge(ptile);
+  send_tile_info(NULL, ptile);
+}
+
+/****************************************************************************
+  Handles unit information from the client, to make edits to units.
+
+  FIXME: We do some checking of input but not enough.
+****************************************************************************/
+void handle_edit_unit(struct connection *pc, struct packet_edit_unit *packet)
+{
+  struct tile *ptile = map_pos_to_tile(packet->x, packet->y);
+  struct unit_type *punittype = get_unit_type(packet->type);
+  struct player *pplayer = get_player(packet->owner);
+  struct unit *punit;
+
+  if (pc->access_level != ALLOW_HACK || !game.info.is_edit_mode
+      || !ptile || !punittype || !pplayer
+      || (packet->create_new && packet->delete)) {
+    return;
+  }
+
+  /* check if a unit with this id already exists on this tile, if
+   * so, then this is an edit, otherwise, we create a new unit */
+  if (packet->create_new) {
+    struct city *homecity
+      = player_find_city_by_id(pplayer, packet->homecity);
+
+    if (is_non_allied_unit_tile(ptile, pplayer)) {
+      notify_player(pplayer, ptile, E_BAD_COMMAND,
+                    _("Cannot create unit on enemy tile."));
+      return;
+    }
+    /* FIXME: should use can_unit_exist_at_tile here. */
+    if (!(ptile->city
+         && !(is_sailing_unittype(punittype)
+              && !is_ocean_near_tile(ptile)))
+       && !is_native_terrain(punittype, ptile->terrain)) {
+      notify_player(pplayer, ptile, E_BAD_COMMAND,
+                    _("Cannot create %s unit on this terrain."),
+                    punittype->name);
+      return;
+    }
+
+    punit = create_unit(pplayer, ptile, punittype,
+                        packet->veteran,
+                       homecity ? homecity->id : 0,
+                       packet->movesleft);
+  } else {
+    punit = find_unit_by_id(packet->id);
+    if (!punit) {
+      freelog(LOG_ERROR, "can't find unit to edit!");
+      return;
+    } 
+  }
+
+  if (packet->delete) {
+    wipe_unit(punit);
+    return;
+  }
+
+  punit->hp = CLIP(0, packet->hp, punittype->hp);
+  punit->activity_count = packet->activity_count;
+  punit->fuel = CLIP(0, packet->fuel, punittype->fuel);
+  punit->paradropped = BOOL_VAL(packet->paradropped);
+  if (find_unit_by_id(packet->transported_by)) {
+    punit->transported_by = packet->transported_by;
+  } else {
+    punit->transported_by = -1;
+  }
+
+  /* FIXME: resolve_unit_stacks? */
+  /* FIXME: refresh homecity? */
+
+  /* update playertiles and send updates to the clients */
+  update_tile_knowledge(ptile);
+  send_unit_info(NULL, punit);
+}
+
+/****************************************************************************
+  We do some checking of input but not enough.
+****************************************************************************/
+void handle_edit_city(struct connection *pc, struct packet_edit_city *packet)
+{
+  struct tile *ptile = map_pos_to_tile(packet->x, packet->y);
+  struct city *pcity;
+  struct player *pplayer = get_player(packet->owner);
+  int i;
+  int old_traderoutes[NUM_TRADEROUTES];
+
+  if (pc->access_level != ALLOW_HACK || !game.info.is_edit_mode
+      || !pplayer || !ptile) {
+    return;
+  }
+
+  pcity = tile_get_city(ptile);
+  if (!pcity) {
+    if (!city_can_be_built_here(ptile, NULL)) {
+      notify_player(pplayer, ptile, E_BAD_COMMAND,
+                    _("Cannot build city on this tile."));
+      return;
+    }
+
+    /* new city */
+    create_city(pplayer, ptile, city_name_suggestion(pplayer, ptile));
+    pcity = tile_get_city(ptile);
+
+    if (!pcity) {
+      notify_player(pplayer, ptile, E_BAD_COMMAND,
+                   _("Could not create city."));
+      return;
+    }
+  }
+
+  if (!city_change_size(pcity, CLIP(0, packet->size, MAX_CITY_SIZE))) {
+    /* City died. */
+    return;
+  }
+
+  /* FIXME: should probably be a different packet_edit_trade_route. */
+  for (i = 0; i < NUM_TRADEROUTES; i++) {
+    old_traderoutes[i] = pcity->trade[i];
+  }
+  for (i = 0; i < NUM_TRADEROUTES; i++) {
+    struct city *oldcity = find_city_by_id(old_traderoutes[i]);
+    struct city *newcity = find_city_by_id(packet->trade[i]);
+
+    /*
+     * This complicated bit of logic either deletes or creates trade routes.
+     *
+     * FIXME: What happens if (oldcity && newcity && oldcity != newcity) ?
+     */
+    if (oldcity && !newcity) {
+      remove_trade_route(pcity, oldcity);
+    } else if (newcity && !oldcity && can_cities_trade(pcity, newcity)) {
+      establish_trade_route(pcity, newcity);
+    }
+  }
+
+  pcity->food_stock = MAX(packet->food_stock, 0);
+  pcity->shield_stock = MAX(packet->shield_stock, 0);
+
+  pcity->did_buy = packet->did_buy;
+  pcity->did_sell = packet->did_sell;
+  pcity->was_happy = packet->was_happy;
+  pcity->airlift = packet->airlift;
+
+  pcity->turn_last_built = CLIP(0, packet->turn_last_built, game.info.turn);
+  pcity->turn_founded = CLIP(0, packet->turn_founded, game.info.turn);
+  pcity->before_change_shields = MAX(packet->before_change_shields, 0);
+  pcity->disbanded_shields = MAX(packet->disbanded_shields, 0);
+  pcity->caravan_shields = MAX(packet->caravan_shields, 0);
+  pcity->last_turns_shield_surplus
+    = MAX(packet->last_turns_shield_surplus, 0);
+
+  /* FIXME: Might want to check these values. */
+  pcity->changed_from.is_unit = packet->changed_from_is_unit;
+  pcity->changed_from.value = packet->changed_from_id;
+
+  /* make everything sane.  Note some refreshes may already have been
+   * done above. */
+  city_refresh(pcity);
+
+  /* send update back to client */
+  send_city_info(NULL, pcity);  
+}
+
+/**************************************************************************
+ right now there are no checks whatsoever in the server. beware.
+***************************************************************************/
+void handle_edit_player(struct connection *pc, 
+                        struct packet_edit_player *packet)
+{
+  struct player *pplayer = get_player(packet->playerno);
+  struct nation_type *pnation = get_nation_by_idx(packet->nation);
+  struct team *pteam = team_get_by_id(packet->team);
+  struct government *pgov = get_government(packet->government);
+#if 0 /* Unused: see below */
+  struct player_research *research;
+  int i;
+#endif
+
+  /* FIXME: Are NULL teams allowed? */
+  if (pc->access_level != ALLOW_HACK || !game.info.is_edit_mode
+      || !pplayer || !pnation || !pteam || !pgov) {
+    return;
+  }
+
+  sz_strlcpy(pplayer->name, packet->name);
+  sz_strlcpy(pplayer->username, packet->username);
+
+  pplayer->nation = pnation;
+  pplayer->is_male = packet->is_male;
+  pplayer->team = pteam;
+
+  pplayer->economic.gold = MAX(packet->gold, 0);
+  pplayer->economic.tax = CLIP(0, packet->tax, 100);
+  pplayer->economic.science = CLIP(0, packet->science, 100 - packet->tax);
+  pplayer->economic.luxury = 100 - packet->tax - packet->science;
+
+  pplayer->government = pgov;
+  pplayer->target_government = get_government(packet->target_government);
+
+#if 0 /* FIXME: These values need to be checked */
+  pplayer->embassy = packet->embassy;
+  pplayer->gives_shared_vision = packet->gives_shared_vision;
+  pplayer->city_style = packet->city_style;
+  for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
+    pplayer->ai.love[i] = packet->love[i];
+  }
+
+  for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
+    pplayer->diplstates[i].type = packet->diplstates[i].type;
+    pplayer->diplstates[i].turns_left = packet->diplstates[i].turns_left;
+    pplayer->diplstates[i].contact_turns_left
+      = packet->diplstates[i].contact_turns_left;
+    pplayer->diplstates[i].has_reason_to_cancel
+      = packet->diplstates[i].has_reason_to_cancel;
+  }
+
+  for (i = 0; i < B_LAST/*game.num_impr_types*/; i++) {
+     pplayer->small_wonders[i] = packet->small_wonders[i];
+  }
+  
+  /* FIXME: What's an AI value doing being set here? */
+  pplayer->ai.science_cost = packet->science_cost;
+
+  pplayer->bulbs_last_turn = packet->bulbs_last_turn;
+
+  research = get_player_research(pplayer);
+  research->bulbs_researched = packet->bulbs_researched;
+  research->techs_researched = packet->techs_researched;
+  research->researching = packet->researching;
+  research->future_tech = packet->future_tech;
+  research->tech_goal = packet->tech_goal;
+
+  pplayer->is_alive = packet->is_alive;
+  pplayer->ai.barbarian_type = packet->barbarian_type;
+  pplayer->revolution_finishes = packet->revolution_finishes;
+  pplayer->ai.control = packet->ai;
+#endif
+
+  /* TODO: and probably a bunch more stuff here */
+
+  /* send update back to client */
+  send_player_info(NULL, pplayer);  
+}
Index: server/unittools.c
===================================================================
--- server/unittools.c  (revision 11583)
+++ server/unittools.c  (working copy)
@@ -2061,7 +2061,8 @@
   }
 
   /* Safe terrain according to player map? */
-  if (!is_native_terrain(punit, map_get_player_tile(ptile, pplayer)->terrain)) 
{
+  if (!is_native_terrain(punit->type,
+                         map_get_player_tile(ptile, pplayer)->terrain)) {
     notify_player(pplayer, ptile, E_BAD_COMMAND,
                      _("This unit cannot paradrop into %s."),
                        get_name(map_get_player_tile(ptile, pplayer)->terrain));
Index: server/Makefile.am
===================================================================
--- server/Makefile.am  (revision 11583)
+++ server/Makefile.am  (working copy)
@@ -39,6 +39,7 @@
                diplhand.h      \
                diplomats.c     \
                diplomats.h     \
+               edithand.c      \
                gamehand.c      \
                gamehand.h      \
                gotohand.c      \
Index: common/packets.def
===================================================================
--- common/packets.def  (revision 11583)
+++ common/packets.def  (working copy)
@@ -199,6 +199,8 @@
 type BV_ROLES          = bitvector(bv_roles)
 type BV_TERRAIN_FLAGS  = bitvector(bv_terrain_flags)
 type BV_CITY_OPTIONS    = bitvector(bv_city_options)
+type BV_SPECIAL         = bitvector(bv_special)
+type BV_PLAYER          = bitvector(bv_player)
 type DIPLSTATE         = diplstate(struct player_diplstate)
 type VISION            = uint32(unsigned int)
 
@@ -242,7 +244,7 @@
   Spaceship
   Ruleset
 
-The last used packet number is 117.
+The last used packet number is 129.
 ****************************************************/
 
 
@@ -357,6 +359,7 @@
   UINT8 aifill;
 
   BOOL is_new_game;   # TRUE only in pregame for "new" (not loaded) games
+  BOOL is_edit_mode;  # If set, editing is allowed
   FLOAT seconds_to_phasedone;
   UINT32 timeout;
   TURN turn;
@@ -1347,3 +1350,92 @@
   STRING graphic_str[MAX_LEN_NAME];
   STRING graphic_alt[MAX_LEN_NAME];
 end
+
+/************** Editing hash packets **********************/
+ 
+PACKET_EDIT_TILE=125;cs,handle-per-conn,dsend
+  COORD x, y; key
+  TERRAIN terrain;
+  RESOURCE resource;
+  BV_SPECIAL special;
+end
+
+PACKET_EDIT_UNIT=126;cs,handle-per-conn,lsend
+  UNIT id; key
+  BOOL create_new, delete;
+  PLAYER owner;
+  COORD x,y;
+  CITY homecity;
+
+  UINT8 veteran;
+  BOOL paradropped;
+
+  UNIT_TYPE type;
+  UNIT transported_by; /* Only valid if transported is set. */
+  UINT8 movesleft, hp, fuel, activity_count;
+
+  # Other values of the unit (done_moving, orders, etc.) can be set directly
+  # through regular packets.  Some values are simply caches and cannot
+  # be controlled even by a privilidged client.
+end
+
+PACKET_EDIT_CITY=127;cs,handle-per-conn,lsend
+  PLAYER owner;
+  COORD x, y; key # cities are keyed by tile not by ID
+  UINT8 size;
+  UINT16 food_stock, shield_stock;
+  UINT16 trade[NUM_TRADEROUTES];
+  TURN turn_last_built;
+  UINT8 changed_from_id;
+  BOOL changed_from_is_unit;
+  UINT16 before_change_shields;
+  UINT16 disbanded_shields;
+  UINT16 caravan_shields;
+  UINT16 last_turns_shield_surplus;
+  BV_IMPRS improvements;
+  BOOL did_buy, did_sell, was_happy, airlift, diplomat_investigate;
+  TURN turn_founded;
+
+  # Other values of the city (worker placement, worklist, etc.) can be set
+  # directly through regular packets.  Some values are simply caches and
+  # cannot be controlled even by a privilidged client.
+end
+
+PACKET_EDIT_PLAYER=128;cs,handle-per-conn,lsend
+  PLAYER playerno; key 
+  STRING name[MAX_LEN_NAME];
+  STRING username[MAX_LEN_NAME];
+  BOOL is_observer;
+  BOOL is_male;
+  GOVERNMENT government;
+  GOVERNMENT target_government;
+  BV_PLAYER embassy;
+  UINT8 city_style;
+  NATION nation;
+  TEAM team;
+  BOOL phase_done;
+  TURN nturns_idle;
+  BOOL is_alive;
+  DIPLSTATE diplstates[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
+  GOLD gold;
+  PERCENT tax, science,luxury;
+  UINT16 bulbs_last_turn;
+  UINT32 bulbs_researched;
+  UINT32 techs_researched;
+  UINT8 researching;
+  UINT16 science_cost;
+  UINT16 future_tech;
+  UINT8 tech_goal;
+  BOOL is_connected;
+  TURN revolution_finishes;
+  BOOL ai;
+  UINT8 barbarian_type;
+  VISION gives_shared_vision;
+  BIT_STRING inventions[A_LAST+1];
+  SINT16 love[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
+  UINT16 small_wonders[B_LAST]; diff
+end
+
+PACKET_EDIT_MODE=129;cs,handle-per-conn,dsend
+  BOOL state;
+end
Index: common/city.c
===================================================================
--- common/city.c       (revision 11583)
+++ common/city.c       (working copy)
@@ -2376,7 +2376,15 @@
   /* Set up the worklist */
   init_worklist(&pcity->worklist);
 
-  {
+  if (!ptile) {
+    /* HACK: if a "dummy" city is created with no tile, the regular
+     * operations to choose a build target would fail.  This situation
+     * probably should be forbidden, but currently it might happen during
+     * map editing.  This fallback may also be dangerous if it gives an
+     * invalid production. */
+    pcity->production.is_unit = TRUE;
+    pcity->production.value = 0;
+  } else {
     struct unit_type *u = best_role_unit(pcity, L_FIRSTBUILD);
 
     if (u) {
Index: common/movement.c
===================================================================
--- common/movement.c   (revision 11583)
+++ common/movement.c   (working copy)
@@ -174,17 +174,17 @@
     return TRUE;
   }
 
-  return is_native_terrain(punit, ptile->terrain);
+  return is_native_terrain(punit->type, ptile->terrain);
 }
 
 /****************************************************************************
   This terrain is native to unit. Units that require fuel dont survive
   even on native terrain. All terrains are native to air units.
 ****************************************************************************/
-bool is_native_terrain(const struct unit *punit,
+bool is_native_terrain(const struct unit_type *punittype,
                        const struct terrain *pterrain)
 {
-  switch (punit->type->move_type) {
+  switch (punittype->move_type) {
   case LAND_MOVING:
     return !is_ocean(pterrain);
   case SEA_MOVING:
Index: common/movement.h
===================================================================
--- common/movement.h   (revision 11583)
+++ common/movement.h   (working copy)
@@ -30,7 +30,7 @@
 bool is_heli_unittype(const struct unit_type *punittype);
 bool is_ground_unittype(const struct unit_type *punittype);
 
-bool is_native_terrain(const struct unit *punit,
+bool is_native_terrain(const struct unit_type *punittype,
                        const struct terrain *pterrain);
 bool can_unit_exist_at_tile(const struct unit *punit, const struct tile 
*ptile);
 bool can_unit_survive_at_tile(const struct unit *punit,
Index: common/terrain.h
===================================================================
--- common/terrain.h    (revision 11583)
+++ common/terrain.h    (working copy)
@@ -41,6 +41,17 @@
 
 BV_DEFINE(bv_special, S_LAST);
 
+#define specials_iterate(special)                                          \
+{                                                                          \
+  enum tile_special_type special;                                          \
+                                                                           \
+  for (special = 0; special < S_LAST; special++) {
+
+#define specials_iterate_end                                               \
+  }                                                                        \
+}
+
+
 #define T_NONE (NULL) /* A special flag meaning no terrain type. */
 #define T_UNKNOWN (NULL) /* An unknown terrain. */
 
Index: common/game.c
===================================================================
--- common/game.c       (revision 11583)
+++ common/game.c       (working copy)
@@ -247,6 +247,7 @@
   game.info.government_when_anarchy_id = G_MAGIC;   /* flag */
 
   game.info.is_new_game   = TRUE;
+  game.info.is_edit_mode = FALSE;
   game.simultaneous_phases_stored = GAME_DEFAULT_SIMULTANEOUS_PHASES;
   game.timeoutint    = GAME_DEFAULT_TIMEOUTINT;
   game.timeoutintinc = GAME_DEFAULT_TIMEOUTINTINC;
Index: common/map.c
===================================================================
--- common/map.c        (revision 11583)
+++ common/map.c        (working copy)
@@ -623,7 +623,7 @@
 
   if (punit) {
     pclass = get_unit_class(punit->type);
-    native = is_native_terrain(punit, t2->terrain);
+    native = is_native_terrain(punit->type, t2->terrain);
   }
 
   if (game.info.slow_invasions
Index: client/control.c
===================================================================
--- client/control.c    (revision 11583)
+++ client/control.c    (working copy)
@@ -2712,3 +2712,11 @@
     set_unit_focus_and_select(punit2);
   } unit_list_iterate_end;
 }
+
+/**************************************************************************
+  Toggle editor mode in the server.
+**************************************************************************/
+void key_editor_toggle(void)
+{
+  dsend_packet_edit_mode(&aconnection, !game.info.is_edit_mode);
+}
Index: client/gui-gtk-2.0/menu.c
===================================================================
--- client/gui-gtk-2.0/menu.c   (revision 11583)
+++ client/gui-gtk-2.0/menu.c   (working copy)
@@ -40,6 +40,7 @@
 #include "connectdlg.h"
 #include "control.h"
 #include "dialogs.h"
+#include "editdlg.h"
 #include "finddlg.h"
 #include "gotodlg.h"
 #include "graphics.h"
@@ -158,6 +159,9 @@
   MENU_REPORT_MESSAGES,
   MENU_REPORT_DEMOGRAPHIC,
   MENU_REPORT_SPACESHIP,
+ 
+  MENU_EDITOR_TOGGLE,
+  MENU_EDITOR_TOOLS,
 
   MENU_HELP_LANGUAGES,
   MENU_HELP_CONNECTING,
@@ -580,6 +584,21 @@
   }
 }
 
+/****************************************************************************
+  Callback function for when an item is chosen from the "editor" menu.
+****************************************************************************/
+static void editor_menu_callback(gpointer callback_data,
+                                 guint callback_action, GtkWidget *widget)
+{   
+  switch(callback_action) {
+  case MENU_EDITOR_TOGGLE:
+    key_editor_toggle();
+    break;
+  case MENU_EDITOR_TOOLS:
+    editdlg_show_tools();
+    break;
+  }
+}
 
 /****************************************************************
 ...
@@ -905,6 +924,15 @@
        reports_menu_callback,  MENU_REPORT_DEMOGRAPHIC                         
        },
   { "/" N_("Reports") "/" N_("S_paceship"),            "F12",
        reports_menu_callback,  MENU_REPORT_SPACESHIP                           
        },
+
+  /* Editor menu */
+  { "/" N_("_Editor"), NULL, NULL, 0, "<Branch>" },
+  { "/" N_("_Editor") "/tearoff1", NULL, NULL, 0, "<Tearoff>" },
+  { "/" N_("_Editor") "/" N_("Editing Mode"), NULL,
+    editor_menu_callback, MENU_EDITOR_TOGGLE, "<CheckItem>" },
+  { "/" N_("_Editor") "/" N_("_Tools"), NULL,
+    editor_menu_callback, MENU_EDITOR_TOOLS },
+
   /* Help menu ... */
   { "/" N_("_Help"),                                   NULL,
        NULL,                   0,                                      
"<Branch>"      },
@@ -1296,6 +1324,9 @@
 
     menus_set_active("<main>/_View/_Full Screen", fullscreen_mode);
 
+    menus_set_active("<main>/_Editor/Editing Mode", game.info.is_edit_mode);
+    menus_set_sensitive("<main>/_Editor/_Tools", game.info.is_edit_mode);
+
     /* Remaining part of this function: Update Orders menu */
 
     if (!can_client_issue_orders()) {
Index: client/gui-gtk-2.0/editdlg.c
===================================================================
--- client/gui-gtk-2.0/editdlg.c        (revision 0)
+++ client/gui-gtk-2.0/editdlg.c        (revision 0)
@@ -0,0 +1,479 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2005 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "fcintl.h"
+#include "shared.h"
+#include "support.h"
+
+#include "game.h"
+#include "government.h"
+#include "packets.h"
+
+#include "editor.h"
+
+#include "editdlg.h"
+#include "gui_main.h"
+#include "gui_stuff.h"
+
+#define TABLE_WIDTH 3
+#define TOOL_WIDTH 5
+
+typedef struct {
+  const char *name;
+  int paint;
+} paint_item;
+
+static paint_item *terrains;
+
+static paint_item specials[] = {
+  { N_("clear all"), S_LAST },
+  { NULL, S_ROAD },
+  { NULL, S_IRRIGATION },
+  { NULL, S_RAILROAD },
+  { NULL, S_MINE },
+  { NULL, S_POLLUTION },
+  { NULL, S_HUT },
+  { NULL, S_FORTRESS },
+  { NULL, S_RIVER },
+  { NULL, S_FARMLAND },
+  { NULL, S_AIRBASE },
+  { NULL, S_FALLOUT }
+};
+
+static char *tool_names[ETOOL_LAST] = {
+  N_("Paint"), N_("Unit"), N_("City"), N_("Player"), N_("Delete")
+};
+
+#define SPECIALS_NUM ARRAY_SIZE(specials)
+
+static GtkWidget *toolwin;
+static GtkWidget *notebook;
+
+static GList *tool_group;
+static GList *map_group;
+
+/*****************************************************************************
+ handle the toggle buttons' toggle events
+*****************************************************************************/
+static void tool_toggled(GtkWidget *widget, int tool)
+{
+  editor_set_selected_tool_type(tool);
+  /* switch pages if necessary */
+  gtk_notebook_set_page(GTK_NOTEBOOK(notebook), tool);
+}
+
+/****************************************************************************
+  callback for button that is in a group. we want to untoggle all the
+  other buttons in the group
+*****************************************************************************/
+static void toggle_group_callback(GtkWidget *w, gpointer data)
+{
+  int i;
+  GList *group = (GList *)data;
+
+  /* untoggle all the other buttons in the group toggle this one */
+  for (i = 0 ; i < g_list_length(group); i++) {
+    GtkWidget *button = (GtkWidget *)g_list_nth_data(group, i);
+    int id = (int)g_object_get_data(G_OBJECT(button), "sigid");
+
+    g_signal_handlers_block_by_func(button,
+                                    G_CALLBACK(toggle_group_callback), data);
+    g_signal_handler_block(button, id);
+    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (button == w));
+    g_signal_handlers_unblock_by_func(button,
+                                      G_CALLBACK(toggle_group_callback), data);
+    g_signal_handler_unblock(button, id);
+  }
+}
+
+/**************************************************************************
+ fill the tools[]-array with the missing values: pixmaps, buttons, ids
+***************************************************************************/
+static void create_tools(GtkWidget *win, GtkWidget *parent)
+{
+  GtkWidget *button, *table;
+  int i, sig;
+
+  table = gtk_table_new(TOOL_WIDTH, 2, FALSE);
+  gtk_box_pack_start(GTK_BOX(parent), table, FALSE, TRUE, 0);
+
+  for (i = 0; i < ETOOL_LAST; i++) {
+    button = gtk_toggle_button_new_with_label(tool_names[i]);
+
+    /* must do this here. we can't call tool_toggled on palette creation */
+    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+                                (i == 0) ? TRUE : FALSE);
+
+    sig = g_signal_connect(button, "toggled",
+                           G_CALLBACK(tool_toggled), GINT_TO_POINTER(i));
+
+    tool_group = g_list_append(tool_group, button);
+
+    /* add this group and the signal id to widget internal data */
+    g_signal_connect(button, "toggled", G_CALLBACK(toggle_group_callback),
+                     (gpointer)tool_group);
+    g_object_set_data(G_OBJECT(button), "sigid", GINT_TO_POINTER(sig));
+
+    /* do the rest for both types of buttons */
+    gtk_table_attach(GTK_TABLE(table), button,
+                     i % TOOL_WIDTH, i % TOOL_WIDTH + 1, i / TOOL_WIDTH,
+                     i / TOOL_WIDTH + 1, GTK_FILL, GTK_FILL, 1, 1);
+
+  }
+
+  gtk_widget_show_all(table);
+}
+
+/****************************************************************************
+ select a paint type and depress the particular button
+*****************************************************************************/
+static void set_selected_paint(GtkWidget *w, gpointer data)
+{
+  int id = GPOINTER_TO_INT(data);
+  enum editor_paint_type paint
+    = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(w), "paint"));
+
+  editor_set_selected_paint_type(paint);
+
+  assert(paint >= 0 && paint < EPAINT_LAST);
+  switch(paint){
+  case EPAINT_TERRAIN:
+    editor_set_selected_terrain(get_terrain(terrains[id].paint));
+    break;
+  case EPAINT_SPECIAL:
+    editor_set_selected_special(specials[id].paint);
+    break;
+  case EPAINT_LAST:
+    break;
+  }
+}
+
+/******************************************************************
+ KILLOFF: this is for demonstration purposes only (and not demonstration of
+          coding goodness to be sure!)
+*******************************************************************/
+static void unit_callback(GtkSpinButton *spinbutton, gpointer data)
+{
+  struct unit *punit = editor_get_selected_unit();
+  int param = GPOINTER_TO_INT(data);
+
+  switch (param) {
+  case 0:
+    punit->owner = get_player(gtk_spin_button_get_value_as_int(spinbutton));
+    break;
+  case 1:
+    punit->type = get_unit_type(gtk_spin_button_get_value_as_int(spinbutton));
+    break;
+  case 2:
+    punit->moves_left = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 3:
+    punit->activity = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 4:
+    punit->activity_target = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 5:
+    punit->activity_count = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  }
+}
+
+/******************************************************************
+ KILLOFF: this is for demonstration purposes only (and not demonstration of
+          coding goodness to be sure!)
+*******************************************************************/
+static void city_callback(GtkSpinButton *spinbutton, gpointer data)
+{
+  struct city *pcity = editor_get_selected_city();
+  int param = GPOINTER_TO_INT(data);
+
+  switch (param) {
+  case 0:
+    pcity->owner = get_player(gtk_spin_button_get_value_as_int(spinbutton));
+    break;
+  case 1:
+    pcity->size = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 2:
+    pcity->food_stock = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 3:
+    pcity->shield_stock = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  case 4:
+    pcity->pollution = gtk_spin_button_get_value_as_int(spinbutton);
+    break;
+  }
+}
+
+#if 0
+/******************************************************************
+ KILLOFF: this is for demonstration purposes only (and not demonstration of
+          coding goodness to be sure!)
+*******************************************************************/
+static void player_callback(GtkSpinButton *spinbutton, gpointer data)
+{
+}
+#endif
+
+/******************************************************************
+...
+*******************************************************************/
+static GtkWidget *create_map_palette(void)
+{
+  GtkWidget *button, *vbox;
+  GtkWidget *table = gtk_table_new(12, TABLE_WIDTH, TRUE);
+  int i, j, sig; 
+  int magic[3] = { 0, 5, 11 }; /* magic numbers to make the table look good */
+  int types_num[] = { game.control.terrain_count, SPECIALS_NUM };
+  paint_item *ptype[EPAINT_LAST] = { NULL, specials };
+  
+  terrains = fc_realloc(terrains,
+                       game.control.terrain_count * sizeof(*terrains));
+  ptype[0] = terrains;
+  
+  vbox = gtk_vbox_new(TRUE, 5);
+  
+  for(i = 0; i < EPAINT_LAST; i++) {
+    for(j = 0; j < types_num[i]; j++) {
+      paint_item *item = &ptype[i][j];
+
+      switch(i) {
+      case EPAINT_TERRAIN:
+        item->paint = j;
+        item->name = get_terrain(item->paint)->name;
+        break;
+      case EPAINT_SPECIAL:
+        if (!item->name) {
+         item->name = get_special_name(item->paint);
+       }
+        break;
+      }
+
+      button = gtk_toggle_button_new_with_label(item->name);
+
+      gtk_table_attach(GTK_TABLE(table), button,
+                       j % TABLE_WIDTH, j % TABLE_WIDTH + 1,
+                       j / TABLE_WIDTH + magic[i],  
+                       j / TABLE_WIDTH + magic[i] + 1, 
+                       GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
+
+      gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
+                                  (i == 0 && j == 0) ? TRUE : FALSE);
+      sig = g_signal_connect(button, "toggled",
+                           G_CALLBACK(set_selected_paint), GINT_TO_POINTER(j));
+      gtk_object_set_data(GTK_OBJECT(button), "paint", GINT_TO_POINTER(i));
+
+      /* add this button to a group */
+      map_group = g_list_append(map_group, button);
+
+      /* add this group and the signal id to widget internal data */
+      g_signal_connect(button, "toggled", G_CALLBACK(toggle_group_callback),
+                       (gpointer)map_group);
+      g_object_set_data(G_OBJECT(button), "sigid", GINT_TO_POINTER(sig));
+    }
+  }
+  
+  editor_set_selected_terrain(get_terrain(terrains[0].paint));
+  gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 5);
+  gtk_widget_show_all(vbox);
+  
+  return vbox;
+}
+
+/******************************************************************
+ ...
+*******************************************************************/
+static GtkWidget *create_units_palette(void)
+{
+#define NUM_PARAMS 6
+
+  GtkWidget *hbox, *vbox, *label, *sb;
+  GtkAdjustment *adj;
+  int i;
+  struct unit *punit = editor_get_selected_unit();
+
+  const char *names[NUM_PARAMS] = { _("Owner"),
+                                   _("Type"),
+                                    _("Moves Left"),
+                                   _("Activity"),
+                                    _("Activity Target"),
+                                   _("Activity Count") };
+  int inits[NUM_PARAMS][3] = {
+    {punit->owner->player_no, 0, game.info.nplayers - 1},
+    {punit->type->index, 0, game.control.num_unit_types - 1},
+    {punit->moves_left, 0, 200},
+    {punit->activity, 0, ACTIVITY_LAST},
+    {punit->activity_target, 0, S_LAST},
+    {punit->activity_count, 0, 200}
+  };
+
+  vbox = gtk_vbox_new(FALSE, 5);
+
+  for (i = 0; i < NUM_PARAMS; i++) {
+    adj = (GtkAdjustment *)gtk_adjustment_new(inits[i][0], inits[i][1], 
+                                              inits[i][2], 1.0, 5.0, 5.0);
+    hbox = gtk_hbox_new(FALSE, 5);
+    sb = gtk_spin_button_new(adj, 1, 0);
+    label = gtk_label_new(names[i]);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), sb, TRUE, TRUE, 0);
+
+    g_signal_connect(sb, "value-changed", G_CALLBACK(unit_callback),
+                     GINT_TO_POINTER(i));
+  }
+
+  return vbox;
+#undef NUM_PARAMS
+}
+
+/******************************************************************
+ ...
+*******************************************************************/
+static GtkWidget *create_city_palette(void)
+{
+#define NUM_PARAMS 5
+
+  GtkWidget *hbox, *vbox, *label, *sb;
+  GtkAdjustment *adj;
+  int i;
+  struct city *pcity = editor_get_selected_city();
+
+  const char *names[NUM_PARAMS] = { _("Owner"), _("Size"),
+                                    _("Food"), _("Shields"),
+                                    _("Pollution") };
+  int inits[NUM_PARAMS][3] = {
+    {pcity->owner->player_no, 0, game.info.nplayers - 1},
+    {pcity->size, 1, 63},
+    {pcity->food_stock, 0, 10000},
+    {pcity->shield_stock, 0, 10000},
+    {pcity->pollution, 0, 2000}
+  };
+
+  vbox = gtk_vbox_new(FALSE, 5);
+
+  for (i = 0; i < NUM_PARAMS; i++) {
+    adj = (GtkAdjustment *)gtk_adjustment_new(inits[i][0], inits[i][1], 
+                                              inits[i][2], 1.0, 5.0, 5.0);
+    hbox = gtk_hbox_new(FALSE, 5);
+    sb = gtk_spin_button_new(adj, 1, 0);
+    label = gtk_label_new(names[i]);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), sb, TRUE, TRUE, 0);
+
+    g_signal_connect(sb, "value-changed", G_CALLBACK(city_callback),
+                     GINT_TO_POINTER(i));
+  }
+
+  return vbox;
+#undef NUM_PARAMS
+}
+
+/******************************************************************
+ ...
+*******************************************************************/
+static GtkWidget *create_player_palette(void)
+{
+  GtkWidget *vbox;
+
+  vbox = gtk_vbox_new(FALSE, 5);
+
+  return vbox;
+}
+
+/******************************************************************
+...
+*******************************************************************/
+static void create_toolsdlg(void)
+{
+  GtkWidget *palette, *vbox;
+
+  if (toolwin) {
+    return;
+  }
+
+  editor_init_tools();
+
+  toolwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  setup_dialog(toolwin, toplevel);
+  gtk_window_set_title(GTK_WINDOW(toolwin), _("Editing Tools"));
+  gtk_container_set_border_width(GTK_CONTAINER(toolwin), 5);
+  gtk_window_set_policy(GTK_WINDOW(toolwin), FALSE, FALSE, FALSE);
+  g_signal_connect(toolwin, "delete_event",
+                  G_CALLBACK(editdlg_hide_tools), NULL);
+  g_signal_connect(toolwin, "destroy",
+                  GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+                  &toolwin);
+
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(toolwin), vbox);
+
+  create_tools(toolwin, vbox);
+  notebook = gtk_notebook_new(); /* apparently, it must be here: set_page... */
+  gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 2);
+
+  gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
+  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+  gtk_box_pack_start(GTK_BOX(vbox), notebook, FALSE, FALSE, 0);
+
+  palette = create_map_palette();
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), palette, NULL);
+
+  palette = create_units_palette();
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), palette, NULL);
+
+  palette = create_city_palette();
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), palette, NULL);
+
+  palette = create_player_palette();
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), palette, NULL);
+
+  gtk_widget_show_all(toolwin);
+}
+
+/****************************************************************************
+  show the editor toolbox window
+****************************************************************************/
+void editdlg_show_tools(void)
+{
+  if (!toolwin) {
+    create_toolsdlg();
+  }
+  gtk_widget_show(toolwin);
+}
+
+/****************************************************************************
+  hide the editor toolbox window
+****************************************************************************/
+void editdlg_hide_tools(void)
+{
+  if (toolwin) {
+    gtk_widget_hide(toolwin);
+  }
+}
Index: client/gui-gtk-2.0/editdlg.h
===================================================================
--- client/gui-gtk-2.0/editdlg.h        (revision 0)
+++ client/gui-gtk-2.0/editdlg.h        (revision 0)
@@ -0,0 +1,23 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2005 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__TOOLSDLG_H
+#define FC__TOOLSDLG_H
+
+#include <gtk/gtk.h>
+
+#include "shared.h"
+
+void editdlg_show_tools(void);
+void editdlg_hide_tools(void);
+
+#endif  /* FC__TOOLSDLG_H */
Index: client/gui-gtk-2.0/dialogs.c
===================================================================
--- client/gui-gtk-2.0/dialogs.c        (revision 11583)
+++ client/gui-gtk-2.0/dialogs.c        (working copy)
@@ -54,6 +54,7 @@
 #include "tilespec.h"
 
 #include "dialogs.h"
+#include "editdlg.h"
 #include "wldlg.h"
 
 /******************************************************************/
@@ -1265,5 +1266,6 @@
 void popdown_all_game_dialogs(void)
 {
   gui_dialog_destroy_all();
+  editdlg_hide_tools();
 }
 
Index: client/gui-gtk-2.0/Makefile.am
===================================================================
--- client/gui-gtk-2.0/Makefile.am      (revision 11583)
+++ client/gui-gtk-2.0/Makefile.am      (working copy)
@@ -43,6 +43,8 @@
        dialogs.h       \
        diplodlg.c      \
        diplodlg.h      \
+       editdlg.c       \
+       editdlg.h       \
        diplomat_dialog.c \
        finddlg.c       \
        finddlg.h       \
Index: client/control.h
===================================================================
--- client/control.h    (revision 11583)
+++ client/control.h    (working copy)
@@ -196,6 +196,8 @@
 void key_unit_assign_battlegroup(int battlegroup, bool append);
 void key_unit_select_battlegroup(int battlegroup, bool append);
 
+void key_editor_toggle(void);
+
 /* don't change this unless you also put more entries in data/Freeciv */
 #define MAX_NUM_UNITS_BELOW 4
 
Index: client/mapctrl_common.c
===================================================================
--- client/mapctrl_common.c     (revision 11583)
+++ client/mapctrl_common.c     (working copy)
@@ -33,16 +33,16 @@
 #include "clinet.h"
 #include "cma_core.h"
 #include "control.h"
+#include "editor.h"
 #include "fcintl.h"
 #include "goto.h"
+#include "mapctrl_common.h"
 #include "mapctrl_g.h"
 #include "mapview_g.h"
 #include "options.h"
 #include "overview_common.h"
 #include "tilespec.h"
 
-#include "mapctrl_common.h"
-
 /* Selection Rectangle */
 static int rec_anchor_x, rec_anchor_y;  /* canvas coordinates for anchor */
 static struct tile *rec_canvas_center_tile;
@@ -531,7 +531,9 @@
 {
   struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
 
-  if (can_client_change_view() && ptile) {
+  if (game.info.is_edit_mode) {
+    editor_do_click(ptile);
+  } else if (can_client_change_view() && ptile) {
     /* FIXME: Some actions here will need to check can_client_issue_orders.
      * But all we can check is the lowest common requirement. */
     do_map_click(ptile, qtype);
Index: client/editor.c
===================================================================
--- client/editor.c     (revision 0)
+++ client/editor.c     (revision 0)
@@ -0,0 +1,251 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2005 - The Freeciv Poject
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "support.h"
+
+#include "game.h"
+#include "map.h"
+#include "packets.h"
+
+#include "clinet.h"
+#include "control.h"
+#include "editor.h"
+
+/* where the selected terrain and specials for editing are stored */
+static enum editor_tool_type selected_tool = ETOOL_PAINT;
+static enum tile_special_type selected_special = S_LAST;
+static struct terrain *selected_terrain = NULL;
+static enum editor_paint_type selected_paint_type = EPAINT_TERRAIN;
+static struct unit *selected_unit;
+static struct city *selected_city;
+
+/****************************************************************************
+  Initialize the editor tools data.
+****************************************************************************/
+void editor_init_tools(void)
+{ 
+  if (!selected_unit) {
+    selected_unit = create_unit_virtual(game.player_ptr, 0,
+                                       get_unit_type(0), 0);
+  }
+  if (!selected_city) {
+    selected_city = create_city_virtual(game.player_ptr, NULL, "");
+  }
+}
+
+/****************************************************************************
+  Returns the currently selected editor tool type.
+****************************************************************************/
+void editor_set_selected_tool_type(enum editor_tool_type type)
+{
+  selected_tool = type;
+}
+
+/****************************************************************************
+  Returns the currently selected editor paint type.
+****************************************************************************/
+void editor_set_selected_paint_type(enum editor_paint_type type)
+{
+  selected_paint_type = type;
+}
+
+/****************************************************************************
+  Sets the selected editor terrain.
+****************************************************************************/
+void editor_set_selected_terrain(struct terrain *pterrain)
+{
+  selected_terrain = pterrain;
+}
+
+/****************************************************************************
+  Sets the selected editor special.
+****************************************************************************/
+void editor_set_selected_special(enum tile_special_type special)
+{
+  selected_special = special;
+}
+
+/****************************************************************************
+  Returns the selected unit.
+****************************************************************************/
+struct unit *editor_get_selected_unit(void)
+{
+  return selected_unit;
+}
+
+/****************************************************************************
+  Returns the selected city.
+****************************************************************************/
+struct city *editor_get_selected_city(void)
+{
+  return selected_city;
+}
+
+/****************************************************************************
+ problem: could be multiple units on a particular tile
+ TODO: edit existing units
+****************************************************************************/
+static void do_unit(struct tile *ptile)
+{
+  struct packet_edit_unit packet;
+
+  packet.create_new = TRUE; /* ? */
+  packet.delete = FALSE;
+
+  packet.id = selected_unit->id;
+  packet.owner = selected_unit->owner->player_no;
+
+  packet.x = ptile->x;
+  packet.y = ptile->y;
+
+  packet.homecity = selected_unit->homecity;
+
+  packet.veteran = selected_unit->veteran;
+  packet.paradropped = selected_unit->paradropped;
+
+  packet.type = selected_unit->type->index;
+  packet.transported_by = selected_unit->transported_by;
+
+  packet.movesleft = selected_unit->moves_left;
+  packet.hp = selected_unit->hp;
+  packet.fuel = selected_unit->fuel;
+
+  packet.activity_count = selected_unit->activity_count;
+
+  send_packet_edit_unit(&aconnection, &packet);
+}
+
+/****************************************************************************
+ basically package_city in citytools.c
+****************************************************************************/
+static void do_city(struct tile *ptile)
+{
+  struct packet_edit_city packet;
+  struct city *pcity = selected_city;
+  int i;
+
+  packet.owner = pcity->owner->player_no;
+  packet.x = ptile->x;
+  packet.y = ptile->y;
+
+  packet.size = pcity->size;
+  for (i = 0; i < NUM_TRADEROUTES; i++) {
+    packet.trade[i] = pcity->trade[i];
+  }
+
+  packet.food_stock = pcity->food_stock;
+  packet.shield_stock = pcity->shield_stock;
+
+  packet.turn_last_built = pcity->turn_last_built;
+  packet.turn_founded = pcity->turn_founded;
+  packet.changed_from_is_unit = pcity->changed_from.is_unit;
+  packet.changed_from_id = pcity->changed_from.value;
+  packet.before_change_shields = pcity->before_change_shields;
+  packet.disbanded_shields = pcity->disbanded_shields;
+  packet.caravan_shields = pcity->caravan_shields;
+  packet.last_turns_shield_surplus = pcity->last_turns_shield_surplus;
+
+  packet.diplomat_investigate = FALSE; /* FIXME: this overwrites the value! */
+
+  packet.airlift = pcity->airlift;
+  packet.did_buy = pcity->did_buy;
+  packet.did_sell = pcity->did_sell;
+  packet.was_happy = pcity->was_happy;
+
+  BV_CLR_ALL(packet.improvements);
+  impr_type_iterate(building) {
+    if (city_got_building(pcity, building)) {
+      BV_SET(packet.improvements, building);
+    }
+  } impr_type_iterate_end;
+
+  send_packet_edit_city(&aconnection, &packet);
+}
+
+#if 0
+/****************************************************************************
+ basically package_city in citytools.c
+****************************************************************************/
+void do_edit_player(void)
+{
+  struct packet_edit_player packet;
+
+  send_packet_edit_city(&aconnection, &packet);
+}
+#endif
+
+/****************************************************************************
+  Does the current paint operation onto the tile.
+
+  For instance, if the paint operation is paint-terrain, then we just change
+  the current tile's terrain to the selected terrain.
+****************************************************************************/
+static void do_paint(struct tile *ptile)
+{
+  struct tile tile = *ptile;
+
+  switch (selected_paint_type) {
+  case EPAINT_TERRAIN:
+    tile.terrain = selected_terrain;
+    break;
+  case EPAINT_SPECIAL:
+    /* add new special to existing specials on the tile */
+    if (selected_special == S_LAST) {
+      tile_clear_all_specials(&tile);
+    } else {
+      tile_add_special(&tile, selected_special);
+    }
+    break;
+  case EPAINT_LAST:
+    assert(FALSE);
+    break;
+  } 
+
+  /* send the result to the server for changing */
+  /* FIXME: No way to change resources. */
+  dsend_packet_edit_tile(&aconnection, ptile->x, ptile->y,
+                        tile.terrain->index,
+                        tile.resource ? tile.resource->index : -1,
+                        tile.special);
+}
+
+/****************************************************************************
+  if the client is in edit_mode, then this function captures clicks on the
+  map canvas.
+****************************************************************************/
+void editor_do_click(struct tile *ptile)
+{
+  switch(selected_tool) {
+  case ETOOL_PAINT:
+    do_paint(ptile);
+    break;
+  case ETOOL_UNIT:
+    do_unit(ptile);
+    break;
+  case ETOOL_CITY:
+    do_city(ptile);
+    break;
+  case ETOOL_PLAYER:
+  case ETOOL_DELETE:
+  case ETOOL_LAST:
+    break;
+  }
+}
Index: client/Makefile.am
===================================================================
--- client/Makefile.am  (revision 11583)
+++ client/Makefile.am  (working copy)
@@ -145,6 +145,8 @@
        colors_common.h         \
        control.c       \
        control.h       \
+       editor.c                \
+       editor.h                \
        ggzclient.c     \
        ggzclient.h     \
        goto.c          \
Index: client/editor.h
===================================================================
--- client/editor.h     (revision 0)
+++ client/editor.h     (revision 0)
@@ -0,0 +1,49 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 2005 - The Freeciv Poject
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifndef FC__TOOLS_H
+#define FC__TOOLS_H
+
+#include "fc_types.h"
+
+enum editor_tool_type {
+  ETOOL_PAINT,
+  ETOOL_UNIT,
+  ETOOL_CITY,
+  ETOOL_PLAYER,
+  ETOOL_DELETE,
+  ETOOL_LAST
+};
+
+enum editor_paint_type {
+  EPAINT_TERRAIN,
+  EPAINT_SPECIAL,
+  EPAINT_LAST
+};
+
+typedef void (*ToolFunction)(struct tile *ptile);
+
+void editor_init_tools(void);
+void editor_show_tools(void);
+
+void editor_set_selected_tool_type(enum editor_tool_type type);
+void editor_set_selected_paint_type(enum editor_paint_type type);
+void editor_set_selected_terrain(struct terrain *pterrain);
+void editor_set_selected_special(enum tile_special_type special);
+
+struct unit *editor_get_selected_unit(void);
+struct city *editor_get_selected_city(void);
+
+void editor_do_click(struct tile *ptile);
+
+#endif /* FC__TOOLS_H */

[Prev in Thread] Current Thread [Next in Thread]