#include #include #include #include "outfit.h" #include "physics.h" #include "main.h" #include "log.h" #include "rng.h" #include "pilot.h" #include "collision.h" #include "player.h" #include "weapon.h" #define weapon_isSmart(w) (w->think) // Some stuff from pilot. extern Pilot** pilot_stack; extern int pilots; // Ai stuff. extern void ai_attacked(Pilot* attacked, const unsigned int attacker); typedef struct Weapon { Solid* solid; // Actually has its own solid. :D unsigned int parent; // The pilot that just shot at you! unsigned int target; // Target to hit. Only used by seeking stuff. const Outfit* outfit; // Related outfit that fired. unsigned int timer; // Mainly used to see when the weapon was fired. // Update position and render. void(*update)(struct Weapon*, const double, WeaponLayer); // Position update and render. void(*think)(struct Weapon*); // Some missiles need to be inteligent. } Weapon; // Behind Pilot layer. static Weapon** wbackLayer = NULL; // Behind pilots. static int nwbackLayer = 0; // Number of elements. static int mwbackLayer = 0; // Allocated memory size. // Behind player layer. static Weapon** wfrontLayer = NULL; // Behind pilots. static int nwfrontLayer = 0; // Number of elements. static int mwfrontLayer = 0; // Allocated memory size. static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, const unsigned int parent, const unsigned int target); static void weapon_render(const Weapon* w); static void weapons_updateLayer(const double dt, const WeaponLayer layer); static void weapon_update(Weapon* w, const double dt, WeaponLayer layer); static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer); static void weapon_destroy(Weapon* w, WeaponLayer layer); static void weapon_free(Weapon* w); // Think. static void think_seeker(Weapon* w); // Draw the minimap weapons (player.c). #define PIXEL(x,y) if((shape == RADAR_RECT && ABS(x) < w/2. && ABS(y)solid->pos.x - player->solid->pos.x) / res; y = (wbackLayer[i]->solid->pos.y - player->solid->pos.y) / res; PIXEL(x,y); } for(i = 0; i < nwfrontLayer; i++) { x = (wfrontLayer[i]->solid->pos.x - player->solid->pos.x) / res; y = (wfrontLayer[i]->solid->pos.y - player->solid->pos.y) / res; PIXEL(x,y); } } #undef PIXEL // Pause/Unpause the weapon system. void weapons_pause(void) { int i; unsigned int t = SDL_GetTicks(); for(i = 0; i < nwbackLayer; i++) wbackLayer[i]->timer -= t; for(i = 0; i < nwfrontLayer; i++) wfrontLayer[i]->timer -= t; } void weapons_unpause(void) { int i; unsigned int t = SDL_GetTicks(); for(i = 0; i < nwbackLayer; i++) wbackLayer[i]->timer += t; for(i = 0; i < nwfrontLayer; i++) wfrontLayer[i]->timer += t; } static void think_seeker(Weapon* w) { if(w->target == w->parent) return; // HEY! Self harm is not allowed. Pilot* p = pilot_get(w->target); if(p == NULL) return; // Can't do anything with no pilots. double diff = angle_diff(w->solid->dir, vect_angle(&w->solid->pos, &p->solid->pos)); w->solid->dir_vel = 10 * diff * w->outfit->turn; // Face the target. if(w->solid->dir_vel > w->outfit->turn) w->solid->dir_vel = w->outfit->turn; else if(w->solid->dir_vel < -w->outfit->turn) w->solid->dir_vel = -w->outfit->turn; vect_pset(&w->solid->force, w->outfit->thrust, w->solid->dir); if(VMOD(w->solid->vel) > w->outfit->speed) // We should not go any faster. vect_pset(&w->solid->vel, w->outfit->speed, VANGLE(w->solid->vel)); } // Update all the weapon layers. void weapons_update(const double dt) { weapons_updateLayer(dt, WEAPON_LAYER_BG); weapons_updateLayer(dt, WEAPON_LAYER_FG); } // Update all weapons in the layer. static void weapons_updateLayer(const double dt, const WeaponLayer layer) { Weapon** wlayer; int* nlayer; switch(layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; } int i; Weapon* w; for(i = 0; i < (*nlayer); i++) { w = wlayer[i]; switch(wlayer[i]->outfit->type) { case OUTFIT_TYPE_MISSILE_SEEK_AMMO: if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->duration)) { weapon_destroy(wlayer[i], layer); continue; } break; default: // Check see if it exceeds distance. if(SDL_GetTicks() > (wlayer[i]->timer + 1000*(unsigned int) wlayer[i]->outfit->range/wlayer[i]->outfit->speed)) { weapon_destroy(wlayer[i],layer); continue; } break; } weapon_update(wlayer[i], dt, layer); // If the weapon has been deleted we are going to have to hold back one. if(w != wlayer[i]) i--; } } // Render all the weapons. void weapons_render(const WeaponLayer layer) { Weapon** wlayer; int* nlayer; int i; switch(layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; } for(i = 0; i < (*nlayer); i++) weapon_render(wlayer[i]); } // Render the weapons. static void weapon_render(const Weapon* w) { int sx, sy; // Get the sprite corresponding to the direction facing. gl_getSpriteFromDir(&sx, &sy, w->outfit->gfx_space, w->solid->dir); gl_blitSprite(w->outfit->gfx_space, w->solid->pos.x, w->solid->pos.y, sx, sy, NULL); } // Update the weapon. static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { int i, wsx, wsy, psx, psy; gl_getSpriteFromDir(&wsx, &wsy, w->outfit->gfx_space, w->solid->dir); for(i = 0; i < pilots; i++) { gl_getSpriteFromDir(&psx, &psy, pilot_stack[i]->ship->gfx_space, pilot_stack[i]->solid->dir); if(w->parent == pilot_stack[i]->id) continue; // Hey! That's you. if((weapon_isSmart(w)) && (pilot_stack[i]->id == w->target) && CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos, pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) { weapon_hit(w, pilot_stack[i], layer); return; } else if(!weapon_isSmart(w) && !areAllies(pilot_get(w->parent)->faction, pilot_stack[i]->faction) && CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos, pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) { weapon_hit(w, pilot_stack[i], layer); return; } } if(weapon_isSmart(w)) (*w->think)(w); (*w->solid->update)(w->solid, dt); } // Good shot. static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer) { // Someone should let the ai know it's been attacked. if(!pilot_isPlayer(p)) ai_attacked(p, w->parent); if(w->parent == PLAYER_ID) // Make hostile to player. pilot_setFlag(p, PILOT_HOSTILE); // Let the ship know that is should take some kind of damage. pilot_hit(p, w->outfit->damage_shield, w->outfit->damage_armour); // We don't need the weapon particle any longer. weapon_destroy(w, layer); } static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, unsigned int parent, const unsigned int target) { Vec2 v; double mass = 1; // Presumer lasers have a mass of 1. double rdir = dir; // Real direction (accuracy). Weapon* w = MALLOC_L(Weapon); w->parent = parent; // Non-Changeable. w->target = target; // Non-Changeable. w->outfit = outfit; // Non-Changeable. w->update = weapon_update; w->timer = SDL_GetTicks(); w->think = NULL; switch(outfit->type) { case OUTFIT_TYPE_BOLT: // Need accuracy and speed based on player. -- Another contribution from VLack. rdir += RNG(-outfit->accuracy/2., outfit->accuracy/2.)/180.*M_PI; if((rdir > 2.*M_PI) || (rdir < 0.)) rdir = fmod(rdir, 2.*M_PI); vectcpy(&v, vel); vect_cadd(&v, outfit->speed*cos(rdir), outfit->speed*sin(rdir)); w->solid = solid_create(mass, rdir, pos, &v); break; case OUTFIT_TYPE_MISSILE_SEEK_AMMO: mass = w->outfit->mass; w->solid = solid_create(mass, dir, pos, vel); w->think = think_seeker; // Eeek!!! break; default: // Just dump it where the player is. w->solid = solid_create(mass, dir, pos, vel); break; } return w; } // Add a new weapon. void weapon_add(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, unsigned int parent, unsigned int target) { if(!outfit_isWeapon(outfit) && !outfit_isAmmo(outfit)) { ERR("Trying to create a weapon from a non-Weapon type Outfit"); return; } WeaponLayer layer = (parent == PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG; Weapon* w = weapon_create(outfit, dir, pos, vel, parent, target); // Set the propper layer. Weapon** curLayer = NULL; int* mLayer = NULL; int* nLayer = NULL; switch(layer) { case WEAPON_LAYER_BG: curLayer = wbackLayer; nLayer = &nwbackLayer; mLayer = &mwbackLayer; break; case WEAPON_LAYER_FG: curLayer = wfrontLayer; nLayer = &nwfrontLayer; mLayer = &mwfrontLayer; break; default: ERR("Invalid WEAPON_LAYER specified."); return; } if(*mLayer > *nLayer) // More memory allocated than what we need. curLayer[(*nLayer)++] = w; else { // Need to allocate more memory. switch(layer) { case WEAPON_LAYER_BG: curLayer = wbackLayer = realloc(curLayer, (++(*mLayer))*sizeof(Weapon*)); break; case WEAPON_LAYER_FG: curLayer = wfrontLayer = realloc(curLayer, (++(*mLayer))*sizeof(Weapon*)); break; } curLayer[(*nLayer)++] = w; } } // Destroy the weapon. static void weapon_destroy(Weapon* w, WeaponLayer layer) { int i; Weapon** wlayer; int* nlayer; switch(layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; } for(i = 0; wlayer[i] != w; i++); // Get us to the current posision. weapon_free(wlayer[i]); wlayer[i] = NULL; (*nlayer)--; for(; i < (*nlayer); i++) wlayer[i] = wlayer[i+1]; } // Clear the weapon. static void weapon_free(Weapon* w) { solid_free(w->solid); free(w); } // Clear all the weapons, do not free the layers. void weapon_clear(void) { int i; for(i = 0; i < nwbackLayer; i++) weapon_free(wbackLayer[i]); nwbackLayer = 0; for(i = 0; i < nwfrontLayer; i++) weapon_free(wfrontLayer[i]); nwfrontLayer = 0; } void weapon_exit(void) { weapon_clear(); free(wbackLayer); free(wfrontLayer); }