xref: /openbsd/games/hunt/huntd/draw.c (revision ac1fa6a8)
1 /*	$OpenBSD: draw.c,v 1.11 2017/01/21 08:22:57 krw Exp $	*/
2 /*	$NetBSD: draw.c,v 1.2 1997/10/10 16:33:04 lukem Exp $	*/
3 /*
4  * Copyright (c) 1983-2003, Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  * + Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * + Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in the
15  *   documentation and/or other materials provided with the distribution.
16  * + Neither the name of the University of California, San Francisco nor
17  *   the names of its contributors may be used to endorse or promote
18  *   products derived from this software without specific prior written
19  *   permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/select.h>
35 #include <string.h>
36 
37 #include "conf.h"
38 #include "hunt.h"
39 #include "server.h"
40 
41 static char	translate(char);
42 static int	player_sym(PLAYER *, int, int);
43 static void	drawstatus(PLAYER *);
44 static void	see(PLAYER *, int);
45 
46 /*
47  * drawmaze:
48  *	Draw the entire maze on a player's screen.
49  */
50 void
drawmaze(PLAYER * pp)51 drawmaze(PLAYER *pp)
52 {
53 	int	x;
54 	char	*sp;
55 	int	y;
56 	char	*endp;
57 
58 	/* Clear the client's screen: */
59 	clrscr(pp);
60 	/* Draw the top row of the maze: */
61 	outstr(pp, pp->p_maze[0], WIDTH);
62 	for (y = 1; y < HEIGHT - 1; y++) {
63 		endp = &pp->p_maze[y][WIDTH];
64 		for (x = 0, sp = pp->p_maze[y]; sp < endp; x++, sp++)
65 			if (*sp != SPACE) {
66 				cgoto(pp, y, x);
67 				/* Draw the player as themselves */
68 				if (pp->p_x == x && pp->p_y == y)
69 					outch(pp, translate(*sp));
70 				/* Possibly draw other players as team nrs */
71 				else if (is_player(*sp))
72 					outch(pp, player_sym(pp, y, x));
73 				else
74 					outch(pp, *sp);
75 			}
76 	}
77 	/* Draw the last row of the maze: */
78 	cgoto(pp, HEIGHT - 1, 0);
79 	outstr(pp, pp->p_maze[HEIGHT - 1], WIDTH);
80 	drawstatus(pp);
81 }
82 
83 /*
84  * drawstatus - put up the status lines (this assumes the screen
85  *		size is 80x24 with the maze being 64x24)
86  */
87 static void
drawstatus(PLAYER * pp)88 drawstatus(PLAYER *pp)
89 {
90 	int	i;
91 	PLAYER	*np;
92 
93 	outyx(pp, STAT_AMMO_ROW, STAT_LABEL_COL, "Ammo:");
94 	ammo_update(pp);
95 
96 	outyx(pp, STAT_GUN_ROW,  STAT_LABEL_COL, "Gun:");
97 	outyx(pp, STAT_GUN_ROW,  STAT_VALUE_COL, "%3s",
98 		(pp->p_ncshot < conf_maxncshot) ? "ok" : "");
99 
100 	outyx(pp, STAT_DAM_ROW,  STAT_LABEL_COL, "Damage:");
101 	outyx(pp, STAT_DAM_ROW,  STAT_VALUE_COL, "%2d/%2d",
102 		pp->p_damage, pp->p_damcap);
103 
104 	outyx(pp, STAT_KILL_ROW, STAT_LABEL_COL, "Kills:");
105 	outyx(pp, STAT_KILL_ROW, STAT_VALUE_COL, "%3d",
106 		(pp->p_damcap - conf_maxdam) / 2);
107 
108 	outyx(pp, STAT_PLAY_ROW, STAT_LABEL_COL, "Player:");
109 	for (i = STAT_PLAY_ROW + 1, np = Player; np < End_player; np++, i++) {
110 		outyx(pp, i, STAT_NAME_COL, "%5.2f%c%-10.10s %c",
111 			np->p_ident->i_score, stat_char(np),
112 			np->p_ident->i_name, np->p_ident->i_team);
113 	}
114 
115 	outyx(pp, STAT_MON_ROW, STAT_LABEL_COL, "Monitor:");
116 	for (i = STAT_MON_ROW + 1, np = Monitor; np < End_monitor; np++, i++) {
117 		outyx(pp, i++, STAT_NAME_COL, "%5.5s %-10.10s %c",
118 			" ", np->p_ident->i_name, np->p_ident->i_team);
119 	}
120 }
121 
122 /*
123  * look
124  *	check and update the visible area around the player
125  */
126 void
look(PLAYER * pp)127 look(PLAYER *pp)
128 {
129 	int	x, y;
130 
131 	x = pp->p_x;
132 	y = pp->p_y;
133 
134 	/*
135 	 * The player is aware of all objects immediately adjacent to
136 	 * their position:
137 	 */
138 	check(pp, y - 1, x - 1);
139 	check(pp, y - 1, x    );
140 	check(pp, y - 1, x + 1);
141 	check(pp, y    , x - 1);
142 	check(pp, y    , x    );
143 	check(pp, y    , x + 1);
144 	check(pp, y + 1, x - 1);
145 	check(pp, y + 1, x    );
146 	check(pp, y + 1, x + 1);
147 
148 	switch (pp->p_face) {
149 	  /* The player can see down corridors in directions except behind: */
150 	  case LEFTS:
151 		see(pp, LEFTS);
152 		see(pp, ABOVE);
153 		see(pp, BELOW);
154 		break;
155 	  case RIGHT:
156 		see(pp, RIGHT);
157 		see(pp, ABOVE);
158 		see(pp, BELOW);
159 		break;
160 	  case ABOVE:
161 		see(pp, ABOVE);
162 		see(pp, LEFTS);
163 		see(pp, RIGHT);
164 		break;
165 	  case BELOW:
166 		see(pp, BELOW);
167 		see(pp, LEFTS);
168 		see(pp, RIGHT);
169 		break;
170 	  /* But they don't see too far when they are flying about: */
171 	  case FLYER:
172 		break;
173 	}
174 
175 	/* Move the cursor back over the player: */
176 	cgoto(pp, y, x);
177 }
178 
179 /*
180  * see
181  *	Look down a corridor, or towards an open space. This
182  *	is a simulation of visibility from the player's perspective.
183  */
184 static void
see(PLAYER * pp,int face)185 see(PLAYER *pp, int face)
186 {
187 	char	*sp;
188 	int	y, x;
189 
190 	/* Start from the player's position: */
191 	x = pp->p_x;
192 	y = pp->p_y;
193 
194 	#define seewalk(dx, dy)						\
195 		x += (dx);						\
196 		y += (dy);						\
197 		sp = &Maze[y][x];					\
198 		while (See_over[(int)*sp]) {				\
199 			x += (dx);					\
200 			y += (dy);					\
201 			sp += ((dx) + (dy) * sizeof Maze[0]);		\
202 			check(pp, y + dx, x + dy);			\
203 			check(pp, y, x);				\
204 			check(pp, y - dx, x - dy);			\
205 		}
206 
207 	switch (face) {
208 	  case LEFTS:
209 		seewalk(-1, 0); break;
210 	  case RIGHT:
211 		seewalk(1, 0); break;
212 	  case ABOVE:
213 		seewalk(0, -1); break;
214 	  case BELOW:
215 		seewalk(0, 1); break;
216 	}
217 }
218 
219 /*
220  * check
221  *	The player is aware of a cell in the maze.
222  *	Ensure it is shown properly on their screen.
223  */
224 void
check(PLAYER * pp,int y,int x)225 check(PLAYER *pp, int y, int x)
226 {
227 	int	index;
228 	int	ch;
229 	PLAYER	*rpp;
230 
231 	if (pp == ALL_PLAYERS) {
232 		for (pp = Player; pp < End_player; pp++)
233 			check(pp, y, x);
234 		for (pp = Monitor; pp < End_monitor; pp++)
235 			check(pp, y, x);
236 		return;
237 	}
238 
239 	index = y * sizeof Maze[0] + x;
240 	ch = ((char *) Maze)[index];
241 	if (ch != ((char *) pp->p_maze)[index]) {
242 		rpp = pp;
243 		cgoto(rpp, y, x);
244 		if (x == rpp->p_x && y == rpp->p_y)
245 			outch(rpp, translate(ch));
246 		else if (is_player(ch))
247 			outch(rpp, player_sym(rpp, y, x));
248 		else
249 			outch(rpp, ch);
250 		((char *) rpp->p_maze)[index] = ch;
251 	}
252 }
253 
254 /*
255  * showstat
256  *	Update the status of a player on everyone's screen
257  */
258 void
showstat(PLAYER * pp)259 showstat(PLAYER *pp)
260 {
261 
262 	outyx(ALL_PLAYERS,
263 		STAT_PLAY_ROW + 1 + (pp - Player), STAT_SCAN_COL,
264 		"%c", stat_char(pp));
265 }
266 
267 /*
268  * drawplayer:
269  *	Draw the player on the screen and show him to everyone who's scanning
270  *	unless he is cloaked.
271  *	The 'draw' flag when false, causes the 'saved under' character to
272  *	be drawn instead of the player; effectively un-drawing the player.
273  */
274 void
drawplayer(PLAYER * pp,FLAG draw)275 drawplayer(PLAYER *pp, FLAG draw)
276 {
277 	PLAYER	*newp;
278 	int	x, y;
279 
280 	x = pp->p_x;
281 	y = pp->p_y;
282 
283 	/* Draw or un-draw the player into the master map: */
284 	Maze[y][x] = draw ? pp->p_face : pp->p_over;
285 
286 	/* The monitors can always see this player: */
287 	for (newp = Monitor; newp < End_monitor; newp++)
288 		check(newp, y, x);
289 
290 	/* Check if other players can see this player: */
291 	for (newp = Player; newp < End_player; newp++) {
292 		if (!draw) {
293 			/* When un-drawing, show everyone what was under */
294 			check(newp, y, x);
295 			continue;
296 		}
297 		if (newp == pp) {
298 			/* The player can always see themselves: */
299 			check(newp, y, x);
300 			continue;
301 		}
302 		/* Check if the other player just run out of scans */
303 		if (newp->p_scan == 0) {
304 			/* The other player is no longer scanning: */
305 			newp->p_scan--;
306 			showstat(newp);
307 		/* Check if the other play is scannning */
308 		} else if (newp->p_scan > 0) {
309 			/* If this player's not cloacked, draw him: */
310 			if (pp->p_cloak < 0)
311 				check(newp, y, x);
312 			/* And this uses up a scan. */
313 			newp->p_scan--;
314 		}
315 	}
316 
317 	/* Use up one point of cloak time when drawing: */
318 	if (draw && pp->p_cloak >= 0) {
319 		pp->p_cloak--;
320 		/* Check if we ran out of cloak: */
321 		if (pp->p_cloak < 0)
322 			showstat(pp);
323 	}
324 }
325 
326 /*
327  * message:
328  *	Write a message at the bottom of the screen.
329  */
330 void
message(PLAYER * pp,char * s)331 message(PLAYER *pp, char *s)
332 {
333 	cgoto(pp, HEIGHT, 0);
334 	outstr(pp, s, strlen(s));
335 	ce(pp);
336 }
337 
338 /*
339  * translate:
340  *	Turn a player character into a more personal player character.
341  *	ie: {,},!,i becomes <,>,v,^
342  */
343 static char
translate(char ch)344 translate(char ch)
345 {
346 	switch (ch) {
347 	  case LEFTS:
348 		return '<';
349 	  case RIGHT:
350 		return '>';
351 	  case ABOVE:
352 		return '^';
353 	  case BELOW:
354 		return 'v';
355 	}
356 	return ch;
357 }
358 
359 /*
360  * player_sym:
361  *	Return the symbol for the player at (y,x) when viewed by player 'pp'.
362  *	ie: - unteamed players appear as {,},!,i
363  *	    - unteamed monitors see all players as team digits
364  *	    - teamed players see other players on their team, as a digit
365  */
366 static int
player_sym(PLAYER * pp,int y,int x)367 player_sym(PLAYER *pp, int y, int x)
368 {
369 	PLAYER	*npp;
370 
371 	npp = play_at(y, x);
372 	if (npp->p_ident->i_team == ' ')
373 		return Maze[y][x];
374 	if (pp->p_ident->i_team == '*')
375 		return npp->p_ident->i_team;
376 	if (pp->p_ident->i_team != npp->p_ident->i_team)
377 		return Maze[y][x];
378 	return pp->p_ident->i_team;
379 }
380