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