5e959d2bb0ebf56f3a8b858f6e1a3e353f233bb0
[vms-empire.git] / util.c
1 /* $Id$  - (c) Copyright 1987, 1988 Chuck Simmons */
2
3 /*
4  *    Copyright (C) 1987, 1988 Chuck Simmons
5  * 
6  * See the file COPYING, distributed with empire, for restriction
7  * and warranty information.
8  */
9
10 /*
11 util.c -- various utility routines.
12 */
13
14 #include <curses.h>
15 #include <ctype.h>
16 #include "empire.h"
17 #include "extern.h"
18
19
20 /*
21 Convert a string to uppercase.
22 Shirley this is defined elsewhere?
23 */
24
25 #include <ctype.h>
26
27 void
28 tupper (str)
29 char    *str;
30 {
31         while (*str) {
32                 if (islower (*str)) *str = upper (*str);
33                 str++;
34         }
35 }
36
37 /*
38 Convert a character to uppercase (if it is lowercase)
39 */
40
41 char
42 upper (c)
43 char c;
44 {
45         if (islower (c))
46                 return toupper (c);
47         else return c;
48 }
49
50 /*
51 Clear the end of a specified line starting at the specified column.
52 */
53
54 void
55 clreol(linep, colp)
56 int linep, colp;
57 {
58         (void) move (linep, colp);
59         (void) clrtoeol();
60 }
61
62 /*
63 Initialize the terminal.
64 */
65
66 void
67 ttinit()
68 {
69         (void) initscr();
70         (void) noecho();
71         (void) crmode();
72 #ifdef A_COLOR
73         init_colors();
74 #endif /* A_COLOR */
75         lines = LINES;
76         cols = COLS;
77         if (lines > MAP_HEIGHT + NUMTOPS + 1)
78                 lines = MAP_HEIGHT + NUMTOPS + 1;
79         if (cols > MAP_WIDTH + NUMSIDES)
80                 cols = MAP_WIDTH + NUMSIDES;
81 }
82
83
84 /*
85 Clear the screen.  We must also kill information maintained about the
86 display.
87 */
88
89 void
90 clear_screen () {
91         (void) clear ();
92         (void) refresh ();
93         kill_display ();
94 }
95
96 /*
97 Redraw the screen.
98 */
99
100 void
101 redraw () {
102         (void) clearok (curscr, TRUE);
103         (void) refresh ();
104 }
105
106 /*
107 Wait a little bit to give user a chance to see a message.  We refresh
108 the screen and pause for a few milliseconds.
109 */
110
111 void
112 delay () {
113         (void) refresh ();
114         (void) napms (delay_time); /* pause a bit */
115 }
116
117
118 /*
119 Clean up the display.  This routine gets called as we leave the game.
120 */
121
122 void
123 close_disp()
124 {
125         (void) move (LINES - 1, 0);
126         (void) clrtoeol ();
127         (void) refresh ();
128         (void) endwin ();
129 }
130
131 /*
132 Position the cursor and output a string.
133 */
134
135 void
136 /* VARARGS3 */
137 pos_str (row, col, str, a, b, c, d, e, f, g, h)
138 int row, col;
139 char *str;
140 int a, b, c, d, e, f, g, h;
141 {
142         (void) move (row, col);
143         addprintf (str, a, b, c, d, e, f, g, h);
144 }
145
146 void
147 /* VARARGS1 */
148 addprintf (str, a, b, c, d, e, f, g, h)
149 char *str;
150 int a, b, c, d, e, f, g, h;
151 {
152         char junkbuf[STRSIZE];
153         
154         (void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
155         (void) addstr (junkbuf);
156 }
157
158 /*
159 Report a bug.
160 */
161
162 void
163 assert (expression, file, line)
164 char *expression;
165 char *file;
166 int line;
167 {
168         char buf[STRSIZE];
169         int a;
170
171         (void) move (lines, 0);
172         close_disp ();
173
174         (void) sprintf (buf, "assert failed: file %s line %d: %s",
175                         file, line, expression);
176
177         a = 1; /* keep lint quiet */
178         a /= 0; /* force a core dump */
179         a = a; /* keep lint quiet */
180 }
181
182 /*
183 End the game by cleaning up the display.
184 */
185
186 void
187 empend ()
188 {
189         close_disp ();
190         exit (0);
191 }
192
193 /*
194  * 03a 01Apr88 aml .Hacked movement algorithms for computer.
195  * 02b 01Jun87 aml .First round of bug fixes.
196  * 02a 01Jan87 aml .Translated to C.
197  * 01b 27May85 cal .Fixed round number update bug. Made truename simple.
198  * 01a 01Sep83 cal .Taken from a Decus tape
199  */
200
201 void
202 ver ()
203 {
204         (void) addstr ("EMPIRE, Version 5.00 site Amdahl 1-Apr-1988");
205 }
206
207 /*
208 Here is a little routine to perform consistency checking on the
209 database.  I'm finding that my database is becoming inconsistent,
210 and I've no idea why.  Possibly this will help.
211
212 We perform the following functions:
213
214 1)  Make sure no list contains loops.
215
216 2)  Make sure every object is in either the free list with 0 hits,
217 or it is in the correct object list and a location list with non-zero hits,
218 and an appropriate owner.
219
220 3)  Make sure every city is on the map.
221
222 4)  Make sure every object is in the correct location and that
223 objects on the map have non-zero hits.
224
225 5)  Make sure every object in a cargo list has a ship pointer.
226
227 6)  Make sure every object with a ship pointer is in that ship's
228 cargo list.
229 */
230
231 static int in_free[LIST_SIZE]; /* TRUE if object in free list */
232 static int in_obj[LIST_SIZE]; /* TRUE if object in obj list */
233 static int in_loc[LIST_SIZE]; /* TRUE if object in a loc list */
234 static int in_cargo[LIST_SIZE]; /* TRUE if object in a cargo list */
235
236 void
237 check () {
238         void check_cargo(), check_obj(), check_obj_cargo();
239         
240         long i, j;
241         piece_info_t *p;
242         
243         /* nothing in any list yet */
244         for (i = 0; i < LIST_SIZE; i++) {
245                 in_free[i] = 0;
246                 in_obj[i] = 0;
247                 in_loc[i] = 0;
248                 in_cargo[i] = 0;
249         }
250                 
251         /* Mark all objects in free list.  Make sure objects in free list
252         have zero hits. */
253         
254         for (p = free_list; p != NULL; p = p->piece_link.next) {
255                 i = p - object;
256                 ASSERT (!in_free[i]);
257                 in_free[i] = 1;
258                 ASSERT (p->hits == 0);
259                 if (p->piece_link.prev)
260                         ASSERT (p->piece_link.prev->piece_link.next == p);
261         }
262         
263         /* Mark all objects in the map.
264         Check that cities are in corect location.
265         Check that objects are in correct location,
266         have a good owner, and good hits. */
267         
268         for (i = 0; i < MAP_SIZE; i++) {
269                 if (map[i].cityp) ASSERT (map[i].cityp->loc == i);
270                 
271                 for (p = map[i].objp; p != NULL; p = p->loc_link.next) {
272                         ASSERT (p->loc == i);
273                         ASSERT (p->hits > 0);
274                         ASSERT (p->owner == USER || p->owner == COMP);
275                                 
276                         j = p - object;
277                         ASSERT (!in_loc[j]);
278                         in_loc[j] = 1;
279                         
280                         if (p->loc_link.prev)
281                                 ASSERT (p->loc_link.prev->loc_link.next == p);
282                 }
283         }
284
285         /* make sure all cities are on map */
286
287         for (i = 0; i < NUM_CITY; i++)
288                 ASSERT (map[city[i].loc].cityp == &(city[i]));
289
290         /* Scan object lists. */
291         
292         check_obj (comp_obj, COMP);
293         check_obj (user_obj, USER);
294         
295         /* Scan cargo lists. */
296         
297         check_cargo (user_obj[TRANSPORT], ARMY);
298         check_cargo (comp_obj[TRANSPORT], ARMY);
299         check_cargo (user_obj[CARRIER], FIGHTER);
300         check_cargo (comp_obj[CARRIER], FIGHTER);
301         
302         /* Make sure all objects with ship pointers are in cargo. */
303
304         check_obj_cargo (comp_obj);
305         check_obj_cargo (user_obj);
306         
307         /* Make sure every object is either free or in loc and obj list. */
308
309         for (i = 0; i < LIST_SIZE; i++)
310                 ASSERT (in_free[i] != (in_loc[i] && in_obj[i]));
311 }
312
313 /*
314 Check object lists.  We look for:
315
316 1)  Loops and bad prev pointers.
317
318 2)  Dead objects.
319
320 3)  Invalid types.
321
322 4)  Invalid owners.
323 */
324
325 void
326 check_obj (list, owner)
327 piece_info_t **list;
328 int owner;
329 {
330         long i, j;
331         piece_info_t *p;
332         
333         for (i = 0; i < NUM_OBJECTS; i++)
334         for (p = list[i]; p != NULL; p = p->piece_link.next) {
335                 ASSERT (p->owner == owner);
336                 ASSERT (p->type == i);
337                 ASSERT (p->hits > 0);
338                 
339                 j = p - object;
340                 ASSERT (!in_obj[j]);
341                 in_obj[j] = 1;
342         
343                 if (p->piece_link.prev)
344                         ASSERT (p->piece_link.prev->piece_link.next == p);
345         }
346 }
347
348 /*
349 Check cargo lists.  We assume object lists are valid.
350 as we will place bits in the 'in_cargo' array that are used by
351 'check_obj'.
352
353 Check for:
354
355 1)  Number of items in list is same as cargo count.
356
357 2)  Type of cargo is correct.
358
359 3)  Location of cargo matches location of ship.
360
361 4)  Ship pointer of cargo points to correct ship.
362
363 5)  There are no loops in cargo list and prev ptrs are correct.
364
365 6)  All cargo is alive.
366 */
367
368 void
369 check_cargo (list, cargo_type)
370 piece_info_t *list;
371 int cargo_type;
372 {
373         piece_info_t *p, *q;
374         long j, count;
375         
376         for (p = list; p != NULL; p = p->piece_link.next) {
377                 count = 0;
378                 for (q = p->cargo; q != NULL; q = q->cargo_link.next) {
379                         count += 1; /* count items in list */
380                         ASSERT (q->type == cargo_type);
381                         ASSERT (q->owner == p->owner);
382                         ASSERT (q->hits > 0);
383                         ASSERT (q->ship == p);
384                         ASSERT (q->loc == p->loc);
385                         
386                         j = q - object;
387                         ASSERT (!in_cargo[j]);
388                         in_cargo[j] = 1;
389
390                         if (p->cargo_link.prev)
391                                 ASSERT (p->cargo_link.prev->cargo_link.next == p);
392                 }
393                 ASSERT (count == p->count);
394         }
395 }
396
397 /*
398 Scan through object lists making sure every object with a ship
399 pointer appears in a cargo list.  We assume object and cargo
400 lists are valid.
401 */
402
403 void
404 check_obj_cargo (list)
405 piece_info_t **list;
406 {
407         piece_info_t *p;
408         long i;
409
410         for (i = 0; i < NUM_OBJECTS; i++)
411         for (p = list[i]; p != NULL; p = p->piece_link.next) {
412                 if (p->ship) ASSERT (in_cargo[p-object]);
413         }
414 }