#include #include #include #include // 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; static int mpilots = 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_shootWeapon(Pilot* p, PilotOutfit* w, const unsigned int t); 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) { // Binary search. int l, m, h; l = 0; h = pilots-1; while(l <= h) { m = (l+h)/2; if(pilot_stack[m]->id > id) h = m-1; else if(pilot_stack[m]->id < id) l = m+1; else break; } if(m == (pilots-1)) return PLAYER_ID; else return pilot_stack[m+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(!pilot_isDisabled(pilot_stack[i]) && ((!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) { if(id == PLAYER_ID) return player; // Special case player. // Binary search. int l, m, h; l = 0; h = pilots-1; while(l <= h) { m = (l+h)/2; if(pilot_stack[m]->id > id) h = m-1; else if(pilot_stack[m]->id < id) l = m+1; else return pilot_stack[m]; } return NULL; } // Mkay, this is how we shoot. Listen up. void pilot_shoot(Pilot* p, const unsigned int target, const int secondary) { int i; if(!p->outfits) return; // No outfits. if(!secondary) { // Primary weapons. for(i = 0; i < p->noutfits; i++) // Cycle through outfits to find primary weapons. if(!outfit_isProp(p->outfits[i].outfit, OUTFIT_PROP_WEAP_SECONDARY)) pilot_shootWeapon(p, &p->outfits[i], target); } else { if(!p->secondary) return; // No secondary weapon. pilot_shootWeapon(p, p->secondary, target); } } static void pilot_shootWeapon(Pilot* p, PilotOutfit* w, const unsigned int t) { // WElll... Trying to shoot when you have no ammo?? FUUU int quantity = (outfit_isAmmo(w->outfit) && p->secondary) ? p->secondary->quantity : w->quantity; // Check to see if weapon is ready. if((SDL_GetTicks() - w->timer) < (w->outfit->delay / quantity)) return; // Regular weapons. if(outfit_isWeapon(w->outfit)) { // Different weapons. switch(w->outfit->type) { case OUTFIT_TYPE_BOLT: weapon_add(w->outfit, p->solid->dir, &p->solid->pos, &p->solid->vel, p->id, t); // Can't shoot for a while. w->timer = SDL_GetTicks(); break; default: break; } } // Missile launchers. // Must be secondary weapon, Shootee can't be the target. else if(outfit_isLauncher(w->outfit) && (w == p->secondary) && (p->id != t)) { if(p->ammo && (p->ammo->quantity > 0)) { weapon_add(p->ammo->outfit, p->solid->dir, &p->solid->pos, &p->solid->vel, p->id, t); w->timer = SDL_GetTicks(); // Can't shoot for a while. p->ammo->quantity -= 1; // There's no getting this one back. } } } // Damage the pilot. void pilot_hit(Pilot* p, const double damage_shield, const double damage_armour) { if(p->shield - damage_shield > 0.) p->shield -= damage_shield; else if(p->shield > 0.) { // Shields can take part of the blow. p->armour -= p->shield/damage_shield*damage_armour; p->shield = 0.; } else if(p->armour-damage_armour > 0.) p->armour -= damage_armour; else p->armour = 0.; } // Set the pilot's ammo based on their secondary weapon. void pilot_setAmmo(Pilot* p) { int i; char* name; if((p->secondary == NULL) || !outfit_isLauncher(p->secondary->outfit)) { p->ammo = NULL; return; } name = p->secondary->outfit->ammo; for(i = 0; i < p->noutfits; i++) if(strcmp(p->outfits[i].outfit->name, name)==0) { p->ammo = p->outfits + i; return; } p->ammo = NULL; } // 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.x, p->solid->pos.y, sx, sy, NULL); } // Update the pilot. static void pilot_update(Pilot* pilot, const double dt) { if(pilot != player && pilot->armour < PILOT_DISABLED_ARMOUR * pilot->armour_max) { // We are disabled. pilot_setFlag(pilot, PILOT_DISABLED); // Come to a halt slowly. vect_pset(&pilot->solid->vel, VMOD(pilot->solid->vel) * (1. - dt*0.10), VANGLE(pilot->solid->vel)); vectnull(&pilot->solid->force); pilot->solid->dir_vel = 0.; // Stop it from turning. // Update the solid. pilot->solid->update(pilot->solid, dt); return; } // We are still alive. else if(pilot->armour < pilot->armour_max) pilot->armour += pilot->ship->armour_regen*dt; else pilot->shield += pilot->ship->shield_regen*dt; if(pilot->armour > pilot->armour_max) pilot->armour = pilot->armour_max; if(pilot->shield > pilot->shield_max) pilot->shield = pilot->shield_max; // 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)); } } // ==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 armour. pilot->armour_max = ship->armour; pilot->shield_max = ship->shield; pilot->energy_max = ship->energy; pilot->armour = pilot->armour_max; pilot->shield = pilot->shield_max; pilot->energy = pilot->energy_max; // Initially idle. pilot->task = NULL; // Outfits. pilot->outfits = NULL; pilot->secondary = NULL; pilot->ammo = 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; mpilots = 1; } pilot_stack[0] = dyn; } else { // Add to the stack. pilots++; // There is a new pilot. if(pilots >= mpilots) // Need to grow. pilot_stack = realloc(pilot_stack, ++mpilots*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); pilot_stack = NULL; pilots = 0; } // Clean up the pilots - Leaves the player. void pilots_clean(void) { int i; for(i = 1; i < pilots; i++) pilot_free(pilot_stack[i]); pilots = 1; } // Update all pilots. void pilots_update(double dt) { int i; for(i = 0; i < pilots; i++) { if(pilot_stack[i]->think && !pilot_isDisabled(pilot_stack[i])) 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]); } } // Render all the pilots. void pilots_render(void) { int i; for(i = 1; i < pilots; i++) // Skip the player. 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); fleet_stack = NULL; } nfleets = 0; }