]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* w_sprite.c |
2 | * | |
3 | * Micropolis, Unix Version. This game was released for the Unix platform | |
4 | * in or about 1990 and has been modified for inclusion in the One Laptop | |
5 | * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If | |
6 | * you need assistance with this program, you may contact: | |
7 | * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org. | |
8 | * | |
9 | * This program is free software: you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation, either version 3 of the License, or (at | |
12 | * your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. You should have received a | |
18 | * copy of the GNU General Public License along with this program. If | |
19 | * not, see <http://www.gnu.org/licenses/>. | |
20 | * | |
21 | * ADDITIONAL TERMS per GNU GPL Section 7 | |
22 | * | |
23 | * No trademark or publicity rights are granted. This license does NOT | |
24 | * give you any right, title or interest in the trademark SimCity or any | |
25 | * other Electronic Arts trademark. You may not distribute any | |
26 | * modification of this program using the trademark SimCity or claim any | |
27 | * affliation or association with Electronic Arts Inc. or its employees. | |
28 | * | |
29 | * Any propagation or conveyance of this program must include this | |
30 | * copyright notice and these terms. | |
31 | * | |
32 | * If you convey this program (or any modifications of it) and assume | |
33 | * contractual liability for the program to recipients of it, you agree | |
34 | * to indemnify Electronic Arts for any liability that those contractual | |
35 | * assumptions impose on Electronic Arts. | |
36 | * | |
37 | * You may not misrepresent the origins of this program; modified | |
38 | * versions of the program must be marked as such and not identified as | |
39 | * the original program. | |
40 | * | |
41 | * This disclaimer supplements the one included in the General Public | |
42 | * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS | |
43 | * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY | |
44 | * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF | |
45 | * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS | |
46 | * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES, | |
47 | * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY, | |
48 | * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY | |
49 | * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING, | |
50 | * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST | |
51 | * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL | |
52 | * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE | |
53 | * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE | |
54 | * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE | |
55 | * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR | |
56 | * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME | |
57 | * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED | |
58 | * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A | |
59 | * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY | |
60 | * NOT APPLY TO YOU. | |
61 | */ | |
62 | #include "sim.h" | |
63 | ||
64 | ||
65 | Tcl_HashTable SpriteCmds; | |
66 | short CrashX, CrashY; | |
67 | int absDist; | |
68 | short Cycle; | |
69 | ||
70 | SimSprite *GlobalSprites[OBJN]; | |
71 | ||
72 | SimSprite *NewSprite(char *name, int type, int x, int y); | |
6f214ac0 MG |
73 | void MonsterHere(int x, int y); |
74 | void MakeShipHere(int x, int y); | |
75 | void StartFire(int x, int y); | |
76 | void OFireZone(int Xloc, int Yloc, int ch); | |
77 | void Destroy(int ox, int oy); | |
78 | void ExplodeSprite(SimSprite *sprite); | |
79 | int CanDriveOn(int x, int y); | |
80 | void DoBusSprite(SimSprite *sprite); | |
81 | void DoExplosionSprite(SimSprite *sprite); | |
82 | void DoTornadoSprite(SimSprite *sprite); | |
83 | void DoMonsterSprite(SimSprite *sprite); | |
84 | void DoShipSprite(SimSprite *sprite); | |
85 | void DoAirplaneSprite(SimSprite *sprite); | |
86 | void DoCopterSprite(SimSprite *sprite); | |
87 | void DoTrainSprite(SimSprite *sprite); | |
88 | void DrawSprite(SimView *view, SimSprite *sprite); | |
89 | void InitSprite(SimSprite *sprite, int x, int y); | |
6a5fa4e0 MG |
90 | |
91 | ||
92 | #define TRA_GROOVE_X -39 | |
93 | #define TRA_GROOVE_Y 6 | |
94 | #define BUS_GROOVE_X -39 | |
95 | #define BUS_GROOVE_Y 6 | |
96 | ||
97 | #define SPRITECMD_ACCESS_INT(var) \ | |
98 | int SpriteCmd##var(SPRITE_ARGS) { \ | |
99 | int val; \ | |
100 | if ((argc != 2) && (argc != 3)) return (TCL_ERROR); \ | |
101 | if (argc == 3) { \ | |
102 | if (Tcl_GetInt(interp, argv[2], &val) != TCL_OK) return (TCL_ERROR); \ | |
103 | sprite->var = val; \ | |
104 | } \ | |
105 | sprintf(interp->result, "%d", sprite->var); \ | |
106 | return (TCL_OK); \ | |
107 | } | |
108 | ||
109 | ||
110 | #define SPRITECMD_GET_STR(var) \ | |
111 | int SpriteCmd##var(SPRITE_ARGS) { \ | |
112 | sprintf(interp->result, "%s", sprite->var); \ | |
113 | return (TCL_OK); \ | |
114 | } | |
115 | ||
116 | ||
117 | int | |
118 | DoSpriteCmd(CLIENT_ARGS) | |
119 | { | |
120 | SimSprite *sprite = (SimSprite *) clientData; | |
121 | Tcl_HashEntry *ent; | |
122 | int result = TCL_OK; | |
123 | int (*cmd)(); | |
124 | ||
125 | if (argc < 2) { | |
126 | return TCL_ERROR; | |
127 | } | |
128 | ||
6f214ac0 | 129 | if ((ent = Tcl_FindHashEntry(&SpriteCmds, argv[1]))) { |
6a5fa4e0 MG |
130 | cmd = (int (*)())ent->clientData; |
131 | Tk_Preserve((ClientData) sprite); | |
132 | result = cmd(sprite, interp, argc, argv); | |
133 | Tk_Release((ClientData) sprite); | |
134 | } else { | |
135 | Tcl_AppendResult(interp, "unknown command name: \"", | |
136 | argv[0], " ", argv[1], "\".", (char *) NULL); | |
137 | result = TCL_ERROR; | |
138 | } | |
139 | return result; | |
140 | } | |
141 | ||
142 | ||
143 | int | |
144 | SpriteCmd(CLIENT_ARGS) | |
145 | { | |
146 | SimSprite *sprite; | |
147 | int type; | |
148 | ||
149 | if ((argc != 3) || | |
150 | (Tcl_GetInt(interp, argv[2], &type) != TCL_OK) || | |
151 | (type < 1) || (type >= OBJN)) { | |
152 | return TCL_ERROR; | |
153 | } | |
154 | ||
155 | sprite = NewSprite(argv[1], type, 0, 0); | |
156 | sprite->frame = 0; | |
157 | ||
158 | Tcl_CreateCommand(interp, sprite->name, | |
159 | DoSpriteCmd, (ClientData) sprite, (void (*)()) NULL); | |
160 | ||
161 | interp->result = sprite->name; | |
162 | return TCL_OK; | |
163 | } | |
164 | ||
165 | ||
166 | SPRITECMD_GET_STR(name) | |
167 | SPRITECMD_ACCESS_INT(type) | |
168 | SPRITECMD_ACCESS_INT(frame) | |
169 | SPRITECMD_ACCESS_INT(x) | |
170 | SPRITECMD_ACCESS_INT(y) | |
171 | SPRITECMD_ACCESS_INT(width) | |
172 | SPRITECMD_ACCESS_INT(height) | |
173 | SPRITECMD_ACCESS_INT(x_offset) | |
174 | SPRITECMD_ACCESS_INT(y_offset) | |
175 | SPRITECMD_ACCESS_INT(x_hot) | |
176 | SPRITECMD_ACCESS_INT(y_hot) | |
177 | SPRITECMD_ACCESS_INT(orig_x) | |
178 | SPRITECMD_ACCESS_INT(orig_y) | |
179 | SPRITECMD_ACCESS_INT(dest_x) | |
180 | SPRITECMD_ACCESS_INT(dest_y) | |
181 | SPRITECMD_ACCESS_INT(count) | |
182 | SPRITECMD_ACCESS_INT(sound_count) | |
183 | SPRITECMD_ACCESS_INT(dir) | |
184 | SPRITECMD_ACCESS_INT(new_dir) | |
185 | SPRITECMD_ACCESS_INT(step) | |
186 | SPRITECMD_ACCESS_INT(flag) | |
187 | SPRITECMD_ACCESS_INT(control) | |
188 | SPRITECMD_ACCESS_INT(turn) | |
189 | SPRITECMD_ACCESS_INT(accel) | |
190 | SPRITECMD_ACCESS_INT(speed) | |
191 | ||
192 | ||
193 | int SpriteCmdExplode(SPRITE_ARGS) | |
194 | { | |
195 | ExplodeSprite(sprite); | |
196 | return TCL_OK; | |
197 | } | |
198 | ||
199 | ||
200 | int SpriteCmdInit(SPRITE_ARGS) | |
201 | { | |
202 | int x, y; | |
203 | ||
204 | if (argc != 4) { | |
205 | return (TCL_ERROR); | |
206 | } | |
207 | if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) || | |
208 | (x < 0) || (x >= (WORLD_X <<4)) || | |
209 | (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) || | |
210 | (y < 0) || (y >= (WORLD_Y <<4))) { | |
211 | return (TCL_ERROR); | |
212 | } | |
213 | InitSprite(sprite, x, y); | |
214 | return TCL_OK; | |
215 | } | |
216 | ||
217 | ||
6f214ac0 MG |
218 | void |
219 | sprite_command_init(void) | |
6a5fa4e0 MG |
220 | { |
221 | int i; | |
222 | ||
223 | Tcl_CreateCommand(tk_mainInterp, "sprite", SpriteCmd, | |
224 | (ClientData)NULL, (void (*)()) NULL); | |
225 | ||
226 | Tcl_InitHashTable(&SpriteCmds, TCL_STRING_KEYS); | |
227 | ||
228 | #define SPRITE_CMD(cmd) HASHED_CMD(Sprite, cmd) | |
229 | ||
230 | SPRITE_CMD(name); | |
231 | SPRITE_CMD(type); | |
232 | SPRITE_CMD(frame); | |
233 | SPRITE_CMD(x); | |
234 | SPRITE_CMD(y); | |
235 | SPRITE_CMD(width); | |
236 | SPRITE_CMD(height); | |
237 | SPRITE_CMD(x_offset); | |
238 | SPRITE_CMD(y_offset); | |
239 | SPRITE_CMD(x_hot); | |
240 | SPRITE_CMD(y_hot); | |
241 | SPRITE_CMD(orig_x); | |
242 | SPRITE_CMD(orig_y); | |
243 | SPRITE_CMD(dest_x); | |
244 | SPRITE_CMD(dest_y); | |
245 | SPRITE_CMD(count); | |
246 | SPRITE_CMD(sound_count); | |
247 | SPRITE_CMD(dir); | |
248 | SPRITE_CMD(new_dir); | |
249 | SPRITE_CMD(step); | |
250 | SPRITE_CMD(flag); | |
251 | SPRITE_CMD(control); | |
252 | SPRITE_CMD(turn); | |
253 | SPRITE_CMD(accel); | |
254 | SPRITE_CMD(speed); | |
255 | SPRITE_CMD(Explode); | |
256 | SPRITE_CMD(Init); | |
257 | ||
258 | for (i = 0; i < OBJN; i++) { | |
259 | GlobalSprites[i] = NULL; | |
260 | } | |
261 | } | |
262 | ||
263 | ||
264 | SimSprite *FreeSprites = NULL; | |
265 | ||
266 | SimSprite * | |
267 | NewSprite(char *name, int type, int x, int y) | |
268 | { | |
269 | SimSprite *sprite; | |
270 | ||
271 | if (FreeSprites) { | |
272 | sprite = FreeSprites; | |
273 | FreeSprites = sprite->next; | |
274 | } else { | |
275 | sprite = (SimSprite *)ckalloc(sizeof (SimSprite)); | |
276 | } | |
277 | ||
278 | sprite->name = (char *)ckalloc(strlen(name) + 1); | |
279 | strcpy(sprite->name, name); | |
280 | sprite->type = type; | |
281 | ||
282 | InitSprite(sprite, x, y); | |
283 | ||
284 | sim->sprites++; sprite->next = sim->sprite; sim->sprite = sprite; | |
285 | ||
286 | return sprite; | |
287 | } | |
288 | ||
289 | ||
6f214ac0 | 290 | void |
6a5fa4e0 MG |
291 | InitSprite(SimSprite *sprite, int x, int y) |
292 | { | |
293 | sprite->x = x; sprite->y = y; | |
294 | sprite->frame = 0; | |
295 | sprite->orig_x = sprite->orig_y = 0; | |
296 | sprite->dest_x = sprite->dest_y = 0; | |
297 | sprite->count = sprite->sound_count = 0; | |
298 | sprite->dir = sprite->new_dir = 0; | |
299 | sprite->step = sprite->flag = 0; | |
300 | sprite->control = -1; | |
301 | sprite->turn = 0; | |
302 | sprite->accel = 0; | |
303 | sprite->speed = 100; | |
304 | ||
305 | if (GlobalSprites[sprite->type] == NULL) { | |
306 | GlobalSprites[sprite->type] = sprite; | |
307 | } | |
308 | ||
309 | switch (sprite->type) { | |
310 | ||
311 | case TRA: | |
312 | sprite->width = sprite->height = 32; | |
313 | sprite->x_offset = 32; sprite->y_offset = -16; | |
314 | sprite->x_hot = 40; sprite->y_hot = -8; | |
315 | sprite->frame = 1; | |
316 | sprite->dir = 4; | |
317 | break; | |
318 | ||
319 | case SHI: | |
320 | sprite->width = sprite->height = 48; | |
321 | sprite->x_offset = 32; sprite->y_offset = -16; | |
322 | sprite->x_hot = 48; sprite->y_hot = 0; | |
323 | if (x < (4 <<4)) sprite->frame = 3; | |
324 | else if (x >= ((WORLD_X - 4) <<4)) sprite->frame = 7; | |
325 | else if (y < (4 <<4)) sprite->frame = 5; | |
326 | else if (y >= ((WORLD_Y - 4) <<4)) sprite->frame = 1; | |
327 | else sprite->frame = 3; | |
328 | sprite->new_dir = sprite->frame; | |
329 | sprite->dir = 10; | |
330 | sprite->count = 1; | |
331 | break; | |
332 | ||
333 | case GOD: | |
334 | sprite->width = sprite->height = 48; | |
335 | sprite->x_offset = 24; sprite->y_offset = 0; | |
336 | sprite->x_hot = 40; sprite->y_hot = 16; | |
337 | if (x > ((WORLD_X <<4) / 2)) { | |
338 | if (y > ((WORLD_Y <<4) / 2)) sprite->frame = 10; | |
339 | else sprite->frame = 7; | |
340 | } else if (y > ((WORLD_Y <<4) / 2)) sprite->frame = 1; | |
341 | else sprite->frame = 4; | |
342 | sprite->count = 1000; | |
343 | sprite->dest_x = PolMaxX <<4; | |
344 | sprite->dest_y = PolMaxY <<4; | |
345 | sprite->orig_x = sprite->x; | |
346 | sprite->orig_y = sprite->y; | |
347 | break; | |
348 | ||
349 | case COP: | |
350 | sprite->width = sprite->height = 32; | |
351 | sprite->x_offset = 32; sprite->y_offset = -16; | |
352 | sprite->x_hot = 40; sprite->y_hot = -8; | |
353 | sprite->frame = 5; | |
354 | sprite->count = 1500; | |
355 | sprite->dest_x = Rand((WORLD_X <<4) - 1); | |
356 | sprite->dest_y = Rand((WORLD_Y <<4) - 1); | |
357 | sprite->orig_x = x - 30; | |
358 | sprite->orig_y = y; | |
359 | break; | |
360 | ||
361 | case AIR: | |
362 | sprite->width = sprite->height = 48; | |
363 | sprite->x_offset = 24; sprite->y_offset = 0; | |
364 | sprite->x_hot = 48; sprite->y_hot = 16; | |
365 | if (x > ((WORLD_X - 20) <<4)) { | |
366 | sprite->x -= 100 + 48; | |
367 | sprite->dest_x = sprite->x - 200; | |
368 | sprite->frame = 7; | |
369 | } else { | |
370 | sprite->dest_x = sprite->x + 200; | |
371 | sprite->frame = 11; | |
372 | } | |
373 | sprite->dest_y = sprite->y; | |
374 | break; | |
375 | ||
376 | case TOR: | |
377 | sprite->width = sprite->height = 48; | |
378 | sprite->x_offset = 24; sprite->y_offset = 0; | |
379 | sprite->x_hot = 40; sprite->y_hot = 36; | |
380 | sprite->frame = 1; | |
381 | sprite->count = 200; | |
382 | break; | |
383 | ||
384 | case EXP: | |
385 | sprite->width = sprite->height = 48; | |
386 | sprite->x_offset = 24; sprite->y_offset = 0; | |
387 | sprite->x_hot = 40; sprite->y_hot = 16; | |
388 | sprite->frame = 1; | |
389 | break; | |
390 | ||
391 | case BUS: | |
392 | sprite->width = sprite->height = 32; | |
393 | sprite->x_offset = 30; sprite->y_offset = -18; | |
394 | sprite->x_hot = 40; sprite->y_hot = -8; | |
395 | sprite->frame = 1; | |
396 | sprite->dir = 1; | |
397 | break; | |
398 | ||
399 | } | |
400 | } | |
401 | ||
402 | ||
6f214ac0 MG |
403 | void |
404 | DestroyAllSprites(void) | |
6a5fa4e0 MG |
405 | { |
406 | SimSprite *sprite; | |
407 | ||
408 | for (sprite = sim->sprite; sprite != NULL; sprite = sprite->next) { | |
409 | sprite->frame = 0; | |
410 | } | |
411 | } | |
412 | ||
413 | ||
6f214ac0 | 414 | void |
6a5fa4e0 MG |
415 | DestroySprite(SimSprite *sprite) |
416 | { | |
417 | SimView *view; | |
418 | SimSprite **sp; | |
419 | ||
420 | for (view = sim->editor; view != NULL; view = view->next) | |
421 | if (view->follow == sprite) | |
422 | view->follow = NULL; | |
423 | ||
424 | if (GlobalSprites[sprite->type] == sprite) { | |
425 | GlobalSprites[sprite->type] = (SimSprite *)NULL; | |
426 | } | |
427 | ||
428 | if (sprite->name != NULL) { | |
429 | ckfree(sprite->name); | |
430 | sprite->name = NULL; | |
431 | } | |
432 | ||
433 | for (sp = &sim->sprite; *sp != NULL; sp = &((*sp)->next)) { | |
434 | if (sprite == (*sp)) { | |
435 | *sp = sprite->next; | |
436 | break; | |
437 | } | |
438 | } | |
439 | ||
440 | sprite->next = FreeSprites; | |
441 | FreeSprites = sprite; | |
442 | } | |
443 | ||
444 | ||
445 | SimSprite * | |
446 | GetSprite(int type) | |
447 | { | |
448 | SimSprite *sprite; | |
449 | ||
450 | if (((sprite = GlobalSprites[type]) == NULL) || | |
451 | (sprite->frame == 0)) | |
452 | return (SimSprite *)NULL; | |
453 | else | |
454 | return sprite; | |
455 | } | |
456 | ||
457 | ||
458 | SimSprite * | |
459 | MakeSprite(int type, int x, int y) | |
460 | { | |
461 | SimSprite *sprite; | |
462 | ||
463 | if ((sprite = GlobalSprites[type]) == NULL) { | |
464 | sprite = NewSprite("", type, x, y); | |
465 | } else { | |
466 | InitSprite(sprite, x, y); | |
467 | } | |
468 | return sprite; | |
469 | } | |
470 | ||
471 | ||
472 | SimSprite * | |
473 | MakeNewSprite(int type, int x, int y) | |
474 | { | |
475 | SimSprite *sprite; | |
476 | ||
477 | sprite = NewSprite("", type, x, y); | |
478 | return sprite; | |
479 | } | |
480 | ||
481 | ||
6f214ac0 | 482 | void |
6a5fa4e0 MG |
483 | DrawObjects(SimView *view) |
484 | { | |
485 | SimSprite *sprite; | |
486 | ||
487 | /* XXX: sort these by layer */ | |
488 | /* | |
489 | if (z = Oframe[TRA]) DrawTrain(view, z); | |
490 | if (z = Oframe[SHI]) DrawBoat(view, z); | |
491 | if (z = Oframe[GOD]) DrawMonster(view, z); | |
492 | if (z = Oframe[COP]) DrawCopter(view, z); | |
493 | if (z = Oframe[AIR]) DrawPlane(view, z); | |
494 | if (z = Oframe[TOR]) DrawTor(view, z); | |
495 | if (z = Oframe[EXP]) DrawExp(view, z); | |
496 | */ | |
497 | ||
498 | for (sprite = sim->sprite; sprite != NULL; sprite = sprite->next) { | |
499 | DrawSprite(view, sprite); | |
500 | } | |
501 | } | |
502 | ||
503 | ||
6f214ac0 | 504 | void |
6a5fa4e0 MG |
505 | DrawSprite(SimView *view, SimSprite *sprite) |
506 | { | |
507 | Pixmap pict, mask; | |
508 | int x, y, i; | |
509 | ||
510 | if (sprite->frame == 0) | |
511 | return; | |
512 | ||
513 | i = (sprite->frame - 1) * 2; | |
514 | pict = view->x->objects[sprite->type][i]; | |
515 | mask = view->x->objects[sprite->type][i + 1]; | |
516 | ||
517 | x = sprite->x | |
518 | - ((view->tile_x <<4) - view->screen_x) | |
519 | + sprite->x_offset; | |
520 | y = sprite->y | |
521 | - ((view->tile_y <<4) - view->screen_y) | |
522 | + sprite->y_offset; | |
523 | ||
524 | XSetClipMask(view->x->dpy, view->x->gc, mask); | |
525 | XSetClipOrigin(view->x->dpy, view->x->gc, x, y); | |
526 | XCopyArea(view->x->dpy, pict, view->pixmap2, view->x->gc, | |
527 | 0, 0, sprite->width, sprite->height, x, y); | |
528 | XSetClipMask(view->x->dpy, view->x->gc, None); | |
529 | XSetClipOrigin(view->x->dpy, view->x->gc, 0, 0); | |
530 | } | |
531 | ||
532 | ||
533 | short GetChar(int x, int y) | |
534 | { | |
535 | x >>= 4; | |
536 | y >>= 4; | |
537 | if (!TestBounds(x, y)) | |
538 | return(-1); | |
539 | else | |
540 | return(Map[x][y] & LOMASK); | |
541 | } | |
542 | ||
543 | ||
544 | short TurnTo(int p, int d) | |
545 | { | |
546 | if (p == d) return(p); | |
547 | if (p < d) | |
548 | if ((d - p) < 4) p++; | |
549 | else p--; | |
550 | else | |
551 | if ((p - d) < 4) p--; | |
552 | else p++; | |
553 | if (p > 8) p = 1; | |
554 | if (p < 1) p = 8; | |
555 | return(p); | |
556 | } | |
557 | ||
558 | ||
6f214ac0 | 559 | int |
6a5fa4e0 MG |
560 | TryOther(int Tpoo, int Told, int Tnew) |
561 | { | |
562 | register short z; | |
563 | ||
564 | z = Told + 4; | |
565 | if (z > 8) z -= 8; | |
566 | if (Tnew != z) return(0); | |
567 | if ((Tpoo == POWERBASE) || (Tpoo == POWERBASE + 1) || | |
568 | (Tpoo == RAILBASE) || (Tpoo == RAILBASE + 1)) | |
569 | return(1); | |
570 | return(0); | |
571 | } | |
572 | ||
573 | ||
574 | short SpriteNotInBounds(SimSprite *sprite) | |
575 | { | |
576 | int x = sprite->x + sprite->x_hot; | |
577 | int y = sprite->y + sprite->y_hot; | |
578 | ||
579 | if ((x < 0) || (y < 0) || | |
580 | (x >= (WORLD_X <<4)) || | |
581 | (y >= (WORLD_Y <<4))) { | |
582 | return (1); | |
583 | } | |
584 | return (0); | |
585 | } | |
586 | ||
587 | ||
588 | short GetDir(int orgX, int orgY, int desX, int desY) | |
589 | { | |
590 | static short Gdtab[13] = { 0, 3, 2, 1, 3, 4, 5, 7, 6, 5, 7, 8, 1 }; | |
591 | int dispX, dispY, z; | |
592 | ||
593 | dispX = desX - orgX; | |
594 | dispY = desY - orgY; | |
595 | if (dispX < 0) | |
596 | if (dispY < 0) z = 11; | |
597 | else z = 8; | |
598 | else | |
599 | if (dispY < 0) z = 2; | |
600 | else z = 5; | |
601 | if (dispX < 0) dispX = -dispX; | |
602 | if (dispY < 0) dispY = -dispY; | |
603 | ||
604 | absDist = dispX + dispY; | |
605 | ||
606 | if ((dispX <<1) < dispY) z++; | |
607 | else if ((dispY <<1) < dispY) z--; | |
608 | ||
609 | if ((z < 0) || (z > 12)) z = 0; | |
610 | ||
611 | return (Gdtab[z]); | |
612 | } | |
613 | ||
614 | ||
6f214ac0 | 615 | int |
6a5fa4e0 MG |
616 | GetDis(int x1, int y1, int x2, int y2) |
617 | { | |
618 | register short dispX, dispY; | |
619 | ||
620 | if (x1 > x2) dispX = x1 - x2; | |
621 | else dispX = x2 - x1; | |
622 | if (y1 > y2) dispY = y1 - y2; | |
623 | else dispY = y2 - y1; | |
624 | ||
625 | return (dispX + dispY); | |
626 | } | |
627 | ||
628 | ||
629 | int CheckSpriteCollision(SimSprite *s1, SimSprite *s2) | |
630 | { | |
631 | if ((s1->frame != 0) && (s2->frame != 0) && | |
632 | GetDis(s1->x + s1->x_hot, s1->y + s1->y_hot, | |
633 | s2->x + s2->x_hot, s2->y + s2->y_hot) < 30) | |
634 | return(1); | |
635 | return(0); | |
636 | } | |
637 | ||
638 | ||
6f214ac0 | 639 | void MoveObjects(void) |
6a5fa4e0 MG |
640 | { |
641 | SimSprite *sprite; | |
642 | ||
643 | if (!SimSpeed) return; | |
644 | Cycle++; | |
645 | ||
646 | for (sprite = sim->sprite; sprite != NULL;) { | |
647 | if (sprite->frame) { | |
648 | switch (sprite->type) { | |
649 | case TRA: | |
650 | DoTrainSprite(sprite); | |
651 | break; | |
652 | case COP: | |
653 | DoCopterSprite(sprite); | |
654 | break; | |
655 | case AIR: | |
656 | DoAirplaneSprite(sprite); | |
657 | break; | |
658 | case SHI: | |
659 | DoShipSprite(sprite); | |
660 | break; | |
661 | case GOD: | |
662 | DoMonsterSprite(sprite); | |
663 | break; | |
664 | case TOR: | |
665 | DoTornadoSprite(sprite); | |
666 | break; | |
667 | case EXP: | |
668 | DoExplosionSprite(sprite); | |
669 | break; | |
670 | case BUS: | |
671 | DoBusSprite(sprite); | |
672 | break; | |
673 | } | |
674 | sprite = sprite->next; | |
675 | } else { | |
676 | if (sprite->name[0] == '\0') { | |
677 | SimSprite *s = sprite; | |
678 | sprite = sprite->next; | |
679 | DestroySprite(s); | |
680 | } else { | |
681 | sprite = sprite->next; | |
682 | } | |
683 | } | |
684 | } | |
685 | } | |
686 | ||
687 | ||
6f214ac0 | 688 | void |
6a5fa4e0 MG |
689 | DoTrainSprite(SimSprite *sprite) |
690 | { | |
691 | static short Cx[4] = { 0, 16, 0, -16 }; | |
692 | static short Cy[4] = { -16, 0, 16, 0 }; | |
693 | static short Dx[5] = { 0, 4, 0, -4, 0 }; | |
694 | static short Dy[5] = { -4, 0, 4, 0, 0 }; | |
695 | static short TrainPic2[5] = { 1, 2, 1, 2, 5 }; | |
696 | register short z, dir, dir2; | |
697 | short c; | |
698 | ||
699 | if ((sprite->frame == 3) || (sprite->frame == 4)) | |
700 | sprite->frame = TrainPic2[sprite->dir]; | |
701 | sprite->x += Dx[sprite->dir]; | |
702 | sprite->y += Dy[sprite->dir]; | |
703 | if (!(Cycle & 3)) { | |
704 | dir = Rand16() & 3; | |
705 | for (z = dir; z < (dir + 4); z++) { | |
706 | dir2 = z & 3; | |
707 | if (sprite->dir != 4) { | |
708 | if (dir2 == ((sprite->dir + 2) & 3)) continue; | |
709 | } | |
710 | c = GetChar(sprite->x + Cx[dir2] + 48, | |
711 | sprite->y + Cy[dir2]); | |
712 | if (((c >= RAILBASE) && (c <= LASTRAIL)) || /* track? */ | |
713 | (c == RAILVPOWERH) || | |
714 | (c == RAILHPOWERV)) { | |
715 | if ((sprite->dir != dir2) && | |
716 | (sprite->dir != 4)) { | |
717 | if ((sprite->dir + dir2) == 3) | |
718 | sprite->frame = 3; | |
719 | else | |
720 | sprite->frame = 4; | |
721 | } else | |
722 | sprite->frame = TrainPic2[dir2]; | |
723 | ||
724 | if ((c == RAILBASE) || (c == (RAILBASE + 1))) | |
725 | sprite->frame = 5; | |
726 | sprite->dir = dir2; | |
727 | return; | |
728 | } | |
729 | } | |
730 | if (sprite->dir == 4) { | |
731 | sprite->frame = 0; | |
732 | return; | |
733 | } | |
734 | sprite->dir = 4; | |
735 | } | |
736 | } | |
737 | ||
738 | ||
6f214ac0 | 739 | void |
6a5fa4e0 MG |
740 | DoCopterSprite(SimSprite *sprite) |
741 | { | |
742 | static short CDx[9] = { 0, 0, 3, 5, 3, 0, -3, -5, -3 }; | |
743 | static short CDy[9] = { 0, -5, -3, 0, 3, 5, 3, 0, -3 }; | |
744 | register short z, d, x, y; | |
745 | ||
746 | if (sprite->sound_count > 0) sprite->sound_count--; | |
747 | ||
748 | if (sprite->control < 0) { | |
749 | ||
750 | if (sprite->count > 0) sprite->count--; | |
751 | ||
752 | if (!sprite->count) { | |
753 | /* Attract copter to monster and tornado so it blows up more often */ | |
754 | SimSprite *s = GetSprite(GOD); | |
755 | if (s != NULL) { | |
756 | sprite->dest_x = s->x; | |
757 | sprite->dest_y = s->y; | |
758 | } else { | |
759 | s = GetSprite(TOR); | |
760 | if (s != NULL) { | |
761 | sprite->dest_x = s->x; | |
762 | sprite->dest_y = s->y; | |
763 | } else { | |
764 | sprite->dest_x = sprite->orig_x; | |
765 | sprite->dest_y = sprite->orig_y; | |
766 | } | |
767 | } | |
768 | } | |
769 | if (!sprite->count) { /* land */ | |
770 | GetDir(sprite->x, sprite->y, sprite->orig_x, sprite->orig_y); | |
771 | if (absDist < 30) { | |
772 | sprite->frame = 0; | |
773 | return; | |
774 | } | |
775 | } | |
776 | } else { | |
777 | GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
778 | if (absDist < 16) { | |
779 | sprite->dest_x = sprite->orig_x; | |
780 | sprite->dest_y = sprite->orig_y; | |
781 | sprite->control = -1; | |
782 | } | |
783 | } | |
784 | ||
785 | if (!sprite->sound_count) { /* send report */ | |
786 | x = (sprite->x + 48) >>5; | |
787 | y = sprite->y >>5; | |
788 | if ((x >= 0) && | |
789 | (x < (WORLD_X >>1)) && | |
790 | (y >= 0) && | |
791 | (y < (WORLD_Y >>1))) { | |
792 | /* Don changed from 160 to 170 to shut the #$%#$% thing up! */ | |
793 | if ((TrfDensity[x][y] > 170) && ((Rand16() & 7) == 0)) { | |
794 | SendMesAt(-41, (x <<1) + 1, (y <<1) + 1); | |
795 | MakeSound("city", "HeavyTraffic"); /* chopper */ | |
796 | sprite->sound_count = 200; | |
797 | } | |
798 | } | |
799 | } | |
800 | z = sprite->frame; | |
801 | if (!(Cycle & 3)) { | |
802 | d = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
803 | z = TurnTo(z, d); | |
804 | sprite->frame = z; | |
805 | } | |
806 | ||
807 | sprite->x += CDx[z]; | |
808 | sprite->y += CDy[z]; | |
809 | } | |
810 | ||
811 | ||
6f214ac0 | 812 | void |
6a5fa4e0 MG |
813 | DoAirplaneSprite(SimSprite *sprite) |
814 | { | |
815 | static short CDx[12] = { 0, 0, 6, 8, 6, 0, -6, -8, -6, 8, 8, 8 }; | |
816 | static short CDy[12] = { 0, -8, -6, 0, 6, 8, 6, 0, -6, 0, 0, 0 }; | |
817 | ||
818 | register short z, d; | |
819 | ||
820 | z = sprite->frame; | |
821 | ||
822 | if (!(Cycle % 5)) { | |
823 | if (z > 8) { /* TakeOff */ | |
824 | z--; | |
825 | if (z < 9) z = 3; | |
826 | sprite->frame = z; | |
827 | } else { /* goto destination */ | |
828 | d = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
829 | z = TurnTo(z, d); | |
830 | sprite->frame = z; | |
831 | } | |
832 | } | |
833 | ||
834 | if (absDist < 50) { /* at destination */ | |
835 | sprite->dest_x = Rand((WORLD_X * 16) + 100) - 50; | |
836 | sprite->dest_y = Rand((WORLD_Y * 16) + 100) - 50; | |
837 | } | |
838 | ||
839 | /* deh added test for !Disasters */ | |
840 | if (!NoDisasters) { | |
841 | SimSprite *s; | |
842 | int explode = 0; | |
843 | ||
844 | for (s = sim->sprite; s != NULL; s = s->next) { | |
845 | if ((s->frame != 0) && | |
846 | ((s->type == COP) || | |
847 | ((sprite != s) && | |
848 | (s->type == AIR))) && | |
849 | CheckSpriteCollision(sprite, s)) { | |
850 | ExplodeSprite(s); | |
851 | explode = 1; | |
852 | } | |
853 | } | |
854 | if (explode) | |
855 | ExplodeSprite(sprite); | |
856 | } | |
857 | ||
858 | sprite->x += CDx[z]; | |
859 | sprite->y += CDy[z]; | |
860 | if (SpriteNotInBounds(sprite)) sprite->frame = 0; | |
861 | } | |
862 | ||
863 | ||
6f214ac0 | 864 | void |
6a5fa4e0 MG |
865 | DoShipSprite(SimSprite *sprite) |
866 | { | |
867 | static short BDx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 }; | |
868 | static short BDy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 }; | |
869 | static short BPx[9] = { 0, 0, 2, 2, 2, 0, -2, -2, -2 }; | |
870 | static short BPy[9] = { 0, -2, -2, 0, 2, 2, 2, 0, -2 }; | |
871 | static short BtClrTab[8] = { RIVER, CHANNEL, POWERBASE, POWERBASE + 1, | |
872 | RAILBASE, RAILBASE + 1, BRWH, BRWV }; | |
873 | register short x, y, z, t = RIVER; | |
874 | short tem, pem; | |
875 | ||
876 | if (sprite->sound_count > 0) sprite->sound_count--; | |
877 | if (!sprite->sound_count) { | |
878 | if ((Rand16() & 3) == 1) { | |
879 | if ((ScenarioID == 2) && /* San Francisco */ | |
880 | (Rand(10) < 5)) { | |
bf4857d3 | 881 | MakeSound("city", "HonkHonk-Low"); |
6a5fa4e0 MG |
882 | } else { |
883 | MakeSound("city", "HonkHonk-Low"); | |
884 | } | |
885 | } | |
886 | sprite->sound_count = 200; | |
887 | } | |
888 | ||
889 | if (sprite->count > 0) sprite->count--; | |
890 | if (!sprite->count) { | |
891 | sprite->count = 9; | |
892 | if (sprite->frame != sprite->new_dir) { | |
893 | sprite->frame = TurnTo(sprite->frame, sprite->new_dir); | |
894 | return; | |
895 | } | |
896 | tem = Rand16() & 7; | |
897 | for (pem = tem; pem < (tem + 8); pem++) { | |
898 | z = (pem & 7) + 1; | |
899 | ||
900 | if (z == sprite->dir) continue; | |
901 | x = ((sprite->x + (48 - 1)) >>4) + BDx[z]; | |
902 | y = (sprite->y >>4) + BDy[z]; | |
903 | if (TestBounds(x, y)) { | |
904 | t = Map[x][y] & LOMASK; | |
905 | if ((t == CHANNEL) || (t == BRWH) || (t == BRWV) || | |
906 | TryOther(t, sprite->dir, z)) { | |
907 | sprite->new_dir = z; | |
908 | sprite->frame = TurnTo(sprite->frame, sprite->new_dir); | |
909 | sprite->dir = z + 4; | |
910 | if (sprite->dir > 8) sprite->dir -= 8; | |
911 | break; | |
912 | } | |
913 | } | |
914 | } | |
915 | if (pem == (tem + 8)) { | |
916 | sprite->dir = 10; | |
917 | sprite->new_dir = (Rand16() & 7) + 1; | |
918 | } | |
919 | } else { | |
920 | z = sprite->frame; | |
921 | if (z == sprite->new_dir) { | |
922 | sprite->x += BPx[z]; | |
923 | sprite->y += BPy[z]; | |
924 | } | |
925 | } | |
926 | if (SpriteNotInBounds(sprite)) { | |
927 | sprite->frame = 0; | |
928 | return; | |
929 | } | |
e7265f7d MG |
930 | if (!NoDisasters) { |
931 | for (z = 0; z < 8; z++) { | |
932 | if (t == BtClrTab[z]) break; | |
933 | if (z == 7) { | |
934 | ExplodeSprite(sprite); | |
935 | Destroy(sprite->x + 48, sprite->y); | |
936 | } | |
6a5fa4e0 MG |
937 | } |
938 | } | |
939 | } | |
940 | ||
941 | ||
6f214ac0 | 942 | void |
6a5fa4e0 MG |
943 | DoMonsterSprite(SimSprite *sprite) |
944 | { | |
945 | static short Gx[5] = { 2, 2, -2, -2, 0 }; | |
946 | static short Gy[5] = { -2, 2, 2, -2, 0 }; | |
947 | static short ND1[4] = { 0, 1, 2, 3 }; | |
948 | static short ND2[4] = { 1, 2, 3, 0 }; | |
949 | static short nn1[4] = { 2, 5, 8, 11 }; | |
950 | static short nn2[4] = { 11, 2, 5, 8 }; | |
951 | register short d, z, c; | |
952 | ||
953 | if (sprite->sound_count > 0) sprite->sound_count--; | |
954 | ||
955 | if (sprite->control < 0) { | |
956 | /* business as usual */ | |
957 | ||
958 | if (sprite->control == -2) { | |
959 | d = (sprite->frame - 1) / 3; | |
960 | z = (sprite->frame - 1) % 3; | |
961 | if (z == 2) sprite->step = 0; | |
962 | if (z == 0) sprite->step = 1; | |
963 | if (sprite->step) z++; | |
964 | else z--; | |
965 | c = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
966 | if (absDist < 18) { | |
967 | sprite->control = -1; | |
968 | sprite->count = 1000; | |
969 | sprite->flag = 1; | |
970 | sprite->dest_x = sprite->orig_x; | |
971 | sprite->dest_y = sprite->orig_y; | |
972 | } else { | |
973 | c = (c - 1) / 2; | |
974 | if (((c != d) && (!Rand(5))) || | |
975 | (!Rand(20))) { | |
976 | int diff = (c - d) & 3; | |
977 | if ((diff == 1) || (diff == 3)) { | |
978 | d = c; | |
979 | } else { | |
980 | if (Rand16() & 1) d++; else d--; | |
981 | d &= 3; | |
982 | } | |
983 | } else { | |
984 | if (!Rand(20)) { | |
985 | if (Rand16() & 1) d++; else d--; | |
986 | d &= 3; | |
987 | } | |
988 | } | |
989 | } | |
990 | } else { | |
991 | ||
992 | d = (sprite->frame - 1) / 3; | |
993 | ||
994 | if (d < 4) { /* turn n s e w */ | |
995 | z = (sprite->frame - 1) % 3; | |
996 | if (z == 2) sprite->step = 0; | |
997 | if (z == 0) sprite->step = 1; | |
998 | if (sprite->step) z++; | |
999 | else z--; | |
1000 | GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
1001 | if (absDist < 60) { | |
1002 | if (sprite->flag == 0) { | |
1003 | sprite->flag = 1; | |
1004 | sprite->dest_x = sprite->orig_x; | |
1005 | sprite->dest_y = sprite->orig_y; | |
1006 | } else { | |
1007 | sprite->frame = 0; | |
1008 | return; | |
1009 | } | |
1010 | } | |
1011 | c = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y); | |
1012 | c = (c - 1) / 2; | |
1013 | if ((c != d) && (!Rand(10))) { | |
1014 | if (Rand16() & 1) z = ND1[d]; | |
1015 | else z = ND2[d]; | |
1016 | d = 4; | |
1017 | if (!sprite->sound_count) { | |
bf4857d3 | 1018 | MakeSound("city", "Monster"); /* monster */ |
6a5fa4e0 MG |
1019 | sprite->sound_count = 50 + Rand(100); |
1020 | } | |
1021 | } | |
1022 | } else { | |
1023 | d = 4; | |
1024 | c = sprite->frame; | |
1025 | z = (c - 13) & 3; | |
1026 | if (!(Rand16() & 3)) { | |
1027 | if (Rand16() & 1) z = nn1[z]; | |
1028 | else z = nn2[z]; | |
1029 | d = (z - 1) / 3; | |
1030 | z = (z - 1) % 3; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | } else { | |
1035 | /* somebody's taken control of the monster */ | |
1036 | ||
1037 | d = sprite->control; | |
1038 | z = (sprite->frame - 1) % 3; | |
1039 | ||
1040 | if (z == 2) sprite->step = 0; | |
1041 | if (z == 0) sprite->step = 1; | |
1042 | if (sprite->step) z++; | |
1043 | else z--; | |
1044 | } | |
1045 | ||
1046 | z = (((d * 3) + z) + 1); | |
1047 | if (z > 16) z = 16; | |
1048 | sprite->frame = z; | |
1049 | ||
1050 | sprite->x += Gx[d]; | |
1051 | sprite->y += Gy[d]; | |
1052 | ||
1053 | if (sprite->count > 0) sprite->count--; | |
1054 | c = GetChar(sprite->x + sprite->x_hot, sprite->y + sprite->y_hot); | |
eac62ca7 | 1055 | if (c == -1) { |
6a5fa4e0 MG |
1056 | sprite->frame = 0; /* kill zilla */ |
1057 | } | |
1058 | ||
1059 | { SimSprite *s; | |
1060 | for (s = sim->sprite; s != NULL; s = s->next) { | |
1061 | if ((s->frame != 0) && | |
1062 | ((s->type == AIR) || | |
1063 | (s->type == COP) || | |
1064 | (s->type == SHI) || | |
1065 | (s->type == TRA)) && | |
1066 | CheckSpriteCollision(sprite, s)) { | |
1067 | ExplodeSprite(s); | |
1068 | } | |
1069 | } | |
1070 | } | |
1071 | ||
1072 | Destroy(sprite->x + 48, sprite->y + 16); | |
1073 | } | |
1074 | ||
1075 | ||
6f214ac0 | 1076 | void |
6a5fa4e0 MG |
1077 | DoTornadoSprite(SimSprite *sprite) |
1078 | { | |
1079 | static short CDx[9] = { 2, 3, 2, 0, -2, -3 }; | |
1080 | static short CDy[9] = { -2, 0, 2, 3, 2, 0 }; | |
1081 | register short z; | |
1082 | ||
1083 | z = sprite->frame; | |
1084 | ||
1085 | if (z == 2) /* cycle animation... post Rel */ | |
1086 | if (sprite->flag) | |
1087 | z = 3; | |
1088 | else | |
1089 | z = 1; | |
1090 | else { | |
1091 | if (z == 1) | |
1092 | sprite->flag = 1; | |
1093 | else | |
1094 | sprite->flag = 0; | |
1095 | z = 2; | |
1096 | } | |
1097 | ||
1098 | if (sprite->count > 0) sprite->count--; | |
1099 | ||
1100 | sprite->frame = z; | |
1101 | ||
1102 | { SimSprite *s; | |
1103 | for (s = sim->sprite; s != NULL; s = s->next) { | |
1104 | if ((s->frame != 0) && | |
1105 | ((s->type == AIR) || | |
1106 | (s->type == COP) || | |
1107 | (s->type == SHI) || | |
1108 | (s->type == TRA)) && | |
1109 | CheckSpriteCollision(sprite, s)) { | |
1110 | ExplodeSprite(s); | |
1111 | } | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | z = Rand(5); | |
1116 | sprite->x += CDx[z]; | |
1117 | sprite->y += CDy[z]; | |
1118 | if (SpriteNotInBounds(sprite)) sprite->frame = 0; | |
1119 | ||
1120 | if ((sprite->count != 0) && | |
1121 | (!Rand(500))) | |
1122 | sprite->frame = 0; | |
1123 | ||
1124 | Destroy(sprite->x + 48, sprite->y + 40); | |
1125 | } | |
1126 | ||
1127 | ||
6f214ac0 | 1128 | void |
6a5fa4e0 MG |
1129 | DoExplosionSprite(SimSprite *sprite) |
1130 | { | |
1131 | short x, y; | |
1132 | ||
1133 | if (!(Cycle & 1)) { | |
1134 | if (sprite->frame == 1) { | |
1135 | MakeSound("city", "Explosion-High"); /* explosion */ | |
1136 | x = (sprite->x >>4) + 3; | |
1137 | y = (sprite->y >>4); | |
1138 | SendMesAt(32, x, y); | |
1139 | } | |
1140 | sprite->frame++; | |
1141 | } | |
1142 | ||
1143 | if (sprite->frame > 6) { | |
1144 | sprite->frame = 0; | |
1145 | ||
1146 | StartFire(sprite->x + 48 - 8, sprite->y + 16); | |
1147 | StartFire(sprite->x + 48 - 24, sprite->y); | |
1148 | StartFire(sprite->x + 48 + 8, sprite->y); | |
1149 | StartFire(sprite->x + 48 - 24, sprite->y + 32); | |
1150 | StartFire(sprite->x + 48 + 8, sprite->y + 32); | |
1151 | return; | |
1152 | } | |
1153 | } | |
1154 | ||
1155 | ||
6f214ac0 | 1156 | void |
6a5fa4e0 MG |
1157 | DoBusSprite(SimSprite *sprite) |
1158 | { | |
1159 | static short Dx[5] = { 0, 1, 0, -1, 0 }; | |
1160 | static short Dy[5] = { -1, 0, 1, 0, 0 }; | |
1161 | static short Dir2Frame[4] = { 1, 2, 1, 2 }; | |
6f214ac0 | 1162 | int dx, dy, tx, ty, otx, oty; |
6a5fa4e0 | 1163 | int turned = 0; |
6f214ac0 | 1164 | int speed = 0, z; |
6a5fa4e0 MG |
1165 | |
1166 | #ifdef DEBUGBUS | |
1167 | printf("Bus dir %d turn %d frame %d\n", | |
1168 | sprite->dir, sprite->turn, sprite->frame); | |
1169 | #endif | |
1170 | ||
1171 | if (sprite->turn) { | |
1172 | if (sprite->turn < 0) { /* ccw */ | |
1173 | if (sprite->dir & 1) { /* up or down */ | |
1174 | sprite->frame = 4; | |
1175 | } else { /* left or right */ | |
1176 | sprite->frame = 3; | |
1177 | } | |
1178 | sprite->turn++; | |
1179 | sprite->dir = (sprite->dir - 1) & 3; | |
1180 | } else { /* cw */ | |
1181 | if (sprite->dir & 1) { /* up or down */ | |
1182 | sprite->frame = 3; | |
1183 | } else { /* left or right */ | |
1184 | sprite->frame = 4; | |
1185 | } | |
1186 | sprite->turn--; | |
1187 | sprite->dir = (sprite->dir + 1) & 3; | |
1188 | } | |
1189 | turned = 1; | |
1190 | } else { | |
1191 | /* finish turn */ | |
1192 | if ((sprite->frame == 3) || (sprite->frame == 4)) { | |
1193 | turned = 1; | |
1194 | sprite->frame = Dir2Frame[sprite->dir]; | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | if (sprite->speed == 0) { | |
1199 | /* brake */ | |
1200 | dx = 0; dy = 0; | |
1201 | } else { /* cruise at traffic speed */ | |
1202 | ||
1203 | tx = (sprite->x + sprite->x_hot) >>5; | |
1204 | ty = (sprite->y + sprite->y_hot) >>5; | |
1205 | if ((tx >= 0) && | |
1206 | (tx < (WORLD_X >>1)) && | |
1207 | (ty >= 0) && | |
1208 | (ty < (WORLD_Y >>1))) { | |
1209 | z = TrfDensity[tx][ty] >>6; | |
1210 | if (z > 1) z--; | |
1211 | } else z = 0; | |
1212 | ||
1213 | switch (z) { | |
1214 | case 0: | |
1215 | speed = 8; | |
1216 | break; | |
1217 | case 1: | |
1218 | speed = 4; | |
1219 | break; | |
1220 | case 2: | |
1221 | speed = 1; | |
1222 | break; | |
1223 | } | |
1224 | ||
1225 | /* govern speed */ | |
1226 | if (speed > sprite->speed) | |
1227 | speed = sprite->speed; | |
1228 | ||
1229 | if (turned) { | |
1230 | #ifdef DEBUGBUS | |
1231 | printf("turned\n"); | |
1232 | #endif | |
1233 | if (speed > 1) speed = 1; | |
1234 | dx = Dx[sprite->dir] * speed; | |
1235 | dy = Dy[sprite->dir] * speed; | |
1236 | } else { | |
1237 | dx = Dx[sprite->dir] * speed; | |
1238 | dy = Dy[sprite->dir] * speed; | |
1239 | ||
1240 | tx = (sprite->x + sprite->x_hot) >>4; | |
1241 | ty = (sprite->y + sprite->y_hot) >>4; | |
1242 | ||
1243 | /* drift into the right lane */ | |
1244 | switch (sprite->dir) { | |
1245 | case 0: /* up */ | |
1246 | z = ((tx <<4) + 4) - (sprite->x + sprite->x_hot); | |
1247 | if (z < 0) dx = -1; | |
1248 | else if (z > 0) dx = 1; | |
1249 | #ifdef DEBUGBUS | |
1250 | printf("moving up x %x z %d dx %d\n", sprite->x + sprite->x_hot, z, dx); | |
1251 | #endif | |
1252 | break; | |
1253 | case 1: /* right */ | |
1254 | z = ((ty <<4) + 4) - (sprite->y + sprite->y_hot); | |
1255 | if (z < 0) dy = -1; | |
1256 | else if (z > 0) dy = 1; | |
1257 | #ifdef DEBUGBUS | |
1258 | printf("moving right y %x z %d dy %d\n", sprite->y + sprite->y_hot, z, dy); | |
1259 | #endif | |
1260 | break; | |
1261 | case 2: /* down */ | |
1262 | z = ((tx <<4)) - (sprite->x + sprite->x_hot); | |
1263 | if (z < 0) dx = -1; | |
1264 | else if (z > 0) dx = 1; | |
1265 | #ifdef DEBUGBUS | |
1266 | printf("moving down x %x z %d dx %d\n", sprite->x + sprite->x_hot, z, dx); | |
1267 | #endif | |
1268 | break; | |
1269 | case 3: /* left */ | |
1270 | z = ((ty <<4)) - (sprite->y + sprite->y_hot); | |
1271 | if (z < 0) dy = -1; | |
1272 | else if (z > 0) dy = 1; | |
1273 | #ifdef DEBUGBUS | |
1274 | printf("moving left y %x z %d dy %d\n", sprite->y + sprite->y_hot, z, dy); | |
1275 | #endif | |
1276 | break; | |
1277 | } | |
1278 | } | |
1279 | } | |
1280 | #ifdef DEBUGBUS | |
1281 | printf("speed dx %d dy %d\n", dx, dy); | |
1282 | #endif | |
1283 | ||
1284 | #define AHEAD 8 | |
1285 | ||
1286 | otx = (sprite->x + sprite->x_hot + (Dx[sprite->dir] * AHEAD)) >>4; | |
1287 | oty = (sprite->y + sprite->y_hot + (Dy[sprite->dir] * AHEAD)) >>4; | |
1288 | if (otx < 0) otx = 0; else if (otx >= WORLD_X) otx = WORLD_X - 1; | |
1289 | if (oty < 0) oty = 0; else if (oty >= WORLD_Y) oty = WORLD_Y - 1; | |
1290 | ||
1291 | tx = (sprite->x + sprite->x_hot + dx + (Dx[sprite->dir] * AHEAD)) >>4; | |
1292 | ty = (sprite->y + sprite->y_hot + dy + (Dy[sprite->dir] * AHEAD)) >>4; | |
1293 | if (tx < 0) tx = 0; else if (tx >= WORLD_X) tx = WORLD_X - 1; | |
1294 | if (ty < 0) ty = 0; else if (ty >= WORLD_Y) ty = WORLD_Y - 1; | |
1295 | ||
1296 | if ((tx != otx) || (ty != oty)) { | |
1297 | #ifdef DEBUGBUS | |
1298 | printf("drive from tile %d %d to %d %d\n", | |
1299 | otx, oty, tx, ty); | |
1300 | #endif | |
1301 | z = CanDriveOn(tx, ty); | |
1302 | if (z == 0) { | |
1303 | /* can't drive forward into a new tile */ | |
1304 | if (speed == 8) { | |
1305 | bulldozer_tool(NULL, tx, ty); | |
1306 | } else { | |
1307 | } | |
1308 | } else { | |
1309 | /* drive forward into a new tile */ | |
1310 | if (z > 0) { | |
1311 | /* smooth */ | |
1312 | } else { | |
1313 | /* bumpy */ | |
1314 | dx /= 2; | |
1315 | dy /= 2; | |
1316 | } | |
1317 | } | |
1318 | } | |
1319 | ||
1320 | tx = (sprite->x + sprite->x_hot + dx) >>4; | |
1321 | ty = (sprite->y + sprite->y_hot + dy) >>4; | |
1322 | z = CanDriveOn(tx, ty); | |
1323 | if (z > 0) { | |
1324 | /* cool, cruise along */ | |
1325 | } else { | |
1326 | if (z < 0) { | |
1327 | /* bumpy */ | |
1328 | } else { | |
1329 | /* something in the way */ | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | sprite->x += dx; | |
1334 | sprite->y += dy; | |
1335 | ||
1336 | if (!NoDisasters) { | |
1337 | SimSprite *s; | |
1338 | int explode = 0; | |
1339 | ||
1340 | for (s = sim->sprite; s != NULL; s = s->next) { | |
1341 | if ((sprite != s) && | |
1342 | (s->frame != 0) && | |
1343 | ((s->type == BUS) || | |
1344 | ((s->type == TRA) && | |
1345 | (s->frame != 5))) && | |
1346 | CheckSpriteCollision(sprite, s)) { | |
1347 | ExplodeSprite(s); | |
1348 | explode = 1; | |
1349 | } | |
1350 | } | |
1351 | if (explode) | |
1352 | ExplodeSprite(sprite); | |
1353 | } | |
1354 | } | |
1355 | ||
1356 | ||
1357 | int | |
1358 | CanDriveOn(int x, int y) | |
1359 | { | |
1360 | int tile; | |
1361 | ||
1362 | if (!TestBounds(x, y)) | |
1363 | return 0; | |
1364 | ||
1365 | tile = Map[x][y] & LOMASK; | |
1366 | ||
1367 | if (((tile >= ROADBASE) && | |
1368 | (tile <= LASTROAD) && | |
1369 | (tile != BRWH) && | |
1370 | (tile != BRWV)) || | |
1371 | (tile == HRAILROAD) || | |
1372 | (tile == VRAILROAD)) | |
1373 | return 1; | |
1374 | ||
1375 | if ((tile == DIRT) || tally(tile)) | |
1376 | return -1; | |
1377 | ||
1378 | return 0; | |
1379 | } | |
1380 | ||
1381 | ||
6f214ac0 | 1382 | void |
6a5fa4e0 MG |
1383 | ExplodeSprite(SimSprite *sprite) |
1384 | { | |
1385 | int x, y; | |
1386 | ||
1387 | sprite->frame = 0; | |
1388 | ||
1389 | x = sprite->x + sprite->x_hot; | |
1390 | y = sprite->y + sprite->y_hot; | |
1391 | MakeExplosionAt(x, y); | |
1392 | ||
1393 | x = (x >>4); | |
1394 | y = (y >>4); | |
1395 | ||
1396 | switch (sprite->type) { | |
1397 | case AIR: | |
1398 | CrashX = x; | |
1399 | CrashY = y; | |
1400 | SendMesAt(-24, x, y); | |
1401 | break; | |
1402 | case SHI: | |
1403 | CrashX = x; | |
1404 | CrashY = y; | |
1405 | SendMesAt(-25, x, y); | |
1406 | break; | |
1407 | case TRA: | |
1408 | CrashX = x; | |
1409 | CrashY = y; | |
1410 | SendMesAt(-26, x, y); | |
1411 | break; | |
1412 | case COP: | |
1413 | CrashX = x; | |
1414 | CrashY = y; | |
1415 | SendMesAt(-27, x, y); | |
1416 | break; | |
1417 | case BUS: | |
1418 | CrashX = x; | |
1419 | CrashY = y; | |
1420 | SendMesAt(-26, x, y); /* XXX for now */ | |
1421 | break; | |
1422 | } | |
1423 | MakeSound("city", "Explosion-High"); /* explosion */ | |
1424 | return; | |
1425 | } | |
1426 | ||
1427 | ||
1428 | int checkWet(int x) | |
1429 | { | |
1430 | if ((x == POWERBASE) || (x == POWERBASE + 1) || | |
1431 | (x == RAILBASE) || (x == RAILBASE + 1) || | |
1432 | (x == BRWH) || (x == BRWV)) | |
1433 | return(1); | |
1434 | else | |
1435 | return(0); | |
1436 | } | |
1437 | ||
1438 | ||
6f214ac0 | 1439 | void |
6a5fa4e0 MG |
1440 | Destroy(int ox, int oy) |
1441 | { | |
1442 | short t, z, x, y; | |
1443 | ||
1444 | x = ox >>4; | |
1445 | y = oy >>4; | |
1446 | if (!TestBounds(x, y)) | |
1447 | return; | |
1448 | z = Map[x][y]; | |
1449 | t = z & LOMASK; | |
1450 | if (t >= TREEBASE) { | |
1451 | /* TILE_IS_BRIDGE(t) */ | |
1452 | if (!(z & BURNBIT)) { | |
1453 | if ((t >= ROADBASE) && (t <= LASTROAD)) | |
1454 | Map[x][y] = RIVER; | |
1455 | return; | |
1456 | } | |
1457 | if (z & ZONEBIT) { | |
1458 | OFireZone(x, y, z); | |
1459 | if (t > RZB) { | |
1460 | MakeExplosionAt(ox, oy); | |
1461 | } | |
1462 | } | |
1463 | if (checkWet(t)) | |
1464 | Map[x][y] = RIVER; | |
1465 | else | |
1466 | Map[x][y] = (DoAnimation | |
1467 | ? TINYEXP | |
1468 | : (LASTTINYEXP - 3)) | BULLBIT | ANIMBIT; | |
1469 | } | |
1470 | } | |
1471 | ||
1472 | ||
6f214ac0 | 1473 | void |
6a5fa4e0 MG |
1474 | OFireZone(int Xloc, int Yloc, int ch) |
1475 | { | |
1476 | register short Xtem, Ytem; | |
1477 | short x, y, XYmax; | |
1478 | ||
1479 | RateOGMem[Xloc >>3][Yloc >>3] -= 20; | |
1480 | ||
1481 | ch &= LOMASK; | |
1482 | if (ch < PORTBASE) | |
1483 | XYmax = 2; | |
1484 | else | |
1485 | if (ch == AIRPORT) XYmax = 5; | |
1486 | else XYmax = 4; | |
1487 | ||
1488 | for (x = -1; x < XYmax; x++) | |
1489 | for (y = -1; y < XYmax; y++) { | |
1490 | Xtem = Xloc + x; | |
1491 | Ytem = Yloc + y; | |
1492 | if ((Map[Xtem][Ytem] & LOMASK) >= ROADBASE) | |
1493 | Map[Xtem][Ytem] |= BULLBIT; | |
1494 | } | |
1495 | } | |
1496 | ||
1497 | ||
6f214ac0 | 1498 | void |
6a5fa4e0 MG |
1499 | StartFire(int x, int y) |
1500 | { | |
6f214ac0 | 1501 | register int t, z; |
6a5fa4e0 MG |
1502 | |
1503 | x >>= 4; | |
1504 | y >>= 4; | |
1505 | if ((x >= WORLD_X) || | |
1506 | (y >= WORLD_Y) || | |
1507 | (x < 0) || (y < 0)) | |
1508 | return; | |
1509 | z = Map[x][y]; | |
1510 | t = z & LOMASK; | |
1511 | if ((!(z & BURNBIT)) && (t != 0)) return; | |
1512 | if (z & ZONEBIT) return; | |
1513 | Map[x][y] = FIRE + (Rand16() & 3) + ANIMBIT; | |
1514 | } | |
1515 | ||
1516 | ||
6f214ac0 | 1517 | void |
6a5fa4e0 MG |
1518 | GenerateTrain(int x, int y) |
1519 | { | |
1520 | if ((TotalPop > 20) && | |
1521 | (GetSprite(TRA) == NULL) && | |
1522 | (!Rand(25))) { | |
1523 | MakeSprite(TRA, (x <<4) + TRA_GROOVE_X, (y <<4) + TRA_GROOVE_Y); | |
1524 | } | |
1525 | } | |
1526 | ||
1527 | ||
6f214ac0 | 1528 | void |
6a5fa4e0 MG |
1529 | GenerateBus(int x, int y) |
1530 | { | |
1531 | if ((GetSprite(BUS) == NULL) && | |
1532 | (!Rand(25))) { | |
1533 | MakeSprite(BUS, (x <<4) + BUS_GROOVE_X, (y <<4) + BUS_GROOVE_Y); | |
1534 | } | |
1535 | } | |
1536 | ||
1537 | ||
6f214ac0 | 1538 | void |
6a5fa4e0 MG |
1539 | GenerateShip(void) |
1540 | { | |
1541 | register short x, y; | |
1542 | ||
1543 | if (!(Rand16() & 3)) | |
1544 | for (x = 4; x < WORLD_X - 2; x++) | |
1545 | if (Map[x][0] == CHANNEL) { | |
1546 | MakeShipHere(x, 0); | |
1547 | return; | |
1548 | } | |
1549 | if (!(Rand16() & 3)) | |
1550 | for (y = 1; y < WORLD_Y - 2; y++) | |
1551 | if (Map[0][y] == CHANNEL) { | |
1552 | MakeShipHere(0, y); | |
1553 | return; | |
1554 | } | |
1555 | if (!(Rand16() & 3)) | |
1556 | for (x = 4; x < WORLD_X - 2; x++) | |
1557 | if (Map[x][WORLD_Y - 1] == CHANNEL) { | |
1558 | MakeShipHere(x, WORLD_Y - 1); | |
1559 | return; | |
1560 | } | |
1561 | if (!(Rand16() & 3)) | |
1562 | for (y = 1; y < WORLD_Y - 2; y++) | |
1563 | if (Map[WORLD_X - 1][y] == CHANNEL) { | |
1564 | MakeShipHere(WORLD_X - 1, y); | |
1565 | return; | |
1566 | } | |
1567 | } | |
1568 | ||
1569 | ||
6f214ac0 MG |
1570 | void |
1571 | MakeShipHere(int x, int y) | |
6a5fa4e0 MG |
1572 | { |
1573 | MakeSprite(SHI, (x <<4) - (48 - 1), (y <<4)); | |
1574 | } | |
1575 | ||
1576 | ||
6f214ac0 | 1577 | void |
6a5fa4e0 MG |
1578 | MakeMonster(void) |
1579 | { | |
6f214ac0 | 1580 | register int x, y, z, done = 0; |
6a5fa4e0 MG |
1581 | SimSprite *sprite; |
1582 | ||
1583 | if ((sprite = GetSprite(GOD)) != NULL) { | |
1584 | sprite->sound_count = 1; | |
1585 | sprite->count = 1000; | |
1586 | sprite->dest_x = PolMaxX <<4; | |
1587 | sprite->dest_y = PolMaxY <<4; | |
1588 | return; | |
1589 | } | |
1590 | ||
1591 | for (z = 0; z < 300; z++) { | |
1592 | x = Rand(WORLD_X - 20) + 10; | |
1593 | y = Rand(WORLD_Y - 10) + 5; | |
1594 | if ((Map[x][y] == RIVER) || (Map[x][y] == RIVER + BULLBIT)) { | |
1595 | MonsterHere(x, y); | |
1596 | done = 1; | |
1597 | break; | |
1598 | } | |
1599 | } | |
1600 | if (!done == 0) | |
1601 | MonsterHere(60, 50); | |
1602 | } | |
1603 | ||
1604 | ||
6f214ac0 | 1605 | void |
6a5fa4e0 MG |
1606 | MonsterHere(int x, int y) |
1607 | { | |
6a5fa4e0 MG |
1608 | MakeSprite(GOD, (x <<4) + 48, (y <<4)); |
1609 | ClearMes(); | |
1610 | SendMesAt(-21, x + 5, y); | |
1611 | } | |
1612 | ||
1613 | ||
6f214ac0 | 1614 | void |
6a5fa4e0 MG |
1615 | GenerateCopter(int x, int y) |
1616 | { | |
1617 | if (GetSprite(COP) != NULL) return; | |
1618 | ||
1619 | MakeSprite(COP, (x <<4), (y <<4) + 30); | |
1620 | } | |
1621 | ||
1622 | ||
6f214ac0 | 1623 | void |
6a5fa4e0 MG |
1624 | GeneratePlane(int x, int y) |
1625 | { | |
1626 | if (GetSprite(AIR) != NULL) return; | |
1627 | ||
1628 | MakeSprite(AIR, (x <<4) + 48, (y <<4) + 12); | |
1629 | } | |
1630 | ||
1631 | ||
6f214ac0 | 1632 | void |
368d83ae MG |
1633 | MakeAirCrash(void) |
1634 | { | |
1635 | #ifndef NO_AIRCRASH | |
1636 | if (GetSprite(AIR) == NULL) { | |
1637 | short x, y; | |
1638 | ||
1639 | x = Rand(WORLD_X - 20) + 10; | |
1640 | y = Rand(WORLD_Y - 10) + 5; | |
1641 | ||
1642 | GeneratePlane(x, y); | |
1643 | } | |
1644 | ||
1645 | ExplodeSprite(GetSprite(AIR)); | |
1646 | #endif | |
1647 | } | |
1648 | ||
1649 | ||
6f214ac0 | 1650 | void |
6a5fa4e0 MG |
1651 | MakeTornado(void) |
1652 | { | |
1653 | short x, y; | |
1654 | SimSprite *sprite; | |
1655 | ||
1656 | if ((sprite = GetSprite(TOR)) != NULL) { | |
1657 | sprite->count = 200; | |
1658 | return; | |
1659 | } | |
1660 | ||
1661 | x = Rand((WORLD_X <<4) - 800) + 400; | |
1662 | y = Rand((WORLD_Y <<4) - 200) + 100; | |
1663 | MakeSprite(TOR, x, y); | |
1664 | ClearMes(); | |
1665 | SendMesAt(-22, (x >>4) + 3, (y >>4) + 2); | |
1666 | } | |
1667 | ||
1668 | ||
6f214ac0 | 1669 | void |
6a5fa4e0 MG |
1670 | MakeExplosion(int x, int y) |
1671 | { | |
1672 | if ((x >= 0) && (x < WORLD_X) && | |
1673 | (y >= 0) && (y < WORLD_Y)) { | |
1674 | MakeExplosionAt((x << 4) + 8, (y << 4) + 8); | |
1675 | } | |
1676 | } | |
1677 | ||
1678 | ||
6f214ac0 | 1679 | void |
6a5fa4e0 MG |
1680 | MakeExplosionAt(int x, int y) |
1681 | { | |
1682 | MakeNewSprite(EXP, x - 40, y - 16); | |
1683 | } | |
1684 |