#include #include #include #include "outfit.h" #include "physics.h" #include "lephisto.h" #include "log.h" #include "rng.h" #include "pilot.h" #include "collision.h" #include "player.h" #include "spfx.h" #include "weapon.h" #define weapon_isSmart(w) (w->think) #define VOICE_PRIORITY_BOLT 10 // Default. #define VOICE_PRIORITY_AMMO 8 // Higher. // 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. alVoice* voice; // Virtual voise. // 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; } void weapons_delay(unsigned int delay) { int i; for(i = 0; i < nwbackLayer; i++) wbackLayer[i]->timer += delay; for(i = 0; i < nwfrontLayer; i++) wfrontLayer[i]->timer += delay; } // Seeker brain, You get what you pay for. :) static void think_seeker(Weapon* w) { double diff; if(w->target == w->parent) return; // HEY! Self harm is not allowed. Pilot* p = pilot_get(w->target); if(p == NULL) { limit_speed(&w->solid->vel, w->outfit->u.amm.speed); return; } // Ammo isn't locked on yet.. if(SDL_GetTicks() > (w->timer + w->outfit->u.amm.lockon)) { diff = angle_diff(w->solid->dir, vect_angle(&w->solid->pos, &p->solid->pos)); w->solid->dir_vel = 10 * diff * w->outfit->u.amm.turn; // Face the target. if(w->solid->dir_vel > w->outfit->u.amm.turn) w->solid->dir_vel = w->outfit->u.amm.turn; else if(w->solid->dir_vel < -w->outfit->u.amm.turn) w->solid->dir_vel = -w->outfit->u.amm.turn; } vect_pset(&w->solid->force, w->outfit->u.amm.thrust, w->solid->dir); limit_speed(&w->solid->vel, w->outfit->u.amm.speed); } // Smart seeker brain. Much better at homing. static void think_smart(Weapon* w) { double diff; Vec2 tv, sv; if(w->target == w->parent) return; // No self shooting here. Pilot* p = pilot_get(w->target); // No null pilots.. if(p == NULL) { limit_speed(&w->solid->vel, w->outfit->u.amm.speed); return; } if(SDL_GetTicks() > (w->timer + w->outfit->u.amm.lockon)) { vect_cset(&tv, VX(p->solid->pos) + VX(p->solid->vel), VY(p->solid->pos) + VY(p->solid->vel)); vect_cset(&sv, VX(w->solid->pos) + VX(w->solid->vel), VY(w->solid->pos) + VY(w->solid->vel)); diff = angle_diff(w->solid->dir, vect_angle(&tv, &sv)); w->solid->dir_vel = 10*diff*w->outfit->u.amm.turn; // Face the target. if(w->solid->dir_vel > w->outfit->u.amm.turn) w->solid->dir_vel = w->outfit->u.amm.turn; else if(w->solid->dir_vel < -w->outfit->u.amm.turn) w->solid->dir_vel = -w->outfit->u.amm.turn; } vect_pset(&w->solid->force, w->outfit->u.amm.thrust, w->solid->dir); limit_speed(&w->solid->vel, w->outfit->u.amm.speed); } // 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) { // Most missiles behave the same. case OUTFIT_TYPE_MISSILE_SEEK_AMMO: case OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO: case OUTFIT_TYPE_MISSILE_SWARM_AMMO: case OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO: if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->u.amm.duration)) { weapon_destroy(wlayer[i], layer); continue; } break; case OUTFIT_TYPE_BOLT: // Check see if it exceeds distance. case OUTFIT_TYPE_TURRET_BOLT: if(SDL_GetTicks() > wlayer[i]->timer) { weapon_destroy(wlayer[i],layer); continue; } break; default: 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; glTexture* gfx; gfx = outfit_gfx(w->outfit); // Get the sprite corresponding to the direction facing. gl_getSpriteFromDir(&sx, &sy, gfx, w->solid->dir); gl_blitSprite(gfx, 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; glTexture* gfx; gfx = outfit_gfx(w->outfit); gl_getSpriteFromDir(&wsx, &wsy, gfx, w->solid->dir); for(i = 0; i < pilots; i++) { psx = pilot_stack[i]->tsx; psy = pilot_stack[i]->tsy; if(w->parent == pilot_stack[i]->id) continue; // Hey! That's you. if((weapon_isSmart(w)) && (pilot_stack[i]->id == w->target) && CollideSprite(gfx, 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(gfx, 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); // Update the sound. if(w->voice) voice_update(w->voice, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); } // 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); spfx_add(outfit_spfx(w->outfit), VX(w->solid->pos), VY(w->solid->pos), VX(p->solid->vel), VY(p->solid->vel), SPFX_LAYER_BACK); } else spfx_add(outfit_spfx(w->outfit), VX(w->solid->pos), VY(w->solid->pos), VX(p->solid->vel), VY(p->solid->vel), SPFX_LAYER_FRONT); 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->solid, w->parent, outfit_dmgShield(w->outfit), outfit_dmgShield(w->outfit)); // 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->u.blt.accuracy/2., outfit->u.blt.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->u.blt.speed*cos(rdir), outfit->u.blt.speed*sin(rdir)); w->timer += 1000*(unsigned int)outfit->u.blt.range/outfit->u.blt.speed; w->solid = solid_create(mass, rdir, pos, &v); w->voice = sound_addVoice(VOICE_PRIORITY_BOLT, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, w->outfit->u.blt.sound, 0); break; case OUTFIT_TYPE_MISSILE_SEEK_AMMO: mass = w->outfit->mass; w->solid = solid_create(mass, dir, pos, vel); w->think = think_seeker; // Eeek!!! w->voice = sound_addVoice(VOICE_PRIORITY_AMMO, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, w->outfit->u.amm.sound, 0); break; case OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO: mass = w->outfit->mass; w->solid = solid_create(mass, dir, pos, vel); w->think = think_smart; // Smartass. w->voice = sound_addVoice(VOICE_PRIORITY_AMMO, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, w->outfit->u.amm.sound, 0); break; case OUTFIT_TYPE_TURRET_BOLT: if(w->parent != w->target) rdir = vect_angle(pos, &pilot_get(w->target)->solid->pos); rdir += RNG(-outfit->u.blt.accuracy/2., outfit->u.blt.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->u.blt.speed*cos(rdir), outfit->u.blt.speed*sin(rdir)); w->timer += 1000*(unsigned int)outfit->u.blt.range / outfit->u.blt.speed; w->solid = solid_create(mass, rdir, pos, &v); w->voice = sound_addVoice(VOICE_PRIORITY_BOLT, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, w->outfit->u.blt.sound, 0); break; default: // Just dump it where the player is. w->voice = NULL; 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) && !outfit_isTurret(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) { sound_delVoice(w->voice); 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); }