5 static Object
*FireSimpleBullet(int otype
, int btype
, int xoff
=0, int yoff
=0);
6 static int empty_timer
= 0;
10 int sprite
; // sprite to use
11 int level
; // specify what level weapon is at when it fires this shot type
12 int frame
; // specify which frame within sprite
13 uint8_t makes_star
; // 1=make star effect, 2=make star but add x inertia to position
14 int timetolive
; // shot range
15 int damage
; // damage dealt per tick of contact with enemy
16 int speed
; // speed of shot
17 uint8_t manualsetup
; // 1= no auto setup at all, 2= don't use separate vert sprite
18 uint8_t sound
; // specify firing sound
21 BulletInfo bullet_table
[] =
23 // sprite lvl frm st ttl dmg spd manset sound
24 SPR_SHOT_POLARSTAR
, 0, 0, 1, 8, 1, 0x1000, 0, SND_POLAR_STAR_L1_2
, // polarstar l1
25 SPR_SHOT_POLARSTAR
, 1, 1, 1, 12, 2, 0x1000, 0, SND_POLAR_STAR_L1_2
, // polarstar l2
26 SPR_SHOT_POLARSTAR_L3
, 2, 0, 1, 16, 4, 0x1000, 0, SND_POLAR_STAR_L3
, // polarstar l3
28 SPR_SHOT_MGUN_L1
, 0, 0, 1, 20, 2, 0x1000, 0, SND_POLAR_STAR_L1_2
, // mgun l1
30 SPR_SHOT_MGUN_L2
, 1, 0, 1, 20, 4, 0x1000, 0, SND_POLAR_STAR_L1_2
, // mgun l2, white piece
31 SPR_SHOT_MGUN_L2
, 1, 1, 0, 21, 0, 0x1000, 0, 0, // mgun l2, blue piece
32 SPR_SHOT_MGUN_L2
, 1, 2, 0, 22, 0, 0x1000, 0, 0, // mgun l2, dark piece
34 SPR_SHOT_MGUN_L3LEAD
, 2, 0, 1, 20, 6, 0x1000, 0, SND_POLAR_STAR_L3
, // mgun l3
35 SPR_SHOT_MGUN_L3TAIL
, 2, 0, 0, 21, 0, 0x1000, 0, 0, // the very long...
36 SPR_SHOT_MGUN_L3TAIL
, 2, 1, 0, 22, 0, 0x1000, 0, 0, // ...4 piece trail...
37 SPR_SHOT_MGUN_L3TAIL
, 2, 2, 0, 23, 0, 0x1000, 0, 0, // ...of the level 3...
38 SPR_SHOT_MGUN_L3TAIL
, 2, 3, 0, 24, 0, 0x1000, 0, 0, // ...machine gun
40 // damage for missiles is set inside missile.cpp
41 SPR_SHOT_MISSILE1
, 0, 0, 1, 50, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // missile level 1
42 SPR_SHOT_MISSILE2
, 1, 0, 1, 65, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // missile level 2
43 SPR_SHOT_MISSILE3
, 2, 0, 1, 90, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // missile level 3
45 SPR_SHOT_SUPERMISSILE13
,0, 0, 1, 30, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // supermissile l1
46 SPR_SHOT_SUPERMISSILE2
, 1, 0, 1, 40, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // supermissile l2
47 SPR_SHOT_SUPERMISSILE13
,2, 0, 1, 40, 0, 0x0000, 0, SND_POLAR_STAR_L1_2
, // supermissile l3
49 // damages are doubled because fireball can hit twice before dissipating
50 SPR_SHOT_FIREBALL1
, 0, 0, 1, 100, 2, 0x0000, 1, SND_FIREBALL
, // fireball l1
51 SPR_SHOT_FIREBALL23
, 1, 0, 1, 100, 3, 0x0000, 1, SND_FIREBALL
, // fireball l2
52 SPR_SHOT_FIREBALL23
, 2, 0, 1, 100, 3, 0x0000, 1, SND_FIREBALL
, // fireball l3
54 SPR_SHOT_BLADE_L1
, 0, 0, 0, 29, 15, 0x800, 0, SND_FIREBALL
, // Blade L1
55 SPR_SHOT_BLADE_L2
, 1, 0, 0, 17, 6, 0x800, 0, SND_FIREBALL
, // Blade L2
56 SPR_SHOT_BLADE_L3
, 2, 0, 0, 30, 1, 0x800, 0, SND_FIREBALL
, // Blade L3
58 SPR_SHOT_SNAKE_L1
, 0, 0, 1, 20, 4, 0x600, 2, SND_SNAKE_FIRE
, // Snake L1
59 SPR_SHOT_FIREBALL23
, 1, 0, 1, 23, 6, 0x200, 2, SND_SNAKE_FIRE
, // Snake L2
60 SPR_SHOT_FIREBALL23
, 2, 0, 1, 30, 8, 0x200, 2, SND_SNAKE_FIRE
, // Snake L3
62 SPR_SHOT_NEMESIS_L1
, 0, 0, 2, 20, 12, 0x1000, 0, SND_NEMESIS_FIRE
,
63 SPR_SHOT_NEMESIS_L2
, 1, 0, 2, 20, 6, 0x1000, 0, SND_POLAR_STAR_L3
,
64 SPR_SHOT_NEMESIS_L3
, 2, 0, 2, 20, 1, 0x555, 0, 0, // 1/3 speed
66 SPR_SHOT_BUBBLER_L1
, 0, 0, 1, 40, 1, 0x600, 2, SND_BUBBLER_FIRE
,
67 SPR_SHOT_BUBBLER_L2
, 1, 0, 1, 60, 2, 0x600, 2, SND_BUBBLER_FIRE
,
68 SPR_SHOT_BUBBLER_L3
, 2, 0, 1, 100,2, 0x600, 2, SND_BUBBLER_FIRE
,
70 // Spur also messes with it's damage at runtime; see spur.cpp for details.
71 SPR_SHOT_POLARSTAR
, 0, 0, 1, 30, 4, 0x1000, 0, SND_SPUR_FIRE_1
,
72 SPR_SHOT_POLARSTAR
, 1, 1, 1, 30, 8, 0x1000, 0, SND_SPUR_FIRE_2
,
73 SPR_SHOT_POLARSTAR_L3
, 2, 0, 0, 30, 12, 0x1000, 0, SND_SPUR_FIRE_3
,
75 // Curly's Nemesis from Hell (OBJ_CURLY_CARRIED_SHOOTING)
76 SPR_SHOT_NEMESIS_L1
, 0, 0, 1, 20, 12, 0x1000, 0, SND_NEMESIS_FIRE
,
82 // resets anything like charging states etc on player re-init (Player::Init)
85 Weapon
*spur
= &player
->weapons
[WPN_SPUR
];
86 spur
->chargetimer
= 0;
90 init_whimstar(&player
->whimstar
);
96 // switching weapons. have to check for inputs_frozen since justpushed
97 // reads inputs[] directly, not pinputs[].
98 if (!player
->inputs_locked
)
100 if (justpushed(PREVWPNKEY
)) stat_PrevWeapon();
101 if (justpushed(NEXTWPNKEY
)) stat_NextWeapon();
105 if (pinputs
[FIREKEY
])
116 run_whimstar(&player
->whimstar
);
123 void c------------------------------() {}
126 // called when player is trying to fire the current weapon
127 // i.e. the fire button is down.
128 void FireWeapon(void)
130 Weapon
*curweapon
= &player
->weapons
[player
->curWeapon
];
131 int level
= curweapon
->level
;
133 // check if we can fire
134 if (curweapon
->firerate
[level
] != 0)
135 { // rapid/fully-auto fire
136 // decremented in RunWeapon()
137 if (curweapon
->firetimer
)
143 curweapon
->firetimer
= curweapon
->firerate
[level
];
147 { // else must push key for each shot
148 if (lastpinputs
[FIREKEY
])
152 // check if we have enough ammo
153 if (curweapon
->maxammo
> 0 && curweapon
->ammo
<= 0)
155 sound(SND_GUN_CLICK
);
156 if (empty_timer
<= 0)
158 effect(player
->CenterX(), player
->CenterY(), EFFECT_EMPTY
);
170 switch(player
->curWeapon
)
172 case WPN_NONE
: break;
175 PFirePolarStar(level
);
179 PFireFireball(level
);
183 PFireMachineGun(level
);
187 case WPN_SUPER_MISSILE
:
188 PFireMissile(level
, (player
->curWeapon
== WPN_SUPER_MISSILE
));
212 console
.Print("FireWeapon: cannot fire unimplemented weapon %d", player
->curWeapon
);
213 sound(SND_BONK_HEAD
);
219 // "run" the current weapon.
220 // firing = 1 if fire key is currently down, and 0 if it is not.
221 void RunWeapon(bool firing
)
223 Weapon
*curweapon
= &player
->weapons
[player
->curWeapon
];
224 int level
= curweapon
->level
;
226 // bubbler L1 has recharge but not rapid fire,
227 // so it recharges even if the key is held down.
228 if (firing
&& !curweapon
->firerate
[level
] && lastpinputs
[FIREKEY
])
231 // recharge machine gun when it's not firing or it's not selected
232 if ((curweapon
->rechargerate
[level
]) && \
233 (curweapon
->ammo
< curweapon
->maxammo
) && \
236 // start recharging ammo
237 int rate
= curweapon
->rechargerate
[level
];
238 if ((player
->equipmask
& EQUIP_TURBOCHARGE
) && player
->curWeapon
== WPN_MGUN
)
243 // it's greater than OR EQUAL TO, so that we can have rate=0 be no recharge.
244 // Otherwise there would be no value that recharges every frame.
245 if (++curweapon
->rechargetimer
>= rate
)
247 curweapon
->rechargetimer
= 0;
252 for(int i
=0;i
<WPN_COUNT
;i
++)
254 if (player
->weapons
[i
].firetimer
)
255 player
->weapons
[i
].firetimer
--;
257 if ((i
!= player
->curWeapon
) || \
258 (player
->weapons
[i
].ammo
>= player
->weapons
[i
].maxammo
) || \
261 player
->weapons
[i
].rechargetimer
= 0;
267 void c------------------------------() {}
270 // set up the specified bullet to be a shot of type btype
271 // (note: shared by Curly sand-zone boss)
272 void SetupBullet(Object
*shot
, int x
, int y
, int btype
, int dir
)
274 const BulletInfo
*info
= &bullet_table
[btype
];
276 shot
->sprite
= info
->sprite
;
277 shot
->frame
= info
->frame
;
278 shot
->shot
.ttl
= info
->timetolive
;
279 shot
->shot
.damage
= info
->damage
;
280 shot
->shot
.level
= info
->level
;
281 shot
->shot
.btype
= btype
;
282 shot
->shot
.dir
= dir
;
283 shot
->nxflags
|= NXFLAG_NO_RESET_YINERTIA
;
285 if (game
.debug
.infinite_damage
)
286 shot
->shot
.damage
= 255;
291 if (info
->makes_star
== 1)
292 effect(x
, y
, EFFECT_STARPOOF
);
294 if (info
->manualsetup
!= 1)
299 shot
->xinertia
= -info
->speed
;
304 shot
->xinertia
= info
->speed
;
309 shot
->yinertia
= -info
->speed
;
311 if (info
->manualsetup
!= 2) { shot
->sprite
++; }
315 shot
->yinertia
= info
->speed
;
317 if (info
->manualsetup
!= 2) { shot
->sprite
++; }
321 if (info
->makes_star
== 2)
322 effect(x
+shot
->xinertia
/2, y
, EFFECT_STARPOOF
);
324 // have to do this because inertia will get applied later in the tick before the first
325 // time it's drawn so it won't actually appear where we put it if we don't
330 // put shot center at [x,y],
331 // this also centers it within starpoof
332 shot
->x
= x
- (shot
->Width() / 2);
333 shot
->y
= y
- (shot
->Height() / 2);
337 // fire a basic, single bullet
338 static Object
*FireSimpleBullet(int otype
, int btype
, int xoff
, int yoff
)
342 // get location to fire from
343 GetPlayerShootPoint(&x
, &y
);
348 Object
*shot
= CreateObject(0, 0, otype
);
356 SetupBullet(shot
, x
, y
, btype
, dir
);
360 // fires a bullet at an offset from the exact center of the player's shoot point.
361 // FireSimpleBullet can do this too-- but it's xoff/yoff is absolute. This function
362 // takes a parameter for when you are shooting right and extrapolates out the other
363 // directions from that. ALSO, xoff/yoff on FireSimpleBullet moves the star;
364 // this function does not.
365 static Object
*FireSimpleBulletOffset(int otype
, int btype
, int xoff
, int yoff
)
376 case RIGHT
: break; // already in format for RIGHT frame
377 case LEFT
: xoff
= -xoff
; break;
378 case UP
: SWAP(xoff
, yoff
); yoff
= -yoff
; break;
379 case DOWN
: SWAP(xoff
, yoff
); break;
382 Object
*shot
= FireSimpleBullet(otype
, btype
);
391 void c------------------------------() {}
394 static void PFirePolarStar(int level
)
396 // at level 3 only two shots per screen permitted
397 if (level
< 2 || CountObjectsOfType(OBJ_POLAR_SHOT
) < 2)
400 if (level
== 2) xoff
= -5<<CSF
; else xoff
= -4<<CSF
;
402 FireSimpleBulletOffset(OBJ_POLAR_SHOT
, B_PSTAR_L1
+level
, xoff
, 0);
407 void c------------------------------() {}
410 // handles firing the Machine Gun
411 static void PFireMachineGun(int level
)
416 int dir
= (player
->look
) ? player
->look
: player
->dir
;
419 { // level 1 is real easy! no frickin' layers!!
420 shot
= FireSimpleBullet(OBJ_POLAR_SHOT
, B_MGUN_L1
, 0, 0);
424 shot
->xinertia
= random(-0xAA, 0xAA);
426 shot
->yinertia
= random(-0xAA, 0xAA);
430 // drop an OBJ_MGUN_SHOOTER object to fire the layers (trail) of the MGun blast.
431 GetPlayerShootPoint(&x
, &y
);
432 FireLevel23MGun(x
, y
, level
, dir
);
435 // do machine-gun flying
436 if (player
->look
==DOWN
&& level
==2)
442 // fire a level 2 or level 3 MGun blast from position x,y.
443 // Broken out here into a seperate sub so OBJ_CURLY_AI can use it also.
444 void FireLevel23MGun(int x
, int y
, int level
, int dir
)
446 static const uchar no_layers
[] = { 1, 3, 5 };
447 static const int bultype_table
[] = { 0, B_MGUN_L2
, B_MGUN_L3
};
450 // note: this relies on the player AI running before the entity AI...which it does...
451 // so leave it that way, else he wouldn't actually fire for 1 additional frame
452 shot
= CreateObject(x
, y
, OBJ_MGUN_SPAWNER
);
455 shot
->mgun
.bultype
= bultype_table
[level
];
456 shot
->mgun
.nlayers
= no_layers
[level
];
457 shot
->mgun
.wave_amt
= random(-0xAA, 0xAA);
458 shot
->invisible
= true;
462 // handles flying when shooting down using Machine Gun at Level 3
465 if (player
->yinertia
> 0)
467 player
->yinertia
>>= 1;
470 if (player
->yinertia
> -0x400)
472 player
->yinertia
-= 0x200;
473 if (player
->yinertia
< -0x400) player
->yinertia
= -0x400;
478 void c------------------------------() {}
481 // fire the missile launcher.
482 // level: 0 - 2: weapon level from 1 - 3
483 // is_super: bool: true if the player is firing the Super Missile Launcher
484 static void PFireMissile(int level
, bool is_super
)
489 int object_type
= (!is_super
) ? OBJ_MISSILE_SHOT
: OBJ_SUPERMISSILE_SHOT
;
491 // can only fire one missile at once on L1,
492 // two missiles on L2, and two sets of three missiles on L3.
493 static const uint8_t max_missiles_at_once
[] = { 1, 2, 6 };
494 if (CountObjectsOfType(object_type
) >= max_missiles_at_once
[level
])
496 // give back the previously-decremented ammo so they don't lose it (hack)
497 player
->weapons
[player
->curWeapon
].ammo
++;
501 int bullet_type
= (!is_super
) ? B_MISSILE_L1
: B_SUPER_MISSILE_L1
;
502 bullet_type
+= level
;
504 // level 1 & 2 fires just one missile
505 FireSimpleBulletOffset(object_type
, bullet_type
, -4<<CSF
, 0);
507 // level 3 fires three missiles, they wave, and are "offset",
508 // so if it's level 3 fire two more missiles.
512 static const int recoil_upper
[] = { 0x500, 0xd00 };
513 static const int recoil_lower
[] = { 0x700, 0x600 };
515 if (player
->look
==DOWN
|| player
->look
==UP
) { xoff
= (4<<CSF
); yoff
= 0; }
516 else { yoff
= (4<<CSF
); xoff
= 0; }
518 // this one is higher
519 o
= FireSimpleBullet(object_type
, bullet_type
, -xoff
, -yoff
);
520 if (o
->shot
.dir
==LEFT
) o
->xinertia
= recoil_upper
[is_super
];
521 else if (o
->shot
.dir
==RIGHT
) o
->xinertia
= -recoil_upper
[is_super
];
522 else if (o
->shot
.dir
==UP
) o
->yinertia
= recoil_upper
[is_super
];
523 else o
->yinertia
= -recoil_upper
[is_super
];
526 o
= FireSimpleBullet(object_type
, bullet_type
, xoff
, yoff
);
527 if (o
->shot
.dir
==LEFT
) o
->xinertia
= recoil_lower
[is_super
];
528 else if (o
->shot
.dir
==RIGHT
) o
->xinertia
= -recoil_lower
[is_super
];
529 else if (o
->shot
.dir
==UP
) o
->yinertia
= recoil_lower
[is_super
];
530 else o
->yinertia
= -recoil_lower
[is_super
];
535 void c------------------------------() {}
538 static void PFireFireball(int level
)
540 static const int object_types
[] = { OBJ_FIREBALL1
, OBJ_FIREBALL23
, OBJ_FIREBALL23
};
541 static uchar max_fireballs
[] = { 2, 3, 4 };
544 count
= (CountObjectsOfType(OBJ_FIREBALL1
) + CountObjectsOfType(OBJ_FIREBALL23
));
545 if (count
>= max_fireballs
[level
])
550 // the 8px offset fires the shot just a tiny bit behind the player--
551 // you can't see the difference but it makes the shot correctly bounce if
552 // you shoot while flat up against a wall, instead of embedding the fireball
554 Object
*fb
= FireSimpleBulletOffset(object_types
[level
], B_FIREBALL1
+ level
, -8<<CSF
, 0);
555 fb
->dir
= player
->dir
;
556 fb
->nxflags
&= ~NXFLAG_NO_RESET_YINERTIA
;
560 case LEFT
: fb
->xinertia
= -0x400; break;
561 case RIGHT
: fb
->xinertia
= 0x400; break;
564 fb
->xinertia
= player
->xinertia
+ ((player
->dir
==RIGHT
) ? 128 : -128);
565 if (player
->xinertia
) fb
->dir
= (player
->xinertia
> 0) ? RIGHT
:LEFT
;
566 fb
->yinertia
= -0x5ff;
570 fb
->xinertia
= player
->xinertia
;
571 if (player
->xinertia
) fb
->dir
= (player
->xinertia
> 0) ? RIGHT
:LEFT
;
572 fb
->yinertia
= 0x5ff;
578 static void PFireBlade(int level
)
580 int numblades
= CountObjectsOfType(OBJ_BLADE12_SHOT
) + CountObjectsOfType(OBJ_BLADE3_SHOT
);
581 if (numblades
>= 1) return;
583 int dir
= (player
->look
) ? player
->look
: player
->dir
;
585 int x
= player
->CenterX();
586 int y
= player
->CenterY();
590 if (dir
== RIGHT
|| dir
== LEFT
)
593 x
+= (dir
== LEFT
) ? (3 << CSF
) : -(3 << CSF
);
600 case RIGHT
: x
-= (6 << CSF
); y
-= (3 << CSF
); break;
601 case LEFT
: x
+= (6 << CSF
); y
-= (3 << CSF
); break;
602 case UP
: y
+= (6 << CSF
); break;
603 case DOWN
: y
-= (6 << CSF
); break;
607 Object
*shot
= CreateObject(x
, y
, (level
!= 2) ? OBJ_BLADE12_SHOT
: OBJ_BLADE3_SHOT
);
608 SetupBullet(shot
, x
, y
, B_BLADE_L1
+level
, dir
);
612 void c------------------------------() {}
615 static void PFireSnake(int level
)
619 int count
= (CountObjectsOfType(OBJ_SNAKE1_SHOT
) + \
620 CountObjectsOfType(OBJ_SNAKE23_SHOT
));
626 int object_type
= (level
== 0) ? OBJ_SNAKE1_SHOT
: OBJ_SNAKE23_SHOT
;
627 FireSimpleBulletOffset(object_type
, B_SNAKE_L1
+level
, -5<<CSF
, 0);
631 static void PFireNemesis(int level
)
633 if (CountObjectsOfType(OBJ_NEMESIS_SHOT
) >= 2)
636 FireSimpleBullet(OBJ_NEMESIS_SHOT
, B_NEMESIS_L1
+level
);
640 static void PFireBubbler(int level
)
642 static const int max_bubbles
[] = { 4, 16, 16 };
644 int count
= CountObjectsOfType(OBJ_BUBBLER12_SHOT
) + \
645 CountObjectsOfType(OBJ_BUBBLER3_SHOT
);
647 if (count
>= max_bubbles
[level
])
650 int objtype
= (level
!= 2) ? OBJ_BUBBLER12_SHOT
: OBJ_BUBBLER3_SHOT
;
651 FireSimpleBulletOffset(objtype
, B_BUBBLER_L1
+level
, -4<<CSF
, 0);
655 void c------------------------------() {}
658 // Spur fires an initial shot of Polar Star L3, then charges
659 // as long as key is down. Fires when key released.
660 // Released at L1: nothing
661 // Released at L2: thin beam
662 // Released at L3: dual beam
663 // Released at Max: thick beam
665 // Initial shot is not fired if key is held on a different weapon
666 // and then weapon is switched to spur.
668 // fires the regular Polar Star shot when you first push button
669 static void PFireSpur(void)
672 FireSimpleBulletOffset(OBJ_POLAR_SHOT
, B_PSTAR_L3
, -4<<CSF
, 0);
675 // fires and handles charged shots
676 static void PHandleSpur(void)
678 static const int FLASH_TIME
= 10;
679 Weapon
*spur
= &player
->weapons
[WPN_SPUR
];
681 if (player
->curWeapon
!= WPN_SPUR
)
688 if (pinputs
[FIREKEY
])
690 if (!IsWeaponMaxed())
692 int amt
= (player
->equipmask
& EQUIP_TURBOCHARGE
) ? 3 : 2;
697 sound(SND_SPUR_MAXED
);
702 if (spur
->chargetimer
& 2)
704 sound(SND_SPUR_CHARGE_1
+ spur
->level
);
709 { // keep flashing even once at max
710 statusbar
.xpflashcount
= FLASH_TIME
;
712 if (player
->equipmask
& EQUIP_WHIMSTAR
)
713 add_whimstar(&player
->whimstar
);
718 if (spur
->chargetimer
)
720 if (spur
->level
> 0 && can_fire_spur())
722 int level
= IsWeaponMaxed() ? 2 : (spur
->level
- 1);
723 FireSimpleBulletOffset(OBJ_SPUR_SHOT
, B_SPUR_L1
+level
, -4<<CSF
, 0);
726 spur
->chargetimer
= 0;
733 if (statusbar
.xpflashcount
> FLASH_TIME
)
734 statusbar
.xpflashcount
= FLASH_TIME
;
738 static bool can_fire_spur(void)
740 if (CountObjectsOfType(OBJ_SPUR_SHOT
))
746 // returns true if the current weapon has full xp at level 3 (is showing "Max")
747 static bool IsWeaponMaxed(void)
749 Weapon
*wpn
= &player
->weapons
[player
->curWeapon
];
750 return (wpn
->level
== 2) && (wpn
->xp
== wpn
->max_xp
[2]);