xref: /netbsd-src/games/trek/events.c (revision 61357867d7264ecbe2df9b44f73ef6bdced801b8)
1 /*	$NetBSD: events.c,v 1.11 2009/05/24 22:55:03 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  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
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)events.c	8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: events.c,v 1.11 2009/05/24 22:55:03 dholland Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <math.h>
44 #include "getpar.h"
45 #include "trek.h"
46 
47 /*
48 **  CAUSE TIME TO ELAPSE
49 **
50 **	This routine does a hell of a lot.  It elapses time, eats up
51 **	energy, regenerates energy, processes any events that occur,
52 **	and so on.
53 **
54 **      'timewarp' is set if called in a time warp.
55 */
56 
57 int
events(int timewarp)58 events(int timewarp)
59 {
60 	int		i;
61 	char			*p;
62 	int			j = 0;
63 	struct kling		*k;
64 	double			rtime;
65 	double			xdate;
66 	double			idate;
67 	struct event		*ev = NULL;
68 	int			ix, iy;
69 	struct quad	*q;
70 	struct event	*e;
71 	int			evnum;
72 	int			restcancel;
73 
74 	/* if nothing happened, just allow for any Klingons killed */
75 	if (Move.time <= 0.0) {
76 		Now.time = Now.resource / Now.klings;
77 		return (0);
78 	}
79 
80 	/* indicate that the cloaking device is now working */
81 	Ship.cloakgood = 1;
82 
83 	/* idate is the initial date */
84 	idate = Now.date;
85 
86 	/* schedule attacks if resting too long */
87 	if (Move.time > 0.5 && Move.resting)
88 		schedule(E_ATTACK, 0.5, 0, 0, 0);
89 
90 	/* scan the event list */
91 	while (1) {
92 		restcancel = 0;
93 		evnum = -1;
94 		/* xdate is the date of the current event */
95 		xdate = idate + Move.time;
96 
97 		/* find the first event that has happened */
98 		for (i = 0; i < MAXEVENTS; i++) {
99 			e = &Event[i];
100 			if (e->evcode == 0 || (e->evcode & E_GHOST))
101 				continue;
102 			if (e->date < xdate) {
103 				xdate = e->date;
104 				ev = e;
105 				evnum = i;
106 			}
107 		}
108 		e = ev;
109 
110 		/* find the time between events */
111 		rtime = xdate - Now.date;
112 
113 		/* decrement the magic "Federation Resources" pseudo-variable */
114 		Now.resource -= Now.klings * rtime;
115 		/* and recompute the time left */
116 		Now.time = Now.resource / Now.klings;
117 
118 		/* move us up to the next date */
119 		Now.date = xdate;
120 
121 		/* check for out of time */
122 		if (Now.time <= 0.0)
123 			lose(L_NOTIME);
124 #ifdef xTRACE
125 		if (evnum >= 0 && Trace)
126 			printf("xdate = %.2f, evcode %d params %d %d %d\n",
127 				xdate, e->evcode, e->x, e->y, e->systemname);
128 #endif
129 
130 		/* if evnum < 0, no events occurred  */
131 		if (evnum < 0)
132 			break;
133 
134 		/* otherwise one did.  Find out what it is */
135 		switch (e->evcode & E_EVENT) {
136 
137 		  case E_SNOVA:			/* supernova */
138 			/* cause the supernova to happen */
139 			snova(-1, 0);
140 			/* and schedule the next one */
141 			xresched(e, E_SNOVA, 1);
142 			break;
143 
144 		  case E_LRTB:			/* long range tractor beam */
145 			/* schedule the next one */
146 			xresched(e, E_LRTB, Now.klings);
147 			/* LRTB cannot occur if we are docked */
148 			if (Ship.cond != DOCKED) {
149 				/* pick a new quadrant */
150 				i = ranf(Now.klings) + 1;
151 				for (ix = 0; ix < NQUADS; ix++) {
152 					for (iy = 0; iy < NQUADS; iy++) {
153 						q = &Quad[ix][iy];
154 						if (q->stars >= 0)
155 							if ((i -= q->klings)
156 							    <= 0)
157 								break;
158 					}
159 					if (i <= 0)
160 						break;
161 				}
162 
163 				/* test for LRTB to same quadrant */
164 				if (Ship.quadx == ix && Ship.quady == iy)
165 					break;
166 
167 				/* nope, dump him in the new quadrant */
168 				Ship.quadx = ix;
169 				Ship.quady = iy;
170 				printf("\n%s caught in long range tractor "
171 				       "beam\n",
172 					Ship.shipname);
173 				printf("*** Pulled to quadrant %d,%d\n",
174 					Ship.quadx, Ship.quady);
175 				Ship.sectx = ranf(NSECTS);
176 				Ship.secty = ranf(NSECTS);
177 				initquad(0);
178 				/* truncate the move time */
179 				Move.time = xdate - idate;
180 			}
181 			break;
182 
183 		  case E_KATSB:			/* Klingon attacks starbase */
184 			/* if out of bases, forget it */
185 			if (Now.bases <= 0) {
186 				unschedule(e);
187 				break;
188 			}
189 
190 			/* check for starbase and Klingons in same quadrant */
191 			for (i = 0; i < Now.bases; i++) {
192 				ix = Now.base[i].x;
193 				iy = Now.base[i].y;
194 				/* see if a Klingon exists in this quadrant */
195 				q = &Quad[ix][iy];
196 				if (q->klings <= 0)
197 					continue;
198 
199 				/* see if already distressed */
200 				for (j = 0; j < MAXEVENTS; j++) {
201 					e = &Event[j];
202 					if ((e->evcode & E_EVENT) != E_KDESB)
203 						continue;
204 					if (e->x == ix && e->y == iy)
205 						break;
206 				}
207 				if (j < MAXEVENTS)
208 					continue;
209 
210 				/* got a potential attack */
211 				break;
212 			}
213 			e = ev;
214 			if (i >= Now.bases) {
215 				/*
216 				 * not now; wait a while and see if
217 				 * some Klingons move in
218 				 */
219 				reschedule(e, 0.5 + 3.0 * franf());
220 				break;
221 			}
222 			/*
223 			 * schedule a new attack, and a destruction of
224 			 * the base
225 			 */
226 			xresched(e, E_KATSB, 1);
227 			e = xsched(E_KDESB, 1, ix, iy, 0);
228 
229 			/* report it if we can */
230 			if (!damaged(SSRADIO)) {
231 				printf("\nUhura:  Captain, we have received a "
232 				       "distress signal\n");
233 				printf("  from the starbase in quadrant "
234 				       "%d,%d.\n",
235 					ix, iy);
236 				restcancel++;
237 			} else {
238 				/*
239 				 * SSRADIO out, make it so we can't see the
240 				 * distress call but it's still there!!!
241 				 */
242 				e->evcode |= E_HIDDEN;
243 			}
244 			break;
245 
246 		  case E_KDESB:			/* Klingon destroys starbase */
247 			unschedule(e);
248 			q = &Quad[e->x][e->y];
249 			/*
250 			 * if the base has mysteriously gone away, or if the
251 			 * Klingon got tired and went home, ignore this event
252 			 */
253 			if (q->bases <=0 || q->klings <= 0)
254 				break;
255 			/* are we in the same quadrant? */
256 			if (e->x == Ship.quadx && e->y == Ship.quady) {
257 				/* yep, kill one in this quadrant */
258 				printf("\nSpock: ");
259 				killb(Ship.quadx, Ship.quady);
260 			} else {
261 				/* kill one in some other quadrant */
262 				killb(e->x, e->y);
263 			}
264 			break;
265 
266 		  case E_ISSUE:		/* issue a distress call */
267 			xresched(e, E_ISSUE, 1);
268 			/* if we already have too many, throw this one away */
269 			if (Ship.distressed >= MAXDISTR)
270 				break;
271 			/* try a bunch of times to find something suitable */
272 			for (i = 0; i < 100; i++) {
273 				ix = ranf(NQUADS);
274 				iy = ranf(NQUADS);
275 				q = &Quad[ix][iy];
276 				/*
277 				 * need a quadrant which is not the current
278 				 * one, which has some inhabited stars which
279 				 * are not already under attack, which is not
280 				 * supernova'ed, and which has some Klingons
281 				 * in it
282 				 */
283 				if (!((ix == Ship.quadx && iy == Ship.quady) ||
284 				      q->stars < 0 ||
285 				      (q->qsystemname & Q_DISTRESSED) ||
286 				      (q->qsystemname & Q_SYSTEM) == 0 ||
287 				      q->klings <= 0))
288 					break;
289 			}
290 			if (i >= 100)
291 				/* can't seem to find one; ignore this call */
292 				break;
293 
294 			/* got one!!  Schedule its enslavement */
295 			Ship.distressed++;
296 			e = xsched(E_ENSLV, 1, ix, iy, q->qsystemname);
297 			q->qsystemname = (e - Event) | Q_DISTRESSED;
298 
299 			/* tell the captain about it if we can */
300 			if (!damaged(SSRADIO)) {
301 				printf("\nUhura: Captain, starsystem %s in "
302 				       "quadrant %d,%d is under attack\n",
303 					Systemname[e->systemname], ix, iy);
304 				restcancel++;
305 			} else {
306 				/* if we can't tell him, make it invisible */
307 				e->evcode |= E_HIDDEN;
308 			}
309 			break;
310 
311 		  case E_ENSLV:		/* starsystem is enslaved */
312 			unschedule(e);
313 			/* see if current distress call still active */
314 			q = &Quad[e->x][e->y];
315 			if (q->klings <= 0) {
316 				/* no Klingons, clean up */
317 				/* restore the system name */
318 				q->qsystemname = e->systemname;
319 				break;
320 			}
321 
322 			/* play stork and schedule the first baby */
323 			e = schedule(E_REPRO, Param.eventdly[E_REPRO] * franf(),
324 				e->x, e->y, e->systemname);
325 
326 			/* report the disaster if we can */
327 			if (!damaged(SSRADIO)) {
328 				printf("\nUhura:  We've lost contact with "
329 				       "starsystem %s\n",
330 					Systemname[e->systemname]);
331 				printf("  in quadrant %d,%d.\n",
332 					e->x, e->y);
333 			} else
334 				e->evcode |= E_HIDDEN;
335 			break;
336 
337 		  case E_REPRO:		/* Klingon reproduces */
338 			/* see if distress call is still active */
339 			q = &Quad[e->x][e->y];
340 			if (q->klings <= 0) {
341 				unschedule(e);
342 				q->qsystemname = e->systemname;
343 				break;
344 			}
345 			xresched(e, E_REPRO, 1);
346 			/* reproduce one Klingon */
347 			ix = e->x;
348 			iy = e->y;
349 			if (Now.klings == 127) {
350 				/* full right now */
351 				break;
352 			}
353 			if (q->klings >= MAXKLQUAD) {
354 				/* this quadrant not ok, pick an adjacent one */
355 				for (i = ix - 1; i <= ix + 1; i++) {
356 					if (i < 0 || i >= NQUADS)
357 						continue;
358 					for (j = iy - 1; j <= iy + 1; j++) {
359 						if (j < 0 || j >= NQUADS)
360 							continue;
361 						q = &Quad[i][j];
362 						/*
363 						 * check for this quad ok (not
364 						 * full & no snova)
365 						 */
366 						if (q->klings >= MAXKLQUAD ||
367 						    q->stars < 0)
368 							continue;
369 						break;
370 					}
371 					if (j <= iy + 1)
372 						break;
373 				}
374 				if (j > iy + 1)
375 					/* cannot create another yet */
376 					break;
377 				ix = i;
378 				iy = j;
379 			}
380 			/* deliver the child */
381 			q->klings++;
382 			Now.klings++;
383 			if (ix == Ship.quadx && iy == Ship.quady) {
384 				/* we must position Klingon */
385 				sector(&ix, &iy);
386 				Sect[ix][iy] = KLINGON;
387 				k = &Etc.klingon[Etc.nkling++];
388 				k->x = ix;
389 				k->y = iy;
390 				k->power = Param.klingpwr;
391 				k->srndreq = 0;
392 				compkldist(Etc.klingon[0].dist ==
393 					Etc.klingon[0].avgdist ? 0 : 1);
394 			}
395 
396 			/* recompute time left */
397 			Now.time = Now.resource / Now.klings;
398 			break;
399 
400 		  case E_SNAP:		/* take a snapshot of the galaxy */
401 			xresched(e, E_SNAP, 1);
402 			p = (char *) Etc.snapshot;
403 			memcpy(p, Quad, sizeof (Quad));
404 			p += sizeof(Quad);
405 			memcpy(p, Event, sizeof (Event));
406 			p += sizeof(Event);
407 			memcpy(p, &Now, sizeof (Now));
408 			Game.snap = 1;
409 			break;
410 
411 		  case E_ATTACK:	/* Klingons attack during rest period */
412 			if (!Move.resting) {
413 				unschedule(e);
414 				break;
415 			}
416 			attack(1);
417 			reschedule(e, 0.5);
418 			break;
419 
420 		  case E_FIXDV:
421 			i = e->systemname;
422 			unschedule(e);
423 
424 			/* de-damage the device */
425 			printf("%s reports repair work on the %s finished.\n",
426 				Device[i].person, Device[i].name);
427 
428 			/* handle special processing upon fix */
429 			switch (i) {
430 
431 			  case LIFESUP:
432 				Ship.reserves = Param.reserves;
433 				break;
434 
435 			  case SINS:
436 				if (Ship.cond == DOCKED)
437 					break;
438 				printf("Spock has tried to recalibrate your "
439 				       "Space Internal Navigation System,\n");
440 				printf("  but he has no standard base to "
441 				       "calibrate to.  Suggest you get\n");
442 				printf("  to a starbase immediately so that "
443 				       "you can properly recalibrate.\n");
444 				Ship.sinsbad = 1;
445 				break;
446 
447 			  case SSRADIO:
448 				restcancel = dumpssradio();
449 				break;
450 			}
451 			break;
452 
453 		  default:
454 			break;
455 		}
456 
457 		if (restcancel && Move.resting &&
458 		    getynpar("Spock: Shall we cancel our rest period"))
459 			Move.time = xdate - idate;
460 
461 	}
462 
463 	/* unschedule an attack during a rest period */
464 	if ((e = Now.eventptr[E_ATTACK]) != NULL)
465 		unschedule(e);
466 
467 	if (!timewarp) {
468 		/* eat up energy if cloaked */
469 		if (Ship.cloaked)
470 			Ship.energy -= Param.cloakenergy * Move.time;
471 
472 		/* regenerate resources */
473 		rtime = 1.0 - exp(-Param.regenfac * Move.time);
474 		Ship.shield += (Param.shield - Ship.shield) * rtime;
475 		Ship.energy += (Param.energy - Ship.energy) * rtime;
476 
477 		/* decrement life support reserves */
478 		if (damaged(LIFESUP) && Ship.cond != DOCKED)
479 			Ship.reserves -= Move.time;
480 	}
481 	return (0);
482 }
483