1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 1991-2001 by
5  *
6  *      Bj�rn Stabell        <bjoern@xpilot.org>
7  *      Ken Ronny Schouten   <ken@xpilot.org>
8  *      Bert Gijsbers        <bert@xpilot.org>
9  *      Dick Balaska         <dick@xpilot.org>
10  *
11  * Copyright (C) 2003-2004 Kristian S�derblom <kps@users.sourceforge.net>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "xpclient_x11.h"
29 
30 static bool texturedShips = false; /* Turned this off because the images drawn
31 				    * don't match the actual shipshape used
32 				    * for wall collisions by the server. */
33 static int ballColor;		/* Color index for ball drawing */
34 static int connColor;		/* Color index for connector drawing */
35 static int teamShotColor;	/* Color index for harmless shot drawing */
36 static int zeroLivesColor;	/* Color to associate with 0 lives */
37 static int oneLifeColor;	/* Color to associate with 1 life */
38 static int twoLivesColor;	/* Color to associate with 2 lives */
39 static int manyLivesColor;	/* Color to associate with >2 lives */
40 static int selfLWColor;		/* Color index for selfLifeWarning */
41 static int enemyLWColor;	/* Color index for enemyLifeWarning */
42 static int teamLWColor;		/* Color index for teamLifeWarning */
43 static int shipNameColor;	/* Color index for ship name drawing */
44 static int mineNameColor;	/* Color index for mine name drawing */
45 static int teamShipColor;   	/* Color index to associate with team 0 */
46 static int team0Color;		/* Color index to associate with team 0 */
47 static int team1Color;		/* Color index to associate with team 1 */
48 static int team2Color;		/* Color index to associate with team 2 */
49 static int team3Color;		/* Color index to associate with team 3 */
50 static int team4Color;		/* Color index to associate with team 4 */
51 static int team5Color;		/* Color index to associate with team 5 */
52 static int team6Color;		/* Color index to associate with team 6 */
53 static int team7Color;		/* Color index to associate with team 7 */
54 static int team8Color;		/* Color index to associate with team 8 */
55 static int team9Color;		/* Color index to associate with team 9 */
56 
57 
58 static int asteroidRawShapes[NUM_ASTEROID_SHAPES][NUM_ASTEROID_POINTS][2] = {
59     { ASTEROID_SHAPE_0 },
60     { ASTEROID_SHAPE_1 },
61 };
62 
63 
64 position_t *asteroidShapes[NUM_ASTEROID_SHAPES][NUM_ASTEROID_POINTS];
65 
66 
Init_asteroids(void)67 int Init_asteroids(void)
68 {
69     int		shp, i;
70     size_t	point_size;
71     size_t	total_size;
72     char	*dynmem;
73 
74     /*
75      * Allocate memory for all the asteroid points.
76      */
77     point_size = sizeof(position_t) * RES;
78     total_size = point_size * NUM_ASTEROID_POINTS * NUM_ASTEROID_SHAPES;
79     if ((dynmem = (char *) malloc(total_size)) == NULL) {
80 	error("Not enough memory for asteroid shapes");
81 	return -1;
82     }
83 
84     /*
85      * For each asteroid-shape rotate all points.
86      */
87     for ( shp = 0; shp < NUM_ASTEROID_SHAPES; shp++ ) {
88 	for ( i = 0; i < NUM_ASTEROID_POINTS; i++ ) {
89 	    asteroidShapes[shp][i] = (position_t *) dynmem;
90 	    dynmem += point_size;
91 	    asteroidShapes[shp][i][0].x = asteroidRawShapes[shp][i][0];
92 	    asteroidShapes[shp][i][0].y = asteroidRawShapes[shp][i][1];
93 	    Rotate_position( &asteroidShapes[shp][i][0] );
94 	}
95     }
96 
97     return 0;
98 }
99 
100 
Gui_paint_item_symbol(int type,Drawable d,GC mygc,int x,int y,int c)101 void Gui_paint_item_symbol(int type, Drawable d, GC mygc, int x, int y, int c)
102 {
103     if (!texturedObjects) {
104 	gcv.stipple = itemBitmaps[type];
105 	gcv.fill_style = FillStippled;
106 	gcv.ts_x_origin = x;
107 	gcv.ts_y_origin = y;
108 	XChangeGC(dpy, mygc,
109 		  GCStipple|GCFillStyle|GCTileStipXOrigin|GCTileStipYOrigin,
110 		  &gcv);
111 	rd.paintItemSymbol(type, d, mygc, x, y, c);
112 	XFillRectangle(dpy, d, mygc, x, y, ITEM_SIZE, ITEM_SIZE);
113 	gcv.fill_style = FillSolid;
114 	XChangeGC(dpy, mygc, GCFillStyle, &gcv);
115     } else
116 	Bitmap_paint(d, BM_ALL_ITEMS, x, y, type);
117 }
118 
Gui_paint_item(int type,Drawable d,GC mygc,int x,int y)119 void Gui_paint_item(int type, Drawable d, GC mygc, int x, int y)
120 {
121     const int		SIZE = ITEM_TRIANGLE_SIZE;
122     XPoint		points[5];
123 
124 #ifndef NO_ITEM_TRIANGLES
125     points[0].x = x - SIZE;
126     points[0].y = y - SIZE;
127     points[1].x = x;
128     points[1].y = y + SIZE;
129     points[2].x = x + SIZE;
130     points[2].y = y - SIZE;
131     points[3] = points[0];
132     SET_FG(colors[BLUE].pixel);
133     rd.drawLines(dpy, d, mygc, points, 4, CoordModeOrigin);
134 #endif
135 
136     SET_FG(colors[RED].pixel);
137 #if 0
138     str[0] = itemtype_ptr[i].type + '0';
139     str[1] = '\0';
140     rd.drawString(dpy, d, mygc,
141 		  x - XTextWidth(gameFont, str, 1)/2,
142 		  y + SIZE - 1,
143 		  str, 1);
144 #endif
145     Gui_paint_item_symbol(type, d, mygc,
146 			  x - ITEM_SIZE/2,
147 			  y - SIZE + 2, ITEM_PLAYFIELD);
148 }
149 
Gui_paint_item_object(int type,int x,int y)150 void Gui_paint_item_object(int type, int x, int y)
151 {
152     Gui_paint_item(type, drawPixmap, gameGC, WINSCALE(X(x)), WINSCALE(Y(y)));
153 }
154 
Gui_paint_ball(int x,int y,int style)155 void Gui_paint_ball(int x, int y, int style)
156 {
157     unsigned long rgb = 0;
158 
159     x = X(x);
160     y = Y(y);
161 
162     /*
163      * kps - currently style 0xff means no style.
164      * This code assumes num_polygon_styles < 256.
165      */
166     if (style >= 0 && style < num_polygon_styles)
167 	rgb = polygon_styles[style].rgb;
168 
169     if (!texturedObjects) {
170 	/* hack */
171 	if (rgb == 0)
172 	    Arc_add(ballColor, x - BALL_RADIUS, y - BALL_RADIUS,
173 		    2 * BALL_RADIUS, 2 * BALL_RADIUS, 0, 64 * 360);
174 	else
175 	    Arc_add_rgb(rgb, ballColor, x - BALL_RADIUS, y - BALL_RADIUS,
176 			2 * BALL_RADIUS, 2 * BALL_RADIUS, 0, 64 * 360);
177     } else {
178 	if (style == 0xff) {
179 	    Bitmap_paint(drawPixmap, BM_BALL, WINSCALE(x - BALL_RADIUS),
180 			 WINSCALE(y - BALL_RADIUS), 0);
181 	} else {
182 	    Bitmap_paint_blended(drawPixmap, BM_BALL_GRAY,
183 				 WINSCALE(x - BALL_RADIUS),
184 				 WINSCALE(y - BALL_RADIUS), rgb);
185 	}
186     }
187 }
188 
189 
Gui_paint_ball_connector(int x_1,int y_1,int x_2,int y_2)190 void Gui_paint_ball_connector(int x_1, int y_1, int x_2, int y_2)
191 {
192     x_2 = X(x_2);
193     y_2 = Y(y_2);
194     x_1 = X(x_1);
195     y_1 = Y(y_1);
196     Segment_add(connColor, x_1, y_1, x_2, y_2);
197 }
198 
Gui_paint_mine_name(int x,int y,char * name)199 static void Gui_paint_mine_name(int x, int y, char *name)
200 {
201     int		name_len, name_width;
202 
203     if (!name || !mineNameColor)
204 	return;
205 
206     SET_FG(colors[mineNameColor].pixel);
207 
208     name_len = strlen(name);
209     name_width = XTextWidth(gameFont, name, name_len);
210 
211     rd.drawString(dpy, drawPixmap, gameGC,
212 		  WINSCALE(x) - name_width / 2,
213 		  WINSCALE(y + 4) + gameFont->ascent + 1,
214 		  name, name_len);
215 }
216 
Gui_paint_mine(int x,int y,int teammine,char * name)217 void Gui_paint_mine(int x, int y, int teammine, char *name)
218 {
219     if (!texturedObjects) {
220 	static double	lastScaleFactor;
221 	static XPoint	mine_points[21];
222 	static XPoint	world_mine_points[21] = {
223 	    { 0, 0 },
224 	    { 1, 0 },
225 	    { 0, -1 },
226 	    { 4, 0 },
227 	    { 0, -1 },
228 	    { 6, 0 },
229 	    { 0, 1 },
230 	    { 4, 0 },
231 	    { 0, 1 },
232 	    { 1, 0 },
233 	    { 0, 2 },
234 	    { -1, 0 },
235 	    { 0, 1 },
236 	    { -4, 0 },
237 	    { 0, 1 },
238 	    { -6, 0 },
239 	    { 0, -1 },
240 	    { -4, 0 },
241 	    { 0, -1 },
242 	    { -1, 0 },
243 	    { 0, -2 }
244 	};
245 
246 	if (lastScaleFactor != clData.scaleFactor) {
247 	    int			i;
248 	    lastScaleFactor = clData.scaleFactor;
249 	    for (i = 1; i < 21; ++i) {
250 		mine_points[i].x = WINSCALE(world_mine_points[i].x);
251 		mine_points[i].y = WINSCALE(world_mine_points[i].y);
252 	    }
253 	}
254 
255 	x = X(x);
256 	y = Y(y);
257 	mine_points[0].x = WINSCALE(x - 8);
258 	mine_points[0].y = WINSCALE(y - 1);
259 	if (teammine == 0) {
260 	    SET_FG(colors[BLUE].pixel);
261 	    rd.fillRectangle(dpy, drawPixmap, gameGC,
262 			     WINSCALE(x - 7), WINSCALE(y - 2),
263 			     UWINSCALE(15), UWINSCALE(5));
264 	}
265 
266 	SET_FG(colors[WHITE].pixel);
267 	rd.drawLines(dpy, drawPixmap, gameGC,
268 		   mine_points, 21, CoordModePrevious);
269 
270 	if (name)
271 	    Gui_paint_mine_name(x, y, name);
272     }
273     else {
274 	x = X(x);
275 	y = Y(y);
276 	if (teammine == 0) {
277 	    SET_FG(colors[BLUE].pixel);
278 	    Bitmap_paint(drawPixmap, BM_MINE_OTHER, WINSCALE(x - 10),
279 			 WINSCALE(y - 7), 0);
280 	}
281 	else {
282 	    SET_FG(colors[WHITE].pixel);
283 	    Bitmap_paint(drawPixmap, BM_MINE_TEAM, WINSCALE(x - 10),
284 			 WINSCALE(y - 7), 0);
285 	}
286 
287 	if (name)
288 	    Gui_paint_mine_name(x, y, name);
289     }
290 }
291 
292 
Gui_paint_spark(int color,int x,int y)293 void Gui_paint_spark(int color, int x, int y)
294 {
295     color = spark_color[color];
296 
297     Rectangle_add(color,
298 		  x - sparkSize/2,
299 		  y - sparkSize/2,
300 		  sparkSize, sparkSize);
301 
302 }
303 
304 
Gui_paint_wreck(int x,int y,bool deadly,int wtype,int rot,int size)305 void Gui_paint_wreck(int x, int y, bool deadly, int wtype, int rot, int size)
306 {
307     int color, cnt, tx, ty;
308     static XPoint points[NUM_WRECKAGE_POINTS+2];
309 
310     for (cnt = 0; cnt < NUM_WRECKAGE_POINTS; cnt++) {
311 	tx = (int)(wreckageShapes[wtype][cnt][rot].x * size) >> 8;
312 	ty = (int)(wreckageShapes[wtype][cnt][rot].y * size) >> 8;
313 
314 	points[cnt].x = WINSCALE(X(x + tx));
315 	points[cnt].y = WINSCALE(Y(y + ty));
316 
317     }
318     points[cnt++] = points[0];
319 
320     color = (deadly) ? WHITE: RED;
321     SET_FG(colors[color].pixel);
322     rd.drawLines(dpy, drawPixmap, gameGC, points, cnt, 0);
323 }
324 
Gui_paint_asteroids_begin(void)325 void Gui_paint_asteroids_begin(void)
326 {
327 }
328 
Gui_paint_asteroids_end(void)329 void Gui_paint_asteroids_end(void)
330 {
331 }
332 
Gui_paint_asteroid(int x,int y,int type,int rot,int size)333 void Gui_paint_asteroid(int x, int y, int type, int rot, int size)
334 {
335     int cnt, tx, ty;
336     static XPoint points[NUM_ASTEROID_POINTS+2];
337 
338     type = type % NUM_ASTEROID_SHAPES;
339     for (cnt = 0; cnt < NUM_ASTEROID_POINTS; cnt++) {
340 	tx = (int)(asteroidShapes[type][cnt][rot].x * size * 1.4);
341 	ty = (int)(asteroidShapes[type][cnt][rot].y * size * 1.4);
342 
343 	points[cnt].x = WINSCALE(X(x + tx));
344 	points[cnt].y = WINSCALE(Y(y + ty));
345     }
346     points[cnt++] = points[0];
347 
348     SET_FG(colors[WHITE].pixel);
349     rd.drawLines(dpy, drawPixmap, gameGC, points, cnt, 0);
350 }
351 
352 
Gui_paint_nastyshot(int color,int x,int y,int size)353 static void Gui_paint_nastyshot(int color, int x, int y, int size)
354 {
355     int z = size;
356 
357     if (rfrac() < 0.5) {
358 	Segment_add(color,
359 		    x - z, y - z,
360 		    x + z, y + z);
361 	Segment_add(color,
362 		    x + z, y - z,
363 		    x - z, y + z);
364     } else {
365 	Segment_add(color,
366 		    x - z, y,
367 		    x  + z, y);
368 	Segment_add(color,
369 		    x, y - z,
370 		    x, y + z);
371     }
372 }
373 
374 
Gui_paint_fastshot(int color,int x,int y)375 void Gui_paint_fastshot(int color, int x, int y)
376 {
377     /* this is for those pesky invisible shots */
378     if (color == 0)
379 	return;
380 
381     if (!texturedObjects) {
382         int z = shotSize/2;
383 
384 	if (instruments.showNastyShots)
385 	    Gui_paint_nastyshot(color, x, y, z);
386 	else {
387 	    /* Show round shots - jiman392 */
388 	    if (shotSize > 2) {
389 		SET_FG(colors[color].pixel);
390 		rd.fillArc(dpy, drawPixmap, gameGC,
391 			   WINSCALE(x - z), WINSCALE(y - z),
392 			   UWINSCALE(shotSize), UWINSCALE(shotSize),
393 			   0, 64*360);
394 	    } else
395 		Rectangle_add(color,
396 			      x - z,
397 			      y - z,
398 			      shotSize, shotSize);
399 	}
400     }
401     else {
402 	int s_size = MIN(shotSize, 16);
403 	int z = s_size / 2;
404 
405 	Bitmap_paint(drawPixmap, BM_BULLET, WINSCALE(x) - z,
406 		     WINSCALE(y) - z, s_size - 1);
407     }
408 }
409 
Gui_paint_teamshot(int x,int y)410 void Gui_paint_teamshot(int x, int y)
411 {
412     int color = teamShotColor;
413 
414     if (color == 0)
415 	return;
416 
417     if (!texturedObjects) {
418         int z = teamShotSize/2;
419 
420 	if (instruments.showNastyShots)
421 	    Gui_paint_nastyshot(color, x, y, z);
422 	else {
423 	    /* Show round shots - jiman392 */
424 	    if (teamShotSize > 2) {
425 		SET_FG(colors[color].pixel);
426 		rd.fillArc(dpy, drawPixmap, gameGC,
427 			   WINSCALE(x - z), WINSCALE(y - z),
428 			   UWINSCALE(teamShotSize), UWINSCALE(teamShotSize),
429 			   0, 64*360);
430 	    } else
431 		Rectangle_add(color,
432 			      x - z,
433 			      y - z,
434 			      teamShotSize, teamShotSize);
435 	}
436     }
437     else {
438 	int s_size = MIN(teamShotSize, 16);
439 	int z = s_size / 2;
440 	Bitmap_paint(drawPixmap, BM_BULLET_OWN, WINSCALE(x) - z,
441 		     WINSCALE(y) - z, s_size - 1);
442     }
443 }
444 
445 
Gui_paint_missiles_begin(void)446 void Gui_paint_missiles_begin(void)
447 {
448     SET_FG(colors[WHITE].pixel);
449     XSetLineAttributes(dpy, gameGC, 4,
450 		       LineSolid, CapButt, JoinMiter);
451 }
452 
453 
Gui_paint_missiles_end(void)454 void Gui_paint_missiles_end(void)
455 {
456     XSetLineAttributes(dpy, gameGC, 0,
457 		       LineSolid, CapButt, JoinMiter);
458 }
459 
460 
Gui_paint_missile(int x,int y,int len,int dir)461 void Gui_paint_missile(int x, int y, int len, int dir)
462 {
463    int		x_1, x_2, y_1, y_2;
464 
465     x_1 = X(x);
466     y_1 = Y(y);
467     x_2 = (int)(x_1 - tcos(dir) * len);
468     y_2 = (int)(y_1 + tsin(dir) * len);
469     rd.drawLine(dpy, drawPixmap, gameGC,
470 		WINSCALE(x_1), WINSCALE(y_1), WINSCALE(x_2), WINSCALE(y_2));
471 }
472 
473 
Gui_paint_lasers_begin(void)474 void Gui_paint_lasers_begin(void)
475 {
476     XSetLineAttributes(dpy, gameGC, 3,
477 		       LineSolid, CapButt, JoinMiter);
478 }
479 
480 
Gui_paint_lasers_end(void)481 void Gui_paint_lasers_end(void)
482 {
483     XSetLineAttributes(dpy, gameGC, 0,
484 		       LineSolid, CapButt, JoinMiter);
485 }
486 
487 
Gui_paint_laser(int color,int x_1,int y_1,int len,int dir)488 void Gui_paint_laser(int color, int x_1, int y_1, int len, int dir)
489 {
490     int		x_2, y_2;
491 
492     x_2 = (int)(x_1 + len * tcos(dir));
493     y_2 = (int)(y_1 + len * tsin(dir));
494     if ((unsigned)(color) >= NUM_COLORS)
495 	color = WHITE;
496     SET_FG(colors[color].pixel);
497     rd.drawLine(dpy, drawPixmap, gameGC,
498 		WINSCALE(X(x_1)), WINSCALE(Y(y_1)),
499 		WINSCALE(X(x_2)), WINSCALE(Y(y_2)));
500 }
501 
502 
Gui_paint_paused(int x,int y,int count)503 void Gui_paint_paused(int x, int y, int count)
504 {
505     if (!texturedObjects) {
506 	int		x_0, y_0;
507 	static int	pauseCharWidth = -1;
508 	const unsigned	half_pause_size = 3*BLOCK_SZ/7;
509 
510 	if (pauseCharWidth < 0)
511 	    pauseCharWidth = XTextWidth(gameFont, "P", 1);
512 
513 	SET_FG(colors[BLUE].pixel);
514 	x_0 = X(x - half_pause_size);
515 	y_0 = Y(y + half_pause_size);
516 	rd.fillRectangle(dpy, drawPixmap, gameGC,
517 			 WINSCALE(x_0), WINSCALE(y_0),
518 			 UWINSCALE(2*half_pause_size+1),
519 			 UWINSCALE(2*half_pause_size+1));
520 	if (count <= 0 || loopsSlow % 10 >= 5) {
521 	    SET_FG(colors[WHITE].pixel);
522 	    rd.drawRectangle(dpy, drawPixmap, gameGC,
523 			     WINSCALE(x_0 - 1),
524 			     WINSCALE(y_0 - 1),
525 			     UWINSCALE(2*(half_pause_size+1)),
526 			     UWINSCALE(2*(half_pause_size+1)));
527 	    rd.drawString(dpy, drawPixmap, gameGC,
528 			  WINSCALE(X(x)) - pauseCharWidth/2,
529 			  WINSCALE(Y(y-1)) + gameFont->ascent/2,
530 			  "P", 1);
531 	}
532     } else
533 	Bitmap_paint(drawPixmap, BM_PAUSED, WINSCALE(X(x - BLOCK_SZ / 2)),
534 		     WINSCALE(Y(y + BLOCK_SZ / 2)),
535 		     (count <= 0 || loopsSlow % 10 >= 5) ? 1 : 0);
536 }
537 
538 
539 /* Create better graphics for this. */
Gui_paint_appearing(int x,int y,int id,int count)540 void Gui_paint_appearing(int x, int y, int id, int count)
541 {
542     const unsigned hsize = 3 * BLOCK_SZ / 7;
543     other_t *other = Other_by_id(id);
544     int color = other ? Life_color(other) : 0;
545 
546     if (!color)
547 	color = WHITE;
548 
549     /* Make a note we are doing the base warning */
550     if (version >= 0x4F12) {
551 	homebase_t *base = Homebase_by_id(id);
552 	if (base != NULL)
553 	    base->appeartime = (long)(loops + (count * clientFPS) / 120);
554     }
555 
556     SET_FG(colors[color].pixel);
557     rd.fillRectangle(dpy, drawPixmap, gameGC,
558 		     SCALEX(x - (int)hsize),
559 		     SCALEY(y - (int)hsize + (int)(count / 180. * hsize + 1)),
560 		     UWINSCALE(2 * hsize + 1),
561 		     UWINSCALE((unsigned)(count / 180. * hsize + 1)));
562 }
563 
564 
Gui_paint_ecm(int x,int y,int size)565 void Gui_paint_ecm(int x, int y, int size)
566 {
567     Arc_add(WHITE,
568 	    X(x - size / 2),
569 	    Y(y + size / 2),
570 	    size, size, 0, 64 * 360);
571 }
572 
573 
Gui_paint_refuel(int x_0,int y_0,int x_1,int y_1)574 void Gui_paint_refuel(int x_0, int y_0, int x_1, int y_1)
575 {
576     if (!texturedObjects) {
577 	rd.drawLine(dpy, drawPixmap, gameGC,
578 		    WINSCALE(X(x_0)), WINSCALE(Y(y_0)),
579 		    WINSCALE(X(x_1)), WINSCALE(Y(y_1)));
580     }
581     else {
582 	int size = WINSCALE(8);
583 	double dx, dy;
584 	int i;
585 	int fuel[16] = { 1, 2, 3, 3, 2, 1, 0, 1, 2, 3, 2, 1, 2, 3, 3, 2 };
586 
587 	x_0 = WINSCALE(X(x_0));
588 	y_0 = WINSCALE(Y(y_0));
589 	x_1 = WINSCALE(X(x_1));
590 	y_1 = WINSCALE(Y(y_1));
591 	dx = (double)(x_1 - x_0) / 16;
592 	dy = (double)(y_1 - y_0) / 16;
593 	for (i = 0; i < 16; i++) {
594 	    Bitmap_paint(drawPixmap, BM_REFUEL,
595 			 (int)(x_0 + (dx * i) - size / 2),
596 			 (int)(y_0 + (dy * i) - size / 2),
597 			 fuel[(loops + 16 - i) % 16]);
598 	}
599     }
600 }
601 
602 
Gui_paint_connector(int x_0,int y_0,int x_1,int y_1,int tractor)603 void Gui_paint_connector(int x_0, int y_0, int x_1, int y_1, int tractor)
604 {
605     if (tractor)
606 	rd.setDashes(dpy, gameGC, 0, cdashes, NUM_CDASHES);
607     else
608 	rd.setDashes(dpy, gameGC, 0, dashes, NUM_DASHES);
609 
610     rd.drawLine(dpy, drawPixmap, gameGC,
611 		WINSCALE(X(x_0)), WINSCALE(Y(y_0)),
612 		WINSCALE(X(x_1)), WINSCALE(Y(y_1)));
613     if (tractor)
614 	rd.setDashes(dpy, gameGC, 0, dashes, NUM_DASHES);
615 }
616 
617 
Gui_paint_transporter(int x_0,int y_0,int x_1,int y_1)618 void Gui_paint_transporter(int x_0, int y_0, int x_1, int y_1)
619 {
620     rd.drawLine(dpy, drawPixmap, gameGC,
621 		WINSCALE(X(x_0)), WINSCALE(Y(y_0)),
622 		WINSCALE(X(x_1)), WINSCALE(Y(y_1)));
623 }
624 
625 
Gui_paint_all_connectors_begin(void)626 void Gui_paint_all_connectors_begin(void)
627 {
628     unsigned long	mask;
629 
630     SET_FG(colors[connColor].pixel);
631     if (gcv.line_style != LineOnOffDash) {
632 	gcv.line_style = LineOnOffDash;
633 	mask = GCLineStyle;
634 #ifndef NO_ROTATING_DASHES
635 	mask |= GCDashOffset;
636 #endif
637 	XChangeGC(dpy, gameGC, mask, &gcv);
638     }
639 
640 }
641 
642 
Gui_paint_ships_begin(void)643 void Gui_paint_ships_begin(void)
644 {
645     gcv.dash_offset = WINSCALE(DASHES_LENGTH - (loops % DASHES_LENGTH));
646 }
647 
648 
Gui_paint_ships_end(void)649 void Gui_paint_ships_end(void)
650 {
651    unsigned long	mask;
652    if (gcv.line_style != LineSolid) {
653 	gcv.line_style = LineSolid;
654 	mask = GCLineStyle;
655 	XChangeGC(dpy, gameGC, mask, &gcv);
656     }
657     gcv.dash_offset = 0;
658 }
659 
660 
Gui_paint_rounddelay(int x,int y)661 static void Gui_paint_rounddelay(int x, int y)
662 {
663     char s[12];
664     int	 t, text_width;
665 
666     sprintf(s, "%d", roundDelay / FPS);
667     t = strlen(s);
668     SET_FG(colors[WHITE].pixel);
669     text_width = XTextWidth(gameFont, s, t);
670     rd.drawString(dpy, drawPixmap, gameGC,
671 		  WINSCALE(X(x)) - text_width / 2,
672 		  WINSCALE(Y(y)) + gameFont->ascent/2,
673 		  s, t);
674 }
675 
676 
677 /*  Here starts the paint functions for ships  (MM) */
Gui_paint_ship_name(int x,int y,other_t * other)678 static void Gui_paint_ship_name(int x, int y, other_t *other)
679 {
680     Check_name_string(other);
681     if (shipNameColor) {
682 	int color = Life_color(other);
683 	if (!color)
684 	    color = shipNameColor;
685 
686 	SET_FG(colors[color].pixel);
687 	rd.drawString(dpy, drawPixmap, gameGC,
688 		      WINSCALE(X(x)) - other->name_width / 2,
689 		      WINSCALE(Y(y) + 16) + gameFont->ascent,
690 		      other->id_string, other->name_len);
691     } else
692 	SET_FG(colors[BLUE].pixel);
693 
694     if (instruments.showLivesByShip
695 	&& BIT(Setup->mode, LIMITED_LIVES)) {
696 	char keff[4] = "";
697 
698 	sprintf(keff, "%03d", other->life);
699 	rd.drawString(dpy, drawPixmap, gameGC,
700 		      WINSCALE(X(x) + SHIP_SZ),
701 		      WINSCALE(Y(y) - SHIP_SZ) + gameFont->ascent,
702 		      &keff[2], 1);
703     }
704 }
705 
706 
Gui_is_my_tank(other_t * other)707 static int Gui_is_my_tank(other_t *other)
708 {
709     char	tank_name[MAX_NAME_LEN];
710 
711     if (self == NULL
712 	|| other == NULL
713 	|| other->mychar != 'T'
714 	|| (BIT(Setup->mode, TEAM_PLAY)
715 	&& self->team != other->team)) {
716 	    return 0;
717     }
718 
719     if (strlcpy(tank_name, self->nick_name, MAX_NAME_LEN) < MAX_NAME_LEN)
720 	strlcat(tank_name, "'s tank", MAX_NAME_LEN);
721 
722     if (strcmp(tank_name, other->nick_name))
723 	return 0;
724 
725     return 1;
726 }
727 
Gui_calculate_ship_color(int id,other_t * other)728 static int Gui_calculate_ship_color(int id, other_t *other)
729 {
730     int ship_color = WHITE;
731 
732     if (BIT(Setup->mode, TEAM_PLAY)
733 	&& eyesId != id
734 	&& other != NULL
735 	&& eyeTeam == other->team) {
736 	/* Paint teammates and allies ships with last life in teamLWColor */
737 	if (BIT(Setup->mode, LIMITED_LIVES)
738 	    && (other->life == 0))
739 	    ship_color = teamLWColor;
740 	else
741 	    ship_color = teamShipColor;
742     }
743 
744     if (eyes != NULL
745 	&& eyesId != id
746 	&& other != NULL
747 	&& eyes->alliance != ' '
748 	&& eyes->alliance == other->alliance) {
749 	/* Paint teammates and allies ships with last life in teamLWColor */
750 	if (BIT(Setup->mode, LIMITED_LIVES)
751 	    && (other->life == 0))
752 	    ship_color = teamLWColor;
753 	else
754 	    ship_color = teamShipColor;
755     }
756 
757     if (Gui_is_my_tank(other))
758 	ship_color = BLUE;
759 
760     if (roundDelay > 0 && ship_color == WHITE)
761 	ship_color = RED;
762 
763     /* Check for team color */
764     if (other && BIT(Setup->mode, TEAM_PLAY)) {
765 	int team_color = Team_color(other->team);
766 	if (team_color)
767 	    return team_color;
768     }
769 
770     /* Vato color hack start, edited by mara & kps */
771     if (BIT(Setup->mode, LIMITED_LIVES)) {
772 	/* Paint your ship in selfLWColor when on last life */
773 	if (eyes != NULL
774 	    && eyes->id == id
775 	    && eyes->life == 0) {
776 	    ship_color = selfLWColor;
777 	}
778 
779 	/* Paint enemy ships with last life in enemyLWColor */
780 	if (eyes != NULL
781 	    && eyes->id != id
782 	    && other != NULL
783 	    && eyeTeam != other->team
784 	    && other->life == 0) {
785 	    ship_color = enemyLWColor;
786 	}
787     }
788     /* Vato color hack end */
789 
790     return ship_color;
791 }
792 
793 
Gui_paint_marking_lights(int id,int x,int y,shipshape_t * ship,int dir)794 static void Gui_paint_marking_lights(int id, int x, int y,
795 				     shipshape_t *ship, int dir)
796 {
797     int lcnt;
798 
799     if (((loopsSlow + id) & 0xF) == 0) {
800 	for (lcnt = 0; lcnt < ship->num_l_light; lcnt++) {
801 	    position_t l_light = Ship_get_l_light_position(ship, lcnt, dir);
802 	    Rectangle_add(RED,
803 			  X(x + l_light.x) - 2,
804 			  Y(y + l_light.y) - 2,
805 			  6, 6);
806 	    Segment_add(RED,
807 			X(x + l_light.x)-8,
808 			Y(y + l_light.y),
809 			X(x + l_light.x)+8,
810 			Y(y + l_light.y));
811 	    Segment_add(RED,
812 			X(x + l_light.x),
813 			Y(y + l_light.y)-8,
814 			X(x + l_light.x),
815 			Y(y + l_light.y)+8);
816 	}
817     } else if (((loopsSlow + id) & 0xF) == 2) {
818 	for (lcnt = 0; lcnt < ship->num_r_light; lcnt++) {
819 	    int rightLightColor = maxColors > 4 ? 4 : BLUE;
820 	    position_t r_light = Ship_get_r_light_position(ship, lcnt, dir);
821 	    Rectangle_add(rightLightColor,
822 			  X(x + r_light.x)-2,
823 			  Y(y + r_light.y)-2,
824 			  6, 6);
825 	    Segment_add(rightLightColor,
826 			X(x + r_light.x)-8,
827 			Y(y + r_light.y),
828 			X(x + r_light.x)+8,
829 			Y(y + r_light.y));
830 	    Segment_add(rightLightColor,
831 			X(x + r_light.x),
832 			Y(y + r_light.y)-8,
833 			X(x + r_light.x),
834 			Y(y + r_light.y)+8);
835 	}
836     }
837 }
838 
839 
Gui_paint_shields_deflectors(int x,int y,int radius,int shield,int deflector,int eshield,int ship_color)840 static void Gui_paint_shields_deflectors(int x, int y, int radius, int shield,
841 					 int deflector, int eshield,
842 					 int ship_color)
843 {
844     int		e_radius = radius + 4;
845     int		half_radius = radius >> 1;
846     int		half_e_radius = e_radius >> 1;
847     int		scolor = -1;
848     int		ecolor = -1;
849 
850     if (shield)
851 	scolor = ship_color;
852     if (deflector)
853 	ecolor = loopsSlow & 0x02 ? RED : BLUE;
854     if (eshield && shield) {
855 	if (ecolor != -1) {
856 	    scolor = ecolor;
857 	    ecolor = ship_color;
858 	} else
859 	    scolor = ecolor = ship_color;
860     }
861 
862     if (ecolor != -1) {		/* outer shield */
863 	SET_FG(colors[ecolor].pixel);
864 	rd.drawArc(dpy, drawPixmap, gameGC,
865 		   WINSCALE(X(x - half_e_radius)),
866 		   WINSCALE(Y(y + half_e_radius)),
867 		   (unsigned)WINSCALE(e_radius),
868 		   (unsigned)WINSCALE(e_radius),
869 		   0, 64 * 360);
870     }
871     if (scolor != -1) {
872 	SET_FG(colors[scolor].pixel);
873 	rd.drawArc(dpy, drawPixmap, gameGC,
874 		   WINSCALE(X(x - half_radius)),
875 		   WINSCALE(Y(y + half_radius)),
876 		   (unsigned)WINSCALE(radius),
877 		   (unsigned)WINSCALE(radius),
878 		   0, 64 * 360);
879     }
880 }
881 
882 static void Set_drawstyle_dashed(int ship_color);
883 
Gui_paint_ship_cloaked(int ship_color,XPoint * points,int point_count)884 static void Gui_paint_ship_cloaked(int ship_color, XPoint *points,
885 				   int point_count)
886 {
887     Set_drawstyle_dashed(ship_color);
888     rd.drawLines(dpy, drawPixmap, gameGC, points, point_count, 0);
889 }
890 
Gui_paint_ship_phased(int ship_color,XPoint * points,int point_count)891 static void Gui_paint_ship_phased(int ship_color, XPoint *points,
892 				  int point_count)
893 {
894     Gui_paint_ship_cloaked(ship_color, points, point_count);
895 }
896 
generic_paint_ship(int x,int y,int ang,int ship)897 static void generic_paint_ship(int x, int y, int ang, int ship)
898 {
899     Bitmap_paint(drawPixmap, ship,
900 		 WINSCALE(X(x) - 16), WINSCALE(Y(y) - 16), ang);
901 }
902 
903 
Gui_paint_ship_uncloaked(int id,XPoint * points,int ship_color,int point_count)904 static void Gui_paint_ship_uncloaked(int id, XPoint *points,
905 				     int ship_color, int point_count)
906 {
907     if (gcv.line_style != LineSolid) {
908 	gcv.line_style = LineSolid;
909 	XChangeGC(dpy, gameGC, GCLineStyle, &gcv);
910     }
911     SET_FG(colors[ship_color].pixel);
912     rd.drawLines(dpy, drawPixmap, gameGC, points, point_count, 0);
913 
914     if (lock_id == id && id != -1 && lock_dist != 0)
915 	rd.fillPolygon(dpy, drawPixmap, gameGC,
916 		       points, point_count,
917 		       Complex, CoordModeOrigin);
918 }
919 
920 
Set_drawstyle_dashed(int ship_color)921 static void Set_drawstyle_dashed(int ship_color)
922 {
923     unsigned long mask;
924     if (gcv.line_style != LineOnOffDash) {
925 	gcv.line_style = LineOnOffDash;
926 	mask = GCLineStyle;
927 #ifndef NO_ROTATING_DASHES
928 	mask |= GCDashOffset;
929 #endif
930 	XChangeGC(dpy, gameGC, mask, &gcv);
931     }
932     SET_FG(colors[ship_color].pixel);
933 }
934 
set_shipshape(int world_x,int world_y,int dir,shipshape_t * ship,XPoint * points)935 static int set_shipshape(int world_x, int world_y,
936 			 int dir, shipshape_t *ship, XPoint *points)
937 {
938     int cnt;
939     position_t ship_point_pos;
940     XPoint *xpts = points;
941     double x, y;
942 
943     for (cnt = 0; cnt < ship->num_points; cnt++) {
944 	ship_point_pos = Ship_get_point_position(ship, cnt, dir);
945 	x = (world_x - world.x + ship_point_pos.x) / clData.scaleFactor;
946 	y = (world.y + ext_view_height - world_y - ship_point_pos.y)
947 		/ clData.scaleFactor;
948 	xpts->x = (short)rint(x);
949 	xpts->y = (short)rint(y);
950 	xpts++;
951     }
952     points[cnt++] = points[0];
953 
954     return cnt;
955 }
956 
957 
Gui_paint_ship(int x,int y,int dir,int id,int cloak,int phased,int shield,int deflector,int eshield)958 void Gui_paint_ship(int x, int y, int dir, int id, int cloak, int phased,
959 		    int shield, int deflector, int eshield)
960 {
961     int			cnt, ship_color;
962     other_t		*other;
963     shipshape_t		*ship;
964     XPoint		points[64];
965     int			ship_shape;
966 
967     ship = Ship_by_id(id);
968     other = Other_by_id(id);
969     ship_color = WHITE;
970 
971     /* mara attempts similar behaviour to the kth ss hack */
972     if ((!instruments.showShipShapes)
973 	&& (self != NULL)
974 	&& (self->id != id))
975 	cnt = set_shipshape(x, y, dir, Default_ship(), points);
976     else if ((!instruments.showMyShipShape)
977 	       && (self != NULL)
978 	       && (self->id == id))
979 	cnt = set_shipshape(x, y, dir, Default_ship(), points);
980     else
981 	cnt = set_shipshape(x, y, dir, ship, points);
982 
983     /*
984      * Determine if the name of the player should be drawn below
985      * his/her ship.
986      */
987     if (self != NULL
988 	&& self->id != id
989 	&& other != NULL)
990 	Gui_paint_ship_name(x, y, other);
991 
992     if (roundDelay > 0 && roundDelay % FPS < FPS/2) {
993 	Gui_paint_rounddelay(x, y);
994 	return;
995     }
996 
997     if (!(ship_color = Gui_calculate_ship_color(id, other))) return;
998 
999     if (cloak == 0 && phased == 0) {
1000 	if (!texturedObjects || !texturedShips) {
1001 	    Gui_paint_ship_uncloaked(id, points, ship_color, cnt);
1002 	    /* shipshapeshack by Mara */
1003 	    if (instruments.showShipShapesHack) {
1004 		Segment_add(ship_color,
1005 			    (X(x + SHIP_SZ * tcos(dir))),
1006 			    (Y(y + SHIP_SZ * tsin(dir))),
1007 			    (X(x + (SHIP_SZ + 12) * tcos(dir))),
1008 			    (Y(y + (SHIP_SZ + 12) * tsin(dir))));
1009 		Arc_add(ship_color,
1010 			X(x - SHIP_SZ), Y(y + SHIP_SZ),
1011 			2 * SHIP_SZ, 2 * SHIP_SZ,
1012 			0, 64 * 360);
1013 	    }
1014 	} else {
1015 	    if (ship_color == BLUE)
1016 		ship_shape = BM_SHIP_FRIEND;
1017 	    else if (self != NULL && self->id != id)
1018 		ship_shape = BM_SHIP_ENEMY;
1019 	    else
1020 		ship_shape = BM_SHIP_SELF;
1021 
1022 	    generic_paint_ship(x, y, dir, ship_shape);
1023 	}
1024 
1025     }
1026 
1027     if (phased)
1028 	Gui_paint_ship_phased(ship_color, points, cnt);
1029     else if (cloak)
1030 	Gui_paint_ship_cloaked(ship_color, points, cnt);
1031 
1032     if (markingLights)
1033         Gui_paint_marking_lights(id, x, y, ship, dir);
1034 
1035     if (shield || deflector) {
1036 	Set_drawstyle_dashed(ship_color);
1037 	Gui_paint_shields_deflectors(x, y, ship->shield_radius,
1038 				     shield, deflector,
1039 				     eshield, ship_color);
1040     }
1041 }
1042 
1043 
Team_color(int team)1044 int Team_color(int team)
1045 {
1046     /* This code assumes we have max 10 teams. */
1047     assert(MAX_TEAMS == 10);
1048     switch (team) {
1049     case 0:	return team0Color;
1050     case 1:	return team1Color;
1051     case 2:	return team2Color;
1052     case 3:	return team3Color;
1053     case 4:	return team4Color;
1054     case 5:	return team5Color;
1055     case 6:	return team6Color;
1056     case 7:	return team7Color;
1057     case 8:	return team8Color;
1058     case 9:	return team9Color;
1059     default:    break;
1060     }
1061     return 0;
1062 }
1063 
Life_color(other_t * other)1064 int Life_color(other_t *other)
1065 {
1066     int color = 0; /* default is 'no special color' */
1067 
1068     if (other
1069 	&& (other->mychar == ' ' || other->mychar == 'R')
1070 	&& BIT(Setup->mode, LIMITED_LIVES))
1071 	color = Life_color_by_life(other->life);
1072     return color;
1073 }
1074 
Life_color_by_life(int life)1075 int Life_color_by_life(int life)
1076 {
1077     int color;
1078 
1079     if (life > 2)
1080 	color = manyLivesColor;
1081     else if (life == 2)
1082 	color = twoLivesColor;
1083     else if (life == 1)
1084 	color = oneLifeColor;
1085     else /* we catch all */
1086 	color = zeroLivesColor;
1087     return color;
1088 }
1089 
1090 
1091 
1092 static xp_option_t guiobject_options[] = {
1093     COLOR_INDEX_OPTION(
1094 	"teamShotColor",
1095 	2,
1096 	&teamShotColor,
1097 	"Which color number to use for drawing harmless shots.\n"),
1098 
1099     COLOR_INDEX_OPTION(
1100 	"ballColor",
1101 	1,
1102 	&ballColor,
1103 	"Which color number to use for drawing balls.\n"),
1104 
1105     COLOR_INDEX_OPTION(
1106 	"connColor",
1107 	2,
1108 	&connColor,
1109 	"Which color number to use for drawing connectors.\n"),
1110 
1111     COLOR_INDEX_OPTION(
1112 	"zeroLivesColor",
1113 	5,
1114 	&zeroLivesColor,
1115 	"Which color to associate with ships with zero lives left.\n"
1116 	"This can be used to paint for example ship and base names.\n"),
1117 
1118     COLOR_INDEX_OPTION(
1119 	"oneLifeColor",
1120 	11,
1121 	&oneLifeColor,
1122 	"Which color to associate with ships with one life left.\n"
1123 	"This can be used to paint for example ship and base names.\n"),
1124 
1125     COLOR_INDEX_OPTION(
1126 	"twoLivesColor",
1127 	4,
1128 	&twoLivesColor,
1129 	"Which color to associate with ships with two lives left.\n"
1130 	"This can be used to paint for example ship and base names.\n"),
1131 
1132     COLOR_INDEX_OPTION(
1133 	"manyLivesColor",
1134 	0,
1135 	&manyLivesColor,
1136 	"Which color to associate with ships with more than two lives left.\n"
1137 	"This can be used to paint for example ship and base names.\n"),
1138 
1139     COLOR_INDEX_OPTION(
1140 	"selfLWColor",
1141 	3,
1142 	&selfLWColor,
1143 	"Which color to use to paint your ship in when on last life.\n"
1144 	"Original color for this is red.\n"),
1145 
1146     COLOR_INDEX_OPTION(
1147 	"enemyLWColor",
1148 	3,
1149 	&enemyLWColor,
1150 	"Which color to use to paint enemy ships in when on last life.\n"
1151 	"Original color for this is red.\n"),
1152 
1153     COLOR_INDEX_OPTION(
1154 	"teamLWColor",
1155 	2,
1156 	&teamLWColor,
1157 	"Which color to use to paint teammate ships in when on last life.\n"
1158 	"Original color for this is green.\n"),
1159 
1160     COLOR_INDEX_OPTION(
1161 	"shipNameColor",
1162 	2,
1163 	&shipNameColor,
1164 	"Which color number to use for drawing names of ships\n"
1165 	"(unless drawn in one of the life colors).\n"),
1166 
1167     COLOR_INDEX_OPTION(
1168 	"mineNameColor",
1169 	2,
1170 	&mineNameColor,
1171 	"Which color number to use for drawing names of mines.\n"),
1172 
1173     COLOR_INDEX_OPTION(
1174 	"teamShipColor",
1175 	2,
1176 	&teamShipColor,
1177 	"Which color number to use for drawing your teammates.\n"),
1178 
1179     COLOR_INDEX_OPTION(
1180 	"team0Color",
1181 	0,
1182 	&team0Color,
1183 	"Which color number to use for drawing team 0 objects.\n"),
1184 
1185     COLOR_INDEX_OPTION(
1186 	"team1Color",
1187 	0,
1188 	&team1Color,
1189 	"Which color number to use for drawing team 1 objects.\n"),
1190 
1191     COLOR_INDEX_OPTION(
1192 	"team2Color",
1193 	0,
1194 	&team2Color,
1195 	"Which color number to use for drawing team 2 objects.\n"),
1196 
1197     COLOR_INDEX_OPTION(
1198 	"team3Color",
1199 	0,
1200 	&team3Color,
1201 	"Which color number to use for drawing team 3 objects.\n"),
1202 
1203     COLOR_INDEX_OPTION(
1204 	"team4Color",
1205 	0,
1206 	&team4Color,
1207 	"Which color number to use for drawing team 4 objects.\n"),
1208 
1209     COLOR_INDEX_OPTION(
1210 	"team5Color",
1211 	0,
1212 	&team5Color,
1213 	"Which color number to use for drawing team 5 objects.\n"),
1214 
1215     COLOR_INDEX_OPTION(
1216 	"team6Color",
1217 	0,
1218 	&team6Color,
1219 	"Which color number to use for drawing team 6 objects.\n"),
1220 
1221     COLOR_INDEX_OPTION(
1222 	"team7Color",
1223 	0,
1224 	&team7Color,
1225 	"Which color number to use for drawing team 7 objects.\n"),
1226 
1227     COLOR_INDEX_OPTION(
1228 	"team8Color",
1229 	0,
1230 	&team8Color,
1231 	"Which color number to use for drawing team 8 objects.\n"),
1232 
1233     COLOR_INDEX_OPTION(
1234 	"team9Color",
1235 	0,
1236 	&team9Color,
1237 	"Which color number to use for drawing team 9 objects.\n"),
1238 };
1239 
Store_guiobject_options(void)1240 void Store_guiobject_options(void)
1241 {
1242     STORE_OPTIONS(guiobject_options);
1243 }
1244