1 #include <stdlib.h>
2 #include <math.h>
3 #include <SDL/SDL.h>
4 #include "thing.h"
5 #include "tile.h"
6 #include "physics.h"
7 #include "game.h"
8 
9 #define LVLMAX 500
10 #define THINGMAX 250
11 #define BOSSMAX 10
12 #define PARTICLEMAX 2500
13 
14 #define ACCEL 0.4
15 #define FRICT 0.4
16 #define BERETFRICT 0.7
17 #define LINKACCEL 2
18 #define LINKSPEED 8
19 #define TAP_TIME 8
20 #define GRAVITY 0.4
21 #define JUMP_SPEED 6
22 #define WALK_SPEED 2.5
23 #define RUN_SPEED 4
24 #define TELEMODIF 0.175
25 #define TELECAP 17.5
26 #define TELEACCEL 3
27 #define SPR_SIZE 30
28 #define CORNER 6
29 #define FRICTMOD 0.08
30 #define ICYMOD 0.15
31 #define FLOATMOD 0.15
32 #define SMALLSIZE 20
33 #define TINYSIZE 10
34 #define ENEMYCRASH 13
35 #define RUNMARGIN 9
36 #define JUMPGRACE 5
37 #define NOMOVETIME 3
38 #define MOVERSPEED 7.5
39 #define MOVERACC 3
40 #define SWITCH_BOUNCE_ERROR 10
41 #define PI 3.14159265
42 #define TURRET_SHOOT_SPEED 13.5
43 #define BLOCKSTER_SHOOT_SPEED 16
44 #define BLOCKSTER_SHOOT_VSPEED 3
45 #define BLOCKSTER_COUNT 14
46 #define GHOST_DIST 190
47 #define GHOST_TOO_FAST 7
48 #define GHOST_CHASE_DIST 300
49 #define REINC_MARGIN 10
50 #define REINC_CENTER_OFFST 30
51 #define TOPHATMAXSUBTYPE 3
52 #define SHIELDVEL 12
53 #define SHIELDGENNUM 5
54 
55 #define GREENFLAG 0x1
56 #define REDFLAG 0x2
57 #define CREATORFLAG 0x4
58 #define NOCOLLIDEFLAG 0x8
59 #define KEYPRESSED 0x10
60 #define REINCARNATE 0x20
61 
62 int leftTime=0, rightTime=0, upTime=0;
63 int runMargin=0, jumpGrace=0, holdShift=0, shiftRun=0;
64 
65 ThingNode* thingnodes[THINGMAX+1];
66 int floorchecked[THINGMAX+1];
67 int bossvars[BOSSMAX];
68 
get_bossvars()69 int* get_bossvars() {
70   return bossvars;
71 }
72 
get_jumpgrace()73 int get_jumpgrace() { return jumpGrace; }
set_jumpgrace(int val)74 void set_jumpgrace(int val) { jumpGrace = val; }
get_runmargin()75 int get_runmargin() { return runMargin; }
set_runmargin(int val)76 void set_runmargin(int val) { runMargin = val; }
77 
initialize_thingnodes()78 void initialize_thingnodes() {
79   int i;
80   for (i=0; i<THINGMAX+1; i++)
81     thingnodes[i] = 0;
82 }
83 
84 
85 // Find the closest object of the given type to the given coordinates
86 // If the given type is NOTYPE, search for the closest pickupable object
closest_thing(Thing things[THINGMAX],int bcx,int bcy,int * count,int searchtype)87 int closest_thing(Thing things[THINGMAX], int bcx, int bcy,
88 		  int* count, int searchtype) {
89   int temp, temp2 = 10000, temp3 = -1, temp4;
90   int ocx, ocy, cnt=0;
91   for (temp=0; temp<THINGMAX; temp++) {
92     if ((things[temp].type == searchtype ||
93 	 (searchtype == NOTYPE && things[temp].pickup && !things[temp].telething)) &&
94 	 !things[temp].dead) {
95       cnt++;
96       if ((!things[temp].telething && things[temp].subtype != 1) ||
97 	  searchtype != STONEY) {
98         ocx = things[temp].x+things[temp].width/2;
99         ocy = things[temp].y+things[temp].height/2;
100         temp4 = sqrt((ocx-bcx)*(ocx-bcx)+(ocy-bcy)*(ocy-bcy));
101         if (temp4 < temp2) {
102           temp2 = temp4;
103           temp3 = temp;
104         }
105       }
106     }
107   }
108   if (count) *count = cnt;
109   return temp3;
110 }
111 
112 
clear_floorchecked()113 void clear_floorchecked() {
114   int i;
115   for (i=0; i<THINGMAX+1; i++)
116     floorchecked[i] = 0;
117 }
118 
119 
abs_f(float f)120 float abs_f(float f) {
121   return f>0?f:-f;
122 }
123 
124 
pos_f(float f)125 float pos_f(float f) {
126   return f==0?0:(f>0?1:-1);
127 }
128 
129 
rand_to(int upper)130 int rand_to(int upper) {
131   int randval = upper*(rand()/(1.0*RAND_MAX));
132   if (randval == upper) return 0;
133   return randval;
134 }
135 
136 
update_particles(Particle particles[PARTICLEMAX],int gravdir)137 void update_particles(Particle particles[PARTICLEMAX], int gravdir) {
138   int i;
139   for (i = 0; i < PARTICLEMAX; i++) {
140     if (particles[i].time > 0) {
141       particles[i].time--;
142       particles[i].x += particles[i].vx;
143       particles[i].y += particles[i].vy;
144 
145       // deal with gravity
146       switch (gravdir) {
147       case DOWN: particles[i].vy += GRAVITY*0.25; break;
148       case LEFT: particles[i].vx -= GRAVITY*0.25; break;
149       case RIGHT: particles[i].vx += GRAVITY*0.25; break;
150       case UP: particles[i].vy -= GRAVITY*0.25; break;
151       }
152 
153       // deal with friction
154       if (gravdir == DOWN || gravdir == UP)
155         particles[i].vx = apply_friction(FRICTMOD, particles[i].vx);
156       else
157         particles[i].vy = apply_friction(FRICTMOD, particles[i].vy);
158     }
159   }
160 }
161 
162 
make_expl(int x,int y,float vx,float vy,int color,int force,int number)163 void make_expl(int x, int y, float vx, float vy,
164                int color, int force, int number) {
165   int i = 0;
166   float angle, vel;
167   for (i = 0; i < number; i++) {
168     angle = rand_to(360)*PI/180;
169     vel = force+(1.0*(rand_to(51)-25)/25);
170     create_particle(x,y,vx+vel*cos(angle),vy+vel*sin(angle),
171                     color, 10+rand_to(10));
172   }
173 }
174 
175 
make_circle(int x,int y,int color1,int color2,int radius,int number)176 void make_circle(int x, int y, int color1, int color2,
177                  int radius, int number) {
178   int i = 0;
179   float angle;
180   for (i = 0; i < number; i++) {
181     angle = rand_to(360)*PI/180;
182     create_particle(x+(radius+rand_to(4))*cos(angle),
183                     y+(radius+rand_to(4))*sin(angle),
184                     rand_to(3)*cos(angle),rand_to(3)*sin(angle),
185                     (i%2==0)?color1:color2, 3+rand_to(5));
186   }
187 }
188 
189 
tile_collision(Thing * this,Thing things[250],int tiles[500][500][3],int xpos,int ypos,int index,int barely)190 void tile_collision(Thing* this, Thing things[250],
191                     int tiles[500][500][3], int xpos,
192                     int ypos, int index, int barely) {
193   if (!(xpos < 0 || ypos < 0 || xpos >= 500 || ypos >= 500)) {
194     int thistile = tiles[xpos][ypos][0];
195     if (!barely && thistile == CHOICEONLY) {
196       if (this->type == BERET) {
197         tiles[xpos][ypos][0] = BERETONLY;
198         tiles[xpos][ypos][1] = BERETONLYF;
199 	play_sound(SND_CHOICEBERET+rand_to(3));
200       } else if (this->solid) {
201         tiles[xpos][ypos][0] = OBJONLY;
202         tiles[xpos][ypos][1] = OBJONLYF;
203 	play_sound(SND_CHOICEOBJECT+rand_to(3));
204       }
205     }
206     if ((this->type == ANTIMATTER || this->type == ANTISEEKER ||
207 	 this->type == MATTERLY || this->type == ANTIFECTLING) &&
208 	thistile != EMPTY && thistile != ANTITILE &&
209         !(thistile >= OBJONLY && thistile <= CHOICEONLY) &&
210         !(thistile >= MOVERU && thistile <= MOVERL) &&
211         !(thistile == DARKNESSTILE)) {
212       tiles[xpos][ypos][0] = EMPTY;
213       tiles[xpos][ypos][1] = NOTSOLIDB;
214       fix_tile_borders(xpos,ypos,1);
215       make_expl(xpos*30+15,ypos*30+15,0,0,PURPLE,5,75);
216       if (on_screen(this->x, this->y, this->width, this->height))
217 	play_sound(SND_ANTIMATTER+rand_to(3));
218     }
219     if (this->solid && !barely && !this->infront) {
220       if (!this->telething) {
221         if (thistile == MOVERU)
222           this->vy = approach(this->vy, -MOVERSPEED, MOVERACC);
223         else if (thistile == MOVERR)
224           this->vx = approach(this->vx, MOVERSPEED, MOVERACC);
225         else if (thistile == MOVERD)
226           this->vy = approach(this->vy, MOVERSPEED, MOVERACC);
227         else if (thistile == MOVERL)
228           this->vx = approach(this->vx, -MOVERSPEED, MOVERACC);
229       }
230       if (this->animate) {
231         if (thistile == MOVERR) this->dir = 1;
232         else if (thistile == MOVERL) this->dir = 0;
233       }
234       if (this->type == PLATFORM) {
235 	if ((this->subtype == 0 || this->subtype == 1) &&
236 	    (thistile == MOVERL || thistile == MOVERR)) {
237 	  this->startx = this->x + this->vx;
238 	} else if ((this->subtype == 2 || this->subtype == 3) &&
239 		   (thistile == MOVERU || thistile == MOVERD)) {
240 	  this->starty = this->y + this->vy;
241 	}
242       }
243     }
244     if (thistile >= SPIKEU && thistile <= SPIKEL &&
245         this->animate && this->solid && barely && this->type != ANTISEEKER &&
246         ((thistile == SPIKEU && this->vy >= 0 &&
247 	  this->y+this->height-CORNER < ypos*SPR_SIZE &&
248 	  this->x <= (xpos+1)*SPR_SIZE-CORNER && this->x+this->width >= xpos*SPR_SIZE+CORNER) ||
249          (thistile == SPIKED && this->vy <= 0 &&
250 	  this->y+CORNER > (ypos+1)*SPR_SIZE &&
251 	  this->x <= (xpos+1)*SPR_SIZE-CORNER && this->x+this->width >= xpos*SPR_SIZE+CORNER) ||
252          (thistile == SPIKEL && this->vx >= 0 &&
253 	  this->x+this->width-CORNER < xpos*SPR_SIZE) ||
254          (thistile == SPIKER && this->vx <= 0 &&
255 	  this->x+CORNER > (xpos+1)*SPR_SIZE))) {
256       destroy_thing(this, TOUCH, things, index);
257     }
258     if (thistile == ANTITILE && this->solid && barely && !this->dead &&
259         this->type != ANTIMATTER && this->type != ANTISEEKER &&
260 	this->type != MATTERLY && this->type != ANTIFECTLING &&
261 	this->type != MATTERFRAG) {
262       destroy_thing(this, TOUCH, things, index);
263       make_expl(this->x+this->width/2,this->y+this->height/2,0,0,
264                 PURPLE,5,75);
265       if (on_screen(this->x, this->y, this->width, this->height))
266 	play_sound(SND_ANTIMATTER+rand_to(3));
267     }
268   }
269 }
270 
271 
272 // returns the reverse of the given direction
rev_dir(int dir)273 int rev_dir(int dir) {
274   switch (dir) {
275   case UP : return DOWN;
276   case RIGHT : return LEFT;
277   case DOWN : return UP;
278   case LEFT : return RIGHT;
279   }
280   printf("Error: unknown direction %d\n", dir);
281   return dir;
282 }
283 
284 
285 // traverse the linked list to determine whether this can be stood on
check_ontele(Thing * this,ThingNode * thisnode,int index)286 void check_ontele(Thing* this, ThingNode* thisnode, int index) {
287   floorchecked[index] = 1;
288 
289   ThingNode* curnode;
290   for (curnode = thisnode; curnode != 0; curnode = curnode->next) {
291     if (floorchecked[curnode->thingindex]) {
292       this->ontele = -1;
293       break;
294     }
295     if (curnode->thing->ontele > -1) {
296       check_ontele(curnode->thing, thingnodes[curnode->thingindex],
297                    curnode->thingindex);
298     }
299     if (curnode->thing->ontele == -1) {
300       this->ontele = -1;
301       break;
302     } else if (curnode->thing->ontele == 1)
303       this->ontele = 1;
304   }
305 
306   if (this->type == BERET &&
307       (this->ontele == 1 || (thisnode != 0 && this->ontele == 0))) {
308     this->jumpdelay = 5;
309   }
310 }
311 
312 
thing_collision(Thing * this,Thing * other,int collision,int tindex,int oindex,int * gravdir,Thing things[250],int * switchflags,int isthisthing)313 void thing_collision(Thing* this, Thing* other,
314                      int collision, int tindex, int oindex,
315                      int *gravdir, Thing things[250], int *switchflags,
316                      int isthisthing) {
317   int i, temp;
318   float angle;
319   float cx = this->x+this->width/2, cy = this->y+this->height/2;
320   float ocx = other->x+other->width/2, ocy = other->y+other->height/2;
321 
322   // do standing on block stuff, but only if it's this thing
323   if (this->ontele > -1 && isthisthing && this->solid && other->solid) {
324 
325     // calculate gravity modifiers
326     int tgravmod = *gravdir, ogravmod = *gravdir;
327     if (this->islinked == -1 && this->gravmod != NONE)
328       tgravmod = this->gravmod;
329     if (this->islinked > -1) {
330       if (things[this->islinked].gravmod != NONE) tgravmod = things[this->link].gravmod;
331       if ((things[this->islinked].type == LINKBLOCK && things[this->islinked].subtype == 2) ||
332           (this->type == LINKBLOCK && this->subtype == 3))
333         tgravmod = rev_dir(tgravmod);
334     }
335     if (other->islinked == -1 && other->gravmod != NONE)
336       ogravmod = other->gravmod;
337     if (other->islinked > -1) {
338       if (things[other->islinked].gravmod != NONE) ogravmod = things[other->link].gravmod;
339       if ((things[other->islinked].type == LINKBLOCK && things[other->islinked].subtype == 2) ||
340           (other->type == LINKBLOCK && other->subtype == 3))
341         ogravmod = rev_dir(ogravmod);
342     }
343     if (collision == tgravmod) {
344       if (tgravmod == rev_dir(ogravmod)) {
345         this->ontele = -1;
346       } else {
347         ThingNode* newnode = malloc(sizeof(ThingNode));
348         newnode->thing = other;
349         newnode->next = thingnodes[tindex];
350         newnode->thingindex = oindex;
351         thingnodes[tindex] = newnode;
352       }
353     }
354   }
355 
356 /*   if (this->telething && collision == UP && other->type == BERET && */
357 /*       !(*switchflags & KEYPRESSED) && this->vx != 0 && */
358 /*       abs(this->vx) < MOVEBERETHISPEED ) { */
359 /*     other->vx = approach(other->vx, cx-ocx, 2); */
360 /*     other->anim = 4; */
361 /*   } */
362 
363   switch (this->type) {
364   case TOPHATSHIELD :
365     if (other->type != TOPHAT && !other->dead && other->solid) {
366       temp = (other->width+other->height)/4; // avg/2
367       if (sqrt((cx-ocx)*(cx-ocx)+(cy-ocy)*(cy-ocy)) <= this->width/2+temp - 10) {
368 	angle = atan2(ocy-cy, ocx-cx);
369 	other->vx = cos(angle)*SHIELDVEL;
370 	other->vy = sin(angle)*SHIELDVEL-12;
371 	if (abs(other->vx) < 10) {
372 	  if (ocx > cx) this->vx = 15;
373 	  else this->vx = -15;
374 	}
375       }
376     }
377     break;
378   case ANTIMATTER : case ANTISEEKER : case MATTERLY :
379     if (other->type != ANTISEEKER && other->type != ANTIMATTER &&
380 	other->type != MATTERLY && other->type != ANTIFECTLING &&
381 	!(other->type == MATTERFRAG && this->type == MATTERLY) &&
382 	other->solid) {
383       destroy_thing(other, TOUCH, things, oindex);
384       make_expl(ocx,ocy,0,0,PURPLE,6,75);
385       if (on_screen(this->x, this->y, this->width, this->height))
386 	play_sound(SND_ANTIMATTER+rand_to(3));
387     }
388     break;
389   case REINCARNATOR :
390     if (this->subtype == 0 && other->type == BERET &&
391         ocx > cx - REINC_MARGIN &&
392         ocx < cx + REINC_MARGIN &&
393         ocy > cy + REINC_CENTER_OFFST - REINC_MARGIN &&
394         ocy < cy + REINC_CENTER_OFFST + REINC_MARGIN) {
395       *switchflags |= REINCARNATE;
396       for (i = 0; i < THINGMAX; i++) {
397         if (things[i].type == REINCARNATOR && things[i].subtype == 1) {
398           things[i].subtype = 0;
399           make_expl(things[i].x+15,things[i].y+30,0,0,WHITE,4,60);
400           break;
401         }
402       }
403       this->subtype = 1;
404       make_expl(this->x+15,this->y+30,0,0,ORANGE,4,60);
405       play_sound(SND_REGENINIT);
406     }
407     break;
408   case CARRIER :
409     if (collision == UP && !this->jump &&
410         !this->telething && other->solid && !other->infront) {
411       other->vx = approach(other->vx, cx-ocx, 2);
412       if (other->type == BERET) other->anim = 4;
413     }
414     break;
415   case BERET :
416     if (other->deathtouch) destroy_thing(this, TOUCH, things, tindex);
417     if (other->type == WHITEMEDAL || other->type == MEDALCORNER ||
418         other->type == MEDALFRAGMENT) {
419       if (!other->dead) collect_thing(other);
420       destroy_thing(other, COLLECT, things, oindex);
421     }
422     if (!this->jump && (other->type == DOOR || other->type == FINISHDOOR || other->type == READSIGN) &&
423         this->x > other->x - this->width/2 && this->x < other->x + other->width - this->width/2 &&
424         this->y + this->height/2 > other->y)
425       this->timer = oindex;
426     if (!this->jump && other->type == SIGN) this->timer = oindex;
427     break;
428   case AURAHELPER :
429     if (other->animate && other->type != AURADROP && !other->dead) {
430       temp = (other->width+other->height)/4; // avg/2
431       if (sqrt((cx-ocx)*(cx-ocx)+(cy-ocy)*(cy-ocy)) < this->width/2+temp-4) {
432         destroy_thing(other, BOMBED, things, oindex);
433         make_expl(ocx,ocy,0,0,RED,6,75);
434       }
435     }
436     break;
437   case FIREBALL :
438     if (other->animate && oindex != this->status)
439       destroy_thing(other, BOMBED, things, oindex);
440     break;
441   case SPIKEBLOCK :
442     if (other->animate ||
443         (other->type == SPIKEBLOCK && other->vx == 0 && other->vy == 0 &&
444          !other->telething)) {
445       destroy_thing(other, BOMBED, things, oindex);
446       if (other->type == BLOCKSTER)
447         destroy_thing(this, BOMBED, things, tindex);
448     }
449     break;
450   case BOMBHELPER :
451     if (other->animate || other->type == BOMB ||
452         (other->type >= WOODBLOCK && other->type <= STONEBLOCK && other->subtype == 3)) {
453       temp = (other->width+other->height)/4; // avg/2
454       if (sqrt((cx-ocx)*(cx-ocx)+(cy-ocy)*(cy-ocy)) <=
455           this->width/2+temp) destroy_thing(other, BOMBED, things, oindex);
456     } else if (other->type == SOLIDSWITCH || other->type == GRAVITYSWITCH) {
457       if (other->timer == 0) {
458         if (other->type == SOLIDSWITCH) {
459           other->status = !other->status;
460           *switchflags ^= (other->subtype?REDFLAG:GREENFLAG);
461 	  play_sound((this->subtype?SND_SWITCHRD:SND_SWITCHGR)+rand_to(3));
462         } else {
463           other->anim += (other->subtype ? 1 : -1);
464           if (other->anim < UP) other->anim = LEFT;
465           if (other->anim > LEFT) other->anim = UP;
466           *gravdir = other->anim;
467 	  play_sound(SND_SWITCHGV+rand_to(3));
468         }
469         other->timer = 30;
470       }
471     }
472     break;
473   case ANTIFECTLING :
474     if (other->type != ANTISEEKER && other->type != ANTIMATTER &&
475 	other->type != MATTERLY && other->type != ANTIFECTLING &&
476 	other->solid) {
477       destroy_thing(other, TOUCH, things, oindex);
478       make_expl(ocx,ocy,0,0,PURPLE,6,75);
479       if (on_screen(this->x, this->y, this->width, this->height))
480 	play_sound(SND_ANTIMATTER+rand_to(3));
481     }
482   case INFECTLING :
483     if ((other->type != INFECTLING ||
484 	 other->subtype != 1) && other->type != ANTIFECTLING &&
485         (this->subtype == 1 || other->animate || this->type == ANTIFECTLING) &&
486         this->timer == 0 && !other->dead && other->solid &&
487 	!(other->type == TOPHAT && bossvars[0] < SHIELDGENNUM) &&
488 	!(other->type == FIREBALL) &&
489 	!(this->type == ANTIFECTLING && other->type != ANTIMATTER &&
490 	  other->type != MATTERLY && other->type != ANTISEEKER)) {
491       float vx = other->vx, vy = other->vy;
492       int startx = other->startx+other->width/2-this->width/2;
493       int starty = other->starty+other->height/2-this->height/2;
494       int islinked = (other->type == LINKBLOCK &&
495                       other->subtype%2==1) ? -1 : other->islinked;
496       destroy_thing(other, INFECT, things, oindex);
497       if (oindex == 250) {
498         other->solid = 0; // to stop Beret from colliding with object
499         for (i = 0; i < 250; i++) {
500           if (things[i].type == NOTYPE) break;
501         }
502         if (i < 250) oindex = i;
503       }
504       if (oindex < 250) {
505         make_thing(oindex, this->type, this->subtype,
506                    other->x+other->width/2-this->width/2,
507                    other->y+other->height/2-this->height/2,
508                    0, things);
509         things[oindex].vx = vx;
510         things[oindex].vy = vy;
511         things[oindex].startx = startx;
512         things[oindex].starty = starty;
513         things[oindex].islinked = islinked;
514         if (islinked > -1) things[islinked].link = oindex;
515         things[oindex].timer = 6;
516         make_expl(things[oindex].x+things[oindex].width/2,
517                   things[oindex].y+things[oindex].height/2,
518                   cap_val(vx/8,3), cap_val(vy/8,3),
519                   this->type == ANTIFECTLING ? PURPLE : (this->subtype ? WHITE : GRAY),
520 		  3, 25);
521       }
522     }
523     break;
524   }
525 }
526 
527 
528 // returns the number of subtypes in the given type
subtype_count(int type)529 int subtype_count(int type) {
530   switch (type) {
531   case SMWOODBLOCK: case SMICEBLOCK: case SMSTONEBLOCK:
532   case ANNOYINGBLOCK: case INFECTLING: case SPIKEBALL:
533   case MEDALFRAGMENT: case ROBOT: case STONEY:
534   case HOPPER: case SOLIDSWITCH:
535   case GRAVITYSWITCH:
536     return 2;
537   case BOMB: case FAKEBOMB : case FAKEBLOCK: case DOOR: case MATTERFRAG:
538     return 3;
539   case WOODBLOCK: case ICEBLOCK: case STONEBLOCK: case GRAVITYBLOCK:
540   case LINKBLOCK: case MEDALCORNER: case BIGBLOCK: case VSIGN:
541   case PLATFORM: case TELESEEKER: case SIGN: case SOLIDBLOCK:
542   case SPIKEBLOCK:
543     return 4;
544   case WHITEDIE: case BLACKDIE:
545     return 6;
546   case TOPHAT:
547     return TOPHATMAXSUBTYPE;
548   }
549   return 1;
550 }
551 
552 
553 // sets the elements in args to the correct values for
554 // drawing the given object
555 // set args[2] = -1 for no drawing
draw_thing(Thing * this,int args[6])556 void draw_thing(Thing* this, int args[6]) {
557   int temp;
558   args[0] = this->x;
559   args[1] = this->y;
560   args[4] = 1;
561   args[5] = 1;
562 
563   switch(this->type) {
564   case GRAVITYSWITCH :
565     args[2] = 6;
566     args[3] = 12+4*this->subtype+this->anim;
567     break;
568   case TURRET :
569     args[2] = 10+this->anim;
570     args[3] = 13-this->dir;
571     break;
572   case FIREBALL :
573     args[2] = 11;
574     args[3] = 5;
575     break;
576   case BLOCKSTER :
577     args[2] = 18;
578     args[3] = 10 + (int)this->anim;
579     break;
580   case SPIKEBLOCK :
581     args[2] = 18;
582     args[3] = this->subtype;
583     break;
584   case STONEY :
585     if (this->subtype == 2) {
586       args[2] = 16+this->dir;
587       args[3] = 9;
588     } else {
589       args[2] = 16+this->anim;
590       args[3] = this->subtype?6+this->dir:8;
591     }
592     break;
593   case SOLIDSWITCH :
594     args[2] = 6+this->subtype;
595     args[3] = this->anim;
596     break;
597   case SOLIDBLOCK :
598     args[2] = 4+this->subtype/2;
599     args[3] = this->anim;
600     break;
601   case PLATFORM :
602     if ((this->subtype == 0 && this->starty - this->timer + 30 <= this->y) ||
603 	(this->subtype == 1 && this->starty + this->timer - 30 >= this->y)) {
604       args[1] += rand_to(3) - 1;
605     } else if ((this->subtype == 2 && this->startx + this->timer - 30 >= this->x) ||
606 	       (this->subtype == 3 && this->startx - this->timer + 30 <= this->x)) {
607       args[0] += rand_to(3) - 1;
608     }
609     args[2] = 3;
610     args[3] = 12+this->subtype;
611     args[4] = (this->subtype > 1 ? 2 : 1);
612     break;
613   case BIGBLOCK :
614     args[2] = (this->subtype == 3 ? 5 : 3);
615     args[3] = (this->subtype == 3 ? 6 : 4+this->subtype);
616     if (this->subtype > 1) {
617       args[4] = 2;
618       args[5] = 2;
619     }
620     break;
621   case MEDALFRAGMENT :
622     if (this->subtype < 2) {
623       args[2] = 2;
624       args[3] = 6+this->subtype;
625     } else {
626       args[2] = 8;
627       args[3] = 4+this->subtype;
628     }
629 
630     break;
631   case MEDALCORNER :
632     if (this->subtype < 4) {
633       args[2] = (this->subtype < 3 ? 4 : 3);
634       args[3] = (this->subtype < 3 ? 8+this->subtype : 10);
635     } else if (this->subtype == 4) {
636       args[2] = 15;
637       args[3] = 15;
638     } else if (this->subtype == 5) {
639       args[2] = 17;
640       args[3] = 16;
641     } else if (this->subtype == 6) {
642       args[2] = 16;
643       args[3] = 17;
644     } else if (this->subtype == 7) {
645       args[2] = 16;
646       args[3] = 16;
647     }
648     break;
649   case WHITEMEDAL :
650     if (this->subtype == 0) {
651       args[2] = 5;
652       args[3] = 15;
653     } else {
654       args[2] = 15;
655       args[3] = 14;
656     }
657     break;
658   case ANTIMATTER :
659     args[0] -= 2;
660     args[2] = 15;
661     args[3] = 11;
662     break;
663   case FAKEBOMB :
664     if ((int)this->anim == 0) {
665       args[2] = 2;
666       args[3] = 2 + this->subtype;
667     } else {
668       args[2] = 15 + (int)this->anim;
669       args[3] = 10 + this->subtype + this->dir*3;
670     }
671     break;
672   case AURADROP :
673     args[2] = 9+this->dir*3+this->anim;
674     if ((int)this->anim == 3) args[2] -= 2;
675     args[3] = 11;
676     break;
677   case TOPHATSHIELD :
678     args[2] = 12;
679     args[3] = 14;
680     args[4] = 3;
681     args[5] = 3;
682     break;
683   case AURAHELPER :
684     args[2] = 11;
685     args[3] = 6;
686     args[4] = 5;
687     args[5] = 5;
688     break;
689   case BOMBHELPER :
690     args[2] = -1;
691     break;
692   case GHOST :
693     if (this->anim >= 3) args[2] = -1;
694     else {
695       args[2] = 8 + this->dir;
696       args[3] = 14 + this->anim;
697     }
698     break;
699   case REINCARNATOR :
700     args[2] = 8;
701     args[3] = 3 * this->subtype;
702     args[5] = 3;
703     break;
704   case BERET :
705     if (this->subtype == 0) {
706       args[3] = (int)this->anim%8;
707       if (this->jump) {
708 	args[3] = (this->vy<=0)?8:9;
709       }
710       args[0] = this->x-6+this->dir;
711       args[1] = this->y-6;
712       args[2] = 1-this->dir;
713       break;
714     } // otherwise draw as Top Hat
715   case TOPHAT :
716     if (!this->jump) {
717       args[2] = 7 + (int)this->anim%8;
718       args[3] = 18 + this->dir;
719     } else {
720       args[2] = 15;
721       args[3] = (this->vy<=0?16:18) + this->dir;
722     }
723     args[0] = this->x-6+this->dir;
724     args[1] = this->y-6;
725     break;
726   case SHIELDGEN :
727     args[0] = this->x-5;
728     args[1] = this->y-5;
729     args[3] = 17;
730     if ((int)this->anim % 4 == 0) args[2] = 9;
731     else if ((int)this->anim == 1 || (int)this->anim == 3) args[2] = 11;
732     else if ((int)this->anim == 2) args[2] = 12;
733     else if ((int)this->anim == 5 || (int)this->anim == 7) args[2] = 13;
734     else args[2] = 14;
735     break;
736   case WOODBLOCK :
737   case SMWOODBLOCK :
738   case ICEBLOCK :
739   case SMICEBLOCK :
740   case STONEBLOCK :
741   case SMSTONEBLOCK :
742     args[2] = (this->type-WOODBLOCK)%3;
743     args[3] = 10+this->subtype+(this->type<SMWOODBLOCK?0:4);
744     break;
745   case FAKEBLOCK :
746     if (this->anim < 1) {
747       args[2] = this->subtype;
748       args[3] = 10;
749     } else {
750       args[2] = 12+this->subtype+this->dir*3;
751       args[3] = 2+this->anim;
752     }
753     break;
754   case GHOSTBLOCK :
755     args[2] = 6;
756     args[3] = 9+this->anim;
757     break;
758   case WEIGHT :
759     args[0] -= 2;
760     args[2] = 4;
761     args[3] = 13;
762     break;
763   case BOMB :
764     args[2] = 2;
765     args[3] = 2+this->subtype;
766     break;
767   case SIGN :
768     args[2] = 7+this->subtype/2;
769     args[3] = 10+this->subtype%2;
770     break;
771   case VSIGN :
772     args[2] = 7;
773     args[3] = 13+this->subtype;
774     break;
775   case READSIGN :
776     args[2] = 7;
777     args[3] = 12;
778     break;
779   case DOOR :
780     if (this->subtype < 2) {
781       args[2] = 2;
782       args[3] = this->subtype;
783     } else {
784       args[2] = 8;
785       args[3] = 12;
786       args[4] = 2;
787       args[5] = 2;
788     }
789     break;
790   case FINISHDOOR :
791     args[2] = 7;
792     args[3] = 8;
793     args[4] = 2;
794     args[5] = 2;
795     break;
796   case STICKYBOMB :
797     if (this->status == 0) {
798       args[2] = 2;
799       args[3] = 5;
800     } else {
801       temp = this->timer < 300 ? 30 : 5;
802       if (this->timer % temp < 2) {
803         args[2] = 4 + (this->status == CRASHD || this->status == CRASHL);
804         args[3] = 11 + (this->status == CRASHU || this->status == CRASHL);
805       } else {
806         args[2] = 2 + (this->status == CRASHD || this->status == CRASHL);
807         args[3] = 8 + (this->status == CRASHU || this->status == CRASHL);
808       }
809     }
810     break;
811   case WHITEDIE :
812   case BLACKDIE :
813     args[2] = this->subtype%3;
814     args[3] = 16+2*(this->type-WHITEDIE)+this->subtype/3;
815     break;
816   case FLOATBLOCK :
817     args[2] = 6;
818     args[3] = 8;
819     break;
820   case GRAVITYBLOCK :
821     args[2] = 3+this->subtype%2;
822     args[3] = 16+this->subtype/2;
823     break;
824   case ANNOYINGBLOCK :
825     args[2] = 7;
826     args[3] = 6+this->subtype;
827     break;
828   case TELEBLOCK :
829     args[2] = 5;
830     args[3] = 10-this->status/2;
831     break;
832   case LINKBLOCK :
833     args[2] = 3+this->subtype%2;
834     args[3] = 18+this->subtype/2;
835     break;
836   case ANTISEEKER :
837     args[2] = 10+this->dir;
838     args[3] = 16;
839     break;
840   case MATTERFRAG :
841     args[0] = this->x-1;
842     args[1] = this->y-1;
843     if ((this->subtype % 3) < 2) {
844       args[2] = 10+(this->subtype%3);
845       args[3] = 15;
846     } else {
847       args[2] = 10;
848       args[3] = 17;
849     }
850     break;
851   case MATTERLY :
852     args[2] = 10+this->dir;
853     args[3] = 14;
854     break;
855   case TELESEEKER :
856     args[2] = 9+this->dir;
857     if (this->subtype == 2) args[3] = 9;
858     else if (this->subtype == 3) args[3] = 10;
859     else args[3] = (this->subtype == 1 ? 6 : 3)+this->anim;
860     break;
861   case ROBOT :
862     args[2] = 9+this->dir+3*this->subtype;
863     args[3] = this->anim;
864     break;
865   case HOPPER :
866     args[2] = 14+this->dir+2*this->subtype;
867     args[3] = this->status/4.2;
868     break;
869   case CARRIER :
870     args[2] = 11;
871     args[3] = 3+this->anim;
872     break;
873   case SPIKEBALL :
874     args[2] = 11;
875     args[3] = this->anim;
876     break;
877   case ANTIFECTLING :
878     args[2] = 7;
879     args[3] = 17;
880     break;
881   case INFECTLING :
882     args[2] = 5;
883     args[3] = 13 + this->subtype;
884     break;
885   }
886 }
887 
888 
889 // find an empty spot in the array of things, and return the
890 // index of that spot or -1 if no spot is found
find_empty(Thing things[250])891 int find_empty(Thing things[250]) {
892   int i;
893   for (i=0;i<250;i++)
894     if (things[i].type == NOTYPE) return i;
895   return -1;
896 }
897 
898 
899 // removes this thing, probably with a fancy explosion
destroy_thing(Thing * this,int method,Thing things[250],int index)900 void destroy_thing(Thing* this, int method,
901                    Thing things[250], int index) {
902   int i, temp;
903   float cx = this->x+this->width/2, cy = this->y+this->height/2;
904   float vx = cap_val(this->vx/8,3), vy = cap_val(this->vy/8,3);
905 
906   if (this->link > -1 && this->link < 250) {
907     if (this->type == AURADROP && things[this->link].type == AURAHELPER) {
908       if (method == PIT)
909 	destroy_thing(&things[this->link], PIT, things, this->link);
910       else {
911 	things[this->link].vx = cap_val(this->vx,12);
912 	things[this->link].vy = cap_val(this->vy,12);
913 	things[this->link].startx = cx;
914 	things[this->link].starty = cy;
915 	things[this->link].status = 1;
916       }
917     } else if (this->type == LINKBLOCK && (this->subtype == 0 || this->subtype == 2) &&
918 	       things[this->link].type == PLATFORM) {
919       things[this->link].startx = things[this->link].x;
920       things[this->link].starty = things[this->link].y;
921     }
922   }
923 
924 
925 
926   if (!this->dead) {
927 
928     // Sound effects for deaths.
929     if (on_screen(this->x, this->y, this->width, this->height) &&
930 	(method == INFECT ||
931 	 !(this->type == BOMB || this->type == STICKYBOMB || this->type == FAKEBOMB || this->type == FIREBALL))) {
932       if ((this->type == GHOST && method != PIT && method != INFECT) ||
933 	  method == TOUCH || method == BOMBED || (method >= CRASHU && method <= CRASHL))
934 	play_sound(SND_POP+rand_to(3));
935       else if (method == INSIDE) {
936 	if (this->type == BERET) play_sound(SND_SQUELCH+rand_to(3));
937 	else play_sound(SND_CRUNCH+rand_to(3));
938       } else if (method == COLLECT) play_sound(SND_COLLECT+rand_to(6));
939       else if (method == INFECT) play_sound(SND_INFECT);
940     }
941 
942     if (this->type == TOPHAT) {
943       make_thing(THINGMAX-1, DOOR, 2, 585, 900, 0, things);
944       make_thing(THINGMAX-2, DOOR, 2, 975, 900, 0, things);
945       make_thing(THINGMAX-3, DOOR, 2, 1425, 900, 0, things);
946       make_thing(THINGMAX-4, DOOR, 2, 1845, 900, 0, things);
947       things[THINGMAX-1].dir = 7;
948       things[THINGMAX-2].dir = 7;
949       things[THINGMAX-3].dir = 7;
950       things[THINGMAX-4].dir = 7;
951       make_expl(615,930,0,0,GRAY,10,300);
952       make_expl(1005,930,0,0,GRAY,10,300);
953       make_expl(1455,930,0,0,GRAY,10,300);
954       make_expl(1875,930,0,0,GRAY,10,300);
955       set_beat_last_level();
956     } else if (this->type == BLOCKSTER) {
957       if (bossvars[4] > 25) {
958         bossvars[4] -= 75;
959         bossvars[1]++;
960       }
961       if (++bossvars[3] == BLOCKSTER_COUNT) {
962         bossvars[BOSSMAX-1] = 1;
963         make_thing(THINGMAX-1, FINISHDOOR, 0, 1155,
964                    450, 0, things);
965         make_expl(1185,480,0,0,WHITE,10,600);
966       }
967     } else if (this->type == MATTERLY) {
968       bossvars[BOSSMAX-1] = 1;
969       make_thing(THINGMAX-1, FINISHDOOR, 0, 360, 810, 0, things);
970       make_expl(390, 840, 0, 0, WHITE, 10, 600);
971     } else if (this->type == STONEY) {
972       if (this->subtype != 1) {
973         temp = closest_thing(things, cx, cy, &bossvars[1], STONEY);
974         bossvars[1]--;
975       } else if (this->subtype == 1) {
976         temp = closest_thing(things, cx, cy, &bossvars[1], STONEY);
977         if (temp > -1) {
978           things[temp].subtype = 1;
979           things[temp].anim = 2.5;
980           things[temp].status = -1;
981           bossvars[0] = 0;
982         }
983       }
984       if (bossvars[1] == 0) {
985         make_thing(THINGMAX-1, FINISHDOOR, 0, 750,
986                    660, 0, things);
987         make_expl(780,690,0,0,WHITE,10,600);
988         bossvars[BOSSMAX-1] = 1;
989       } else if (bossvars[1] <= 15) {
990         for (temp=0; temp<THINGMAX; temp++) {
991           if (things[temp].type == STONEY &&
992               things[temp].subtype < 2 && temp != index) {
993             things[temp].subtype = 2;
994             make_expl(things[temp].x+things[temp].width/2,
995                       things[temp].y+things[temp].height/2,
996                       0,0,RED,5,100);
997           }
998         }
999       }
1000     }
1001 
1002     if (this->type == MEDALFRAGMENT && method != COLLECT && this->subtype < 2) kill_fragment();
1003   }
1004 
1005   this->vx = 0;
1006   this->vy = 0;
1007   this->nomove = 1;
1008 
1009   if (this->islinked > -1) {
1010     this->islinked = -1;
1011   }
1012   for (i=0;i<250;i++) {
1013     if (things[i].link == index && things[i].type == LINKBLOCK) {
1014       things[i].link = -1;
1015       things[i].islinked = -1;
1016     }
1017   }
1018 
1019   if (method != PIT && !this->dead) {
1020     switch (this->type) {
1021     case SHIELDGEN :
1022       bossvars[0]++;
1023       make_expl(bossvars[1]+45,bossvars[2]+45,0,0,RED,10,150);
1024       make_expl(bossvars[1]+45,bossvars[2]+45,0,0,PURPLE,8,150);
1025       make_expl(cx,cy,vx,vy,PURPLE,5,75);
1026       make_expl(cx,cy,vx,vy,RED,5,10);
1027       break;
1028     case TOPHATSHIELD :
1029       make_expl(cx,cy,vx,vy,PURPLE,10,150);
1030       make_expl(cx,cy,vx,vy,TRANSPARENT,8,200);
1031       break;
1032     case GHOST :
1033       make_expl(cx,cy,vx,vy,TRANSPARENT,4,75);
1034       break;
1035     case REINCARNATOR :
1036       make_expl(cx,cy,vx,vy,GRYELLOW,5,75);
1037       make_expl(cx,cy,vx,vy,BROWN,5,75);
1038       break;
1039     case GRAVITYSWITCH :
1040       if (this->subtype == 0) make_expl(cx,cy,vx,vy,BROWN,5,100);
1041       else make_expl(cx,cy,vx,vy,PURPLE,5,100);
1042       make_expl(cx,cy,vx,vy,BLUE,5,25);
1043       break;
1044     case TURRET :
1045       make_expl(cx,cy,vx,vy,ORANGE,5,50);
1046       make_expl(cx,cy,vx,vy,YELLOW,5,50);
1047       make_expl(cx,cy,vx,vy,GRAY,5,25);
1048       break;
1049     case FIREBALL :
1050       make_expl(cx,cy,vx,vy,ORANGE,1,6);
1051       make_expl(cx,cy,vx,vy,RED,1,6);
1052       break;
1053     case SOLIDSWITCH :
1054       if (this->subtype == 0) make_expl(cx,cy,vx,vy,GREEN,5,100);
1055       else make_expl(cx,cy,vx,vy,RED,5,100);
1056       make_expl(cx,cy,vx,vy,ORANGE,5,25);
1057       break;
1058     case SOLIDBLOCK :
1059       if (this->subtype/2 == 0) make_expl(cx,cy,vx,vy,GREEN,5,100);
1060       else make_expl(cx,cy,vx,vy,RED,5,100);
1061       break;
1062     case PLATFORM :
1063       switch (this->subtype) {
1064       case 0: make_expl(cx,cy,vx,vy,PURPLE,4,100); break;
1065       case 1: make_expl(cx,cy,vx,vy,AQUA,4,100); break;
1066       case 2: make_expl(cx,cy,vx,vy,GREEN,6,150); break;
1067       case 3: make_expl(cx,cy,vx,vy,RED,6,150); break;
1068       }
1069       break;
1070     case BERET :
1071       make_expl(cx,cy,vx,vy,this->subtype?PURPLE:GREEN,8,150);
1072       make_expl(cx,cy,vx,vy,this->subtype?GRAY:SKY,6,75);
1073       make_expl(cx,cy,vx,vy,BROWN,7,25);
1074       break;
1075     case TOPHAT :
1076       make_expl(cx,cy,vx,vy,PURPLE,17,300);
1077       make_expl(cx,cy,vx,vy,GRAY,16,300);
1078       make_expl(cx,cy,vx,vy,BROWN,15,300);
1079       break;
1080     case FAKEBLOCK :
1081       if (this->subtype == 0) make_expl(cx,cy,vx,vy,BROWN,5,125);
1082       else if (this->subtype == 1) make_expl(cx,cy,vx,vy,AQUA,5,125);
1083       else make_expl(cx,cy,vx,vy,GRAY,5,125);
1084       break;
1085     case WOODBLOCK :
1086       make_expl(cx,cy,vx,vy,BROWN,5,125);
1087       break;
1088     case BIGBLOCK :
1089       make_expl(cx,cy,vx,vy,(this->subtype%2==0?GRAY:BROWN),
1090                 (this->subtype>1?8:5),(this->subtype>1?250:125));
1091       break;
1092     case ICEBLOCK :
1093       make_expl(cx,cy,vx,vy,AQUA,5,125);
1094       break;
1095     case STONEY :
1096     case BLACKDIE :
1097     case STONEBLOCK :
1098       make_expl(cx,cy,vx,vy,GRAY,5,125);
1099       break;
1100     case SMWOODBLOCK :
1101       make_expl(cx,cy,vx,vy,BROWN,3,100);
1102       break;
1103     case SMICEBLOCK :
1104       make_expl(cx,cy,vx,vy,AQUA,3,75);
1105       break;
1106     case WEIGHT :
1107     case SMSTONEBLOCK :
1108       make_expl(cx,cy,vx,vy,GRAY,3,100);
1109       break;
1110     case WHITEMEDAL :
1111       make_expl(cx,cy,vx,vy,YELLOW,5,50);
1112     case WHITEDIE :
1113       make_expl(cx,cy,vx,vy,WHITE,5,125);
1114       break;
1115     case GHOSTBLOCK :
1116       make_expl(cx,cy,vx,vy,TRANSPARENT,5,125);
1117       break;
1118     case MEDALCORNER :
1119       make_expl(cx,cy,vx,vy,ORANGE,3,100);
1120       make_expl(cx,cy,vx,vy,BROWN,3,40);
1121       break;
1122     case MEDALFRAGMENT :
1123       make_expl(cx,cy,vx,vy,AQUA,2,20);
1124       make_expl(cx,cy,vx,vy,BLUE,2,15);
1125       break;
1126     case GRAVITYBLOCK :
1127       make_expl(cx,cy,vx,vy,BLUE,5,25);
1128     case FLOATBLOCK :
1129       make_expl(cx,cy,vx,vy,YELLOW,5,100);
1130       break;
1131     case BLOCKSTER :
1132     case ANNOYINGBLOCK :
1133       make_expl(cx,cy,vx,vy,RED,5,125);
1134       break;
1135     case TELEBLOCK :
1136       make_expl(cx,cy,vx,vy,GREEN,5,40);
1137       make_expl(cx,cy,vx,vy,PURPLE,5,85);
1138       break;
1139     case SPIKEBLOCK :
1140       make_expl(cx,cy,vx,vy,GRAY,3,25);
1141       if (this->subtype > 1) make_expl(cx,cy,vx,vy,GRAY,3,75);
1142       else make_expl(cx,cy,vx,vy,BROWN,3,75);
1143       break;
1144     case LINKBLOCK :
1145       switch (this->subtype) {
1146       case 0 : make_expl(cx,cy,vx,vy,PINK,5,125); break;
1147       case 1 : make_expl(cx,cy,vx,vy,GREEN,5,125); break;
1148       case 2 : make_expl(cx,cy,vx,vy,AQUA,5,125); break;
1149       case 3 : make_expl(cx,cy,vx,vy,PURPLE,5,125); break;
1150       }
1151       break;
1152     case TELESEEKER :
1153       if (this->subtype == 0) make_expl(cx,cy,vx,vy,RED,3,75);
1154       else if (this->subtype == 1) make_expl(cx,cy,vx,vy,GRYELLOW,3,75);
1155       else if (this->subtype == 2) make_expl(cx,cy,vx,vy,AQUA,3,75);
1156       else make_expl(cx,cy,vx,vy,PURPLE,3,75);
1157       break;
1158     case ROBOT :
1159       if (this->subtype == 0) make_expl(cx,cy,vx,vy,GRAY,5,125);
1160       else make_expl(cx,cy,vx,vy,PINK,5,125);
1161       break;
1162     case HOPPER :
1163       if (this->subtype == 0) make_expl(cx,cy,vx,vy,AQUA,3,75);
1164       else make_expl(cx,cy,vx,vy,YELLOW,3,75);
1165       break;
1166     case CARRIER :
1167       make_expl(cx,cy,vx,vy,YELLOW,3,60);
1168       make_expl(cx,cy,vx,vy,GRAY,3,15);
1169       break;
1170     case SPIKEBALL :
1171       make_expl(cx,cy,vx,vy,RED,5,65);
1172       make_expl(cx,cy,vx,vy,ORANGE,5,65);
1173       break;
1174     case AURADROP :
1175       make_expl(cx,cy,vx,vy,RED,5,125);
1176       break;
1177     case INFECTLING :
1178       make_expl(cx,cy,vx,vy,this->subtype?WHITE:GRAY,3,100);
1179       break;
1180     case ANTIFECTLING :
1181       make_expl(cx,cy,vx,vy,PURPLE,3,100);
1182       break;
1183     case MATTERLY : case ANTISEEKER : case MATTERFRAG : case ANTIMATTER :
1184       make_expl(cx,cy,vx,vy,PURPLE,4,80);
1185       break;
1186     case FAKEBOMB :
1187     case STICKYBOMB :
1188     case BOMB :
1189       temp = (this->type == BOMB || this->type == FAKEBOMB) ?
1190         (this->subtype == 0 ? ORANGE :
1191          (this->subtype == 1 ? RED : GRYELLOW)) : BLUE;
1192       if (method != INFECT) {
1193 	if (temp == GRYELLOW) play_sound(SND_BOOM+2);
1194 	else if (temp == RED) play_sound(SND_BOOM+1);
1195 	else play_sound(SND_BOOM);
1196       }
1197       if (method == TOUCH || method == INFECT)
1198         make_expl(cx,cy,vx,vy,temp,4,115);
1199       else if ((temp = find_empty(things)) > -1)
1200         make_thing(temp, BOMBHELPER,
1201                    (this->type == BOMB || this->type == FAKEBOMB) ?
1202                    this->subtype : 3, this->x, this->y,0,things);
1203       break;
1204     }
1205   }
1206 
1207   this->dead = 1;
1208   if (this->animate && this->type != BERET) check_room_dead();
1209 }
1210 
1211 
make_beret(Thing * this,int type,int subtype,int x,int y,int dir,Thing things[250])1212 void make_beret(Thing* this, int type, int subtype,
1213                 int x, int y, int dir, Thing things[250]) {
1214   this->type = type;
1215   this->subtype = subtype;
1216   this->x = x;
1217   this->y = y;
1218   this->startx = x;
1219   this->starty = y;
1220   this->stickx = 0;
1221   this->sticky = 0;
1222   this->vx = 0;
1223   this->vy = 0;
1224   this->jump = 0;
1225   this->dead = 0;
1226   this->timer = -1;
1227   this->status = 0;
1228   this->link = -1;
1229   this->islinked = -1;
1230   this->anim = 0;
1231   this->telething = 0;
1232   this->icy = 0;
1233   this->gravmod = DOWN;
1234   this->floating = 0;
1235   this->solid = 1;
1236   this->nomove = 1;
1237   this->nohmove = 0;
1238   this->novmove = 0;
1239   this->infront = 0;
1240   this->inback = 0;
1241   this->width = 20;
1242   this->height = 24;
1243   this->pickup = 0;
1244   this->dir = dir;
1245   this->speed = WALK_SPEED;
1246   this->crashspeed = -1;
1247   this->animate = 1;
1248 }
1249 
1250 
1251 // returns the value for the object parameter for objects
1252 // of the given type
get_param(int type,int dir,int link)1253 int get_param(int type, int dir, int link) {
1254   int param = 0;
1255   if (type >= TELESEEKER && type < TYPEMAX) param = dir;
1256   else {
1257     switch (type) {
1258     case LINKBLOCK : param = link; break;
1259     }
1260   }
1261   return param;
1262 }
1263 
1264 
1265 // copies the given Thing into the given array at the given index
1266 // puts it into an empty spot if index = -1
1267 // returns the index at which the Thing can be found
copy_thing(Thing * this,Thing things[250],int index)1268 int copy_thing(Thing* this, Thing things[250], int index) {
1269   int param = get_param(this->type, this->dir, this->link);
1270 
1271   if (index == -1) index = find_empty(things);
1272   if (index > -1) {
1273     make_thing(index, this->type, this->subtype,
1274                this->x, this->y, param, things);
1275     things[index].islinked = this->islinked;
1276     if (this->type == DOOR || this->type == SIGN ||
1277         this->type == READSIGN || this->type == MEDALFRAGMENT) {
1278       things[index].dir = this->dir;
1279       things[index].timer = this->timer;
1280       things[index].status = this->status;
1281     }
1282   }
1283   return index;
1284 }
1285 
1286 
make_thing(int index,int type,int subtype,int x,int y,int param,Thing things[250])1287 void make_thing(int index, int type, int subtype,
1288                 int x, int y, int param, Thing things[250]) {
1289   Thing* this = &things[index];
1290 
1291   this->type = type;
1292   this->subtype = subtype;
1293   this->x = x;
1294   this->y = y;
1295   this->startx = x;
1296   this->starty = y;
1297   this->stickx = 0;
1298   this->sticky = 0;
1299   this->vx = 0;
1300   this->vy = 0;
1301   this->speed = 0;
1302   this->jump = 0;
1303   this->dir = 0;
1304   this->dead = 0;
1305   this->timer = 0;
1306   this->status = 0;
1307   this->link = -1;
1308   this->anim = 0;
1309   this->telething = 0;
1310   this->width = 30;
1311   this->height = 30;
1312   this->pickup = 1;
1313   this->icy = 0;
1314   this->gravmod = NONE;
1315   this->floating = 0;
1316   this->solid = 1;
1317   this->deathtouch = 0;
1318   this->nomove = 1;
1319   this->nohmove = 0;
1320   this->novmove = 0;
1321   this->infront = 0;
1322   this->inback = 0;
1323   this->crashspeed = -1;
1324   this->animate = 0;
1325 
1326   switch (type) {
1327   case SHIELDGEN :
1328     this->width = 20;
1329     this->height = 20;
1330     this->floating = 1;
1331     this->animate = 1;
1332     this->deathtouch = 1;
1333     break;
1334   case SOLIDBLOCK :
1335     this->infront = 1;
1336     if (this->subtype%2 == 0) this->anim = 5;
1337     else this->anim = 0;
1338     this->solid = 0;
1339   case GRAVITYSWITCH :
1340   case SOLIDSWITCH :
1341     this->pickup = 0;
1342     this->floating = 1;
1343     break;
1344   case PLATFORM :
1345     this->pickup = 0;
1346     this->floating = 1;
1347     if (this->subtype > 1) {this->width = 60;this->height = 17;}
1348     else this->height = 15;
1349     break;
1350   case BIGBLOCK :
1351     if (subtype > 1) {
1352       this->width = 60;
1353       this->height = 60;
1354     }
1355     if (subtype % 2 == 0) this->pickup = 0;
1356     break;
1357   case SMSTONEBLOCK :
1358     this->width = 15;
1359     this->height = 15;
1360     this->pickup = 0;
1361     break;
1362   case SMICEBLOCK :
1363     this->icy = 1;
1364   case INFECTLING :
1365   case ANTIFECTLING :
1366   case SMWOODBLOCK :
1367     this->width = 15;
1368     this->height = 15;
1369     break;
1370   case WEIGHT :
1371     this->height = 16;
1372     this->width = 26;
1373     break;
1374   case ICEBLOCK :
1375     this->icy = 1;
1376     break;
1377   case STONEBLOCK :
1378     this->pickup = 0;
1379     break;
1380   case FLOATBLOCK :
1381     this->floating = 1;
1382     break;
1383   case GRAVITYBLOCK :
1384     this->gravmod = subtype;
1385     break;
1386   case ANNOYINGBLOCK :
1387     this->floating = 1;
1388     this->pickup = 0;
1389     this->nohmove = subtype;
1390     this->novmove = !subtype;
1391     break;
1392   case TELEBLOCK :
1393     this->floating = 1;
1394     this->pickup = 0;
1395     this->solid = 0;
1396     this->infront = 1;
1397     break;
1398   case LINKBLOCK : // set param = index of link
1399     this->link = param;
1400     if (subtype % 2 == 0) {
1401       if (param > -1) things[param].islinked = index;
1402     } else {
1403       this->islinked = param;
1404     }
1405     break;
1406   case MEDALFRAGMENT :
1407     this->width = 10;
1408     this->height = 10;
1409     this->dir = -1;
1410     break;
1411   case MEDALCORNER :
1412     this->width = 15;
1413     this->height = 15;
1414   case WHITEMEDAL :
1415     this->floating = 1;
1416     this->solid = 0;
1417     this->pickup = 0;
1418     this->inback = 1;
1419     break;
1420   case FAKEBLOCK :
1421     if (subtype == 2) this->pickup = 0;
1422     else if (subtype == 1) this->icy = 1;
1423     this->deathtouch = 1;
1424     this->animate = 1;
1425     break;
1426   case TOPHAT :
1427     this->width = 20;
1428     this->height = 24;
1429     this->pickup = 0;
1430     this->animate = 1;
1431     this->deathtouch = 1;
1432     break;
1433   case REINCARNATOR :
1434     this->pickup = 0;
1435     this->floating = 1;
1436     this->inback = 1;
1437     this->solid = 0;
1438     this->height = 90;
1439     break;
1440   case GHOST :
1441     this->solid = 0;
1442     this->floating = 1;
1443     this->deathtouch = 1;
1444     this->crashspeed = ENEMYCRASH;
1445     this->width = 16;
1446     this->height = 16;
1447     this->animate = 1;
1448     break;
1449   case TELESEEKER : case ANTISEEKER :
1450     this->floating = 1;
1451     this->deathtouch = 1;
1452     this->width = 16;
1453     this->height = 19;
1454     this->animate = 1;
1455     break;
1456   case AURADROP : case ROBOT :
1457     this->deathtouch = 1;
1458     this->crashspeed = ENEMYCRASH;
1459   case TURRET :
1460     this->animate = 1;
1461     break;
1462   case HOPPER :
1463     this->deathtouch = 1;
1464     this->crashspeed = ENEMYCRASH;
1465     this->animate = 1;
1466     this->width = 15;
1467     this->height = 19;
1468     break;
1469   case CARRIER :
1470     this->width = 15;
1471     this->height = 18;
1472     break;
1473   case SPIKEBLOCK :
1474     this->deathtouch = 1;
1475     this->pickup = this->subtype < 2;
1476     break;
1477   case MATTERLY :
1478   case STONEY :
1479   case BLOCKSTER :
1480     if (bossvars[BOSSMAX-1]) {
1481       this->type = NOTYPE;
1482     } else {
1483       this->deathtouch = 1;
1484       this->animate = 1;
1485       this->pickup = (this->type == STONEY);
1486       this->floating = (this->type != BLOCKSTER);
1487     }
1488     break;
1489   case MATTERFRAG :
1490     this->width = 10;
1491     this->height = 10;
1492     this->deathtouch = 1;
1493     break;
1494   case SPIKEBALL :
1495     this->deathtouch = 1;
1496     this->floating = 1;
1497     this->crashspeed = ENEMYCRASH;
1498     this->animate = 1;
1499     break;
1500   case STICKYBOMB :
1501     this->width = 15;
1502     this->height = 15;
1503     break;
1504   case FAKEBOMB :
1505     this->deathtouch = 1;
1506     this->animate = 1;
1507   case BOMB :
1508     this->width = 18;
1509     this->height = 18;
1510     this->crashspeed = 8;
1511     break;
1512   case ANTIMATTER :
1513     this->width = 18;
1514     this->height = 15;
1515     break;
1516   case FINISHDOOR :
1517     if (bossvars[BOSSMAX-1] || subtype == 0 || x == 0) {
1518       this->width = 60;
1519       this->height = 60;
1520     } else {
1521       this->type = NOTYPE;
1522       break;
1523     }
1524   case SIGN :
1525   case VSIGN :
1526   case READSIGN :
1527   case DOOR :
1528     this->floating = 1;
1529     this->solid = 0;
1530     this->inback = 1;
1531     this->pickup = 0;
1532     if (this->type == DOOR && this->subtype == 2) {
1533       this->width = 60;
1534       this->height = 60;
1535     }
1536     break;
1537   case FIREBALL :
1538     this->width = 12;
1539     this->height = 12;
1540     this->pickup = 0;
1541     this->dir = param;
1542     this->vx = this->dir ? TURRET_SHOOT_SPEED : -TURRET_SHOOT_SPEED;
1543     this->crashspeed = 0;
1544     break;
1545   case TOPHATSHIELD :
1546     this->width = SPR_SIZE*3;
1547     this->height = SPR_SIZE*3;
1548     this->solid = 0;
1549     this->floating = 1;
1550     this->infront = 1;
1551     this->pickup = 0;
1552     break;
1553   case AURAHELPER :
1554     this->width = SPR_SIZE*5;
1555     this->height = SPR_SIZE*5;
1556     this->solid = 0;
1557     this->floating = 1;
1558     this->infront = 1;
1559     this->pickup = 0;
1560     break;
1561   case BOMBHELPER :
1562     this->width = this->subtype == 3 ? 15 : 18;
1563     this->height = this->subtype == 3 ? 15 : 18;
1564     this->solid = 0;
1565     this->floating = 1;
1566     this->pickup = 0;
1567     break;
1568   }
1569 
1570   if (this->animate) {
1571     this->gravmod = DOWN;
1572     this->dir = param;
1573   }
1574 }
1575 
1576 
fix_vel(Thing * this)1577 void fix_vel(Thing* this) {
1578   if ((this->collide[LEFT] && this->vx < 0) ||
1579       (this->collide[RIGHT] && this->vx > 0)) this->vx = 0;
1580   if ((this->collide[UP] && this->vy < 0) ||
1581       (this->collide[DOWN] && this->vy > 0)) this->vy = 0;
1582 }
1583 
1584 
cap_val(float val,float cap)1585 float cap_val(float val, float cap) {
1586   if (val < -cap) return -cap;
1587   if (val > cap) return cap;
1588   return val;
1589 }
1590 
1591 
move_fake(Thing * this,int anims,float bounceh,int bcx,int bcy,int tcx,int tcy)1592 void move_fake(Thing *this, int anims, float bounceh,
1593                int bcx, int bcy, int tcx, int tcy) {
1594   if (abs(bcx-tcx) < 9*SPR_SIZE && abs(bcy-tcy) < 6*SPR_SIZE &&
1595       !this->telething && this->islinked == -1 &&
1596       !(bcx <= tcx && this->collide[LEFT]) &&
1597       !(bcx >= tcx && this->collide[RIGHT])) {
1598     this->deathtouch = 1;
1599     this->anim = approach(this->anim, anims, 0.3);
1600     if (this->anim < anims && this->anim >= anims - 0.3) {
1601       play_sound(SND_FAKE+rand_to(2));
1602     }
1603     this->dir = bcx > tcx;
1604     if (abs(bcx-tcx) > 6) this->vx = approach(this->vx, cap_val(bcx-tcx, 8), 0.75);
1605     if (!this->jump && abs(this->vx) < 10 && this->timer == 0) {
1606       this->vy = -bounceh;
1607       this->timer = 10;
1608     }
1609     fix_vel(this);
1610   } else {
1611     this->deathtouch = 0;
1612     this->anim = approach(this->anim, 0, 0.3);
1613   }
1614   if (this->timer > 0) this->timer--;
1615 }
1616 
1617 
move_bnf(Thing * this,int updown,float speed,float accel)1618 void move_bnf(Thing* this, int updown, float speed, float accel) {
1619   int a = updown?UP:LEFT, b = updown?DOWN:RIGHT;
1620   if (this->dir == 0) {
1621     if (!updown) this->vx = approach(this->vx, -speed, accel);
1622     else this->vy = approach(this->vy, -speed, accel);
1623     if (this->collide[a] && !this->collide[b] &&
1624         this->timer == 0) {
1625       this->dir = 1;
1626       this->timer = 10;
1627     }
1628   } else {
1629     if (!updown) this->vx = approach(this->vx, speed, accel);
1630     else this->vy = approach(this->vy, speed, accel);
1631     if (this->collide[b] && !this->collide[a] &&
1632         this->timer == 0) {
1633       this->dir = 0;
1634       this->timer = 10;
1635     }
1636   }
1637   if (this->timer > 0) this->timer--;
1638 }
1639 
1640 
special_thing(Thing * this,Thing things[250],Thing * beret,int * gravdir,int teleused,int index,int * switchflags)1641 void special_thing(Thing* this, Thing things[250],
1642                    Thing* beret, int *gravdir, int teleused,
1643                    int index, int *switchflags) {
1644   int temp, temp2, temp3;
1645   float angle;
1646   int bcx = beret->x+beret->width/2, bcy = beret->y+beret->height/2;
1647   int tcx = this->x+this->width/2, tcy = this->y+this->height/2;
1648 
1649   switch (this->type) {
1650   case SHIELDGEN :
1651     this->anim += 0.4;
1652     if (this->anim >= 8) this->anim = 0;
1653     break;
1654   case TOPHATSHIELD :
1655     this->x = bossvars[1];
1656     this->y = bossvars[2];
1657     if (bossvars[0] == SHIELDGENNUM) {
1658       destroy_thing(this, TOUCH, things, index);
1659     }
1660     break;
1661   case TURRET :
1662     if (++this->status >= 12) this->status = 0;
1663     this->anim = this->status/2;
1664     if ((this->status == 0 || this->status == 6) &&
1665         (temp = find_empty(things)) > -1) {
1666       if (on_screen(this->x, this->y, this->width, this->height))
1667 	play_sound(SND_TURRET+rand_to(3));
1668       make_thing(temp, FIREBALL, 0,
1669 		 this->x+(this->dir?this->width-2:-10),
1670 		 this->y+(this->status==6?16:6),
1671 		 this->dir,things);
1672       make_expl(this->x+(this->dir?this->width-7:7),
1673 		this->y+(this->status == 6?this->height-4:4),
1674 		this->dir?3:-3,0,RED,2,10);
1675       things[temp].status = index;
1676       if (*gravdir == DOWN) things[temp].vy = -1;
1677       if (*gravdir == UP) things[temp].vy = 1;
1678     }
1679     break;
1680   case FIREBALL :
1681     if (this->vx == 0 && this->vy == 0) {
1682       destroy_thing(this, PIT, things, index);
1683     }
1684     break;
1685   case PLATFORM :
1686     if (this->collide[UP]) {
1687       // status = 2 means we should ignore the collision for one frame
1688       // status = 1 means the platform has been activated
1689       // status = 0 means everything is fine and dandy
1690       if (this->status == 2) this->status = 0;
1691       else this->status = 1;
1692     } else if (this->status == 1 && this->dir == 0) {
1693       this->status = 0;
1694       this->timer += 30;
1695       this->dir = 12;
1696       if (on_screen(this->x, this->y, this->width, this->height))
1697 	play_sound(SND_PLATFORM+rand_to(3));
1698     }
1699     temp = this->startx;
1700     temp2 = this->starty;
1701     if (this->dir > 0) this->dir--;
1702     if (this->subtype == 0) temp2 -= this->timer;
1703     else if (this->subtype == 1) temp2 += this->timer;
1704     else if (this->subtype == 2) temp += this->timer;
1705     else if (this->subtype == 3) temp -= this->timer;
1706     if (this->islinked == -1) {
1707       this->vx = approach(this->vx, cap_val(temp-this->x,2.5),1.1);
1708       this->vy = approach(this->vy, cap_val(temp2-this->y,2.5),1.1);
1709     }
1710     break;
1711   case SOLIDBLOCK :
1712     this->status = (*switchflags&(this->subtype/2==0?GREENFLAG:REDFLAG));
1713     if (this->subtype%2 == 1) this->status = !this->status;
1714     if (!this->status && this->anim < 5) this->anim += 0.75;
1715     else if (this->status && this->anim > 0) this->anim -= 0.75;
1716     this->solid = (this->anim <= 2);
1717     break;
1718   case GRAVITYSWITCH :
1719     this->anim = *gravdir;
1720     if (this->islinked == -1) {
1721       this->vx = approach(this->vx, cap_val(this->startx-this->x,4),1.1);
1722       this->vy = approach(this->vy, cap_val(this->starty-this->y,4),1.1);
1723     }
1724     if (this->timer > 0) this->timer--;
1725     break;
1726   case SOLIDSWITCH :
1727     this->status = (*switchflags&(this->subtype==0?GREENFLAG:REDFLAG));
1728     if (this->islinked == -1) {
1729       this->vx = approach(this->vx, cap_val(this->startx-this->x,4),1.1);
1730       this->vy = approach(this->vy, cap_val(this->starty-this->y,4),1.1);
1731     }
1732     if (this->status && this->anim < 5) this->anim += 0.75;
1733     else if (!this->status && this->anim > 0) this->anim -= 0.75;
1734     if (this->timer > 0) this->timer--;
1735     break;
1736   case WHITEMEDAL : case MEDALCORNER :
1737     if (((this->timer+index*13) % 30) == 0) this->vy += 1;
1738     else if (((this->timer+index*13) % 30) == 15) this->vy -= 1;
1739     this->timer++;
1740     break;
1741   case SPIKEBLOCK :
1742     this->timer++;
1743     if (this->timer == 300) destroy_thing(this, BOMBED, things, index);
1744     break;
1745   case MATTERFRAG :
1746     if (this->subtype >= 3 && bossvars[2] == 0 && bossvars[BOSSMAX-1] == 0) {
1747       angle = (index*6+things[bossvars[1]].timer % 90) * PI/45;
1748       temp3 = 60 + 10*cos(things[bossvars[1]].timer % 360 * PI/180);
1749       this->vx =
1750 	approach(this->vx, cap_val(things[bossvars[1]].x+9+cos(angle)*temp3-
1751 				   this->x, 6), 0.75);
1752       this->vy =
1753 	approach(this->vy, cap_val(things[bossvars[1]].y+9+sin(angle)*temp3-
1754 				   this->y, 6), 0.75);
1755     }
1756     break;
1757   case TOPHAT :
1758     switch (this->subtype) {
1759     case 2 :
1760       if (this->status == 0) {
1761 	if ((temp = find_empty(things)) > -1) {
1762 	  make_thing(temp,TOPHATSHIELD,0,this->x-30,this->y-30,0,things);
1763 	  this->status = 1;
1764 	}
1765       }
1766       bossvars[1] = this->x-37;
1767       bossvars[2] = this->y-37;
1768       if (bossvars[4] > 0) {
1769 	bossvars[4]--;
1770 	if (bossvars[4] == 20) bossvars[6] = 0;
1771       }
1772       if (bossvars[6] == 0 && bossvars[4] == 0 &&
1773 	  (temp = closest_thing(things, tcx, tcy, 0, NOTYPE)) > -1) {
1774 	bossvars[6] = temp+1;
1775 	bossvars[4] = 40;
1776       }
1777       bossvars[7] = bcx;
1778       bossvars[8] = bcy;
1779       if (abs(bcy-tcy) < SPR_SIZE*8)
1780 	this->vx = approach(this->vx, cap_val(bcx-tcx, RUN_SPEED), 1.5);
1781       if (bcx > tcx) this->dir = 1;
1782       else this->dir = 0;
1783       if (((this->collide[LEFT] && this->dir == 0) ||
1784 	   (this->collide[RIGHT] && this->dir == 1)) && !this->jump) {
1785 	if (bossvars[3] == 0) {
1786 	  this->vy = -8.25;
1787 	  this->anim = 4;
1788 	  bossvars[3] = 5;
1789 	} else bossvars[3]--;
1790       }
1791       break;
1792     case 1 :
1793       if (this->x > 600) this->vx = approach(this->vx, -WALK_SPEED, 1.5);
1794       if (bossvars[0] == 0 && bcx > this->x) {
1795 	bossvars[6] = 1;
1796 	bossvars[7] = this->x + 150;
1797 	bossvars[8] = this->y - 150;
1798 	bossvars[0] = 1;
1799 	bossvars[1] = 100;
1800 	this->dir = 1;
1801 	break;
1802       }
1803       if (bossvars[0] == 1) {
1804 	if (bossvars[1] > 0) bossvars[1]--;
1805 	else {
1806 	  this->vx = approach(this->vx, -WALK_SPEED, 1.5);
1807 	  this->dir = 0;
1808 	  if (this->x < 225 && !this->jump)
1809 	    destroy_thing(this, PIT, things, index);
1810 	}
1811       }
1812       break;
1813     case 0 :
1814       if (bossvars[0] == 0) {
1815 	if (this->x > 680) {
1816 	  this->vx = approach(this->vx, -RUN_SPEED, 1.5);
1817 	  this->dir = 0;
1818 	} else {
1819 	  if (!this->jump) {
1820 	    if (bossvars[1] == 0) {
1821 	      this->vy = -JUMP_SPEED;
1822 	      this->anim = 4;
1823 	      bossvars[1] = 5;
1824 	      bossvars[2] = 0;
1825 	    } else bossvars[1]--;
1826 	  }
1827 	}
1828       } else {
1829 	if (this->x < 765) {
1830 	  this->vx = approach(this->vx, RUN_SPEED, 1.5);
1831 	  this->dir = 1;
1832 	} else {
1833 	  if (!this->jump) {
1834 	    if (bossvars[1] == 0) {
1835 	      this->vy = -JUMP_SPEED;
1836 	      this->anim = 4;
1837 	      bossvars[1] = 5;
1838 	      bossvars[2] = 0;
1839 	    } else bossvars[1]--;
1840 	  }
1841 	}
1842       }
1843       if (bossvars[3] > 0) bossvars[3]--;
1844       if (bossvars[2] == 0 && bossvars[3] == 0) {
1845 	temp = closest_thing(things, bcx, bcy, 0, SOLIDBLOCK);
1846 	if ((things[temp].subtype < 2) == bossvars[0]) {
1847 	  bossvars[2] = 1;
1848 	  bossvars[3] = 20;
1849 	  bossvars[0] = !bossvars[0];
1850 	}
1851       }
1852       break;
1853     }
1854     // animation
1855     if (this->vx != 0) {
1856       this->anim += abs_f(this->vx)/6;
1857     } else if ((int)this->anim%4 != 0) {
1858       this->anim += .3;
1859     }
1860     // bossvars[6-8] are used for Top Hat's telekinesis:
1861     // bossvars[6]: index of telething + 1
1862     // bossvars[7]: x-pos of telekinesis
1863     // bossvars[8]: y-pos of telekinesis
1864     if (bossvars[6] > 0) {
1865       if (check_can_see(*this, things[bossvars[6]-1]) &&
1866 	  things[bossvars[6]-1].type != NOTYPE &&
1867 	  !things[bossvars[6]-1].telething) {
1868 	things[bossvars[6]-1].vx = approach(things[bossvars[6]-1].vx,
1869 					    cap_val((bossvars[7]-things[bossvars[6]-1].x+
1870 						     things[bossvars[6]-1].width/2)*
1871 						    TELEMODIF,TELECAP), TELEACCEL);
1872 	things[bossvars[6]-1].vy = approach(things[bossvars[6]-1].vy,
1873 					    cap_val((bossvars[8]-things[bossvars[6]-1].y+
1874 						     things[bossvars[6]-1].height/2)*
1875 						    TELEMODIF,TELECAP), TELEACCEL);
1876 	if (things[bossvars[6]-1].solid) {
1877 	  if ((things[bossvars[6]-1].collide[LEFT] && things[bossvars[6]-1].vx < 0) ||
1878 	      (things[bossvars[6]-1].collide[RIGHT] && things[bossvars[6]-1].vx > 0))
1879 	    things[bossvars[6]-1].vx = 0;
1880 	  if ((things[bossvars[6]-1].collide[UP] && things[bossvars[6]-1].vy < 0) ||
1881 	      (things[bossvars[6]-1].collide[DOWN] && things[bossvars[6]-1].vy > 0))
1882 	    things[bossvars[6]-1].vy = 0;
1883 	}
1884       } else {
1885 	bossvars[6] = 0;
1886       }
1887     }
1888     break;
1889   case MATTERLY :
1890     if (this->timer % 250 == 150)
1891       bossvars[2] = 1;
1892     else if (this->timer % 250 == 0) bossvars[2] = 0;
1893     if (bossvars[0] == 0) bossvars[1] = index;
1894     if (bossvars[0] < 20 && this->timer % 10 == 0) {
1895       bossvars[0]++;
1896       if ((temp = find_empty(things)) > -1) {
1897 	angle = (temp*6 + this->timer % 90) * PI/45;
1898 	temp3 = 60;
1899 	make_thing(temp, MATTERFRAG, bossvars[0] % 3 + 3,
1900 		   this->x+9+cos(angle)*temp3,
1901 		   this->y+9+sin(angle)*temp3, 0, things);
1902 	make_expl(this->x+9+cos(angle)*temp3,this->y+9+sin(angle)*temp3,
1903 		  0,0,PURPLE,3,20);
1904       } else bossvars[0] = 30;
1905     }
1906     if (this->timer % 250 > 200 && this->timer % 250 < 220) {
1907       this->vx = approach(this->vx, cap_val(beret->x-this->x,6),0.75);
1908       this->vy = approach(this->vy, cap_val(beret->y-this->y,6),0.75);
1909       this->dir = beret->x > this->x;
1910     } else if (this->timer % 30 == 0) this->vy += 1.5;
1911     else if (this->timer % 30 == 15) this->vy -= 1.5;
1912     this->timer++;
1913     break;
1914   case BLOCKSTER :
1915     if (bossvars[0] == 1) {
1916       bossvars[0] = bossvars[4] + ((int)(beret->x) % 100);
1917       bossvars[2] = bossvars[1];
1918     } else if (bossvars[0] == 0 && beret->x > this->x - 450) {
1919       bossvars[0] = 1700;
1920       bossvars[4] = 1500;
1921       bossvars[1] = 2;
1922     }
1923     if (bossvars[2] > 0) {
1924       if (bossvars[3] > 11 || (this->status == 0 &&
1925         (this->timer + index) % 3 == 0)) {
1926         bossvars[2]--;
1927         this->status = 1;
1928         this->dir = 0;
1929         // Pseudorandomize this variable.
1930         this->timer += bossvars[2] + beret->x + beret->y;
1931       } else {
1932         this->timer--;
1933       }
1934     }
1935     if (this->status == 1) {
1936       // Get the subtype of the spiked block, also pseudorandomly.
1937       temp2 = ((this->timer+(int)(beret->x))%2) +
1938         ((this->timer%3 == 0 || bossvars[3] > 11) ? 0 : 2);
1939       if (this->anim < 4) {
1940 	this->anim += 0.3;
1941 	if (bossvars[7] == 0) {
1942 	  play_sound(SND_FAKE);
1943 	  bossvars[7] = 1;
1944 	}
1945       } else {
1946         if (this->dir < 35) {
1947           this->dir++;
1948           if (this->dir % 6 == 0)
1949             make_expl(this->x-15, this->y+15, 0, 0,
1950                       (temp2 < 2 ? WHITE : RED), 4, 40);
1951         } else {
1952           make_expl(this->x-15, this->y+15, 0, 0, PURPLE, 5, 75);
1953           if ((temp = find_empty(things)) > -1) {
1954 	    play_sound(SND_REGENINIT);
1955             make_thing(temp, SPIKEBLOCK, temp2,
1956                        this->x-30,this->y,0,things);
1957             things[temp].vx = -BLOCKSTER_SHOOT_SPEED;
1958             things[temp].vy = -BLOCKSTER_SHOOT_VSPEED;
1959           }
1960           this->status = 0;
1961 	  bossvars[7] = 0;
1962         }
1963       }
1964     } else if (this->anim > 0) this->anim -= 0.3;
1965     if (bossvars[0] > 0) bossvars[0]--;
1966     break;
1967   case STONEY :
1968     if (this->subtype == 2) {
1969       this->vx = approach(this->vx, 3*pos_f(bcx - tcx), 0.4);
1970       this->vy = approach(this->vy, 3*pos_f(bcy - tcy), 0.4);
1971       if (tcx > bcx) this->dir = 0;
1972       else this->dir = 1;
1973     } else if (this->subtype == 1) {
1974       if (bossvars[0] >= 35 && bossvars[0] < 75) {
1975 
1976         this->vx = approach(this->vx, 6*pos_f(bcx - tcx), 0.6);
1977         this->vy = approach(this->vy, 6*pos_f(bcy - tcy), 0.6);
1978         if (tcx > bcx) this->dir = 0;
1979         else this->dir = 1;
1980 
1981       } else if (bossvars[0] >= 150 && this->status == 0) {
1982         this->status = 1;
1983       }
1984 
1985       bossvars[0]++;
1986 
1987       if (this->telething) this->status = 1;
1988       if (this->status == 1) {
1989         if (this->anim >= 2.5) {
1990 
1991           temp3 = closest_thing(things, bcx, bcy, 0, STONEY);
1992           if (temp3 > -1) {
1993             bossvars[0] = 0;
1994             things[temp3].subtype = 1;
1995             things[temp3].anim = 2.5;
1996             things[temp3].status = -1;
1997             this->subtype = 0;
1998             this->status = 0;
1999             this->anim = index%2;
2000           }
2001         } else this->anim += 0.3;
2002       } else if (this->status == -1) {
2003         if (this->anim <= 0) this->status = 0;
2004         else this->anim -= 0.3;
2005 	if (this->anim < 0) this->anim = 0;
2006       }
2007     } else {
2008       this->anim = index%2;
2009       this->vx = approach(this->vx, 0.3*pos_f(bcx - tcx), 0.18);
2010       this->vy = approach(this->vy, 0.3*pos_f(bcy - tcy), 0.18);
2011     }
2012     if (((this->timer+index*17) % 30) == 0) this->vy += 1.3;
2013     else if (((this->timer+index*17) % 30) == 15) this->vy -= 1.3;
2014     fix_vel(this);
2015     this->timer++;
2016     break;
2017   case GHOST :
2018     temp3 = sqrt(this->vx*this->vx + this->vy*this->vy);
2019     if (temp3 >= GHOST_TOO_FAST) {
2020       this->anim = 0;
2021       this->solid = 1;
2022     } else {
2023       this->solid = 0;
2024       temp = sqrt((bcx-tcx)*(bcx-tcx) + (bcy-tcy)*(bcy-tcy));
2025       if (temp < GHOST_CHASE_DIST) {
2026         temp2 = bcx-tcx;
2027         this->vx = approach(this->vx,
2028                             (abs_f(temp2) > 12 ? 1.5*pos_f(temp2) : 0) +
2029                             ((this->timer + index * 17) % 40 < 20 ? 1 : -1),
2030                             0.6);
2031         temp2 = beret->y - tcy;
2032         this->vy = approach(this->vy,
2033                             (abs_f(temp2) > 12 ? 2.5*pos_f(temp2) : 0) +
2034                             ((this->timer + index * 13) % 40 < 20 ? 1 : -1),
2035                             0.6);
2036         if (bcx > tcx) this->dir = 1;
2037         else this->dir = 0;
2038       }
2039       if (temp >= GHOST_DIST) this->anim = 3;
2040       else this->anim = 2-((GHOST_DIST-temp)/30);
2041       if (this->anim < 0) this->anim = 0;
2042     }
2043     this->timer++;
2044     break;
2045   case ANTISEEKER :
2046       this->vx = approach(this->vx, 3.5*pos_f(bcx - tcx), 0.5);
2047       this->vy = approach(this->vy, 3.5*pos_f(bcy - tcy), 0.5);
2048       if (tcx > bcx) this->dir = 0;
2049       else this->dir = 1;
2050     break;
2051   case TELESEEKER :
2052     if (this->subtype == 3) {
2053       this->vx = approach(this->vx, 3.5*pos_f(tcx - bcx), 0.5);
2054       this->vy = approach(this->vy, 3.5*pos_f(tcy - bcy), 0.5);
2055       if (bcx > tcx) this->dir = 0;
2056       else this->dir = 1;
2057     } else if ((teleused && this->subtype == 0) ||
2058         (!teleused && this->subtype == 1) ||
2059         this->subtype == 2) {
2060       this->anim = 2;
2061       float dx = bcx - tcx;
2062       float dy = bcy - tcy;
2063       if (abs(dx) > 6) this->vx = approach(this->vx, 3.5*pos_f(dx), 0.5);
2064       if (abs(dy) > 6) this->vy = approach(this->vy, 3.5*pos_f(dy), 0.5);
2065       if (bcx > tcx) this->dir = 1;
2066       else if (bcx < tcx) this->dir = 0;
2067     } else {
2068       int blinkcheck = (this->timer+index*13)%75;
2069       if (blinkcheck < 4) this->anim = 2;
2070       else if (blinkcheck < 8 || blinkcheck > 70) this->anim = 1;
2071       else this->anim = 0;
2072       if (((this->timer+index*17) % 30) == 0) this->vy += 2;
2073       else if (((this->timer+index*17) % 30) == 15) this->vy -= 2;
2074     }
2075     fix_vel(this);
2076     this->timer++;
2077     break;
2078   case FAKEBOMB :
2079     move_fake(this, 2, 8, bcx, bcy, tcx, tcy);
2080     break;
2081   case FAKEBLOCK :
2082     move_fake(this, 3, 2.3, bcx, bcy, tcx, tcy);
2083     break;
2084   case GHOSTBLOCK :
2085     if (abs(this->vx) < 2 && abs(this->vy) < 2) {
2086       this->solid = 1;
2087       this->anim = approach(this->anim, 0, 0.3);
2088     } else {
2089       this->solid = 0;
2090       this->anim = approach(this->anim, 2, 0.3);
2091     }
2092     break;
2093   case SPIKEBALL :
2094     if (this->status == 0) this->anim += 0.3;
2095     else this->anim -= 0.3;
2096     if (this->anim <= 0) this->status = 0;
2097     else if (this->anim >= 2.4) this->status = 1;
2098     if (!this->telething) {
2099       if (this->subtype == 0) move_bnf(this, 0, 4, 0.3);
2100       else move_bnf(this, 1, 4, 0.3);
2101     } else this->timer = 0;
2102     break;
2103   case CARRIER :
2104     this->anim += 0.3;
2105     if (this->anim >= 2) this->anim = 0;
2106     if (!this->jump && !this->telething) move_bnf(this, 0, 4.5, 1.5);
2107     else this->timer = 0;
2108     break;
2109   case ANTIFECTLING :
2110   case INFECTLING :
2111     if (this->timer > 0) this->timer--;
2112     break;
2113   case STICKYBOMB :
2114     if (this->status > 0) {
2115       if (this->link != -1) {
2116         Thing *stickto = this->link == -2 ?
2117           beret : &things[this->link];
2118         if (stickto->dead || stickto->type == NOTYPE || !stickto->solid ||
2119             stickto->type == INFECTLING || stickto->type == STICKYBOMB)
2120             destroy_thing(this, BOMBED, things, index);
2121         this->x = stickto->x+this->stickx;
2122         this->y = stickto->y+this->sticky;
2123       } else {
2124         this->x = this->stickx;
2125         this->y = this->sticky;
2126       }
2127       if (this->timer == 375) destroy_thing(this, BOMBED, things, index);
2128       this->timer++;
2129       temp = this->timer < 300 ? 30 : 5;
2130       if (this->timer % temp == 0) play_sound(SND_TICK+rand_to(3));
2131     }
2132     break;
2133   case AURAHELPER :
2134     if (this->status == 0) {
2135       if (things[this->link].type == AURADROP) {
2136         this->x = things[this->link].x-60;
2137         this->y = things[this->link].y-60;
2138       }
2139     } else {
2140       this->vx = approach(this->vx, cap_val(this->startx-(this->x+this->width/2),6),1);
2141       this->vy = approach(this->vy, cap_val(this->starty-(this->y+this->height/2),6),1);
2142     }
2143     break;
2144   case BOMBHELPER :
2145     if (this->timer == (this->subtype == 0 ? 8 :
2146                         (this->subtype == 1 ? 18 :
2147                          (this->subtype == 2 ? 30 : 8))))
2148       destroy_thing(this, PIT, things, index);
2149     this->x -= 6;
2150     this->y -= 6;
2151     this->width += 12;
2152     this->height += 12;
2153     temp = (this->subtype == 0 ? ORANGE :
2154              (this->subtype == 1 ? RED :
2155               (this->subtype == 2 ? GRYELLOW : BLUE)));
2156     temp2 = (this->subtype == 0 ? BROWN :
2157              (this->subtype == 1 ? YELLOW :
2158               (this->subtype == 2 ? GREEN : AQUA)));
2159     if (this->timer % 3 < 2)
2160       make_circle(tcx, tcy, temp, temp2, this->width/2, 200);
2161     this->timer++;
2162     break;
2163   case AURADROP :
2164     if (this->status == 0 && (temp = find_empty(things)) > -1) {
2165       make_thing(temp, AURAHELPER,0,this->x-60,this->y-60,0,things);
2166       things[temp].link = index;
2167       this->status = 1;
2168       this->link = temp;
2169     }
2170     if (!this->jump && !this->telething) {
2171       move_bnf(this, 0, 2.5, 1);
2172     } else this->timer = 0;
2173     this->anim += 0.2;
2174     if (this->anim >= 4) this->anim = 0;
2175     fix_vel(this);
2176     break;
2177   case ROBOT :
2178     if (!this->jump && !this->telething) {
2179       move_bnf(this, 0, this->subtype?4.5:2.3, 1);
2180     } else this->timer = 0;
2181     this->anim += 0.3;
2182     if (this->anim >= 3) this->anim = 0;
2183     fix_vel(this);
2184     break;
2185   case HOPPER :
2186     if (bcx > tcx) this->dir = 1;
2187     else if (bcx < tcx) this->dir = 0;
2188     if (!this->telething && this->islinked == -1) {
2189       if (!this->jump) {
2190         if (this->status >= 12 && !this->jumpdelay) {
2191           this->status = 0;
2192           this->vy = (this->subtype?-8.5:-5.25);
2193           this->jump = 1;
2194 	  if (on_screen(this->x, this->y, this->width, this->height))
2195 	    play_sound(SND_HOP+rand_to(3));
2196           this->vx = (this->dir?1:-1)*(this->subtype?4:3.25);
2197         } else if (++this->status > 12) this->status = 0;
2198       } else if (this->vx == 0 && this->vy <= 0) {
2199         this->vx = (this->dir?2.5:-2.5);
2200       }
2201       fix_vel(this);
2202     }
2203     break;
2204   case ANNOYINGBLOCK :
2205     if (this->subtype == 0) {
2206       this->vx = approach(this->vx, cap_val(bcx - tcx,7.5), 2.5);
2207     } else {
2208       this->vy = approach(this->vy, cap_val(bcy - tcy,7.5), 2.5);
2209     }
2210     fix_vel(this);
2211     break;
2212   case TELEBLOCK :
2213     if (teleused) {
2214       if (this->status < 5) this->status++;
2215       this->solid = 1;
2216     } else {
2217       if (this->status > 0) this->status--;
2218       this->solid = 0;
2219     }
2220     break;
2221   case LINKBLOCK :
2222     if (things[this->link].type == NOTYPE) this->link = -1;
2223     if (this->link > -1) {
2224       switch (this->subtype) {
2225       case 0 :
2226         things[this->link].vx=approach(things[this->link].vx,
2227                                        cap_val(things[this->link].startx+this->x
2228                                                -this->startx-things[this->link].x,LINKSPEED),LINKACCEL);
2229         things[this->link].vy=approach(things[this->link].vy,
2230                                        cap_val(things[this->link].starty+this->y
2231                                                -this->starty-things[this->link].y,LINKSPEED),LINKACCEL);
2232         fix_vel(&things[this->link]);
2233         break;
2234       case 1 :
2235         if (things[this->link].type != NOTYPE) {
2236           this->vx=approach(this->vx, cap_val(-things[this->link].startx-this->x
2237                                               +this->startx+things[this->link].x,LINKSPEED), LINKACCEL);
2238           this->vy=approach(this->vy, cap_val(-things[this->link].starty-this->y
2239                                               +this->starty+things[this->link].y,LINKSPEED), LINKACCEL);
2240           fix_vel(this);
2241         } else {
2242           this->islinked = -1;
2243         }
2244         break;
2245       case 2 :
2246         things[this->link].vx=approach(things[this->link].vx,
2247                                        cap_val(things[this->link].startx-this->x
2248                                                +this->startx-things[this->link].x,LINKSPEED),LINKACCEL);
2249         things[this->link].vy=approach(things[this->link].vy,
2250                                        cap_val(things[this->link].starty-this->y
2251                                                +this->starty-things[this->link].y,LINKSPEED),LINKACCEL);
2252         fix_vel(&things[this->link]);
2253         break;
2254       case 3 :
2255         if (things[this->link].type != NOTYPE) {
2256           this->vx=approach(this->vx,cap_val(things[this->link].startx-this->x
2257                                              +this->startx-things[this->link].x,LINKSPEED),LINKACCEL);
2258           this->vy=approach(this->vy,cap_val(things[this->link].starty-this->y
2259                                              +this->starty-things[this->link].y,LINKSPEED),LINKACCEL);
2260           fix_vel(this);
2261         } else {
2262           this->islinked = -1;
2263         }
2264         break;
2265       }
2266     }
2267     break;
2268   }
2269 }
2270 
2271 
check_crash(Thing * this,Thing * other,int oindex,float vel,int crashtype,Thing things[250],int * gravdir,int * switchflags,int thisindex,int playsound)2272 void check_crash(Thing* this, Thing* other, int oindex, float vel,
2273                  int crashtype, Thing things[250], int *gravdir,
2274                  int *switchflags, int thisindex, int playsound) {
2275 
2276   // Let infection take priority over crashing.
2277   if (other != NULL && other->type == INFECTLING) {
2278   if ((this->type != INFECTLING ||
2279        this->subtype != 1) && this->type != ANTIFECTLING &&
2280       (other->subtype == 1 || this->animate || other->type == ANTIFECTLING) &&
2281       other->timer == 0 && !this->dead && this->solid &&
2282       !(this->type == TOPHAT && bossvars[0] < SHIELDGENNUM) &&
2283       !(this->type == FIREBALL) &&
2284       !(other->type == ANTIFECTLING && this->type != ANTIMATTER &&
2285 	this->type != MATTERLY && this->type != ANTISEEKER)) {
2286     return;
2287   }
2288   }
2289 
2290   if (!(*switchflags & CREATORFLAG)) {
2291     if (this->crashspeed > -1 && vel > this->crashspeed) {
2292       destroy_thing(this, crashtype, things, thisindex);
2293     }
2294 
2295     // Sound effect handling for collisions.
2296     if (playsound && vel > 8 && !(other && other->type == FIREBALL) &&
2297 	on_screen(this->x, this->y, this->width, this->height) &&
2298 	((crashtype == CRASHU && this->vy < 0 &&
2299 	  abs_f(this->vy) > abs_f(this->vx)) ||
2300 	 (crashtype == CRASHD && this->vy > 0 &&
2301 	  abs_f(this->vy) > abs_f(this->vx)) ||
2302 	 (crashtype == CRASHL && this->vx < 0 &&
2303 	  abs_f(this->vx) > abs_f(this->vy)) ||
2304 	 (crashtype == CRASHR && this->vx > 0 &&
2305 	  abs_f(this->vx) > abs_f(this->vy)))) {
2306       if (this->type == WOODBLOCK || this->type == SMWOODBLOCK ||
2307 	  (this->type >= WHITEDIE && this->type <= LINKBLOCK) ||
2308 	  (this->type == FAKEBLOCK && this->subtype == 0)) play_sound(SND_KNOCK+rand_to(3));
2309       else if (this->type == ICEBLOCK || this->type == STONEBLOCK ||
2310 	       this->type == SMICEBLOCK || this->type == SMSTONEBLOCK ||
2311 	       (this->type == FAKEBLOCK && this->subtype > 0))
2312 	play_sound(SND_CLINK+rand_to(3));
2313     }
2314 
2315     switch (this->type) {
2316     case SOLIDSWITCH :
2317       if (vel > 2.3 && this->timer == 0 && (!other || (other->type != GHOST &&
2318                                                        other->type != ANTIMATTER))) {
2319         this->status = !this->status;
2320         *switchflags ^= (this->subtype?REDFLAG:GREENFLAG);
2321 	play_sound((this->subtype?SND_SWITCHRD:SND_SWITCHGR)+rand_to(3));
2322         if (!this->collide[(crashtype-CRASHU+2) % 4] &&
2323             abs_f(this->x - this->startx) < SWITCH_BOUNCE_ERROR &&
2324             abs_f(this->y - this->starty) < SWITCH_BOUNCE_ERROR &&
2325             other) {
2326           if (crashtype == CRASHU || crashtype == CRASHD)
2327             this->vy = cap_val(other->vy, 6);
2328           else this->vx = cap_val(other->vx, 6);
2329         }
2330         this->timer = 22;
2331       }
2332       break;
2333     case GRAVITYSWITCH :
2334       if (vel > 2.3 && this->timer == 0 && (!other || (other->type != GHOST && other->type != ANTIMATTER))) {
2335         this->anim += (this->subtype ? 1 : -1);
2336         if (this->anim < UP) this->anim = LEFT;
2337         if (this->anim > LEFT) this->anim = UP;
2338         *gravdir = this->anim;
2339 	play_sound(SND_SWITCHGV+rand_to(3));
2340         if (!this->collide[(crashtype-CRASHU+2) % 4] &&
2341             abs_f(this->x - this->startx) < SWITCH_BOUNCE_ERROR &&
2342             abs_f(this->y - this->starty) < SWITCH_BOUNCE_ERROR &&
2343             other) {
2344           if (crashtype == CRASHU || crashtype == CRASHD)
2345             this->vy = cap_val(other->vy, 6);
2346           else this->vx = cap_val(other->vx, 6);
2347         }
2348         this->timer = 22;
2349       }
2350       break;
2351     case STICKYBOMB :
2352       if (this->status == 0 && vel > 2.6) {
2353         this->islinked = -1;
2354         this->solid = 0;
2355         this->floating = 1;
2356         this->pickup = 0;
2357         this->infront = 1;
2358         this->status = crashtype;
2359 	play_sound(SND_STICK);
2360         if (other != NULL) {
2361           this->stickx = this->x-other->x;
2362           this->sticky = this->y-other->y;
2363           if (other->type == BERET) this->link = -2;
2364           else this->link = oindex;
2365         } else {
2366           this->stickx = this->x;
2367           this->sticky = this->y;
2368         }
2369       }
2370       break;
2371     case PLATFORM :
2372       // status = 2 means a clip that shouldn't activate the platform was detected
2373       if (this->status == 0 && crashtype == CRASHU && other && other->vy < 0) this->status = 2;
2374       break;
2375     }
2376   }
2377 }
2378 
2379 
collide_sides(Thing * this,int lvlWidth,int lvlHeight)2380 void collide_sides(Thing* this, int lvlWidth, int lvlHeight) {
2381   float thisl = this->x, thist = this->y;
2382   int thisw = this->width, thish = this->height;
2383   float thisr = thisl+thisw, thisb = thist+thish;
2384   if (thisl <= 0) {
2385     this->collide[LEFT] = 1;
2386     this->colltarget[LEFT] = 0;
2387   }
2388   if (thisr >= lvlWidth) {
2389     this->collide[RIGHT] = 1;
2390     this->colltarget[RIGHT] = lvlWidth-thisw;
2391   }
2392   if (thist <= 0) {
2393     this->collide[UP] = 1;
2394     this->colltarget[UP] = -1;
2395   }
2396   if (thisb >= lvlHeight) {
2397     this->collide[DOWN] = 1;
2398     this->colltarget[DOWN] = lvlHeight-thish;
2399   }
2400 }
2401 
2402 
update_thing(Thing * this,int tiles[500][500][3],Thing things[250],Thing * beret,int lvlWidth,int lvlHeight,int index,int * gravdir,int * switchflags,int teleused)2403 void update_thing(Thing* this, int tiles[500][500][3],
2404                   Thing things[250], Thing* beret,
2405                   int lvlWidth, int lvlHeight, int index,
2406                   int *gravdir, int *switchflags, int teleused) {
2407   int i, j;
2408 
2409   // move in small, bite-sized steps
2410   float greater = abs(this->vx) > abs(this->vy) ? this->vx : this->vy;
2411   int stepsize = abs(greater)/CORNER + 1;
2412   if (this->width < SMALLSIZE || this->height < SMALLSIZE) stepsize *= 2;
2413   for (i = 0; i < stepsize; i++) {
2414     this->x += this->vx/stepsize;
2415     this->y += this->vy/stepsize;
2416     if (this->vx == 0 && this->vy == 0) this->nomove = 1;
2417     else this->nomove = 0;
2418     if (!(*switchflags & CREATORFLAG && *switchflags & NOCOLLIDEFLAG)) {
2419       get_collisions(this, tiles, things, beret, thingnodes,
2420                      lvlWidth, lvlHeight, index, gravdir, switchflags);
2421       apply_collisions(this, gravdir, things, switchflags, index);
2422     } else {
2423       for (j = 0; j < 4; j++) {
2424         this->collide[j] = 0;
2425       }
2426       collide_sides(this, lvlWidth, lvlHeight);
2427       apply_collisions(this, gravdir, things, switchflags, index);
2428     }
2429   }
2430 
2431   if (!(*switchflags & CREATORFLAG)) {
2432     special_thing(this, things, beret, gravdir, teleused,
2433                   index, switchflags);
2434 
2435     if (this->ontele > -1) {
2436       clear_floorchecked();
2437       check_ontele(this, thingnodes[index], index);
2438     }
2439 
2440     if (this->jumpdelay > 0) this->jumpdelay--;
2441 
2442     if (this->islinked > -1 && this->type != LINKBLOCK &&
2443         (things[this->islinked].link != index || things[this->islinked].type != LINKBLOCK))
2444       this->islinked = -1;
2445 
2446     if (this->teledelay > 0) this->teledelay--;
2447 
2448     // deal with gravity
2449     int mygravdir = *gravdir;
2450     if (this->islinked == -1 && this->gravmod != NONE)
2451       mygravdir = this->gravmod;
2452     if (!this->collide[mygravdir] && !this->floating &&
2453         !this->telething && this->islinked == -1) {
2454       switch (mygravdir) {
2455       case DOWN: this->vy += GRAVITY; break;
2456       case LEFT: this->vx -= GRAVITY; break;
2457       case RIGHT: this->vx += GRAVITY; break;
2458       case UP: this->vy -= GRAVITY; break;
2459       }
2460     }
2461 
2462     // deal with friction
2463     if (!this->telething && !this->nomove && this->islinked == -1) {
2464       if (mygravdir == DOWN || mygravdir == UP || this->floating)
2465         this->vx = apply_friction((!this->collide[mygravdir] && !this->floating)?FRICTMOD:
2466                                   (this->icy?ICYMOD:(this->floating?FLOATMOD:FRICT)), this->vx);
2467       if (mygravdir == LEFT || mygravdir == RIGHT || this->floating)
2468         this->vy = apply_friction((!this->collide[mygravdir] && !this->floating)?FRICTMOD:
2469                                   (this->icy?ICYMOD:(this->floating?FLOATMOD:FRICT)), this->vy);
2470     }
2471   } else {
2472     this->vx = apply_friction(2, this->vx);
2473     this->vy = apply_friction(2, this->vy);
2474   }
2475 }
2476 
2477 
2478 // update Beret's status
handle_key_input(int key1,int key2,int key3,Thing * this,int tiles[500][500][3],Thing things[250],int lvlWidth,int lvlHeight,int * gravdir,int * switchflags,int walkaway,int runningmode)2479 void handle_key_input(int key1, int key2, int key3,
2480                       Thing* this, int tiles[500][500][3],
2481                       Thing things[250], int lvlWidth, int lvlHeight,
2482                       int *gravdir, int *switchflags, int walkaway,
2483 		      int runningmode) {
2484   int moving = 0;
2485   int i = 0;
2486 
2487   // set the flag to say whether the user is pressing an important key
2488   if (key1 != NONE || key2 != NONE) *switchflags |= KEYPRESSED;
2489   else *switchflags &= ~KEYPRESSED;
2490 
2491   // move in small, bite-sized steps
2492   float greater = abs(this->vx) > abs(this->vy) ? this->vx : this->vy;
2493   int stepsize = abs(greater)/CORNER + 1;
2494   for (i = 0; i < stepsize; i++) {
2495     this->x += this->vx/stepsize;
2496     this->y += this->vy/stepsize;
2497     if (this->vx == 0 && this->vy == 0) this->nomove = 1;
2498     else this->nomove =0;
2499     if (!walkaway) {
2500       get_collisions(this, tiles, things, 0, thingnodes, lvlWidth, lvlHeight,
2501                      250, gravdir, switchflags);
2502       apply_collisions(this, gravdir, things, switchflags, -1);
2503     }
2504   }
2505 
2506   if (this->ontele > -1) {
2507     clear_floorchecked();
2508     check_ontele(this, thingnodes[250], 250);
2509   }
2510 
2511   // detect double tapping or shift for running
2512   if (runningmode) {
2513     runMargin = 0;
2514     leftTime = 0;
2515     rightTime = 0;
2516     if (key3 == SHIFT) {
2517       if (!holdShift) shiftRun = !shiftRun;
2518       holdShift = 1;
2519       this->speed = WALK_SPEED;
2520     } else {
2521       holdShift = 0;
2522       this->speed = RUN_SPEED;
2523     }
2524     //    if (shiftRun) {
2525     //      this->speed = RUN_SPEED;
2526     //    } else {
2527     //      this->speed = WALK_SPEED;
2528     //    }
2529   } else {
2530     holdShift = 0;
2531     shiftRun = 0;
2532     if (key1 == LEFT) {
2533       if (leftTime > 0 && leftTime < TAP_TIME) {
2534 	this->speed = RUN_SPEED;
2535 	runMargin = RUNMARGIN;
2536       }
2537       leftTime = 0;
2538     } else {
2539       if (this->vx >= -WALK_SPEED && this->dir == 0) {
2540 	if (runMargin > 0) runMargin--;
2541 	else this->speed = WALK_SPEED;
2542       }
2543       leftTime++;
2544     }
2545     if (key1 == RIGHT) {
2546       if (rightTime > 0 && rightTime < TAP_TIME) {
2547 	this->speed = RUN_SPEED;
2548 	runMargin = RUNMARGIN;
2549       }
2550       rightTime = 0;
2551     } else {
2552       if (this->vx <= WALK_SPEED && this->dir == 1) {
2553 	if (runMargin > 0) runMargin--;
2554 	else this->speed = WALK_SPEED;
2555       }
2556       rightTime++;
2557     }
2558   }
2559 
2560   if (walkaway == 1) {
2561     this->vx = approach(this->vx, -this->speed, ACCEL);
2562     return;
2563   } else if (walkaway == 2) {
2564     this->vx = approach(this->vx, this->speed, ACCEL);
2565     return;
2566   }
2567 
2568   // deal with jumping
2569   if (key2 == UP) {
2570     upTime++;
2571   } else {
2572     upTime = 0;
2573   }
2574   int jumpcheck = upTime < 6 && !this->jumpdelay &&
2575     !this->status && !this->jump;
2576   if (this->jump) {
2577     if (this->vy < 0) {
2578       if (key2 == UP) {
2579         this->vy += GRAVITY*0.5;
2580       } else {
2581         this->vy += GRAVITY*1.75;
2582       }
2583     } else {
2584       this->vy += GRAVITY;
2585     }
2586   }
2587   if (jumpcheck) jumpGrace = JUMPGRACE;
2588   if (key2 == UP && !this->collide[UP] && (jumpcheck || jumpGrace > 0)) {
2589     this->vy = -JUMP_SPEED;
2590     play_sound(SND_JUMP+rand_to(3));
2591     this->jump = 1;
2592     this->anim = 4;
2593     upTime = 6;
2594     jumpGrace = 0;
2595   }
2596   if (this->jumpdelay > 0) this->jumpdelay--;
2597   if (jumpGrace > 0) jumpGrace--;
2598 
2599   // handle movement
2600   if (key1 == LEFT) {
2601     if (this->vx > -this->speed) {
2602       this->vx = approach(this->vx, -this->speed, ACCEL);
2603       moving = 1;
2604     }
2605     this->dir = 0;
2606   } else if (key1 == RIGHT) {
2607     if (this->vx < this->speed) {
2608       this->vx = approach(this->vx, this->speed, ACCEL);
2609       moving = 1;
2610     }
2611     this->dir = 1;
2612   }
2613 
2614   // deal with animation
2615   if (this->vx != 0) {
2616     float anim_chg = abs_f(this->vx)/6;
2617     this->anim += anim_chg;
2618     int anim_int = (int)this->anim;
2619     if (anim_int % 4 == 0 && (this->anim - anim_int) < anim_chg && !this->jump)
2620       play_sound(SND_STEP+rand_to(3));
2621   } else if ((int)this->anim % 4 != 0) {
2622     this->anim += .3;
2623   }
2624 
2625   // apply friction if not walking and on floor
2626   if (!moving) {
2627     this->vx = apply_friction(BERETFRICT*(this->jump?FRICTMOD:1), this->vx);
2628   }
2629 }
2630