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