xref: /openbsd/games/trek/events.c (revision 7b36286a)
1 /*	$OpenBSD: events.c,v 1.5 2003/06/03 03:01:41 millert 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 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)events.c	8.1 (Berkeley) 5/31/93";
36 #else
37 static char rcsid[] = "$OpenBSD: events.c,v 1.5 2003/06/03 03:01:41 millert 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 
55 int
56 events(warp)
57 	int	warp;		/* set if called in a time warp */
58 {
59 	int		i, j;
60 	char		*p;
61 	struct kling	*k;
62 	double		rtime;
63 	double		xdate;
64 	double		idate;
65 	struct event	*ev = NULL;
66 	int		ix, iy;
67 	struct quad	*q;
68 	struct event	*e;
69 	int		evnum;
70 	int		restcancel;
71 
72 	/* if nothing happened, just allow for any Klingons killed */
73 	if (Move.time <= 0.0)
74 	{
75 		Now.time = Now.resource / Now.klings;
76 		return (0);
77 	}
78 
79 	/* indicate that the cloaking device is now working */
80 	Ship.cloakgood = 1;
81 
82 	/* idate is the initial date */
83 	idate = Now.date;
84 
85 	/* schedule attacks if resting too long */
86 	if (Move.time > 0.5 && Move.resting)
87 		schedule(E_ATTACK, 0.5, 0, 0, 0);
88 
89 	/* scan the event list */
90 	while (1)
91 	{
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 		{
100 			e = &Event[i];
101 			if (e->evcode == 0 || (e->evcode & E_GHOST))
102 				continue;
103 			if (e->date < xdate)
104 			{
105 				xdate = e->date;
106 				ev = e;
107 				evnum = i;
108 			}
109 		}
110 		e = ev;
111 
112 		/* find the time between events */
113 		rtime = xdate - Now.date;
114 
115 		/* decrement the magic "Federation Resources" pseudo-variable */
116 		Now.resource -= Now.klings * rtime;
117 		/* and recompute the time left */
118 		Now.time = Now.resource / Now.klings;
119 
120 		/* move us up to the next date */
121 		Now.date = xdate;
122 
123 		/* check for out of time */
124 		if (Now.time <= 0.0)
125 			lose(L_NOTIME);
126 #		ifdef xTRACE
127 		if (evnum >= 0 && Trace)
128 			printf("xdate = %.2f, evcode %d params %d %d %d\n",
129 				xdate, e->evcode, e->x, e->y, e->systemname);
130 #		endif
131 
132 		/* if evnum < 0, no events occurred  */
133 		if (evnum < 0)
134 			break;
135 
136 		/* otherwise one did.  Find out what it is */
137 		switch (e->evcode & E_EVENT)
138 		{
139 
140 		  case E_SNOVA:			/* supernova */
141 			/* cause the supernova to happen */
142 			snova(-1, 0);
143 			/* and schedule the next one */
144 			xresched(e, E_SNOVA, 1);
145 			break;
146 
147 		  case E_LRTB:			/* long range tractor beam */
148 			/* schedule the next one */
149 			xresched(e, E_LRTB, Now.klings);
150 			/* LRTB cannot occur if we are docked */
151 			if (Ship.cond != DOCKED)
152 			{
153 				/* pick a new quadrant */
154 				i = ranf(Now.klings) + 1;
155 				for (ix = 0; ix < NQUADS; ix++)
156 				{
157 					for (iy = 0; iy < NQUADS; iy++)
158 					{
159 						q = &Quad[ix][iy];
160 						if (q->stars >= 0)
161 							if ((i -= q->klings) <= 0)
162 								break;
163 					}
164 					if (i <= 0)
165 						break;
166 				}
167 
168 				/* test for LRTB to same quadrant */
169 				if (Ship.quadx == ix && Ship.quady == iy)
170 					break;
171 
172 				/* nope, dump him in the new quadrant */
173 				Ship.quadx = ix;
174 				Ship.quady = iy;
175 				printf("\n%s caught in long range tractor beam\n", Ship.shipname);
176 				printf("*** Pulled to quadrant %d,%d\n", Ship.quadx, Ship.quady);
177 				Ship.sectx = ranf(NSECTS);
178 				Ship.secty = ranf(NSECTS);
179 				initquad(0);
180 				/* truncate the move time */
181 				Move.time = xdate - idate;
182 			}
183 			break;
184 
185 		  case E_KATSB:			/* Klingon attacks starbase */
186 			/* if out of bases, forget it */
187 			if (Now.bases <= 0)
188 			{
189 				unschedule(e);
190 				break;
191 			}
192 
193 			/* check for starbase and Klingons in same quadrant */
194 			for (i = 0; i < Now.bases; i++)
195 			{
196 				ix = Now.base[i].x;
197 				iy = Now.base[i].y;
198 				/* see if a Klingon exists in this quadrant */
199 				q = &Quad[ix][iy];
200 				if (q->klings <= 0)
201 					continue;
202 
203 				/* see if already distressed */
204 				for (j = 0; j < MAXEVENTS; j++)
205 				{
206 					e = &Event[j];
207 					if ((e->evcode & E_EVENT) != E_KDESB)
208 						continue;
209 					if (e->x == ix && e->y == iy)
210 						break;
211 				}
212 				if (j < MAXEVENTS)
213 					continue;
214 
215 				/* got a potential attack */
216 				break;
217 			}
218 			e = ev;
219 			if (i >= Now.bases)
220 			{
221 				/* not now; wait a while and see if some Klingons move in */
222 				reschedule(e, 0.5 + 3.0 * franf());
223 				break;
224 			}
225 			/* schedule a new attack, and a destruction of the base */
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 			{
232 				printf("\nUhura:  Captain, we have received a distress signal\n");
233 				printf("  from the starbase in quadrant %d,%d.\n",
234 					ix, iy);
235 				restcancel++;
236 			}
237 			else
238 				/* SSRADIO out, make it so we can't see the distress call */
239 				/* but it's still there!!! */
240 				e->evcode |= E_HIDDEN;
241 			break;
242 
243 		  case E_KDESB:			/* Klingon destroys starbase */
244 			unschedule(e);
245 			q = &Quad[e->x][e->y];
246 			/* if the base has mysteriously gone away, or if the Klingon
247 			   got tired and went home, ignore this event */
248 			if (q->bases <=0 || q->klings <= 0)
249 				break;
250 			/* are we in the same quadrant? */
251 			if (e->x == Ship.quadx && e->y == Ship.quady)
252 			{
253 				/* yep, kill one in this quadrant */
254 				printf("\nSpock: ");
255 				killb(Ship.quadx, Ship.quady);
256 			}
257 			else
258 				/* kill one in some other quadrant */
259 				killb(e->x, e->y);
260 			break;
261 
262 		  case E_ISSUE:		/* issue a distress call */
263 			xresched(e, E_ISSUE, 1);
264 			/* if we already have too many, throw this one away */
265 			if (Ship.distressed >= MAXDISTR)
266 				break;
267 			/* try a whole bunch of times to find something suitable */
268 			for (i = 0; i < 100; i++)
269 			{
270 				ix = ranf(NQUADS);
271 				iy = ranf(NQUADS);
272 				q = &Quad[ix][iy];
273 				/* need a quadrant which is not the current one,
274 				   which has some stars which are inhabited and
275 				   not already under attack, which is not
276 				   supernova'ed, and which has some Klingons in it */
277 				if (!((ix == Ship.quadx && iy == Ship.quady) || q->stars < 0 ||
278 				    (q->qsystemname & Q_DISTRESSED) ||
279 				    (q->qsystemname & Q_SYSTEM) == 0 || q->klings <= 0))
280 					break;
281 			}
282 			if (i >= 100)
283 				/* can't seem to find one; ignore this call */
284 				break;
285 
286 			/* got one!!  Schedule its enslavement */
287 			Ship.distressed++;
288 			e = xsched(E_ENSLV, 1, ix, iy, q->qsystemname);
289 			q->qsystemname = (e - Event) | Q_DISTRESSED;
290 
291 			/* tell the captain about it if we can */
292 			if (!damaged(SSRADIO))
293 			{
294 				printf("\nUhura: Captain, starsystem %s in quadrant %d,%d is under attack\n",
295 					Systemname[e->systemname], ix, iy);
296 				restcancel++;
297 			}
298 			else
299 				/* if we can't tell him, make it invisible */
300 				e->evcode |= E_HIDDEN;
301 			break;
302 
303 		  case E_ENSLV:		/* starsystem is enslaved */
304 			unschedule(e);
305 			/* see if current distress call still active */
306 			q = &Quad[e->x][e->y];
307 			if (q->klings <= 0)
308 			{
309 				/* no Klingons, clean up */
310 				/* restore the system name */
311 				q->qsystemname = e->systemname;
312 				break;
313 			}
314 
315 			/* play stork and schedule the first baby */
316 			e = schedule(E_REPRO, Param.eventdly[E_REPRO] * franf(), e->x, e->y, e->systemname);
317 
318 			/* report the disaster if we can */
319 			if (!damaged(SSRADIO))
320 			{
321 				printf("\nUhura:  We've lost contact with starsystem %s\n",
322 					Systemname[e->systemname]);
323 				printf("  in quadrant %d,%d.\n",
324 					e->x, e->y);
325 			}
326 			else
327 				e->evcode |= E_HIDDEN;
328 			break;
329 
330 		  case E_REPRO:		/* Klingon reproduces */
331 			/* see if distress call is still active */
332 			q = &Quad[e->x][e->y];
333 			if (q->klings <= 0)
334 			{
335 				unschedule(e);
336 				q->qsystemname = e->systemname;
337 				break;
338 			}
339 			xresched(e, E_REPRO, 1);
340 			/* reproduce one Klingon */
341 			ix = e->x;
342 			iy = e->y;
343 			if (Now.klings == 127)
344 				break;		/* full right now */
345 			if (q->klings >= MAXKLQUAD)
346 			{
347 				/* this quadrant not ok, pick an adjacent one */
348 				for (i = ix - 1; i <= ix + 1; i++)
349 				{
350 					if (i < 0 || i >= NQUADS)
351 						continue;
352 					for (j = iy - 1; j <= iy + 1; j++)
353 					{
354 						if (j < 0 || j >= NQUADS)
355 							continue;
356 						q = &Quad[i][j];
357 						/* check for this quad ok (not full & no snova) */
358 						if (q->klings >= MAXKLQUAD || q->stars < 0)
359 							continue;
360 						break;
361 					}
362 					if (j <= iy + 1)
363 						break;
364 				}
365 				if (j > iy + 1)
366 					/* cannot create another yet */
367 					break;
368 				ix = i;
369 				iy = j;
370 			}
371 			/* deliver the child */
372 			q->klings++;
373 			Now.klings++;
374 			if (ix == Ship.quadx && iy == Ship.quady)
375 			{
376 				/* we must position Klingon */
377 				sector(&ix, &iy);
378 				Sect[ix][iy] = KLINGON;
379 				k = &Etc.klingon[Etc.nkling++];
380 				k->x = ix;
381 				k->y = iy;
382 				k->power = Param.klingpwr;
383 				k->srndreq = 0;
384 				compkldist(Etc.klingon[0].dist == Etc.klingon[0].avgdist ? 0 : 1);
385 			}
386 
387 			/* recompute time left */
388 			Now.time = Now.resource / Now.klings;
389 			break;
390 
391 		  case E_SNAP:		/* take a snapshot of the galaxy */
392 			xresched(e, E_SNAP, 1);
393 			p = (char *) Etc.snapshot;
394 			memcpy(p, Quad, sizeof (Quad));
395 			p += sizeof (Quad);
396 			memcpy(p, Event, sizeof (Event));
397 			p += sizeof (Event);
398 			memcpy(p, &Now, sizeof (Now));
399 			Game.snap = 1;
400 			break;
401 
402 		  case E_ATTACK:	/* Klingons attack during rest period */
403 			if (!Move.resting)
404 			{
405 				unschedule(e);
406 				break;
407 			}
408 			attack(1);
409 			reschedule(e, 0.5);
410 			break;
411 
412 		  case E_FIXDV:
413 			i = e->systemname;
414 			unschedule(e);
415 
416 			/* de-damage the device */
417 			printf("%s reports repair work on the %s finished.\n",
418 				Device[i].person, Device[i].name);
419 
420 			/* handle special processing upon fix */
421 			switch (i)
422 			{
423 
424 			  case LIFESUP:
425 				Ship.reserves = Param.reserves;
426 				break;
427 
428 			  case SINS:
429 				if (Ship.cond == DOCKED)
430 					break;
431 				printf("Spock has tried to recalibrate your Space Internal Navigation System,\n");
432 				printf("  but he has no standard base to calibrate to.  Suggest you get\n");
433 				printf("  to a starbase immediately so that you can properly recalibrate.\n");
434 				Ship.sinsbad = 1;
435 				break;
436 
437 			  case SSRADIO:
438 				restcancel = dumpssradio();
439 				break;
440 			}
441 			break;
442 
443 		  default:
444 			break;
445 		}
446 
447 		if (restcancel && Move.resting && getynpar("Spock: Shall we cancel our rest period"))
448 			Move.time = xdate - idate;
449 
450 	}
451 
452 	/* unschedule an attack during a rest period */
453 	if ((e = Now.eventptr[E_ATTACK]))
454 		unschedule(e);
455 
456 	if (!warp)
457 	{
458 		/* eat up energy if cloaked */
459 		if (Ship.cloaked)
460 			Ship.energy -= Param.cloakenergy * Move.time;
461 
462 		/* regenerate resources */
463 		rtime = 1.0 - exp(-Param.regenfac * Move.time);
464 		Ship.shield += (Param.shield - Ship.shield) * rtime;
465 		Ship.energy += (Param.energy - Ship.energy) * rtime;
466 
467 		/* decrement life support reserves */
468 		if (damaged(LIFESUP) && Ship.cond != DOCKED)
469 			Ship.reserves -= Move.time;
470 	}
471 	return (0);
472 }
473