2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 void addBullet(Entity *owner, float dx, float dy)
26 if (!(owner->flags & ENT_BOSS))
28 if (owner->environment == ENV_WATER)
30 if ((owner->currentWeapon != &weapon[WP_PISTOL]) && (owner->currentWeapon != &weapon[WP_AIMEDPISTOL]))
37 Entity *bullet = new Entity();
40 bullet->y = owner->y;// + owner->dy;
42 if (owner != &engine.world)
44 bullet->x += (owner->width / 2);
45 bullet->y += (owner->height / 2);
48 bullet->setName(owner->currentWeapon->name);
49 bullet->id = owner->currentWeapon->id;
51 bullet->dy = owner->currentWeapon->dy + dy;
53 // Add motion of the player and any platform he/she is on to grenades
54 if (owner->currentWeapon->dy)
57 getTrainMotion(owner, tdx, tdy);
58 bullet->dx += owner->dx - tdx;
63 bullet->health = owner->currentWeapon->health;
64 bullet->damage = owner->currentWeapon->damage;
65 bullet->setSprites(owner->currentWeapon->sprite[0], owner->currentWeapon->sprite[1], owner->currentWeapon->sprite[1]);
66 bullet->face = owner->face;
67 bullet->owner = owner;
68 bullet->flags = owner->currentWeapon->flags + ENT_SPARKS + ENT_BULLET + ((owner->flags & ENT_BOSS) ? ENT_BOSS : 0);
70 if (bullet->flags & ENT_EXPLODES)
72 bullet->deathSound = SND_GRENADE;
74 else if (owner->currentWeapon->fireSound > -1)
76 if ((Math::prand() % 2) == 0)
78 bullet->deathSound = SND_RICO1;
82 bullet->deathSound = SND_RICO2;
87 if (owner->currentWeapon->id == WP_STALAGTITE)
89 bullet->deathSound = SND_STONEBREAK;
92 if (owner->currentWeapon->fireSound > -1)
94 audio.playSound(owner->currentWeapon->fireSound, CH_WEAPON, owner->x);
97 if (owner->flags & ENT_AIMS)
99 Math::calculateSlope(player.x + Math::rrand(-20, 20), player.y + Math::rrand(-20, 20), bullet->x, bullet->y, &bullet->dx, &bullet->dy);
100 bullet->dx *= owner->currentWeapon->dx;
101 bullet->dy *= owner->currentWeapon->dy;
104 map.addBullet(bullet);
106 // Adjust the reload time of enemies according to difficulty level
107 owner->reload = owner->currentWeapon->reload;
109 if ((owner != &player) && (game.skill < 3))
111 owner->reload *= (3 - game.skill);
114 if (owner->flags & ENT_ALWAYSFIRES)
119 if (owner == &player)
121 game.incBulletsFired();
123 if (engine.cheatReload)
128 if (game.bulletsFired[game.currentWeapon] == 10000)
130 presentPlayerMedal("10000_Bullets");
135 void destroyBullet(Entity *bullet)
137 if (bullet->deathSound == -1)
144 if (bullet->flags & ENT_SPARKS)
146 audio.playSound(bullet->deathSound, CH_TOUCH, bullet->x);
149 if (bullet->flags & ENT_EXPLODES)
151 addExplosion(bullet->x + (bullet->width / 2), bullet->y + (bullet->height / 2), bullet->damage, bullet->owner);
154 if (bullet->id == WP_STALAGTITE)
156 throwStalagParticles(bullet->x, bullet->y);
161 for (int i = 0 ; i < 3 ; i++)
163 dx = Math::rrand(-30, 30); dx /= 12;
164 dy = Math::rrand(-30, 30); dy /= 12;
166 if (bullet->flags & ENT_SPARKS)
168 map.addParticle(bullet->x, bullet->y, dx, dy, Math::rrand(5, 30), graphics.white, NULL, 0);
172 map.addParticle(bullet->x, bullet->y, dx, dy, Math::rrand(5, 30), graphics.red, NULL, 0);
177 // Just a little convinence function!
178 void removeBullet(Entity *bullet)
181 bullet->deathSound = -1;
182 Math::removeBit(&bullet->flags, ENT_SPARKS);
183 Math::removeBit(&bullet->flags, ENT_PUFFS);
184 Math::removeBit(&bullet->flags, ENT_EXPLODES);
187 void bounceBullet(Entity *bullet, float dx, float dy)
191 bullet->dx = -bullet->dx;
192 bullet->x += bullet->dx;
193 if (bullet->id != WP_LASER)
196 audio.playSound(SND_GRBOUNCE, CH_TOUCH, bullet->x);
198 bullet->face = !bullet->face;
203 bullet->dy = -bullet->dy;
204 bullet->y += bullet->dy;
206 Math::limitFloat(&bullet->dy, -4, 4);
208 if ((bullet->dy > -2) && (bullet->dy <= 0)) bullet->dy = -2;
209 if ((bullet->dy > 0) && (bullet->dy < 2)) bullet->dy = 2;
211 if (bullet->id != WP_LASER)
214 audio.playSound(SND_GRBOUNCE, CH_TOUCH, bullet->x);
217 if ((bullet->dy > -2) && (bullet->dy <= 0)) bullet->dy = -2;
218 if ((bullet->dy > 0) && (bullet->dy < 2)) bullet->dy = 2;
220 bullet->face = !bullet->face;
224 bool bulletHasCollided(Entity *bullet, float dx, float dy)
229 int x = (int)bullet->x >> BRICKSHIFT;
230 int y = (int)bullet->y >> BRICKSHIFT;
232 if ((x < 0) || (y < 0))
234 removeBullet(bullet);
238 if (map.isSolid(x, y))
240 if (map.isBreakable(x, y))
242 if (bullet->flags & ENT_EXPLODES)
244 Math::removeBit(&bullet->flags, ENT_BOUNCES);
245 map.data[x][y] = MAP_AIR;
246 audio.playSound(SND_STONEBREAK, CH_EXPLODE, bullet->x);
247 throwBrickParticles(x << BRICKSHIFT, y << BRICKSHIFT);
251 if ((Math::prand() % 2) == 0)
253 map.data[x][y] = MAP_AIR;
254 audio.playSound(SND_STONEBREAK, CH_EXPLODE, bullet->x);
255 throwBrickParticles(x << BRICKSHIFT, y << BRICKSHIFT);
260 if (bullet->flags & ENT_BOUNCES)
262 bounceBullet(bullet, dx, dy);
269 enemyBulletCollisions(bullet);
271 checkPlayerBulletCollisions(bullet);
273 checkBossBulletCollisions(bullet);
275 checkSwitchContact(bullet);
277 if ((checkTrainContact(bullet, DIR_XY)) || (checkObstacleContact(bullet, DIR_XY)))
279 if (bullet->flags & ENT_BOUNCES)
280 bounceBullet(bullet, dx, dy);
289 Entity *bullet = (Entity*)map.bulletList.getHead();
290 Entity *previous = bullet;
294 while (bullet->next != NULL)
296 bullet = (Entity*)bullet->next;
298 bullet->owner->referenced = true;
300 x = (int)(bullet->x - engine.playerPosX);
301 y = (int)(bullet->y - engine.playerPosY);
303 graphics.blit(bullet->getFaceImage(), x, y, graphics.screen, true);
306 if (bullet->flags & ENT_ONFIRE)
308 addFireParticles(bullet->x + Math::rrand(-8, 8), bullet->y + Math::rrand(-8, 8), 1);
311 if (bullet->flags & ENT_FIRETRAIL)
313 addFireTrailParticle(bullet->x, bullet->y);
316 if (bullet->flags & ENT_PARTICLETRAIL)
318 addColorParticles(x, y, -1, 3);
321 if (bullet->owner == &player)
323 if ((x < -160) || (y < -120) || (x > 800) || (y > 600))
325 removeBullet(bullet);
329 if (bulletHasCollided(bullet, bullet->dx, 0))
331 if (!(bullet->flags & ENT_BOUNCES))
337 if (bulletHasCollided(bullet, 0, bullet->dy))
339 if (!(bullet->flags & ENT_BOUNCES))
347 if (bullet->health == 0)
349 Math::removeBit(&bullet->flags, ENT_SPARKS);
350 Math::removeBit(&bullet->flags, ENT_PUFFS);
353 if (!(bullet->flags & ENT_WEIGHTLESS))
358 if (bullet->health > 0)
364 destroyBullet(bullet);
365 map.bulletList.remove(previous, bullet);