1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
/*
* X11 interface for Atom-4 game board
* Implementation file
*
* $Id: xtriboard.cc,v 1.24 2003/03/13 02:25:17 hsteoh Exp hsteoh $
*/
#include "exception.h"
#include "xtriboard.h"
#define BORDER_WIDTH 1
#define CURSOR_Y_OFFSET -3 // offset from piece already on board
#ifndef DATADIR // should be defined by build script
#define DATADIR "." // search current dir if undefined
#endif
#define TILE_XPM DATADIR "/tritile12.xpm"
char *xtriboard::ballnames[NUM_BALLS]={
DATADIR "/blackball12.xpm",
DATADIR "/redball12.xpm",
DATADIR "/greenball12.xpm",
DATADIR "/yellowball12.xpm",
DATADIR "/blueball12.xpm",
DATADIR "/purpleball12.xpm",
DATADIR "/turqball12.xpm",
DATADIR "/whiteball12.xpm"
};
// To simplify the calculation, we approximate the marble's area as a hexagon
// bounded by the perpendiculars to our tile boundaries, and use linear
// equations to calculate our position.
void xtriboard::calc_bcoor(int rx, int ry, int &bx, int &by) {
int xbase = rx / CELL_WIDTH;
int xrem = rx % CELL_WIDTH;
int ybase = ry / CELL_HEIGHT;
int yrem = ry % CELL_HEIGHT;
// Notes:
// - the equations we use are based on the fact that an equilateral triangle
// of w units per side has its center point at a distance of w/sqrt(3)
// units from each side.
// - we're using 173/100 as an approximation of sqrt(3).
// - because we're shifting the entire board down by (1/2,1/2) so that
// marbles at (0,0) are not clipped, our (bx,by) calculations are
// skewed by (-1,-1) so that they map correctly to real board coors.
if (ybase%2 == 0) { // even row (we number rows from 0)
if (xrem < CELL_WIDTH/2) { // upperleft quadrant
// Determine which side of y = (CELL_WIDTH-x)/sqrt(3) we're on
if (173*yrem < 100*(CELL_WIDTH-xrem)) {
// in upperleft tritant
bx = xbase-1;
by = ybase-1;
return;
}
} else { // upperright quadrant
// Determine which side of y = x/sqrt(3) we're on
if (173*yrem < 100*xrem) {
// in upperright tritant
bx = xbase;
by = ybase-1;
return;
}
}
// Neither; we must be in lower tritant (center marble)
bx = xbase;
by = ybase;
} else { // odd row
if (xrem < CELL_WIDTH/2) { // lowerleft quadrant
// Determine which side of (CELL_HEIGHT-y) = (CELL_WIDTH-x)/sqrt(3)
// we're on
if (173*(CELL_HEIGHT-yrem) < 100*(CELL_WIDTH-xrem)) {
// in lowerleft tritant
bx = xbase-1;
by = ybase;
return;
}
} else {
// Determine which side of (CELL_HEIGHT-y) = x/sqrt(3) we're on
if (173*(CELL_HEIGHT-yrem) < 100*xrem) {
// in lowerright tritant
bx = xbase;
by = ybase;
return;
}
}
// Neither; we must be in upper tritant (center marble)
bx = xbase;
by = ybase-1;
} // endelse(odd/even row)
}
// Notes:
// - we add half a cell width to the width because odd rows are shifted to
// the right by half a cell's width.
// - on top of this, we need to add one full cell's width, to give room for
// marbles at the edge of the board not to be clipped.
// - similarly, for the height, we need to add one full cell's height
xtriboard::xtriboard(xconnection *connection, xsprite_engine *engine,
xwindow *parent, atom4 *g, int x, int y) :
xwindow(connection, parent, x,y,
CELL_WIDTH*g->board_width() + CELL_WIDTH/2,
CELL_HEIGHT*(g->board_height()+1),
BORDER_WIDTH, connection->black(), connection->black()),
eng(engine), game(g) {
Display *disp = conn->display();
cell_wd = g->board_width();
cell_dp = g->board_height();
// Create window buffer ((wd,ht) set up by base ctor)
buffer = XCreatePixmap(disp, win, wd, ht, conn->scrn_depth());
tritile=NULL;
try {
int i;
tritile=new tiled_bckgnd(conn, buffer, TILE_XPM);
tritile->paint(0,0, wd,ht);
// (The following is for easier exception handling)
for (i=0; i<NUM_BALLS; i++) balls[i] = NULL;
for (i=0; i<NUM_BALLS; i++) {
balls[i] = new xflatsprite(eng, ballnames[i]);
if (!balls[i]) throw exception("@Error while loading %s\n",
ballnames[i]);
}
cursor = new xcursor(eng, win, balls[game->current_tile()-'a'],
CELL_WIDTH, CELL_HEIGHT*2);
// Select input that we're interested in
XSelectInput(disp, win, ExposureMask |
EnterWindowMask | LeaveWindowMask |
PointerMotionMask |
FocusChangeMask);
// Paint initial state to buffer
game->get_board()->mapcell(drawcell, this);
} catch(exception &e) {
for (int i=0; i<NUM_BALLS; i++) {
if (balls[i]) delete balls[i];
}
if (tritile) delete tritile;
XFreePixmap(disp, buffer);
throw;
}
}
xtriboard::~xtriboard() {
Display *disp = conn->display();
delete cursor;
for (int i=0; i<NUM_BALLS; i++) {
if (balls[i]) delete balls[i];
}
delete tritile;
XFreePixmap(disp, buffer);
}
void xtriboard::drawcell(int bx, int by, celltype cell, void *context) {
xtriboard *bp = (xtriboard *)context;
if (cell>='a' && cell<'a'+NUM_BALLS) {
bp->draw_at(bx,by, bp->balls[cell-'a']);
}
}
void xtriboard::refresh() {
GC copygc = eng->get_copygc();
// Redraw screen
tritile->paint(0,0, wd,ht);
game->get_board()->mapcell(drawcell, this);
XCopyArea(conn->display(), buffer, win, copygc, 0,0, width(),height(), 0,0);
// Re-sync cursor, just in case current tile has changed
cursor->set_sprite(balls[game->current_tile()-'a']);
}
void xtriboard::expose(XExposeEvent ev) {
int x=ev.x, y=ev.y, w=ev.width, h=ev.height;
GC copygc = eng->get_copygc();
XCopyArea(ev.display, buffer, win, copygc, x,y, w,h, x,y);
}
void xtriboard::mouse_enter(XEnterWindowEvent ev) {
// nothing for now
}
void xtriboard::mouse_leave(XLeaveWindowEvent ev) {
cursor->off(); // turn off cursor when mouse leaves
}
void xtriboard::mouse_move(XMotionEvent ev) {
int x=ev.x, y=ev.y;
int bx,by;
if (game->is_local_turn()) {
calc_bcoor(x,y,bx,by);
if (game->check_legal(bx,by)) {
cursor->move(real_xcoor(bx,by), real_ycoor(bx,by)+CURSOR_Y_OFFSET);
cursor->on();
return;
}
}
// Not in a valid position, not our turn, or round is over, so hide the
// auto cursor
cursor->off();
}
|