[Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-d
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
On Wed, Mar 19, 2003 at 12:14:23AM -0800, Jason Short wrote:
> > +**************************************************************************/
> > +void draw_rectangle(int src_x, int src_y, int dest_x, int dest_y,
> > + bool draw)
> > +{
> > + int x1, y1, x2, y2;
> > + int dist_x, dist_y;
> > + int i, test_x;
> > +
> > + dist_x = real_map_distance(src_x, src_y, dest_x, src_y);
> > + dist_y = real_map_distance(src_x, src_y, src_x, dest_y);
> > +
> > + test_x = src_x + dist_x;
> > + normalize_map_pos(&test_x, &src_y);
>
> Any time you call normalize_map_pos you need to check the result. But
> I'm not quite sure what the correct behavior should be in this case if
> it returns FALSE. Is this impossible (if so, you should assert(0))? Or
> is some other handling needed?
>
> > + if (test_x == dest_x) { /* is dest to the right of src? */
> > + x1 = src_x; x2 = dest_x;
> > + } else {
> > + x1 = dest_x; x2 = src_x;
> > + }
> > + if (dest_y >= src_y) { /* vertical is simple */
> > + y1 = src_y; y2 = dest_y;
> > + } else {
> > + y1 = dest_y; y2 = src_y;
> > + }
>
> This code assumes the current topology, although doing differently may
> turn out to be very difficult. What if the map wraps in the Y
> direction? What if it is a isometric map (i.e., what civ2/smac/civ3 use)?
...
> This particular code looks like it will immediately segfault if you drag
> your rectangle off into "unreal" positions. This is impossible now, but
> please don't assume it. Something like
attached is my solution for the civworld code. it should work for all
topologies [that we are considering].
-mike
/**********************************************************************
Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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.
***********************************************************************/
#include <gtk/gtk.h>
#include <config.h>
#include "fcintl.h"
#include "map.h"
#include "mem.h"
#include "game.h"
#include "tilespec.h"
#include "support.h"
#include "clipfunc.h"
#include "chatline.h"
#include "maptools.h"
#include "mapview.h"
#include "nation_manage.h"
#include "startpos.h"
#include "tools.h"
#include "undo.h"
#include "toolfunc.h"
#include "attributesdlg.h"
#include "citytools.h"
#include "defaults.h"
#include "ed_unittools.h"
#include "ed_citytools.h"
#include "cw_mapview.h"
#include "maphand.h"
extern GdkGC *rubberband_line_gc;
extern GdkGC *civ_gc;
extern GtkWidget *map_canvas;
extern int map_view_x0;
extern int map_view_y0;
extern int draw_private_map;
/* coords in the internal map for drawing rectangles. */
static int first_x = -1, first_y = -1;
static int last_x = -1, last_y = -1;
static int single_paint = 1;
int undo_is_envoked = 0;
/**************************************************************************
...
**************************************************************************/
void sanity_check_specials(int x, int y, enum tile_special_type new_special)
{
struct tile *ptile = map_get_tile(x, y);
int terrain = ptile->terrain;
struct tile_type *type = get_tile_type(terrain);
if (terrain == T_OCEAN) {
map_clear_special(x, y, S_ROAD);
map_clear_special(x, y, S_IRRIGATION);
map_clear_special(x, y, S_RAILROAD);
map_clear_special(x, y, S_MINE);
map_clear_special(x, y, S_POLLUTION);
map_clear_special(x, y, S_HUT);
map_clear_special(x, y, S_FORTRESS);
map_clear_special(x, y, S_RIVER);
map_clear_special(x, y, S_FARMLAND);
map_clear_special(x, y, S_AIRBASE);
map_clear_special(x, y, S_FALLOUT);
}
if (ptile->special & S_RAILROAD)
map_set_special(x, y, S_ROAD);
if (ptile->special & S_MINE
&& terrain != type->mining_result) {
map_clear_special(x, y, S_MINE);
}
if (ptile->special & S_FARMLAND) {
map_set_special(x, y, S_IRRIGATION);
}
if ((ptile->special & S_IRRIGATION || ptile->special & S_FARMLAND)
&& terrain != type->irrigation_result) {
map_clear_special(x, y, S_IRRIGATION);
map_clear_special(x, y, S_FARMLAND);
}
if (ptile->special & S_MINE && ptile->special & S_IRRIGATION) {
if (new_special == S_MINE) {
map_clear_special(x, y, S_IRRIGATION);
map_clear_special(x, y, S_FARMLAND);
} else {
map_clear_special(x, y, S_MINE);
}
}
if (ptile->special & S_SPECIAL_1 && ptile->special & S_SPECIAL_2) {
if (new_special == S_SPECIAL_1) {
map_clear_special(x, y, S_SPECIAL_2);
} else {
map_clear_special(x, y, S_SPECIAL_1);
}
}
}
/*****************************************************************************
directly accessing the player_tile->known and ->seen is more efficient
than going through fog/unfog_area. I think that the maphand.c stuff is in
some way broken. You do not get the behavior you're looking for, mainly
because I think that ->seen shouldn't be unsigned... The only thing that
isn't taken care of is shared vision, but since it ain't implemented here yet,
we don't care...
*****************************************************************************/
static void paint_tile(int x, int y)
{
struct tile *ptile = map_get_tile(x, y);
switch (selected_paint_type) {
case PAINT_TERRAIN:
ptile->terrain = selected_terrain;
sanity_check_specials(x, y, selected_special);
break;
case PAINT_SPECIAL:
if (selected_special == S_NO_SPECIAL)
ptile->special = 0;
else
ptile->special |= selected_special;
sanity_check_specials(x, y, selected_special);
break;
case PAINT_FOG:
if (game.nplayers > 0) {
if (selected_known == TILE_UNKNOWN) {
forget_tile(game.player_ptr, x, y);
} else if(selected_known == TILE_KNOWN_FOGGED) {
struct city *pcity;
if(!map_get_known(x, y, game.player_ptr)) {
map_set_known(x, y, game.player_ptr);
/* if it's unknown, it really should not be seen to begin with */
map_get_player_tile(x, y, game.player_ptr)->seen = 0;
} else if (map_get_player_tile(x, y, game.player_ptr)->seen != 0) {
map_change_seen(x, y, game.player_ptr, -1);
}
reality_check_city(game.player_ptr, x, y);
if ((pcity = map_get_city(x, y))) {
update_dumb_city(game.player_ptr, pcity);
}
map_city_radius_iterate(x, y, x1, y1) {
pcity = map_get_city(x1, y1);
if (pcity && city_owner(pcity) == game.player_ptr) {
update_city_tile_status_map(pcity, x, y);
}
} map_city_radius_iterate_end;
sync_cities();
} else if(selected_known == TILE_KNOWN) {
struct city *pcity;
map_set_known(x, y, game.player_ptr);
map_change_seen(x, y, game.player_ptr, 1);
reality_check_city(game.player_ptr, x, y);
if ((pcity = map_get_city(x, y))) {
update_dumb_city(game.player_ptr, pcity);
}
map_city_radius_iterate(x, y, x1, y1) {
pcity = map_get_city(x1, y1);
if (pcity && city_owner(pcity) == game.player_ptr) {
update_city_tile_status_map(pcity, x, y);
}
} map_city_radius_iterate_end;
sync_cities();
}
} else {
append_output_window(_("You must create a nation "
"before using fogging tools."));
}
break;
case PAINT_START:
put_cross_overlay_tile(x, y);
break;
case PAINT_CITY:
break;
case PAINT_UNIT:
break;
case PAINT_DELETE:
break;
case PAINT_NONE:
/* nothing */
break;
}
if(single_paint){
square_iterate(x, y, 1, x_itr, y_itr)
refresh_tile_mapcanvas(x_itr, y_itr, 1);
square_iterate_end;
}
}
/*****************************************************************************
change the color, i.e. terrain, of the tile at (x, y)
*****************************************************************************/
void start_tile(gint x, gint y)
{
if (!undo_is_envoked) {
create_new_undo_node();
undo_is_envoked = 1;
}
undo_add_tile(x, y);
paint_tile(x, y);
refresh_overview_viewrect();
}
/*****************************************************************************
...
*****************************************************************************/
void end_tile(gint x, gint y)
{
undo_is_envoked = 0;
}
/*****************************************************************************
...
*****************************************************************************/
static void draw_bounding_rectangle()
{
if(is_isometric){
int Nx, Ny, Sx, Sy, Wx, Wy, Ex, Ey;
get_canvas_xy(first_x, first_y, &Nx, &Ny); /* not necessarily N & S at */
get_canvas_xy(last_x, last_y, &Sx, &Sy); /* this point */
if (last_x >= first_x && last_y >= first_y) { /* normal */
get_canvas_xy(first_x, last_y, &Wx, &Wy);
get_canvas_xy(last_x, first_y, &Ex, &Ey);
} else if(last_x <= first_x && last_y <= first_y) { /* switch N & S */
Ex = Nx; Ey = Ny;
Nx = Sx; Ny = Sy;
Sx = Ex; Sy = Ey;
get_canvas_xy(first_x, last_y, &Ex, &Ey);
get_canvas_xy(last_x, first_y, &Wx, &Wy);
} else if(last_x < first_x) { /* start must be eastmost */
Ex = Nx; Ey = Ny;
Wx = Sx; Wy = Sy;
get_canvas_xy(first_x, last_y, &Sx, &Sy);
get_canvas_xy(last_x, first_y, &Nx, &Ny);
} else { /* start must be westmost */
Ex = Sx; Ey = Sy;
Wx = Nx; Wy = Ny;
get_canvas_xy(first_x, last_y, &Nx, &Ny);
get_canvas_xy(last_x, first_y, &Sx, &Sy);
}
Nx += NORMAL_TILE_WIDTH / 2;
Sx += NORMAL_TILE_WIDTH / 2;
Sy += NORMAL_TILE_HEIGHT;
Wy += NORMAL_TILE_HEIGHT / 2;
Ex += NORMAL_TILE_WIDTH;
Ey += NORMAL_TILE_HEIGHT / 2;
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
Nx, Ny, Ex, Ey);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
Ex, Ey, Sx, Sy);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
Wx, Wy, Nx, Ny);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
Sx, Sy, Wx, Wy);
}else{
int x1, y1, x2, y2; /* the 1's are the start tile, 2's are the end tile */
get_canvas_xy(first_x, first_y, &x1, &y1);
get_canvas_xy(last_x, last_y, &x2, &y2);
if(x1 < x2)
x2 += NORMAL_TILE_WIDTH - 1;
else
x1 += NORMAL_TILE_WIDTH - 1;
if(y1 < y2)
y2 += NORMAL_TILE_HEIGHT - 1;
else
y1 += NORMAL_TILE_HEIGHT - 1;
/* draw new rectangle */
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
x1, y1, x1, y2);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
x1, y1, x2, y1);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
x2, y2, x2, y1);
gdk_draw_line(GTK_WIDGET(map_canvas)->window, rubberband_line_gc,
x2, y2, x1, y2);
}
}
/**************************************************************************
...
**************************************************************************/
static void undraw_rect(int init)
{
if (first_x == -1)
return;
draw_bounding_rectangle();
if (init) {
first_x = -1, first_y = -1;
last_x = -1, last_y = -1;
}
}
/**************************************************************************
...
**************************************************************************/
static void draw_rect(void)
{
draw_bounding_rectangle();
}
/*****************************************************************************
start drawing of rectangle: save start tile, draw rubber band rectangle
we cannot assume that input will be continous in the set of whole numbers.
the mouse movement might skip a couple of numbers. The assumption we make is
that the mouse won't skip 5 tiles without actually travelling across
the boundary.
*****************************************************************************/
void start_rect(gint x, gint y)
{
static int old_x;
static int old_gradient = 1;
int f1, f2, u, v;
int gradient;
undraw_rect(FALSE);
if (first_x == -1) {
first_x = last_x = old_x = x;
first_y = y;
}
get_canvas_xy(first_x, first_y, &f1, &f2);
get_canvas_xy(x, y, &u, &v);
if(is_isometric){
gradient = NORMAL_TILE_WIDTH/NORMAL_TILE_HEIGHT * (v - f2) - f1 + u;
}else{
gradient = u - f1;
}
if((old_gradient < 0 && gradient > 0 && gradient > 10 * NORMAL_TILE_WIDTH)
|| (old_gradient > 0 && gradient < 0
&& old_gradient - gradient > 5 * NORMAL_TILE_WIDTH))
gradient = old_gradient;
if(gradient > 0 && x < first_x)
last_x = x + map.xsize;
else if(gradient < 0 && x > first_x)
last_x = x - map.xsize;
else
last_x = x;
old_gradient = gradient;
last_y = y;
draw_rect();
}
/*****************************************************************************
undraw the rectangle, update the map stats, clear the start tile coords
the parameters x,y are not currently used.
*****************************************************************************/
void end_rect(gint x, gint y)
{
int i, j;
int start_x, start_y, end_x, end_y;
int w, h;
undraw_rect(FALSE);
get_canvas_xy(first_x, first_y, &start_x, &start_y);
get_canvas_xy(last_x, last_y, &end_x, &end_y);
if (is_isometric) {
if (first_x > last_x) {
int tmp = first_x;
first_x = last_x;
last_x = tmp;
}
if (first_y > last_y) {
int tmp = first_y;
first_y = last_y;
last_y = tmp;
}
} else {
if (start_x > end_x) {
int tmp = first_x;
first_x = last_x;
last_x = tmp;
}
if (start_y > end_y) {
int tmp = first_y;
first_y = last_y;
last_y = tmp;
}
}
create_new_undo_node();
w = last_x - first_x;
h = last_y - first_y;
normalize_map_pos(&first_x, &first_y);
last_x++; last_y++;
normalize_map_pos(&last_x, &last_y);
for(i = first_x; i != last_x; i++, normalize_map_pos(&i, &j)) {
for(j = first_y; j != last_y; j++, normalize_map_pos(&i, &j)) {
undo_add_tile(i, j);
single_paint = 0;
paint_tile(i, j);
single_paint = 1;
}
}
/* we need the surrounding tiles as well */
update_map_canvas(first_x - 1, first_y - 1, w + 3, h + 3, 1);
refresh_overview_canvas();
refresh_overview_viewrect();
/* rectangle finished; reset values */
first_x = -1; first_y = -1;
last_x = -1; last_y = -1;
}
/*****************************************************************************
add a start position at (x,y)
at this point, these will be raw starting positions. the server will fill
them randomly with races when the game starts.
*****************************************************************************/
void put_start_position(gint x, gint y)
{
/* all the logic is in startpos.c */
if (add_start_position(x, y)) {
paint_tile(x, y);
}
}
/*****************************************************************************
obvious
*****************************************************************************/
void put_city(gint x, gint y)
{
if(game.player_idx == -1)
append_output_window(_("You must make a nation active to place a city."));
else{
struct tile *ptile = map_get_tile(x,y);
if(!city_can_be_built_here(x,y)){
append_output_window(_("You can't build a city here."));
return;
}
unit_list_iterate(ptile->units, punit){
if(punit->owner != game.player_ptr->player_no){
append_output_window(_("You can't place a city on "
"top of an enemy unit."));
return;
}
} unit_list_iterate_end;
if(default_pseudo_city_unfog) {
map_city_radius_iterate(x, y, x_itr, y_itr) {
map_set_known(x_itr, y_itr, game.player_ptr);
} map_city_radius_iterate_end;
map_unfog_pseudo_city_area(game.player_ptr, x, y);
} else {
/* settler would be there to found the city */
unfog_area(game.player_ptr, x, y, 1);
}
create_city(game.player_ptr, x, y,
city_name_suggestion(game.player_ptr, x, y));
copy_city_defaults(map_get_city(x, y));
update_nation_manager();
update_map_canvas_visible();
refresh_overview_canvas();
}
}
/*****************************************************************************
also obvious
*****************************************************************************/
void put_unit(gint x, gint y)
{
struct player *pplayer = game.player_ptr;
struct tile *ptile = map_get_tile(x, y);
struct city *pcity = map_get_city(x, y);
struct unit *punit;
Unit_Type_id type = selected_unitpaint;
if(game.player_idx == -1){
append_output_window(_("You must make a nation active to place a unit."));
return;
}
if(pcity && pplayer != city_owner(pcity)){
append_output_window(_("You can't place a unit in an enemy city."));
return;
}
if( (ptile->terrain == T_OCEAN && is_ground_unittype(type) &&
!ground_unit_transporter_capacity(x, y, game.player_ptr))
|| (ptile->terrain != T_OCEAN && is_water_unit(type)
&& !(pcity && is_at_coast(x, y)) ) ){
append_output_window(_("You can't place the unit here."));
return;
}
unit_list_iterate(ptile->units, punit){
if(punit->owner != pplayer->player_no){
append_output_window(_("You can't place a unit "
"on top of an enemy unit."));
return; /* nope */
}
} unit_list_iterate_end;
punit = create_unit_full(game.player_ptr, x, y, type, 0, 0, -1, -1);
copy_unit_defaults(punit);
update_attributes_dialog();
update_unit_info_label(punit);
update_nation_manager();
update_map_canvas_visible();
refresh_overview_canvas();
}
/*****************************************************************************
delete a city or unit
*****************************************************************************/
void delete_unit(int x, int y)
{
ed_delete_units(x, y);
ed_delete_city(x, y);
update_nation_manager();
update_map_canvas_visible();
refresh_overview_canvas();
}
/*****************************************************************************
copy: copy the rectangle of tiles into the clipboard
the parameters x,y are not currently used.
*****************************************************************************/
void end_copy(gint x, gint y)
{
int i, j, u, v;
int start_x, start_y, end_x, end_y;
int catch;
undraw_rect(FALSE);
get_canvas_xy(first_x, first_y, &start_x, &start_y);
catch = get_canvas_xy(last_x, last_y, &end_x, &end_y);
if(!catch){ /* FIXME: should be able to... */
append_output_window(_("You may not copy with an endpoint "
"outside the visible window"));
first_x = -1; first_y = -1;
last_x = -1; last_y = -1;
return;
}
/* effect change in the square */
if (start_x > end_x) {
int tmp = first_x;
first_x = last_x;
last_x = tmp;
}
if (start_y > end_y) {
int tmp = first_y;
first_y = last_y;
last_y = tmp;
}
init_clipboard(clip, last_x - first_x + 1, last_y - first_y + 1);
normalize_map_pos(&first_x, &first_y);
last_x++; last_y++;
normalize_map_pos(&last_x, &last_y);
for(u = 0, i = first_x; i != last_x; u++, i++, normalize_map_pos(&i, &j)) {
for(v = 0, j = first_y; j != last_y; v++, j++, normalize_map_pos(&i, &j)) {
clip->tiles[u][v] = *map_get_tile(i, j);
}
}
/* rectangle finished; reset values */
first_x = -1; first_y = -1;
last_x = -1; last_y = -1;
}
/*****************************************************************************
Draw a rectangle the size of the tiles from the clipboard
*****************************************************************************/
void start_paste(gint x, gint y)
{
undraw_rect(TRUE);
first_x = x; first_y = y;
last_x = x + clip->width - 1;
last_y = y + clip->height - 1;
normalize_map_pos(&last_x, &last_y);
draw_rect();
}
/*****************************************************************************
paste: copy the rectangle of tiles from the clipboard;
*****************************************************************************/
void end_paste(gint x, gint y)
{
int i, j, map_x, map_y;
struct tile *ptile_map, *ptile_clip;
undraw_rect(TRUE);
create_new_undo_node();
for (i = 0; i < clip->width; i++) {
for (j = 0; j < clip->height; j++) {
map_x = i + x; map_y = j + y;
if (normalize_map_pos(&map_x, &map_y)) {
undo_add_tile(map_x, map_y);
ptile_map = map_get_tile(map_x, map_y);
ptile_clip = &clip->tiles[i][j];
ptile_map->terrain = ptile_clip->terrain;
ptile_map->special = ptile_clip->special;
}
}
}
for (i = -1; i < clip->width + 1; i++) {
for (j = -1; j < clip->height + 1; j++) {
map_x = i + x;
map_y = j + y;
if (normalize_map_pos(&map_x, &map_y)) {
refresh_tile_mapcanvas(map_x, map_y, 1);
}
}
}
refresh_overview_viewrect();
}
- [Freeciv-Dev] (PR#3529) Map Control patch, Jason Short, 2003/03/02
- Message not available
- Message not available
- [Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-drag, a-l@xxxxxxx, 2003/03/20
- Message not available
- [Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-drag, Jason Short, 2003/03/20
- Message not available
- [Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-drag, rwetmore@xxxxxxxxxxxx, 2003/03/21
- Message not available
- [Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-drag, a-l@xxxxxxx, 2003/03/23
- Message not available
- [Freeciv-Dev] Re: (PR#3727) Rectangular selection with right-click-and-drag, Jason Short, 2003/03/23
|
|