
[Change] blit functions use doubles instead of vectors. [Change] Took some time out to clean up space.c bringing it up to xml.h standards.
370 lines
11 KiB
C
370 lines
11 KiB
C
#include <math.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
|
|
#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)<h/2.) || \
|
|
(shape == RADAR_CIRCLE && (((x)*(x)+(y)*(y))<rc))) glVertex2i((x),(y))
|
|
void weapon_minimap(const double res, const double w, const double h, const RadarShape shape) {
|
|
int i, rc;
|
|
double x, y;
|
|
|
|
if(shape == RADAR_CIRCLE) rc = (int)(w*w);
|
|
|
|
for(i = 0; i < nwbackLayer; i++) {
|
|
x = (wbackLayer[i]->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);
|
|
}
|
|
|