439 lines
12 KiB
C
439 lines
12 KiB
C
#include <string.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h> // We don't need this?
|
|
|
|
#include "main.h"
|
|
#include "log.h"
|
|
#include "weapon.h"
|
|
#include "pack.h"
|
|
#include "xml.h"
|
|
#include "pilot.h"
|
|
|
|
#define XML_ID "Fleets" // XML section identifier.
|
|
#define XML_FLEET "fleet"
|
|
|
|
#define FLEET_DATA "../dat/fleet.xml"
|
|
|
|
// Stack of pilot id's to assure uniqueness.
|
|
static unsigned int pilot_id = PLAYER_ID;
|
|
|
|
// Stack of pilots - yes, they come in stacks now.
|
|
Pilot** pilot_stack = NULL; // Not static, it is used in player.c and weapon.c and ai.c
|
|
int pilots = 0;
|
|
extern Pilot* player;
|
|
|
|
// Stack of fleets.
|
|
static Fleet* fleet_stack = NULL;
|
|
static int nfleets = 0;
|
|
|
|
// External.
|
|
extern void ai_destroy(Pilot* p); // Ai.
|
|
extern void ai_think(Pilot* pilot); // Ai.c
|
|
extern void player_think(Pilot* pilot); // Player.c
|
|
extern int gui_load(const char* name); // Player.c
|
|
// Internal.
|
|
static void pilot_update(Pilot* pilot, const double dt);
|
|
void pilot_render(Pilot* pilot);
|
|
static void pilot_free(Pilot* p);
|
|
static Fleet* fleet_parse(const xmlNodePtr parent);
|
|
|
|
// Get the next pilot based on id.
|
|
unsigned int pilot_getNext(const unsigned int id) {
|
|
// Regular search.
|
|
int i;
|
|
for(i = 0; i < pilots; i++)
|
|
if(pilot_stack[i]->id == id)
|
|
break;
|
|
|
|
if(i == pilots-1) return PLAYER_ID;
|
|
|
|
return pilot_stack[i+1]->id;
|
|
}
|
|
|
|
// Get the nearest enemy to the pilot -- Tamir's (insightful) request.
|
|
unsigned int pilot_getNearest(const Pilot* p) {
|
|
unsigned int tp;
|
|
int i;
|
|
double d, td;
|
|
|
|
for(tp = 0, d = 0., i = 0; i < pilots; i++)
|
|
if(areEnemies(p->faction, pilot_stack[i]->faction)) {
|
|
td = vect_dist(&pilot_stack[i]->solid->pos, &p->solid->pos);
|
|
if((!tp) || (td < d)) {
|
|
d = td;
|
|
tp = pilot_stack[i]->id;
|
|
}
|
|
}
|
|
return tp;
|
|
}
|
|
|
|
// Get the nearest hostile enemy to the player.
|
|
unsigned pilot_getHostile(void) {
|
|
unsigned int tp;
|
|
int i;
|
|
double d, td;
|
|
for(tp = PLAYER_ID, d = 0., i = 0; i < pilots; i++)
|
|
if(pilot_isFlag(pilot_stack[i], PILOT_HOSTILE)) {
|
|
td = vect_dist(&pilot_stack[i]->solid->pos, &player->solid->pos);
|
|
if((tp == PLAYER_ID) || (td < d)) {
|
|
d = td;
|
|
tp = pilot_stack[i]->id;
|
|
}
|
|
}
|
|
return tp;
|
|
}
|
|
|
|
// Pull a pilot out of the pilot_stack based on id.
|
|
Pilot* pilot_get(const unsigned int id) {
|
|
// Regular search.
|
|
int i;
|
|
for(i = 0; i < pilots; i++)
|
|
if(pilot_stack[i]->id == id)
|
|
return pilot_stack[i];
|
|
return NULL;
|
|
|
|
if(id == 0) return player;
|
|
#if 0
|
|
// Dichotomical search.
|
|
int i, n;
|
|
for(i = 0, n = pilots/2; n > 0; n /= 2)
|
|
i += (pilot_stack[i+n]->id > id) ? 0 : n;
|
|
return (pilot_stack[i]->id == id) ? pilot_stack[i] : NULL;
|
|
#endif
|
|
}
|
|
|
|
// Mkay, this is how we shoot. Listen up.
|
|
void pilot_shoot(Pilot* p, const int secondary) {
|
|
int i;
|
|
if(!secondary) {
|
|
// Primary weapons.
|
|
if(!p->outfits) return; // No outfits.
|
|
|
|
for(i = 0; i < p->noutfits; i++) // Cycle through outfits to find weapons.
|
|
if(outfit_isWeapon(p->outfits[i].outfit) || // Is a weapon or launch?
|
|
outfit_isLauncher(p->outfits[i].outfit))
|
|
// Ready to shoot again.
|
|
if((SDL_GetTicks()-p->outfits[i].timer) > (p->outfits[i].outfit->delay/p->outfits[i].quantity))
|
|
// Different weapons have different behaviours.
|
|
switch(p->outfits[i].outfit->type) {
|
|
case OUTFIT_TYPE_BOLT:
|
|
weapon_add(p->outfits[i].outfit, p->solid->dir, &p->solid->pos,
|
|
&p->solid->vel, p->id, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG);
|
|
p->outfits[i].timer = SDL_GetTicks();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Damage the pilot.
|
|
void pilot_hit(Pilot* p, const double damage_shield, const double damage_armor) {
|
|
if(p->shield - damage_shield > 0.)
|
|
p->shield -= damage_shield;
|
|
else if(p->shield > 0.) {
|
|
// Shields can take part of the blow.
|
|
p->armor -= p->shield/damage_shield*damage_armor;
|
|
p->shield = 0.;
|
|
}
|
|
else if(p->armor-damage_armor > 0.)
|
|
p->armor -= damage_armor;
|
|
else
|
|
p->armor = 0.;
|
|
}
|
|
|
|
// Render the pilot.
|
|
void pilot_render(Pilot* p) {
|
|
int sx, sy;
|
|
|
|
// Get the sprite corresponding to the direction facing.
|
|
gl_getSpriteFromDir(&sx, &sy, p->ship->gfx_space, p->solid->dir);
|
|
|
|
gl_blitSprite(p->ship->gfx_space, &p->solid->pos, sx, sy, NULL);
|
|
}
|
|
|
|
// Update the pilot.
|
|
static void pilot_update(Pilot* pilot, const double dt) {
|
|
// Regeneration.
|
|
if(pilot->armor < pilot->armor_max)
|
|
pilot->armor += pilot->ship->armor_regen*dt;
|
|
else
|
|
pilot->shield += pilot->ship->shield_regen*dt;
|
|
|
|
if(pilot->armor > pilot->armor_max) pilot->armor = pilot->armor_max;
|
|
if(pilot->shield > pilot->shield_max) pilot->shield = pilot->shield_max;
|
|
|
|
if((pilot->solid->dir > 2.*M_PI) || (pilot->solid->dir < 0.0))
|
|
pilot->solid->dir = fmod(pilot->solid->dir, 2.*M_PI);
|
|
|
|
// Update the solid.
|
|
pilot->solid->update(pilot->solid, dt);
|
|
|
|
if(VMOD(pilot->solid->vel) > pilot->ship->speed) {
|
|
// Should not go faster.
|
|
vect_pset(&pilot->solid->vel, pilot->ship->speed, VANGLE(pilot->solid->vel));
|
|
}
|
|
|
|
pilot_render(pilot);
|
|
}
|
|
|
|
// ==Init pilot.===========================================
|
|
// ship : Ship pilot is flying.
|
|
// name : Pilot's name, if NULL, ships name will be used.
|
|
// dir : Initial facing direction. (radians)
|
|
// vel : Initial velocity.
|
|
// pos : Initial position.
|
|
// flags : Tweaking the pilot.
|
|
// ========================================================
|
|
void pilot_init(Pilot* pilot, Ship* ship, char* name, Faction* faction, AI_Profile* ai,
|
|
const double dir, const Vec2* pos, const Vec2* vel, const int flags) {
|
|
|
|
if(flags & PILOT_PLAYER) // Player is ID 0
|
|
pilot->id = PLAYER_ID;
|
|
else
|
|
pilot->id = ++pilot_id; // New unique pilot id based on pilot_id, Can't be 0.
|
|
|
|
pilot->ship = ship;
|
|
pilot->name = strdup((name == NULL) ? ship->name : name);
|
|
|
|
// Faction.
|
|
pilot->faction = faction;
|
|
|
|
// AI.
|
|
pilot->ai = ai;
|
|
pilot->tcontrol = 0;
|
|
pilot->flags = 0;
|
|
|
|
// Solid.
|
|
pilot->solid = solid_create(ship->mass, dir, pos, vel);
|
|
|
|
// Max shields armor.
|
|
pilot->armor_max = ship->armor;
|
|
pilot->shield_max = ship->shield;
|
|
pilot->energy_max = ship->energy;
|
|
pilot->armor = pilot->armor_max;
|
|
pilot->shield = pilot->shield_max;
|
|
pilot->energy = pilot->energy_max;
|
|
|
|
|
|
// Initially idle.
|
|
pilot->task = NULL;
|
|
|
|
// Outfits.
|
|
pilot->outfits = NULL;
|
|
ShipOutfit* so;
|
|
if(ship->outfit) {
|
|
pilot->noutfits = 0;
|
|
for(so = ship->outfit; so; so = so->next) {
|
|
pilot->outfits = realloc(pilot->outfits, (pilot->noutfits+1)*sizeof(PilotOutfit));
|
|
pilot->outfits[pilot->noutfits].outfit = so->data;
|
|
pilot->outfits[pilot->noutfits].quantity = so->quantity;
|
|
pilot->outfits[pilot->noutfits].timer = 0;
|
|
(pilot->noutfits)++;
|
|
}
|
|
}
|
|
|
|
if(flags & PILOT_PLAYER) {
|
|
pilot->think = player_think; // Players don't need to thing! :P
|
|
pilot->render = NULL;
|
|
pilot_setFlag(pilot, PILOT_PLAYER); // It's a player!
|
|
player = pilot;
|
|
gui_load(pilot->ship->gui); // Load the GUI.
|
|
} else {
|
|
pilot->think = ai_think;
|
|
pilot->render = pilot_render;
|
|
}
|
|
pilot->update = pilot_update;
|
|
|
|
}
|
|
|
|
// Create a new pilot - Params are same as pilot_init. Return pilot's id.
|
|
unsigned int pilot_create(Ship* ship, char* name, Faction* faction, AI_Profile* ai, const double dir,
|
|
const Vec2* pos, const Vec2* vel, const int flags) {
|
|
Pilot* dyn = MALLOC_L(Pilot);
|
|
if(dyn == NULL) {
|
|
WARN("Unable to allocate memory.");
|
|
return 0;
|
|
}
|
|
pilot_init(dyn, ship, name, faction, ai, dir, pos, vel, flags);
|
|
|
|
if(flags & PILOT_PLAYER) {
|
|
// Player.
|
|
if(!pilot_stack) {
|
|
pilot_stack = MALLOC_L(Pilot*);
|
|
pilots = 1;
|
|
}
|
|
pilot_stack[0] = dyn;
|
|
} else {
|
|
// Add to the stack.
|
|
pilot_stack = realloc(pilot_stack, ++pilots*sizeof(Pilot*));
|
|
pilot_stack[pilots-1] = dyn;
|
|
}
|
|
return dyn->id;
|
|
}
|
|
|
|
// Frees and cleans up a pilot.
|
|
static void pilot_free(Pilot* p) {
|
|
solid_free(p->solid);
|
|
free(p->outfits);
|
|
free(p->name);
|
|
ai_destroy(p);
|
|
free(p);
|
|
}
|
|
|
|
// Destroy pilot from stack.
|
|
void pilot_destroy(Pilot* p) {
|
|
int i;
|
|
for(i = 0; i < pilots; i++)
|
|
if(pilot_stack[i] == p)
|
|
break;
|
|
while(i < pilots) {
|
|
pilot_stack[i] = pilot_stack[i+1];
|
|
i++;
|
|
}
|
|
pilot_free(p);
|
|
}
|
|
|
|
// Free the prisoned pilot!
|
|
void pilots_free(void) {
|
|
int i;
|
|
for(i = 0; i < pilots; i++)
|
|
pilot_free(pilot_stack[i]);
|
|
free(pilot_stack);
|
|
}
|
|
|
|
// Update all pilots.
|
|
void pilots_update(double dt) {
|
|
int i;
|
|
for(i = 0; i < pilots; i++) {
|
|
if(pilot_stack[i]->think)
|
|
pilot_stack[i]->think(pilot_stack[i]);
|
|
if(pilot_stack[i]->update)
|
|
pilot_stack[i]->update(pilot_stack[i], dt);
|
|
if(pilot_stack[i]->render)
|
|
pilot_stack[i]->render(pilot_stack[i]);
|
|
}
|
|
}
|
|
|
|
// Return the fleet based on 'name'
|
|
Fleet* fleet_get(const char* name) {
|
|
int i;
|
|
for(i = 0; i < nfleets; i++)
|
|
if(strcmp(name, fleet_stack[i].name)==0)
|
|
return fleet_stack+i;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Parse the fleet node.
|
|
static Fleet* fleet_parse(const xmlNodePtr parent) {
|
|
xmlNodePtr cur, node;
|
|
FleetPilot* pilot;
|
|
char* c;
|
|
node = parent->xmlChildrenNode;
|
|
|
|
Fleet* tmp = CALLOC_L(Fleet);
|
|
|
|
tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); // Already mallocs.
|
|
if(tmp->name == NULL) WARN("Fleet in "FLEET_DATA" has invalid or no name");
|
|
|
|
do {
|
|
// Load all the data.
|
|
if(strcmp((char*)node->name, "faction")==0)
|
|
tmp->faction = faction_get((char*)node->children->content);
|
|
else if(strcmp((char*)node->name, "ai")==0)
|
|
tmp->ai = ai_getProfile((char*)node->children->content);
|
|
else if(strcmp((char*)node->name, "pilots")==0) {
|
|
cur = node->children;
|
|
do {
|
|
if(strcmp((char*)cur->name, "pilot")==0) {
|
|
tmp->npilots++; // Pilot count.
|
|
pilot = MALLOC_L(FleetPilot);
|
|
|
|
// Name is not obligatory. Will only override ship name.
|
|
c = (char*)xmlGetProp(cur, (xmlChar*)"name"); // Mallocs.
|
|
pilot->name = c; // No need to free here however.
|
|
|
|
pilot->ship = ship_get((char*)cur->children->content);
|
|
if(pilot->ship == NULL)
|
|
WARN("Pilot %s in Fleet %s has null ship", pilot->name, tmp->name);
|
|
|
|
c = (char*)xmlGetProp(cur, (xmlChar*)"chance"); // Mallocs.
|
|
pilot->chance = atoi(c);
|
|
if(pilot->chance == 0)
|
|
WARN("Pilot %s in Fleet %s has 0%% chance of appearing", pilot->name, tmp->name);
|
|
if(c) free(c); // Free the external malloc.
|
|
|
|
tmp->pilots = realloc(tmp->pilots, sizeof(FleetPilot)*tmp->npilots);
|
|
memcpy(tmp->pilots+(tmp->npilots-1), pilot, sizeof(FleetPilot));
|
|
free(pilot);
|
|
}
|
|
} while((cur = cur->next));
|
|
}
|
|
} while((node = node->next));
|
|
#define MELEMENT(o,s) if((o) == NULL) WARN("Fleet '%s' missing '"s"' element", tmp->name)
|
|
MELEMENT(tmp->ai, "ai");
|
|
MELEMENT(tmp->faction, "faction");
|
|
MELEMENT(tmp->pilots, "pilots");
|
|
#undef MELEMENT
|
|
|
|
return tmp;
|
|
}
|
|
|
|
// Load the fleets.
|
|
int fleet_load(void) {
|
|
uint32_t bufsize;
|
|
char* buf = pack_readfile(DATA, FLEET_DATA, &bufsize);
|
|
|
|
xmlNodePtr node;
|
|
xmlDocPtr doc = xmlParseMemory(buf, bufsize);
|
|
|
|
Fleet* tmp = NULL;
|
|
|
|
node = doc->xmlChildrenNode; // Ships node.
|
|
if(strcmp((char*)node->name, XML_ID)) {
|
|
ERR("Malformed "FLEET_DATA" file: missing root element '"XML_ID"'");
|
|
return -1;
|
|
}
|
|
node = node->xmlChildrenNode; // First ship node.
|
|
if(node == NULL) {
|
|
ERR("Malformed "FLEET_DATA" file: does not contain elements");
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
if(node->type == XML_NODE_START && strcmp((char*)node->name, XML_FLEET)==0) {
|
|
tmp = fleet_parse(node);
|
|
fleet_stack = realloc(fleet_stack, sizeof(Fleet)*(++nfleets));
|
|
memcpy(fleet_stack+nfleets-1, tmp, sizeof(Fleet));
|
|
free(tmp);
|
|
}
|
|
} while((node = node->next));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
xmlCleanupParser();
|
|
|
|
DEBUG("Loaded %d fleet%c", nfleets, (nfleets==1)?' ':'s');
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Free the fleets.
|
|
void fleet_free(void) {
|
|
int i, j;
|
|
if(fleet_stack != NULL) {
|
|
for(i = 0; i < nfleets; i++) {
|
|
for(j = 0; j < fleet_stack[i].npilots; j++)
|
|
if(fleet_stack[i].pilots[j].name)
|
|
free(fleet_stack[i].pilots[j].name);
|
|
free(fleet_stack[i].name);
|
|
free(fleet_stack[i].pilots);
|
|
}
|
|
free(fleet_stack);
|
|
}
|
|
nfleets = 0;
|
|
}
|
|
|