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