#ifndef BMD_GOBJS_H #define BMD_GOBJS_H #include #include #include #include "FreeImage.h" //the following 3 included for rand(), srand() #include "math.h" #include "stdlib.h" #include "time.h" #include #include using namespace std; using namespace BMD; //enum OrderType {otVOID = 0, otMOVE = 1, otBOMB = 2}; //add use options //enum BombOrderType {otVOID = 0, otDETONATE = 1}; //enum StageDirection {dVOID = 0, dNORTH = -2, dEAST = 1, dSOUTH = 2, dWEST = -1}; //movement directions on the stage static const int dVOID = 0; static const int dNORTH = -2; static const int dEAST = 1; static const int dSOUTH = 2; static const int dWEST = -1; int to_vm_direction(int direction); int from_vm_direction(int vm_dir); static const int tUNKOWN = -1; static const int tEMPTY = 0; static const int tBRICK = 1; static const int tBLOCK = 2; static const int tEXPLOSION = 3; static const int tEXPLOSION_BRICK = 4; int to_vm_tile(int tile); static const float POWERUP_ODDS = 0.2; static const int pVOID = -1; static const int pBOMBS = 0; static const int pSPEED = 1; static const int pPOWER = 2; static const int pKICKING = 3; static const int pDETONATION = 4; static const int pOVER_MAX_INDEX = 5; //whatever the highest powerup index is + 1 int to_vm_powerup(int power_up); int from_vm_powerup(int vm_power_up); enum BomberStatus {bDEAD = -1, bALIVE = 0}; static float NEAR_CRITERION = (float)0.3; //% of duration left before a bomb is considered 'near' an explosion. //some of these are temporary static unsigned int BOMBER_STEPS = 6; //6 steps of motion (time frames) before entering a new tile static unsigned int BOMB_STEPS = 2; //bombs are twice as fast static unsigned int MAX_ALLOWED_BOMBS = 9999; static unsigned int DEFAULT_BOMBS = 2; static unsigned int MIN_MOVE_STEPS = 2; static bool DEFAULT_KICKING = false; static bool DEFAULT_DETONATION = false; static unsigned int DEFAULT_BOMB_DURATION = 65; static unsigned int DEFAULT_BOMB_POWER = 2; static unsigned int STARTING_SCORE = 0; static unsigned int TIMER_DELAY = 200; static unsigned int DEFAULT_EXPLOSION_DURATION = 6; static float DEFAULT_BRICK_DENSITY = (float)0.7; static const char * fname_PLAYER_DATA = "playerdat.txt"; //random integer from 0 to max - 1 function static int rint(int max) { if(max > 0) return rand() % max; return 0; } //returns true with probability ~p static bool prob(float p) { /* float temp = rand() + 1000.0; //added 1000 to increase "precision" return (rint(temp) < p * temp); */ return (double)rand() / double(RAND_MAX) < p; } static int to_vm_movespeed(int step) { return 2*step + 1; } struct Coordinates { unsigned int x; unsigned int y; Coordinates(){x = y = 0;}; inline Coordinates(const unsigned int & i, const unsigned int & j){x = i; y = j;} Value to_value() const; }; /* - Movement between two tiles is controlled by the MovementState. - This object is queried, modified and updated in order to simulate the transition between two tiles. */ class MovementState { private: int direction; int step; //what stage of the movement between two locations is the player in? int total_steps; //total number of steps needed to enter the next tile. void movement(Coordinates & coords) { if(direction > 0) { if(direction == dSOUTH) coords.y++; else coords.x++; //east is also positive } else { if(direction == dNORTH) coords.y--; else coords.x--; //west is negative } } public: void set_steps(int in_steps) { total_steps = in_steps; if(step > total_steps) step--; else if(step < total_steps) //this should always be true step++; } void init(unsigned int in_t_steps) { direction = dVOID; step = 0; total_steps = in_t_steps; } void reset(unsigned int in_direction) { direction = in_direction; step = 0; } int get_direction() const {return direction;} // CHANGE Coordinates reserved(const Coordinates & coords) { Coordinates ret = coords; if(direction * step > 0) return dest(ret); else if(direction * step < 0) return source(ret); return ret; //this should not be reachable } Coordinates dest(const Coordinates & coords) { Coordinates ret = coords; movement(ret); return ret; } //finds the previous Coordinates that would arive at these, opposite of dest Coordinates source(const Coordinates & coords) { Coordinates ret = coords; direction = -1 * direction; //reverse direction movement(ret); direction = -1 * direction; //find ret, reset direction return ret; } bool tick() { if(direction > 0) { if(step >= total_steps) return true; step++;} else if(direction < 0) { if(step <= -total_steps) return true; step--;} return false; //no movement occured } //the directions are set up so that the signs are opposite, or the current is 0 inline bool allowed_redirect(int new_direction) {return !step || !direction || (direction + new_direction == 0);} // CHANGE bool change_direction(int new_direction) { if(allowed_redirect(new_direction)) { direction = new_direction; return true; //successful change } return false; //succssful change } //if the player's "step" variable is zero, the player is at the center of a tile inline bool is_displaced() const {return (step != 0);} //kept giving performance warning //once a tile change has occured, this needs to be called // // updates the MovementState, but also a bomber's coordinates (which are input) // void perform_tile_change()//Coordinates & in_coords) { step *= -1; //coords = in_coords; } //being in a certain step of motion, or moving in a direction implies one is moving inline bool moving(){return (step || direction);} //movement fraction used for rendering double move_fracX() {if(direction == dWEST || direction == dEAST) return ((double)step)/(2*total_steps + 1); return 0.0;} double move_fracY() {if(direction == dNORTH || direction == dSOUTH) return ((double)step)/(2*total_steps + 1); return 0.0;} }; struct BomberOrders { bool bomb_place; bool detonate; //other on use bools int move_direction; BomberOrders() { bomb_place = detonate = false; move_direction = dVOID;} BomberOrders(Value v); void clear() { bomb_place = detonate = false; move_direction = dVOID;} bool move_ordered() {return (move_direction != 0);} }; struct BombOrders { bool detonate; BombOrders() { detonate = false;} }; struct BomberProperties { unsigned int max_bombs; unsigned int move_steps; //half the number of steps to move to next tile bool kicking; bool detonation; BomberProperties() {max_bombs = DEFAULT_BOMBS; move_steps = BOMBER_STEPS; kicking = DEFAULT_KICKING; detonation = DEFAULT_DETONATION;} BomberProperties(unsigned int in_max_bombs, unsigned int in_move_steps) {max_bombs = in_max_bombs; move_steps = in_move_steps; kicking = DEFAULT_KICKING; detonation = DEFAULT_DETONATION;} inline void attempt_inc_bombs() { if(max_bombs < MAX_ALLOWED_BOMBS) max_bombs++;} inline bool attempt_inc_move() { if(move_steps > MIN_MOVE_STEPS) {move_steps--; return true;} return false; } inline void give_kicking() { kicking = true;} inline void give_detonation() { detonation = true;} }; struct BombProperties { unsigned int duration; unsigned int power; unsigned int move_steps; BombProperties() {duration = DEFAULT_BOMB_DURATION; power = DEFAULT_BOMB_POWER; move_steps = BOMB_STEPS;} BombProperties(unsigned int in_duration, unsigned int in_power) {duration = in_duration; power = in_power;} //used only for power up acquisition at the moment void increase_power() {power++;} }; struct ScoringProperties { unsigned int kills; unsigned int deaths; unsigned int score; ScoringProperties() { kills = deaths = 0; score = STARTING_SCORE; } ScoringProperties(unsigned int in_k, unsigned int in_d, unsigned int in_s) { kills = in_k; deaths = in_d; score = in_s; } }; struct ActiveProperties { bool kills; bool deaths; bool score; ActiveProperties() { kills = deaths = score = false; } ActiveProperties(bool in_k, bool in_d, bool in_s) { kills = in_k; deaths = in_d; score = in_s; } }; class Bomber { public: int id; Coordinates coords; MovementState mstate; BomberOrders orders; BomberProperties prop; BombProperties bomb_prop; BomberStatus status; ScoringProperties score; unsigned int team_id; unsigned int nbombs; shared_ptr actor; /* Bomber() { mstate.init() } */ Bomber(unsigned int in_id) : actor(new Actor()) { team_id = 0; id = in_id; status = bALIVE; mstate.init(prop.move_steps); orders.clear(); nbombs = 0; //cerr << "Testing Constructor used for Bomber." << endl; } inline int get_id(){return id;} inline unsigned int get_team_id(){return team_id;} inline void set_max_bombs(unsigned int in_max){prop.max_bombs = in_max;} bool bombs_available(){return nbombs < prop.max_bombs;} void kill(){status = bDEAD;} bool is_alive(){return status != bDEAD;} void gain_powerup(int ptype) { switch(ptype) { case pBOMBS: prop.attempt_inc_bombs(); break; case pSPEED: if(prop.attempt_inc_move()) mstate.set_steps(prop.move_steps); break; case pPOWER: bomb_prop.increase_power(); break; case pKICKING: prop.give_kicking(); break; case pDETONATION: prop.give_detonation(); break; default: break; } } //for testing void rezz() {status = bALIVE;} // convert the bomber to a value Value to_value() const; void call_main() { call_with_catch("main"); } void call_init() { vector args; args.push_back(coords.to_value()); call_with_catch("on_init", args); } // convert the bomber's status to a value Value status_value() const; // DONE: add method for when a bomb explodes void notify_explosion(int bomb_id) { // send a call vector args; args.push_back(Value(bomb_id)); call_with_catch("on_bomb_detonate", args); } void notify_death() { call_with_catch("on_death"); } void notify_teammate_death() { call_with_catch("on_teammate_death"); } // calls the actor vm to get a move, and stores it // in the bomber orders void request_orders(Value input) { vector args; args.push_back(input); try { Value vm_orders = actor->call_function("on_move_request", args); orders = BomberOrders(vm_orders); } catch (runtime_error e) { cerr << "in bomber " << id << ": " << e.what() << endl; actor->get_vm()->reset(); orders = BomberOrders(); } } private: void call_with_catch(string func_name) { vector args; call_with_catch(func_name, args); } void call_with_catch(string func_name, const vector& args) { try { actor->call_function(func_name, args); } catch (runtime_error e) { cerr << "in bomber " << id << ": " << e.what() << endl; actor->get_vm()->reset(); } } }; class Bomb { //unsigned int initial_duration; unsigned int remaining_duration; unsigned int id; unsigned int owner_id; //the ID of the bomber that placed this bomb public: Coordinates coords; MovementState mstate; BombOrders orders; BombProperties prop; bool gone; //unsigned int team_id; Bomb() {gone = false; mstate.init(prop.move_steps);} //testing? Bomb(const unsigned int in_id) { gone = false; id = in_id; prop.duration = remaining_duration = 10000; mstate.init(prop.move_steps); cerr << "Testing Constructor used for Bomb." << endl; } void set( const unsigned int & in_id, const unsigned int & in_owner_id, const BombProperties & in_prop) { gone = false; id = in_id; owner_id = in_owner_id; prop = in_prop; remaining_duration = prop.duration; mstate.init(prop.move_steps); } int get_id() const {return id;} int get_owner_id() const {return owner_id;} bool tick() { remaining_duration--; return (!remaining_duration); //if the 'duration' is zero, return true, signaling a detonation } bool near_explosion(){return (prop.duration * NEAR_CRITERION) >= (float)remaining_duration;} Value to_value(int bomber_id) const; }; class PowerUp { public: int id; int power_type_index; Coordinates coords; //am i having powerups expire? Value to_value() const; }; struct ExplosionDat { Coordinates center; unsigned int dN, dS, dW, dE; //the distance north, south, west, east of the explosion ExplosionDat() {dN = dS = dW = dE = 0;} }; class Explosion { public: unsigned int remaining_duration; unsigned int total_duration; ExplosionDat explosion_data; Explosion(ExplosionDat in_data) { remaining_duration = total_duration = DEFAULT_EXPLOSION_DURATION; explosion_data = in_data; } bool tick() { remaining_duration--; return (!remaining_duration); //if the 'duration' is zero, return true, signaling a fade } }; struct RandMapSettings { unsigned int H, W, start_spots; float density; RandMapSettings() { H = 64; W = 48; start_spots = 4; density = 0.8f;} RandMapSettings( unsigned int in_H, unsigned int in_W, unsigned int in_s_sp, float in_d) { H = in_H; W = in_W; start_spots = in_s_sp; density = in_d;} }; struct GameSettings { int n_bombers; vector vm_bomber_files; vector bomber_teams; char * map_name; //map_name = 0 ==> random map RandMapSettings rmap_settings; ScoringProperties vict_s_prop; ActiveProperties vict_a_prop; }; /* TO DO: - add power up drops to tile.explode() ----------------- DONE - add a random number generator or get one from wx ----- DONE - add real powerups - graphics in 136 ----------------------------------------- more bitmaps indexing taken care of in stage (make sure) ------------- DONE pickup in engine in bomber code, create_powerup(coords) - DONE (also 'use' in engine-bomber code) ------------------ DONE *for now: speed, power, kicking, +bombs(?), detonation - add more later - DONE - implement maps and/or random maps - just a stage function - random is ez ------ DONE - add the widgets to tweak settings in 136 - zoom would be nice - spiff up the bitmaps - improve fresh_id system ------------------------------------------------------- DONE */ struct VMBomber { unsigned int id; unsigned int team_id; Coordinates coords; }; struct VMBomb { bool is_callers; Coordinates coords; }; struct VMPowerUp { unsigned int type; Coordinates coords; }; struct VMTile { int type; }; struct VMBomberStatus { Coordinates position; //coords int player_id; //id int team_id; //team_id int move_rate; //mstate.move_steps bool has_kick; //prop.kicking bool has_detonate; //prop.detonation int radius; //bomb_prop.power int max_bombs; //prop.max_bombs int deployed_bombs; //nbombs }; struct VMInput { vector vm_bombers; vector vm_bombs; vector vm_powerups; unsigned int H,W; VMTile * vm_tiles; //access is based on i*H + j VMBomberStatus vm_bomber_status; VMInput() {vm_tiles = 0;}; }; #endif