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