📄 bomb.cpp
字号:
/*************************************************************************
"I Have No Tomatoes"
Copyright (c) 2004, Mika Halttunen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Mika Halttunen <lsoft@mbnet.fi>
*************************************************************************/
#include <stdio.h>
#include "SDL.h"
#include "SDL_opengl.h"
#include "SDL_image.h"
#include "game.h"
#include "texture.h"
#include "init.h"
#include "player.h"
#include "mymath.h"
#include "tilemap.h"
#include "enemy.h"
#include "bomb.h"
#include "particle.h"
#include "effects.h"
#include "helpers.h"
#include "special_power.h"
#include "soundmusic.h"
#include "comments.h"
// Bomb list
list<BOMB> bomblist;
// Bomb sprite textures
GLuint bomb_tex[NUM_BOMBS];
// Shadow texture from player.cpp
extern GLuint sprite_shadow;
// Add a bomb
void add_bomb(int x, int y, int type, int owner) {
BOMB b;
b.clear();
b.x = x;
b.y = y;
b.owner = owner;
b.type = type;
b.time = 2 * 60; // Two seconds
b.alive = true;
bomblist.push_back(b);
}
// Load bomb textures
void load_bombs() {
bomb_tex[0] = load_png("bomb1.png", true, false, false);
bomb_tex[1] = bomb_tex[0];
}
// Move the bombs
void move_bombs() {
if(bomblist.size() == 0)
return;
list<BOMB>::iterator i;
for(i = bomblist.begin(); i != bomblist.end(); ++i) {
(*i).move();
// Remove the dead bombs
if((*i).alive == false) {
i = bomblist.erase(i);
}
}
}
// Draw the bombs
void draw_bombs() {
if(bomblist.size() == 0)
return;
glColor3f(1,1,1);
list<BOMB>::iterator i;
for(i = bomblist.begin(); i != bomblist.end(); ++i)
if((*i).time > 0)
(*i).draw();
}
// Clear the bombs
void clear_bombs() {
bomblist.clear();
}
#define TRACED_P1 2
#define TRACED_P2 4
// Helper function which traces straight lines and checks the players
void trace_players(int sx, int sy, int dir, bool recurse, int &who) {
// Begin tracing
for(int pos = 1; pos < MAP_W; pos++) {
// Compute the current position (xx,yy)
int xx, yy;
switch(dir) {
default:
case DIR_N: xx = sx; yy = sy - pos; break;
case DIR_E: xx = sx + pos; yy = sy; break;
case DIR_S: xx = sx; yy = sy + pos; break;
case DIR_W: xx = sx - pos; yy = sy; break;
}
// Check if there's a wall
if(map_solid(xx,yy))
break;
// Recurse if necessary
if(recurse) {
if(dir == DIR_N) {
// Go to east or west
if(!map_solid(xx + 1, yy))
trace_players(xx, yy, DIR_E, false, who);
else if(!map_solid(xx - 1, yy))
trace_players(xx, yy, DIR_W, false, who);
}
else if(dir == DIR_E) {
// Go to south or north
if(!map_solid(xx, yy + 1))
trace_players(xx, yy, DIR_S, false, who);
else if(!map_solid(xx, yy - 1))
trace_players(xx, yy, DIR_N, false, who);
}
else if(dir == DIR_S) {
// Go to west or east
if(!map_solid(xx - 1, yy))
trace_players(xx, yy, DIR_W, false, who);
else if(!map_solid(xx + 1, yy))
trace_players(xx, yy, DIR_E, false, who);
}
else if(dir == DIR_W) {
// Go to north or south
if(!map_solid(xx, yy - 1))
trace_players(xx, yy, DIR_N, false, who);
else if(!map_solid(xx, yy + 1))
trace_players(xx, yy, DIR_S, false, who);
}
if((who & TRACED_P1) && (two_players && (who & TRACED_P2)))
return;
}
// Check also for the players
if(p1.alive && (int)p1.get_real_x() == xx && (int)p1.get_real_y() == yy && !p1.jumping && !(using_special_power == 1 && which_special_power == BLUE_POWER_TELEPORT))
who |= TRACED_P1;
if(two_players && p2.alive && (int)p2.get_real_x() == xx && (int)p2.get_real_y() == yy && !p2.jumping && !(using_special_power == 2 && which_special_power == BLUE_POWER_TELEPORT))
who |= TRACED_P2;
if((who & TRACED_P1) && (two_players && (who & TRACED_P2)))
return;
}
}
// Helper function which checks if the player is on the line of fire of the flower power bomb
void trace_flower_power(int bx, int by, int &who) {
// Check the bomb location first
if((int)p1.get_real_x() == bx && (int)p1.get_real_y() == by && p1.alive && !p1.jumping && !(using_special_power == 1 && which_special_power == BLUE_POWER_TELEPORT))
who |= TRACED_P1;
if(two_players && (int)p2.get_real_x() == bx && (int)p2.get_real_y() == by && p2.alive && !p2.jumping && !(using_special_power == 2 && which_special_power == BLUE_POWER_TELEPORT))
who |= TRACED_P2;
if((who & TRACED_P1) && (two_players && (who & TRACED_P2)))
return;
// Sweep to each direction, and check the players
for(int dir = DIR_N; dir <= DIR_W; dir++) {
trace_players(bx, by, dir, true, who);
if((who & TRACED_P1) && (two_players && (who & TRACED_P2)))
break;
}
}
// Helper function which traces straight lines and creates explosions
// Returns the number of enemies killed
int trace_explosion(int sx, int sy, int dir, int type, bool explosions, bool recurse) {
int killed = 0;
// Begin tracing
for(int pos = 1; pos < MAP_W; pos++) {
// Compute the current position (xx,yy)
int xx, yy;
switch(dir) {
default:
case DIR_N: xx = sx; yy = sy - pos; break;
case DIR_E: xx = sx + pos; yy = sy; break;
case DIR_S: xx = sx; yy = sy + pos; break;
case DIR_W: xx = sx - pos; yy = sy; break;
}
// Check if there's a wall
if(map_solid(xx,yy))
break;
// Recurse if necessary
if(recurse) {
if(dir == DIR_N) {
// Go to east or west
if(!map_solid(xx + 1, yy))
trace_explosion(xx, yy, DIR_E, type, explosions, false);
else if(!map_solid(xx - 1, yy))
trace_explosion(xx, yy, DIR_W, type, explosions, false);
}
else if(dir == DIR_E) {
// Go to south or north
if(!map_solid(xx, yy + 1))
trace_explosion(xx, yy, DIR_S, type, explosions, false);
else if(!map_solid(xx, yy - 1))
trace_explosion(xx, yy, DIR_N, type, explosions, false);
}
else if(dir == DIR_S) {
// Go to west or east
if(!map_solid(xx - 1, yy))
trace_explosion(xx, yy, DIR_W, type, explosions, false);
else if(!map_solid(xx + 1, yy))
trace_explosion(xx, yy, DIR_E, type, explosions, false);
}
else if(dir == DIR_W) {
// Go to north or south
if(!map_solid(xx, yy - 1))
trace_explosion(xx, yy, DIR_N, type, explosions, false);
else if(!map_solid(xx, yy + 1))
trace_explosion(xx, yy, DIR_S, type, explosions, false);
}
}
// No wall, create explosion
if(explosions)
create_explosion(xx, yy, (type == BTYP_NORMAL) ? EXP_BOMB_NORMAL : EXP_BOMB_FLOWER);
// Loop through the enemies and kill them
list<ENEMY>::iterator e;
for(e = enemylist.begin(); e != enemylist.end(); ++e) {
if((int)((*e).get_real_x()) == xx && (int)((*e).get_real_y()) == yy && !(*e).dying) {
(*e).die();
killed++;
}
}
// Check also for the players
if(explosions && type == BTYP_NORMAL && p1.alive && (int)p1.get_real_x() == xx && (int)p1.get_real_y() == yy && !p1.jumping && !(using_special_power == 1 && which_special_power == BLUE_POWER_TELEPORT))
p1.die();
if(explosions && two_players && type == BTYP_NORMAL && p2.alive && (int)p2.get_real_x() == xx && (int)p2.get_real_y() == yy && !p2.jumping && !(using_special_power == 2 && which_special_power == BLUE_POWER_TELEPORT))
p2.die();
/* // Check the potato men
if(explosions && potatoman.alive && (int)potatoman.get_real_x() == xx && (int)potatoman.get_real_y() == yy && potatoman.raise_pos == 0.0f) {
// Raise up
potatoman.raise_dir = DIR_UP;
potatoman.raise_pos = 0.0f;
potatoman.anim = 0.0f;
// Play the sound
play_sound(SND_POTATOMAN2, false);
}
*/
}
return killed;
}
// Explode the bomb
// Returns the number of enemies killed
int BOMB::explode(bool explosions) {
int num_killed = 0;
// Reduce the owner's bomb count
if(explosions) {
if(owner == 1)
p1.num_bombs--;
else
p2.num_bombs--;
// Play the explosion sound
play_sound(SND_EXPLO, true);
}
// Sweep to each direction, and wipe everything out
for(int dir = DIR_N; dir <= DIR_W; dir++)
num_killed += trace_explosion(x, y, dir, type, explosions, (type == BTYP_NORMAL) ? false : true);
// Create explosion also to the bomb location
if(explosions)
create_explosion(x, y, (type == BTYP_NORMAL) ? EXP_BOMB_CENTER : EXP_BOMB_CENTER_FLOWER);
// Check the enemies on here also
// Loop through the enemies and kill them
list<ENEMY>::iterator e;
for(e = enemylist.begin(); e != enemylist.end(); ++e) {
if((int)((*e).get_real_x()) == x && (int)((*e).get_real_y()) == y && !(*e).dying) {
(*e).die();
num_killed++;
}
}
// Check also for the players
if(explosions && type == BTYP_NORMAL && p1.alive && (int)p1.get_real_x() == x && (int)p1.get_real_y() == y && !p1.jumping && !(using_special_power == 1 && which_special_power == BLUE_POWER_TELEPORT))
p1.die();
if(explosions && two_players && type == BTYP_NORMAL && p2.alive && (int)p2.get_real_x() == x && (int)p2.get_real_y() == y && !p2.jumping && !(using_special_power == 2 && which_special_power == BLUE_POWER_TELEPORT))
p2.die();
// Check the potato men
/* if(explosions && potatoman.alive && (int)potatoman.get_real_x() == x && (int)potatoman.get_real_y() == y && potatoman.raise_pos == 0.0f) {
// Raise up
potatoman.raise_dir = DIR_UP;
potatoman.raise_pos = 0.0f;
potatoman.anim = 0.0f;
// Play the sound
play_sound(SND_POTATOMAN2, false);
}
*/
return num_killed;
}
// Helper function from player.cpp
void get_random_block_at(int x, int y, int &bx, int &by);
// Move the bomb
void BOMB::move() {
// Animate
if(time > 0) {
anim_angle = add_angle(anim_angle, 5.0f);
// Fly
if(z > 0.5f || z_speed != 0.0f) {
z_speed -= 0.01f;
z += z_speed;
if(z < 0.5f) {
z = 0.5f;
z_speed = 0;
// Play the flower power sound
if(type == BTYP_FLOWER)
play_sound(SND_FLOWERPOWER, false);
}
return;
}
}
// Decrease the time
time--;
// Check if the players are affected by the flower power
// If they are: jump
if(type == BTYP_FLOWER && time <= 40 && time >= 0 && oldx[0] == -1 && oldx[1] == -1) {
int who = 0;
trace_flower_power(x, y, who);
int jtx = 0, jty = 0;
oldx[0] = oldx[1] = oldy[0] = oldy[1] = -1;
if(who & TRACED_P1) {
// P1 jumps
oldx[0] = (int)p1.get_real_x();
oldy[0] = (int)p1.get_real_y();
//oldx[1] = oldy[1] = -1;
get_random_block_at(oldx[0], oldy[0], jtx, jty);
p1.jump(jtx, jty, 2.0f, 0.05f);
// Play the jump sound
play_sound(SND_JUMP, false);
}
if(who & TRACED_P2) {
// P2 jumps
oldx[1] = (int)p2.get_real_x();
oldy[1] = (int)p2.get_real_y();
//oldx[0] = oldy[0] = -1;
get_random_block_at(oldx[1], oldy[1], jtx, jty);
p2.jump(jtx, jty, 2.0f, 0.05f);
// Play the jump sound
play_sound(SND_JUMP, false);
}
}
// Explode?
if(time == 0) {
killed = 0;
killed = explode();
return;
}
// After the initial explosion, we check the cells a few times to
// make sure that the players/enemies touching the explosions flames
// die as they should.
if(time == -10)
killed += explode(false);
else if(time == -20)
killed += explode(false);
else if(time == -30)
killed += explode(false);
else if(time == -35)
killed += explode(false);
else if(time == -40) {
killed += explode(false);
if(type == BTYP_NORMAL) {
alive = false;
// Check if we have killed 5 or more enemies
if(killed >= 5) {
play_sound(SND_KILLED5, false);
// Create the "bonus rain"
create_bonus_rain();
add_comment(COL_DEFAULT, "Five enemies with one bomb!");
}
}
}
// Jump the players back after the flower power explosions
if(type == BTYP_FLOWER && time <= -60) {
if(oldx[0] != -1) {
// P1 jumps
p1.jump(oldx[0], oldy[0], 2.0f, 0.05f);
// Play the jump sound
play_sound(SND_JUMP, false);
}
if(oldx[1] != -1) {
// P2 jumps
p2.jump(oldx[1], oldy[1], 2.0f, 0.05f);
// Play the jump sound
play_sound(SND_JUMP, false);
}
alive = false;
// Decrease the active flower bomb amount
if(owner == 1)
p1.num_flower_bombs--;
else
p2.num_flower_bombs--;
}
// Create some particles
if(time > 0 && RAND(0,100) > 30) {
VECT pos(x + 0.6f, 0.85f + (SIN(anim_angle) * 0.2f), y + 0.6f);
VECT dir;
dir.x = RANDF(-0.01f, 0.01f);
dir.y = RANDF(0.05f, 0.09f);
dir.z = RANDF(-0.01f, 0.01f);
if(type == BTYP_NORMAL) {
float c1[4] = { 1, 0.7f, 0.3f, 1 };
float c2[4] = { 1, 0, 0, 0 };
add_particle(pos, dir, RAND(10,60), 0.05f, 0.2f, c1, c2, part_spark);
}
else if(type == BTYP_FLOWER) {
float c1[4] = { 0.6f, 1, 0.3f, 1 };
float c2[4] = { 0, 1, 0, 0 };
add_particle(pos, dir, RAND(10,60), 0.05f, 0.2f, c1, c2, part_spark);
}
}
}
// Draw the bomb
void BOMB::draw() {
// Translate to the position
glPushMatrix();
glTranslatef(x + 0.51f, 0.0f, y + 0.51f);
// Draw the shadow
glDepthMask(GL_FALSE);
glColor3f(1,1,1);
BIND_TEXTURE(sprite_shadow);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1,1); glVertex3f( 0.5f, 0, -0.5f);
glTexCoord2f(0,1); glVertex3f(-0.5f, 0, -0.5f);
glTexCoord2f(1,0); glVertex3f( 0.5f, 0, 0.5f);
glTexCoord2f(0,0); glVertex3f(-0.5f, 0, 0.5f);
glEnd();
glDepthMask(GL_TRUE);
// Translate up and create waving motion
glTranslatef(0, z + (SIN(anim_angle) * 0.2f), 0);
// Negate the camera rotation
glMultMatrixf(cam_neg_matrix);
// glRotatef(45.0f, 0,1,0);
// glRotatef(-30.0f, 1,0,0);
// Draw the sprite
BIND_TEXTURE(bomb_tex[type]);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1,1); glVertex3f( size, size, size);
glTexCoord2f(0,1); glVertex3f(-size, size, size);
glTexCoord2f(1,0); glVertex3f( size, -size, -size);
glTexCoord2f(0,0); glVertex3f(-size, -size, -size);
glEnd();
glPopMatrix();
}
// Clear the bomb
void BOMB::clear() {
x = y = 0;
type = 0;
size = 0.45f;
owner = 0;
anim_angle = RANDF(0,359);
alive = false;
time = 0;
z = 0.5f;
z_speed = 0.2f;
oldx[0] = oldx[1] = oldy[0] = oldy[1] = -1;
killed = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -