1/*	$NetBSD: events.c,v 1.10 2009/05/24 21:44:56 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
35static char sccsid[] = "@(#)events.c	8.1 (Berkeley) 5/31/93";
36#else
37__RCSID("$NetBSD: events.c,v 1.10 2009/05/24 21:44:56 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
57int
58events(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