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