]>
git.zerfleddert.de Git - micropolis/blob - src/sim/w_sprite.c
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.
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.
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/>.
21 * ADDITIONAL TERMS per GNU GPL Section 7
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.
29 * Any propagation or conveyance of this program must include this
30 * copyright notice and these terms.
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.
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.
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
65 Tcl_HashTable SpriteCmds
;
70 SimSprite
*GlobalSprites
[OBJN
];
72 SimSprite
*NewSprite(char *name
, int type
, int x
, int y
);
75 #define TRA_GROOVE_X -39
76 #define TRA_GROOVE_Y 6
77 #define BUS_GROOVE_X -39
78 #define BUS_GROOVE_Y 6
80 #define SPRITECMD_ACCESS_INT(var) \
81 int SpriteCmd##var(SPRITE_ARGS) { \
83 if ((argc != 2) && (argc != 3)) return (TCL_ERROR); \
85 if (Tcl_GetInt(interp, argv[2], &val) != TCL_OK) return (TCL_ERROR); \
88 sprintf(interp->result, "%d", sprite->var); \
93 #define SPRITECMD_GET_STR(var) \
94 int SpriteCmd##var(SPRITE_ARGS) { \
95 sprintf(interp->result, "%s", sprite->var); \
101 DoSpriteCmd(CLIENT_ARGS
)
103 SimSprite
*sprite
= (SimSprite
*) clientData
;
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
);
118 Tcl_AppendResult(interp
, "unknown command name: \"",
119 argv
[0], " ", argv
[1], "\".", (char *) NULL
);
127 SpriteCmd(CLIENT_ARGS
)
133 (Tcl_GetInt(interp
, argv
[2], &type
) != TCL_OK
) ||
134 (type
< 1) || (type
>= OBJN
)) {
138 sprite
= NewSprite(argv
[1], type
, 0, 0);
141 Tcl_CreateCommand(interp
, sprite
->name
,
142 DoSpriteCmd
, (ClientData
) sprite
, (void (*)()) NULL
);
144 interp
->result
= sprite
->name
;
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
)
176 int SpriteCmdExplode(SPRITE_ARGS
)
178 ExplodeSprite(sprite
);
183 int SpriteCmdInit(SPRITE_ARGS
)
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))) {
196 InitSprite(sprite
, x
, y
);
201 sprite_command_init()
205 Tcl_CreateCommand(tk_mainInterp
, "sprite", SpriteCmd
,
206 (ClientData
)NULL
, (void (*)()) NULL
);
208 Tcl_InitHashTable(&SpriteCmds
, TCL_STRING_KEYS
);
210 #define SPRITE_CMD(cmd) HASHED_CMD(Sprite, cmd)
219 SPRITE_CMD(x_offset
);
220 SPRITE_CMD(y_offset
);
228 SPRITE_CMD(sound_count
);
240 for (i
= 0; i
< OBJN
; i
++) {
241 GlobalSprites
[i
] = NULL
;
246 SimSprite
*FreeSprites
= NULL
;
249 NewSprite(char *name
, int type
, int x
, int y
)
254 sprite
= FreeSprites
;
255 FreeSprites
= sprite
->next
;
257 sprite
= (SimSprite
*)ckalloc(sizeof (SimSprite
));
260 sprite
->name
= (char *)ckalloc(strlen(name
) + 1);
261 strcpy(sprite
->name
, name
);
264 InitSprite(sprite
, x
, y
);
266 sim
->sprites
++; sprite
->next
= sim
->sprite
; sim
->sprite
= sprite
;
272 InitSprite(SimSprite
*sprite
, int x
, int y
)
274 sprite
->x
= x
; sprite
->y
= y
;
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;
286 if (GlobalSprites
[sprite
->type
] == NULL
) {
287 GlobalSprites
[sprite
->type
] = sprite
;
290 switch (sprite
->type
) {
293 sprite
->width
= sprite
->height
= 32;
294 sprite
->x_offset
= 32; sprite
->y_offset
= -16;
295 sprite
->x_hot
= 40; sprite
->y_hot
= -8;
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
;
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
;
331 sprite
->width
= sprite
->height
= 32;
332 sprite
->x_offset
= 32; sprite
->y_offset
= -16;
333 sprite
->x_hot
= 40; sprite
->y_hot
= -8;
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;
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;
351 sprite
->dest_x
= sprite
->x
+ 200;
354 sprite
->dest_y
= sprite
->y
;
358 sprite
->width
= sprite
->height
= 48;
359 sprite
->x_offset
= 24; sprite
->y_offset
= 0;
360 sprite
->x_hot
= 40; sprite
->y_hot
= 36;
366 sprite
->width
= sprite
->height
= 48;
367 sprite
->x_offset
= 24; sprite
->y_offset
= 0;
368 sprite
->x_hot
= 40; sprite
->y_hot
= 16;
373 sprite
->width
= sprite
->height
= 32;
374 sprite
->x_offset
= 30; sprite
->y_offset
= -18;
375 sprite
->x_hot
= 40; sprite
->y_hot
= -8;
388 for (sprite
= sim
->sprite
; sprite
!= NULL
; sprite
= sprite
->next
) {
394 DestroySprite(SimSprite
*sprite
)
399 for (view
= sim
->editor
; view
!= NULL
; view
= view
->next
)
400 if (view
->follow
== sprite
)
403 if (GlobalSprites
[sprite
->type
] == sprite
) {
404 GlobalSprites
[sprite
->type
] = (SimSprite
*)NULL
;
407 if (sprite
->name
!= NULL
) {
408 ckfree(sprite
->name
);
412 for (sp
= &sim
->sprite
; *sp
!= NULL
; sp
= &((*sp
)->next
)) {
413 if (sprite
== (*sp
)) {
419 sprite
->next
= FreeSprites
;
420 FreeSprites
= sprite
;
429 if (((sprite
= GlobalSprites
[type
]) == NULL
) ||
430 (sprite
->frame
== 0))
431 return (SimSprite
*)NULL
;
438 MakeSprite(int type
, int x
, int y
)
442 if ((sprite
= GlobalSprites
[type
]) == NULL
) {
443 sprite
= NewSprite("", type
, x
, y
);
445 InitSprite(sprite
, x
, y
);
452 MakeNewSprite(int type
, int x
, int y
)
456 sprite
= NewSprite("", type
, x
, y
);
461 DrawObjects(SimView
*view
)
465 /* XXX: sort these by layer */
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);
476 for (sprite
= sim
->sprite
; sprite
!= NULL
; sprite
= sprite
->next
) {
477 DrawSprite(view
, sprite
);
482 DrawSprite(SimView
*view
, SimSprite
*sprite
)
487 if (sprite
->frame
== 0)
490 i
= (sprite
->frame
- 1) * 2;
491 pict
= view
->x
->objects
[sprite
->type
][i
];
492 mask
= view
->x
->objects
[sprite
->type
][i
+ 1];
495 - ((view
->tile_x
<<4) - view
->screen_x
)
498 - ((view
->tile_y
<<4) - view
->screen_y
)
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);
510 short GetChar(int x
, int y
)
514 if (!TestBounds(x
, y
))
517 return(Map
[x
][y
] & LOMASK
);
521 short TurnTo(int p
, int d
)
523 if (p
== d
) return(p
);
525 if ((d
- p
) < 4) p
++;
528 if ((p
- d
) < 4) p
--;
536 TryOther(int Tpoo
, int Told
, int Tnew
)
542 if (Tnew
!= z
) return(0);
543 if ((Tpoo
== POWERBASE
) || (Tpoo
== POWERBASE
+ 1) ||
544 (Tpoo
== RAILBASE
) || (Tpoo
== RAILBASE
+ 1))
550 short SpriteNotInBounds(SimSprite
*sprite
)
552 int x
= sprite
->x
+ sprite
->x_hot
;
553 int y
= sprite
->y
+ sprite
->y_hot
;
555 if ((x
< 0) || (y
< 0) ||
556 (x
>= (WORLD_X
<<4)) ||
557 (y
>= (WORLD_Y
<<4))) {
564 short GetDir(int orgX
, int orgY
, int desX
, int desY
)
566 static short Gdtab
[13] = { 0, 3, 2, 1, 3, 4, 5, 7, 6, 5, 7, 8, 1 };
572 if (dispY
< 0) z
= 11;
575 if (dispY
< 0) z
= 2;
577 if (dispX
< 0) dispX
= -dispX
;
578 if (dispY
< 0) dispY
= -dispY
;
580 absDist
= dispX
+ dispY
;
582 if ((dispX
<<1) < dispY
) z
++;
583 else if ((dispY
<<1) < dispY
) z
--;
585 if ((z
< 0) || (z
> 12)) z
= 0;
591 GetDis(int x1
, int y1
, int x2
, int y2
)
593 register short dispX
, dispY
;
595 if (x1
> x2
) dispX
= x1
- x2
;
596 else dispX
= x2
- x1
;
597 if (y1
> y2
) dispY
= y1
- y2
;
598 else dispY
= y2
- y1
;
600 return (dispX
+ dispY
);
604 int CheckSpriteCollision(SimSprite
*s1
, SimSprite
*s2
)
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)
618 if (!SimSpeed
) return;
621 for (sprite
= sim
->sprite
; sprite
!= NULL
;) {
623 switch (sprite
->type
) {
625 DoTrainSprite(sprite
);
628 DoCopterSprite(sprite
);
631 DoAirplaneSprite(sprite
);
634 DoShipSprite(sprite
);
637 DoMonsterSprite(sprite
);
640 DoTornadoSprite(sprite
);
643 DoExplosionSprite(sprite
);
649 sprite
= sprite
->next
;
651 if (sprite
->name
[0] == '\0') {
652 SimSprite
*s
= sprite
;
653 sprite
= sprite
->next
;
656 sprite
= sprite
->next
;
663 DoTrainSprite(SimSprite
*sprite
)
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
;
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
];
679 for (z
= dir
; z
< (dir
+ 4); z
++) {
681 if (sprite
->dir
!= 4) {
682 if (dir2
== ((sprite
->dir
+ 2) & 3)) continue;
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)
696 sprite
->frame
= TrainPic2
[dir2
];
698 if ((c
== RAILBASE
) || (c
== (RAILBASE
+ 1)))
704 if (sprite
->dir
== 4) {
713 DoCopterSprite(SimSprite
*sprite
)
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
;
719 if (sprite
->sound_count
> 0) sprite
->sound_count
--;
721 if (sprite
->control
< 0) {
723 if (sprite
->count
> 0) sprite
->count
--;
725 if (!sprite
->count
) {
726 /* Attract copter to monster and tornado so it blows up more often */
727 SimSprite
*s
= GetSprite(GOD
);
729 sprite
->dest_x
= s
->x
;
730 sprite
->dest_y
= s
->y
;
734 sprite
->dest_x
= s
->x
;
735 sprite
->dest_y
= s
->y
;
737 sprite
->dest_x
= sprite
->orig_x
;
738 sprite
->dest_y
= sprite
->orig_y
;
742 if (!sprite
->count
) { /* land */
743 GetDir(sprite
->x
, sprite
->y
, sprite
->orig_x
, sprite
->orig_y
);
750 GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
752 sprite
->dest_x
= sprite
->orig_x
;
753 sprite
->dest_y
= sprite
->orig_y
;
754 sprite
->control
= -1;
758 if (!sprite
->sound_count
) { /* send report */
759 x
= (sprite
->x
+ 48) >>5;
762 (x
< (WORLD_X
>>1)) &&
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;
775 d
= GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
785 DoAirplaneSprite(SimSprite
*sprite
)
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 };
795 if (z
> 8) { /* TakeOff */
799 } else { /* goto destination */
800 d
= GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
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;
811 /* deh added test for !Disasters */
816 for (s
= sim
->sprite
; s
!= NULL
; s
= s
->next
) {
817 if ((s
->frame
!= 0) &&
820 (s
->type
== AIR
))) &&
821 CheckSpriteCollision(sprite
, s
)) {
827 ExplodeSprite(sprite
);
832 if (SpriteNotInBounds(sprite
)) sprite
->frame
= 0;
836 DoShipSprite(SimSprite
*sprite
)
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
;
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 */
852 MakeSound("city", "HonkHonk-Low -speed 80");
854 MakeSound("city", "HonkHonk-Low");
857 sprite
->sound_count
= 200;
860 if (sprite
->count
> 0) sprite
->count
--;
861 if (!sprite
->count
) {
863 if (sprite
->frame
!= sprite
->new_dir
) {
864 sprite
->frame
= TurnTo(sprite
->frame
, sprite
->new_dir
);
868 for (pem
= tem
; pem
< (tem
+ 8); pem
++) {
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
)) {
879 sprite
->frame
= TurnTo(sprite
->frame
, sprite
->new_dir
);
881 if (sprite
->dir
> 8) sprite
->dir
-= 8;
886 if (pem
== (tem
+ 8)) {
888 sprite
->new_dir
= (Rand16() & 7) + 1;
892 if (z
== sprite
->new_dir
) {
897 if (SpriteNotInBounds(sprite
)) {
901 for (z
= 0; z
< 8; z
++) {
902 if (t
== BtClrTab
[z
]) break;
904 ExplodeSprite(sprite
);
905 Destroy(sprite
->x
+ 48, sprite
->y
);
911 DoMonsterSprite(SimSprite
*sprite
)
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
;
921 if (sprite
->sound_count
> 0) sprite
->sound_count
--;
923 if (sprite
->control
< 0) {
924 /* business as usual */
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
++;
933 c
= GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
935 sprite
->control
= -1;
936 sprite
->count
= 1000;
938 sprite
->dest_x
= sprite
->orig_x
;
939 sprite
->dest_y
= sprite
->orig_y
;
942 if (((c
!= d
) && (!Rand(5))) ||
944 int diff
= (c
- d
) & 3;
945 if ((diff
== 1) || (diff
== 3)) {
948 if (Rand16() & 1) d
++; else d
--;
953 if (Rand16() & 1) d
++; else d
--;
960 d
= (sprite
->frame
- 1) / 3;
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
++;
968 GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
970 if (sprite
->flag
== 0) {
972 sprite
->dest_x
= sprite
->orig_x
;
973 sprite
->dest_y
= sprite
->orig_y
;
979 c
= GetDir(sprite
->x
, sprite
->y
, sprite
->dest_x
, sprite
->dest_y
);
981 if ((c
!= d
) && (!Rand(10))) {
982 if (Rand16() & 1) z
= ND1
[d
];
985 if (!sprite
->sound_count
) {
986 MakeSound("city", "Monster -speed [MonsterSpeed]"); /* monster */
987 sprite
->sound_count
= 50 + Rand(100);
994 if (!(Rand16() & 3)) {
995 if (Rand16() & 1) z
= nn1
[z
];
1003 /* somebody's taken control of the monster */
1005 d
= sprite
->control
;
1006 z
= (sprite
->frame
- 1) % 3;
1008 if (z
== 2) sprite
->step
= 0;
1009 if (z
== 0) sprite
->step
= 1;
1010 if (sprite
->step
) z
++;
1014 z
= (((d
* 3) + z
) + 1);
1021 if (sprite
->count
> 0) sprite
->count
--;
1022 c
= GetChar(sprite
->x
+ sprite
->x_hot
, sprite
->y
+ sprite
->y_hot
);
1025 (sprite
->count
!= 0) &&
1026 (sprite
->control
== -1))) {
1027 sprite
->frame
= 0; /* kill zilla */
1031 for (s
= sim
->sprite
; s
!= NULL
; s
= s
->next
) {
1032 if ((s
->frame
!= 0) &&
1033 ((s
->type
== AIR
) ||
1036 (s
->type
== TRA
)) &&
1037 CheckSpriteCollision(sprite
, s
)) {
1043 Destroy(sprite
->x
+ 48, sprite
->y
+ 16);
1047 DoTornadoSprite(SimSprite
*sprite
)
1049 static short CDx
[9] = { 2, 3, 2, 0, -2, -3 };
1050 static short CDy
[9] = { -2, 0, 2, 3, 2, 0 };
1055 if (z
== 2) /* cycle animation... post Rel */
1068 if (sprite
->count
> 0) sprite
->count
--;
1073 for (s
= sim
->sprite
; s
!= NULL
; s
= s
->next
) {
1074 if ((s
->frame
!= 0) &&
1075 ((s
->type
== AIR
) ||
1078 (s
->type
== TRA
)) &&
1079 CheckSpriteCollision(sprite
, s
)) {
1086 sprite
->x
+= CDx
[z
];
1087 sprite
->y
+= CDy
[z
];
1088 if (SpriteNotInBounds(sprite
)) sprite
->frame
= 0;
1090 if ((sprite
->count
!= 0) &&
1094 Destroy(sprite
->x
+ 48, sprite
->y
+ 40);
1098 DoExplosionSprite(SimSprite
*sprite
)
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
);
1112 if (sprite
->frame
> 6) {
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);
1125 DoBusSprite(SimSprite
*sprite
)
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
;
1136 printf("Bus dir %d turn %d frame %d\n",
1137 sprite
->dir
, sprite
->turn
, sprite
->frame
);
1141 if (sprite
->turn
< 0) { /* ccw */
1142 if (sprite
->dir
& 1) { /* up or down */
1144 } else { /* left or right */
1148 sprite
->dir
= (sprite
->dir
- 1) & 3;
1150 if (sprite
->dir
& 1) { /* up or down */
1152 } else { /* left or right */
1156 sprite
->dir
= (sprite
->dir
+ 1) & 3;
1161 if ((sprite
->frame
== 3) || (sprite
->frame
== 4)) {
1163 sprite
->frame
= Dir2Frame
[sprite
->dir
];
1167 if (sprite
->speed
== 0) {
1170 } else { /* cruise at traffic speed */
1172 tx
= (sprite
->x
+ sprite
->x_hot
) >>5;
1173 ty
= (sprite
->y
+ sprite
->y_hot
) >>5;
1175 (tx
< (WORLD_X
>>1)) &&
1177 (ty
< (WORLD_Y
>>1))) {
1178 z
= TrfDensity
[tx
][ty
] >>6;
1195 if (speed
> sprite
->speed
)
1196 speed
= sprite
->speed
;
1202 if (speed
> 1) speed
= 1;
1203 dx
= Dx
[sprite
->dir
] * speed
;
1204 dy
= Dy
[sprite
->dir
] * speed
;
1206 dx
= Dx
[sprite
->dir
] * speed
;
1207 dy
= Dy
[sprite
->dir
] * speed
;
1209 tx
= (sprite
->x
+ sprite
->x_hot
) >>4;
1210 ty
= (sprite
->y
+ sprite
->y_hot
) >>4;
1212 /* drift into the right lane */
1213 switch (sprite
->dir
) {
1215 z
= ((tx
<<4) + 4) - (sprite
->x
+ sprite
->x_hot
);
1217 else if (z
> 0) dx
= 1;
1219 printf("moving up x %x z %d dx %d\n", sprite
->x
+ sprite
->x_hot
, z
, dx
);
1223 z
= ((ty
<<4) + 4) - (sprite
->y
+ sprite
->y_hot
);
1225 else if (z
> 0) dy
= 1;
1227 printf("moving right y %x z %d dy %d\n", sprite
->y
+ sprite
->y_hot
, z
, dy
);
1231 z
= ((tx
<<4)) - (sprite
->x
+ sprite
->x_hot
);
1233 else if (z
> 0) dx
= 1;
1235 printf("moving down x %x z %d dx %d\n", sprite
->x
+ sprite
->x_hot
, z
, dx
);
1239 z
= ((ty
<<4)) - (sprite
->y
+ sprite
->y_hot
);
1241 else if (z
> 0) dy
= 1;
1243 printf("moving left y %x z %d dy %d\n", sprite
->y
+ sprite
->y_hot
, z
, dy
);
1250 printf("speed dx %d dy %d\n", dx
, dy
);
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;
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;
1265 if ((tx
!= otx
) || (ty
!= oty
)) {
1267 printf("drive from tile %d %d to %d %d\n",
1270 z
= CanDriveOn(tx
, ty
);
1272 /* can't drive forward into a new tile */
1274 bulldozer_tool(NULL
, tx
, ty
);
1278 /* drive forward into a new tile */
1289 tx
= (sprite
->x
+ sprite
->x_hot
+ dx
) >>4;
1290 ty
= (sprite
->y
+ sprite
->y_hot
+ dy
) >>4;
1291 z
= CanDriveOn(tx
, ty
);
1293 /* cool, cruise along */
1298 /* something in the way */
1309 for (s
= sim
->sprite
; s
!= NULL
; s
= s
->next
) {
1310 if ((sprite
!= s
) &&
1312 ((s
->type
== BUS
) ||
1313 ((s
->type
== TRA
) &&
1314 (s
->frame
!= 5))) &&
1315 CheckSpriteCollision(sprite
, s
)) {
1321 ExplodeSprite(sprite
);
1327 CanDriveOn(int x
, int y
)
1331 if (!TestBounds(x
, y
))
1334 tile
= Map
[x
][y
] & LOMASK
;
1336 if (((tile
>= ROADBASE
) &&
1337 (tile
<= LASTROAD
) &&
1340 (tile
== HRAILROAD
) ||
1341 (tile
== VRAILROAD
))
1344 if ((tile
== DIRT
) || tally(tile
))
1351 ExplodeSprite(SimSprite
*sprite
)
1357 x
= sprite
->x
+ sprite
->x_hot
;
1358 y
= sprite
->y
+ sprite
->y_hot
;
1359 MakeExplosionAt(x
, y
);
1364 switch (sprite
->type
) {
1368 SendMesAt(-24, x
, y
);
1373 SendMesAt(-25, x
, y
);
1378 SendMesAt(-26, x
, y
);
1383 SendMesAt(-27, x
, y
);
1388 SendMesAt(-26, x
, y
); /* XXX for now */
1391 MakeSound("city", "Explosion-High"); /* explosion */
1398 if ((x
== POWERBASE
) || (x
== POWERBASE
+ 1) ||
1399 (x
== RAILBASE
) || (x
== RAILBASE
+ 1) ||
1400 (x
== BRWH
) || (x
== BRWV
))
1407 Destroy(int ox
, int oy
)
1413 if (!TestBounds(x
, y
))
1417 if (t
>= TREEBASE
) {
1418 /* TILE_IS_BRIDGE(t) */
1419 if (!(z
& BURNBIT
)) {
1420 if ((t
>= ROADBASE
) && (t
<= LASTROAD
))
1427 MakeExplosionAt(ox
, oy
);
1433 Map
[x
][y
] = (DoAnimation
1435 : (LASTTINYEXP
- 3)) | BULLBIT
| ANIMBIT
;
1440 OFireZone(int Xloc
, int Yloc
, int ch
)
1442 register short Xtem
, Ytem
;
1445 RateOGMem
[Xloc
>>3][Yloc
>>3] -= 20;
1451 if (ch
== AIRPORT
) XYmax
= 5;
1454 for (x
= -1; x
< XYmax
; x
++)
1455 for (y
= -1; y
< XYmax
; y
++) {
1458 if ((Map
[Xtem
][Ytem
] & LOMASK
) >= ROADBASE
)
1459 Map
[Xtem
][Ytem
] |= BULLBIT
;
1464 StartFire(int x
, int y
)
1470 if ((x
>= WORLD_X
) ||
1476 if ((!(z
& BURNBIT
)) && (t
!= 0)) return;
1477 if (z
& ZONEBIT
) return;
1478 Map
[x
][y
] = FIRE
+ (Rand16() & 3) + ANIMBIT
;
1482 GenerateTrain(int x
, int y
)
1484 if ((TotalPop
> 20) &&
1485 (GetSprite(TRA
) == NULL
) &&
1487 MakeSprite(TRA
, (x
<<4) + TRA_GROOVE_X
, (y
<<4) + TRA_GROOVE_Y
);
1492 GenerateBus(int x
, int y
)
1494 if ((GetSprite(BUS
) == NULL
) &&
1496 MakeSprite(BUS
, (x
<<4) + BUS_GROOVE_X
, (y
<<4) + BUS_GROOVE_Y
);
1503 register short x
, y
;
1505 if (!(Rand16() & 3))
1506 for (x
= 4; x
< WORLD_X
- 2; x
++)
1507 if (Map
[x
][0] == CHANNEL
) {
1511 if (!(Rand16() & 3))
1512 for (y
= 1; y
< WORLD_Y
- 2; y
++)
1513 if (Map
[0][y
] == CHANNEL
) {
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);
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
);
1532 MakeShipHere(int x
, int y
, int z
)
1534 MakeSprite(SHI
, (x
<<4) - (48 - 1), (y
<<4));
1540 register x
, y
, z
, done
= 0;
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;
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
)) {
1561 MonsterHere(60, 50);
1565 MonsterHere(int x
, int y
)
1569 MakeSprite(GOD
, (x
<<4) + 48, (y
<<4));
1571 SendMesAt(-21, x
+ 5, y
);
1575 GenerateCopter(int x
, int y
)
1577 if (GetSprite(COP
) != NULL
) return;
1579 MakeSprite(COP
, (x
<<4), (y
<<4) + 30);
1583 GeneratePlane(int x
, int y
)
1585 if (GetSprite(AIR
) != NULL
) return;
1587 MakeSprite(AIR
, (x
<<4) + 48, (y
<<4) + 12);
1596 if ((sprite
= GetSprite(TOR
)) != NULL
) {
1597 sprite
->count
= 200;
1601 x
= Rand((WORLD_X
<<4) - 800) + 400;
1602 y
= Rand((WORLD_Y
<<4) - 200) + 100;
1603 MakeSprite(TOR
, x
, y
);
1605 SendMesAt(-22, (x
>>4) + 3, (y
>>4) + 2);
1609 MakeExplosion(int x
, int y
)
1611 if ((x
>= 0) && (x
< WORLD_X
) &&
1612 (y
>= 0) && (y
< WORLD_Y
)) {
1613 MakeExplosionAt((x
<< 4) + 8, (y
<< 4) + 8);
1618 MakeExplosionAt(int x
, int y
)
1620 MakeNewSprite(EXP
, x
- 40, y
- 16);