From 550869b194433fa07ac7554acef0954a9df9ba79 Mon Sep 17 00:00:00 2001 From: commandblockguy Date: Mon, 18 Jan 2021 00:40:04 -0600 Subject: [PATCH] Rework how objects get killed --- src/game.cpp | 12 ++++++------ src/globals.h | 1 - src/objects/mine.cpp | 22 ++++++++++++++-------- src/objects/mine.h | 2 ++ src/objects/mine_detector.cpp | 7 ++----- src/objects/physicsbody.cpp | 33 +++++++++++++++++++++------------ src/objects/physicsbody.h | 4 +++- src/objects/shell.cpp | 10 ++++++++-- src/objects/shell.h | 4 ++++ src/objects/tank.cpp | 10 +++++----- src/objects/tank.h | 2 ++ 11 files changed, 67 insertions(+), 40 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 4078660..dbbae3e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -13,10 +13,10 @@ bool start_mission(const serialized_tank_t *ser_tanks) { dbg_printf("starting mission\n"); - while(!PhysicsBody::objects.empty()) { - // Delete objects without killing - delete PhysicsBody::objects[0]; + for(auto & obj : PhysicsBody::objects) { + obj->active = false; } + PhysicsBody::remove_inactive(); game.num_tanks = 0; @@ -67,9 +67,11 @@ uint8_t play_mission(const serialized_tank_t *ser_tanks) { for(auto & object : PhysicsBody::objects) { object->tick(); } + PhysicsBody::remove_inactive(); + PhysicsBody::sort(); profiler_end(physics); - if(!game.player_alive) { + if(!game.player) { profiler_end(total); game.lives--; if(!game.lives) { @@ -84,8 +86,6 @@ uint8_t play_mission(const serialized_tank_t *ser_tanks) { return NEXT_LEVEL; } - PhysicsBody::sort(); - render(); game.tick++; diff --git a/src/globals.h b/src/globals.h index 4235f7e..0a5ac85 100644 --- a/src/globals.h +++ b/src/globals.h @@ -16,7 +16,6 @@ typedef struct { uint8_t lives; //Number of remaining tanks. This includes the tank that is currently in use, so a value of 1 means that the game will end the next time the tank is hit. uint8_t total_kills; //Number of enemy tanks destroyed. uint8_t kills[NUM_TANK_TYPES]; - bool player_alive; uint8_t num_tanks; bool alive_tanks[MAX_NUM_TANKS]; Tank *player; diff --git a/src/objects/mine.cpp b/src/objects/mine.cpp index 25c1d5e..c7b2df4 100644 --- a/src/objects/mine.cpp +++ b/src/objects/mine.cpp @@ -20,9 +20,6 @@ Mine::Mine(Tank *tank) { position_x = tank->position_x + (TANK_SIZE - MINE_SIZE) / 2; position_y = tank->position_y + (TANK_SIZE - MINE_SIZE) / 2; - velocity_x = 0; - velocity_y = 0; - parent = tank; new (std::nothrow) MineDetector(this); @@ -45,7 +42,7 @@ void Mine::process() { detonate(); } if(countdown == 0) { - kill(); + active = false; } //todo: range detection @@ -91,6 +88,11 @@ void Mine::render(uint8_t layer) { } void Mine::detonate() { + if(countdown < EXPLOSION_ANIM) { + // Don't explode multiple times + return; + } + countdown = EXPLOSION_ANIM - 1; // todo: The original game uses a radius, not a square @@ -107,15 +109,19 @@ void Mine::detonate() { } // Kill any nearby physics objects - for(auto *it = objects.begin(); it < objects.end();) { - if(*it != this && center_distance_less_than(*it, MINE_EXPLOSION_RADIUS)) { - (**it).kill(); - } else it++; + for(auto & obj : objects) { + if(obj != this && center_distance_less_than(obj, MINE_EXPLOSION_RADIUS)) { + obj->handle_explosion(); + } } generate_bg_tilemap(); } +void Mine::handle_explosion() { + detonate(); +} + void Mine::handle_collision(PhysicsBody *other) { other->collide(this); } diff --git a/src/objects/mine.h b/src/objects/mine.h index 9e6c82f..2fc01c2 100644 --- a/src/objects/mine.h +++ b/src/objects/mine.h @@ -43,6 +43,8 @@ class Mine: public PhysicsBody { void detonate(); + void handle_explosion(); + void handle_collision(PhysicsBody *other); void collide(Tank *tank); void collide(Shell *shell); diff --git a/src/objects/mine_detector.cpp b/src/objects/mine_detector.cpp index 38357f0..bc1a8fd 100644 --- a/src/objects/mine_detector.cpp +++ b/src/objects/mine_detector.cpp @@ -13,14 +13,11 @@ MineDetector::MineDetector(Mine *mine) { position_x = mine->center_x() - MINE_EXPLOSION_RADIUS; position_y = mine->center_y() - MINE_EXPLOSION_RADIUS; - velocity_x = 0; - velocity_y = 0; - parent = mine; } void MineDetector::process() { - if(!parent) kill(); + if(!parent) active = false; if(!primed) { Tank *placer = (Tank*)parent->parent; @@ -43,7 +40,7 @@ void MineDetector::collide(__attribute__((unused)) Tank *tank) { if(parent) { ((Mine*)parent)->countdown = MINE_TRIGGERED; } - kill(); + active = false; } } diff --git a/src/objects/physicsbody.cpp b/src/objects/physicsbody.cpp index 9ce72fd..2fcbddc 100644 --- a/src/objects/physicsbody.cpp +++ b/src/objects/physicsbody.cpp @@ -16,6 +16,11 @@ uint PhysicsBody::center_y() const { } PhysicsBody::PhysicsBody() { + active = true; + parent = nullptr; + velocity_x = 0; + velocity_y = 0; + // todo: compiler bug triggers if this line is the only thing in here objects.push_back(this); @@ -24,19 +29,7 @@ PhysicsBody::PhysicsBody() { } PhysicsBody::~PhysicsBody() { - // Remove from object list - for(auto *it = objects.begin(); it < objects.end();) { - // Inform any children that we no longer exist - if((**it).parent == this) (**it).parent = nullptr; - // Remove from objects list - if(*it == this) { - objects.erase(it); - } else it++; - } -} -void PhysicsBody::kill() { - delete this; } void PhysicsBody::sort() { @@ -52,6 +45,18 @@ void PhysicsBody::sort() { } } +void PhysicsBody::remove_inactive() { + for(auto & obj : objects) { + if(!obj->parent->active) obj->parent = nullptr; + } + for(auto *it = objects.begin(); it < objects.end();) { + if(!(*it)->active) { + delete *it; + it = objects.erase(it); + } else it++; + } +} + bool PhysicsBody::detect_collision(PhysicsBody *other) const { return position_x < other->position_x + (int)other->width && position_x + (int)width > other->position_x && @@ -172,3 +177,7 @@ void PhysicsBody::tick() { void PhysicsBody::handle_tile_collision(__attribute__((unused)) direction_t dir) { } + +void PhysicsBody::handle_explosion() { + // Do nothing, by default +} diff --git a/src/objects/physicsbody.h b/src/objects/physicsbody.h index 5fae6df..7c51b1b 100644 --- a/src/objects/physicsbody.h +++ b/src/objects/physicsbody.h @@ -24,6 +24,7 @@ class PhysicsBody { uint width; uint height; PhysicsBody *parent; + bool active; bool tile_collisions; // Whether or not to collide with holes @@ -44,12 +45,13 @@ class PhysicsBody { void tick(); static void sort(); + static void remove_inactive(); - virtual void kill(); virtual void process() = 0; virtual void render(uint8_t layer); virtual void handle_tile_collision(direction_t dir); + virtual void handle_explosion(); // Polymorphic ping-pong (aka "visitor pattern," apparently) virtual void handle_collision(PhysicsBody *other) = 0; diff --git a/src/objects/shell.cpp b/src/objects/shell.cpp index 66e135f..565feba 100644 --- a/src/objects/shell.cpp +++ b/src/objects/shell.cpp @@ -35,7 +35,6 @@ Shell::Shell(Tank *tank) { Shell::~Shell() { if(parent) { - // todo: see if there's a way to do this without an ugly cast ((Tank*)parent)->num_shells--; } } @@ -70,6 +69,10 @@ void Shell::update_direction() { direction = angle_to_shell_direction(angle); } +void Shell::handle_explosion() { + kill(); +} + void Shell::handle_collision(PhysicsBody *other) { other->collide(this); } @@ -99,7 +102,6 @@ void Shell::handle_tile_collision(direction_t dir) { return; } - //shell_t is still alive if(dir & UP || dir & DOWN) { velocity_y *= -1; update_direction(); @@ -115,3 +117,7 @@ void Shell::handle_tile_collision(direction_t dir) { void Shell::collide(__attribute__((unused)) MineDetector *detector) { // don't do anything } + +void Shell::kill() { + active = false; +} diff --git a/src/objects/shell.h b/src/objects/shell.h index 2a559a1..4870de8 100644 --- a/src/objects/shell.h +++ b/src/objects/shell.h @@ -41,6 +41,10 @@ class Shell: public PhysicsBody { return ((uint8_t) -((angle >> (INT_BITS - 8)) - 64)) >> 4; } + void kill(); + + void handle_explosion(); + void handle_collision(PhysicsBody *other); void collide(Tank *tank); void collide(Shell *shell); diff --git a/src/objects/tank.cpp b/src/objects/tank.cpp index 4175cf6..6b2e57c 100644 --- a/src/objects/tank.cpp +++ b/src/objects/tank.cpp @@ -42,8 +42,6 @@ Tank::Tank(const serialized_tank_t *ser_tank, uint8_t id) { start_y = ser_tank->start_y + 1; position_x = TILE_TO_X_COORD(start_x); position_y = TILE_TO_Y_COORD(start_y); - velocity_x = 0; - velocity_y = 0; barrel_rot = 0; tread_rot = 0; shot_cooldown = 0; @@ -51,7 +49,6 @@ Tank::Tank(const serialized_tank_t *ser_tank, uint8_t id) { if(id == 0) { game.player = this; - game.player_alive = true; } game.num_tanks++; @@ -60,7 +57,6 @@ Tank::Tank(const serialized_tank_t *ser_tank, uint8_t id) { Tank::~Tank() { game.num_tanks--; if(this == game.player) { - game.player_alive = false; game.player = nullptr; } } @@ -72,7 +68,7 @@ void Tank::kill() { game.alive_tanks[id] = false; } - delete this; + active = false; } void Tank::process() { @@ -173,6 +169,10 @@ void Tank::handle_collision(PhysicsBody *other) { other->collide(this); } +void Tank::handle_explosion() { + kill(); +} + void Tank::collide(Tank *tank) { //Figure out if the four corners are colliding bool top_right = tank->is_point_inside(position_x + width, position_y); diff --git a/src/objects/tank.h b/src/objects/tank.h index 0399ef0..e1dfdcf 100644 --- a/src/objects/tank.h +++ b/src/objects/tank.h @@ -86,6 +86,8 @@ class Tank: public PhysicsBody { static const uint8_t max_mines[]; static const uint8_t velocities[]; + void handle_explosion(); + void handle_collision(PhysicsBody *other); void collide(Tank *tank); void collide(Shell *shell);