#include #include #include #include "lephisto.h" #include "log.h" #include "weapon.h" #include "pack.h" #include "xml.h" #include "spfx.h" #include "rng.h" #include "hook.h" #include "pilot.h" #define XML_ID "Fleets" // XML section identifier. #define XML_FLEET "fleet" #define FLEET_DATA "../dat/fleet.xml" #define PILOT_CHUNK 32 // Chunks to increment pilot_stack by. // Stack of pilot id's to assure uniqueness. static unsigned int pilot_id = PLAYER_ID; // id for special mission cargo. static unsigned int mission_cargo_id = 0; // 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; extern unsigned int player_crating; // Stack of fleets. static Fleet* fleet_stack = NULL; static int nfleets = 0; // External. // AI. extern void ai_destroy(Pilot* p); extern void ai_think(Pilot* pilot); extern void ai_create(Pilot* pilot); // Player. extern void player_think(Pilot* pilot); extern void player_brokeHyperspace(void); extern double player_faceHyperspace(void); extern void player_dead(void); extern void player_destroyed(void); extern int gui_load(const char* name); // Internal. static void pilot_shootWeapon(Pilot* p, PilotOutfit* w, const unsigned int t); static void pilot_update(Pilot* pilot, const double dt); static void pilot_hyperspace(Pilot* pilot); void pilot_render(Pilot* pilot); static void pilot_calcStats(Pilot* pilot); void pilot_free(Pilot* p); static Fleet* fleet_parse(const xmlNodePtr parent); static void pilot_dead(Pilot* p); static int pilot_oquantity(Pilot* p, PilotOutfit* w); // 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(!pilot_isDisabled(pilot_stack[i]) && ((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; } // Grab a fleet out of the stack. Fleet* fleet_get(const char* name) { int i; for(i = 0; i < nfleets; i++) if(strcmp(fleet_stack[i].name, name)==0) return &fleet_stack[i]; WARN("Fleet '%s' not found in stack", name); return NULL; } // Attempt to turn the pilot to face dir. double pilot_face(Pilot* p, const float dir) { double diff, turn; diff = angle_diff(p->solid->dir, dir); turn = -10.*diff; if(turn > 1.) turn = 1.; else if(turn < -1.) turn = -1.; p->solid->dir_vel = 0.; if(turn) p->solid->dir_vel -= p->turn * turn; return diff; } // Return quantity of a pilot outfit. static int pilot_oquantity(Pilot* p, PilotOutfit* w) { return (outfit_isAmmo(w->outfit) && p->secondary) ? p->secondary->quantity : w->quantity; } // Get pilot's free weapon space. int pilot_freeSpace(Pilot* p) { int i, s; s = p->ship->cap_weapon; for(i = 0; i < p->noutfits; i++) s -= p->outfits[i].quantity * p->outfits[i].outfit->mass; return s; } // 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) { int quantity, delay; // WElll... Trying to shoot when you have no ammo?? FUUU quantity = pilot_oquantity(p,w); delay = outfit_delay(w->outfit); // Check to see if weapon is ready. if((SDL_GetTicks() - w->timer) < (unsigned int)(delay/quantity)) return; // Regular weapons. if(outfit_isWeapon(w->outfit) || (outfit_isTurret(w->outfit))) { // Different weapons. switch(w->outfit->type) { case OUTFIT_TYPE_TURRET_BOLT: case OUTFIT_TYPE_BOLT: // Enough energy? if(outfit_energy(w->outfit) > p->energy) return; p->energy -= outfit_energy(w->outfit); 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, Shooter can't be the target. else if(outfit_isLauncher(w->outfit) && (w == p->secondary) && (p->id != t)) { if(p->ammo && (p->ammo->quantity > 0)) { // Enough energy? if(outfit_energy(w->outfit) > p->energy) return; p->energy -= outfit_energy(w->outfit); 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 Solid* w, const unsigned int shooter, const double damage_shield, const double damage_armour) { double dam_mod; if(p->shield - damage_shield > 0.) { p->shield -= damage_shield; dam_mod = damage_shield/p->shield_max; } else if(p->shield > 0.) { // Shields can take part of the blow. p->armour -= p->shield/damage_shield*damage_armour; p->shield = 0.; dam_mod = (damage_shield+damage_armour) / (p->shield_max + p->armour_max); } else if(p->armour-damage_armour > 0.) { p->armour -= damage_armour; dam_mod = damage_armour/p->armour_max; // Shake us up a bit. if(p->id == PLAYER_ID) spfx_shake(dam_mod*100.); } else { // We are officially dead. p->armour = 0.; dam_mod = 0.; if(!pilot_isFlag(p, PILOT_DEAD)) { pilot_dead(p); // Adjust the combat rating based on pilot mass and ditto faction. if(shooter == PLAYER_ID) { player_crating += MAX(1, p->ship->mass/50); faction_modPlayer(p->faction, -(p->ship->mass/10)); } } } // Knock back effect is dependent on both damage and mass of the weapon. // should probably turn it into a partial conservative collision.. vect_cadd(&p->solid->vel, w->vel.x * (dam_mod/6. + w->mass/p->solid->mass/6.), w->vel.y * (dam_mod/6. + w->mass/p->solid->mass/6.)); } void pilot_dead(Pilot* p) { if(pilot_isFlag(p, PILOT_DEAD)) return; // She's already dead. // Basically just set the timers.. if(p->id == PLAYER_ID) player_dead(); p->timer[0] = SDL_GetTicks(); // No need for AI anymore. p->ptimer = p->timer[0] + 1000 + (unsigned int)sqrt(10*p->armour_max*p->shield_max); p->timer[1] = p->timer[0]; // Explosion timer. // Flag cleanup - fixes some issues. if(pilot_isFlag(p, PILOT_HYP_PREP)) pilot_rmFlag(p, PILOT_HYP_PREP); if(pilot_isFlag(p, PILOT_HYP_BEGIN)) pilot_rmFlag(p, PILOT_HYP_BEGIN); if(pilot_isFlag(p, PILOT_HYPERSPACE)) pilot_rmFlag(p, PILOT_HYPERSPACE); // Our pilot is now deadz. pilot_setFlag(p, PILOT_DEAD); // Run hook if pilot has a death hook. if(p->hook_type == PILOT_HOOK_DEATH) hook_runID(p->hook); } void pilot_setSecondary(Pilot* p, const char* secondary) { int i; if(secondary == NULL) { p->secondary = NULL; p->ammo = NULL; return; } for(i = 0; i < p->noutfits; i++) { if(strcmp(secondary, p->outfits[i].outfit->name)==0) { p->secondary = &p->outfits[i];; pilot_setAmmo(p); return; } } WARN("Attempted to set pilot '%s' secondary weapon to non-existing '%s'", p->name, secondary); p->secondary = NULL; p->ammo = NULL; } // 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->u.lau.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) { gl_blitSprite(p->ship->gfx_space, p->solid->pos.x, p->solid->pos.y, p->tsx, p->tsy, NULL); } // Update the pilot. static void pilot_update(Pilot* pilot, const double dt) { unsigned int t, l; double a, px, py, vx, vy; if(pilot_isFlag(pilot, PILOT_DEAD)) { t = SDL_GetTicks(); if(t > pilot->ptimer) { if(pilot->id == PLAYER_ID) player_destroyed(); pilot_setFlag(pilot, PILOT_DELETE); // It'll get deleted next frame. return; } if(!pilot_isFlag(pilot, PILOT_EXPLODED) && (t > pilot->ptimer - 200)) { spfx_add(spfx_get("ExpL"), VX(pilot->solid->pos), VY(pilot->solid->pos), VX(pilot->solid->vel), VY(pilot->solid->vel), SPFX_LAYER_BACK); pilot_setFlag(pilot, PILOT_EXPLODED); } else if(t > pilot->timer[1]) { pilot->timer[1] = t + (unsigned int)(100*(double)(pilot->ptimer - pilot->timer[1]) / (double)(pilot->ptimer - pilot->timer[0])); // Random position on ship. a = RNGF()*2.*M_PI; px = VX(pilot->solid->pos) + cos(a)*RNGF()*pilot->ship->gfx_space->sw/2.; py = VY(pilot->solid->pos) + sin(a)*RNGF()*pilot->ship->gfx_space->sh/2.; vx = VX(pilot->solid->vel); vy = VY(pilot->solid->vel); // Set explosions. l = (pilot->id == PLAYER_ID) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK; if(RNGF() > 0.8) spfx_add(spfx_get("ExpM"), px, py, vx, vy, l); else spfx_add(spfx_get("ExpS"), px, py, vx, vy, l); } } else if(pilot->armour <= 0.) // PWNED! pilot_dead(pilot); // Pupose fallthrough to get the movement similar to disabled. 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); gl_getSpriteFromDir(&pilot->tsx, &pilot->tsy, pilot->ship->gfx_space, pilot->solid->dir); return; } // We are still alive. else if(pilot->armour < pilot->armour_max) { pilot->armour += pilot->armour_regen*dt; pilot->energy += pilot->energy_regen*dt; } else { pilot->shield += pilot->shield_regen*dt; pilot->energy += pilot->energy_regen*dt; } if(pilot->armour > pilot->armour_max) pilot->armour = pilot->armour_max; if(pilot->shield > pilot->shield_max) pilot->shield = pilot->shield_max; if(pilot->energy > pilot->energy_max) pilot->energy = pilot->energy_max; // Update the solid. (*pilot->solid->update)(pilot->solid, dt); gl_getSpriteFromDir(&pilot->tsx, &pilot->tsy, pilot->ship->gfx_space, pilot->solid->dir); if(!pilot_isFlag(pilot, PILOT_HYPERSPACE)) { // Limit the speed. if(pilot_isFlag(pilot, PILOT_AFTERBURNER) && // Must have enough energy. (player->energy > pilot->afterburner->outfit->u.afb.energy * dt)) { limit_speed(&pilot->solid->vel, pilot->speed * pilot->afterburner->outfit->u.afb.speed_perc + pilot->afterburner->outfit->u.afb.speed_abs, dt); spfx_shake(SHAKE_DECAY/2. * dt); // Shake goes down at half speed. pilot->energy -= pilot->afterburner->outfit->u.afb.energy * dt; // Energy loss. } else limit_speed(&pilot->solid->vel, pilot->speed, dt); } } // Pilot is getting ready or is in, hyperspace. static void pilot_hyperspace(Pilot* p) { double diff; if(pilot_isFlag(p, PILOT_HYPERSPACE)) { // Pilot is actually in hyperspace. if(SDL_GetTicks() > p->ptimer) { if(p == player) { player_brokeHyperspace(); } else pilot_setFlag(p, PILOT_DELETE); // Set flag to delete pilot. return; } vect_pset(&p->solid->force, p->thrust * 3., p->solid->dir); } else if(pilot_isFlag(p, PILOT_HYP_BEGIN)) { if(SDL_GetTicks() > p->ptimer) { // Engines are ready. p->ptimer = SDL_GetTicks() + HYPERSPACE_FLY_DELAY; pilot_setFlag(p, PILOT_HYPERSPACE); } } else { // Pilot is getting ready for hyperspace. if(VMOD(p->solid->vel) > MIN_VEL_ERR) { diff = pilot_face(p, VANGLE(p->solid->vel) + M_PI); if(ABS(diff) < MAX_DIR_ERR) // Brake. vect_pset(&p->solid->force, p->thrust, p->solid->dir); } else { vectnull(&p->solid->force); // Stop accelerating. // Player should actually face the system she's headed to. if(p == player) diff = player_faceHyperspace(); else diff = pilot_face(p, VANGLE(p->solid->pos)); if(ABS(diff) < MAX_DIR_ERR) { // We should prepare for the jump now. p->solid->dir_vel = 0.; p->ptimer = SDL_GetTicks() + HYPERSPACE_ENGINE_DELAY; pilot_setFlag(p, PILOT_HYP_BEGIN); } } } } int pilot_addOutfit(Pilot* pilot, Outfit* outfit, int quantity) { int i, q; char* s; q = quantity; for(i = 0; i < pilot->noutfits; i++) if(strcmp(outfit->name, pilot->outfits[i].outfit->name)==0) { pilot->outfits[i].quantity += quantity; // Can't be over max. if(pilot->outfits[i].quantity > outfit->max) { q -= pilot->outfits[i].quantity - outfit->max; pilot->outfits[i].quantity = outfit->max; } pilot_calcStats(pilot); return q; } s = (pilot->secondary) ? pilot->secondary->outfit->name : NULL; pilot->outfits = realloc(pilot->outfits, (pilot->noutfits+1)*sizeof(PilotOutfit)); pilot->outfits[pilot->noutfits].outfit = outfit; pilot->outfits[pilot->noutfits].quantity = quantity; // Can't be over max. if(pilot->outfits[pilot->noutfits].quantity > outfit->max) { q -= pilot->outfits[pilot->noutfits].quantity - outfit->max; pilot->outfits[i].quantity = outfit->max; } pilot->outfits[pilot->noutfits].timer = 0; (pilot->noutfits)++; if(outfit_isTurret(outfit)) // Used to speed up AI. pilot_setFlag(pilot, PILOT_HASTURRET); // Hack due to realloc possibility. pilot_setSecondary(pilot, s); pilot_calcStats(pilot); return q; } // Remove an outfit from the pilot. int pilot_rmOutfit(Pilot* pilot, Outfit* outfit, int quantity) { int i, q; char* s; q = quantity; for(i = 0; i < pilot->noutfits; i++) if(strcmp(outfit->name, pilot->outfits[i].outfit->name)==0) { pilot->outfits[i].quantity -= quantity; if(pilot->outfits[i].quantity <= 0) { // We didn't actually remove the full amount. q += pilot->outfits[i].quantity; // Hack in case it reallocs - Can happen even when shrinking. s = (pilot->secondary) ? pilot->secondary->outfit->name : NULL; // Clear it if it's the afterburner. if(&pilot->outfits[i] == pilot->afterburner) pilot->afterburner = NULL; // Remove the outfit. memmove(pilot->outfits+i, pilot->outfits+i+1, sizeof(PilotOutfit)*(pilot->noutfits-i-1)); pilot->noutfits--; pilot->outfits = realloc(pilot->outfits, sizeof(PilotOutfit)*(pilot->noutfits)); pilot_setSecondary(pilot, s); } pilot_calcStats(pilot); // Recalculate stats. return q; } WARN("Failure attempting to remove %d '%s' from pilot '%s'", quantity, outfit->name, pilot->name); return 0; } // Return all the outfits in a nice text form. char* pilot_getOutfits(Pilot* pilot) { int i; char buf[64], *str; str = malloc(sizeof(char)*1024); buf[0] = '\0'; // First outfit. if(pilot->noutfits > 0) snprintf(str, 1024, "%dx %s", pilot->outfits[0].quantity, pilot->outfits[0].outfit->name); // Rest of the outfits. for(i = 1; i < pilot->noutfits; i++) { snprintf(buf, 64, ", %dx %s", pilot->outfits[i].quantity, pilot->outfits[i].outfit->name); strcat(str, buf); } return str; } // Recalculate the pilot's stats based on her outfits. static void pilot_calcStats(Pilot* pilot) { int i; double q; Outfit* o; double ac, sc, ec, fc; // Temp health coeficients to set. // -- Set up the basic stuff. // Movement. pilot->thrust = pilot->ship->thrust; pilot->turn = pilot->ship->turn; pilot->speed = pilot->ship->speed; // Health. ac = pilot->armour / pilot->armour_max; sc = pilot->shield / pilot->shield_max; ec = pilot->energy / pilot->energy_max; fc = pilot->fuel / pilot->fuel_max; pilot->armour_max = pilot->ship->armour; pilot->shield_max = pilot->ship->shield; pilot->energy_max = pilot->ship->energy; pilot->fuel_max = pilot->ship->fuel; pilot->armour_regen = pilot->ship->armour_regen; pilot->shield_regen = pilot->ship->shield_regen; pilot->energy_regen = pilot->ship->energy_regen; // Now add outfit changes. for(i = 0; i < pilot->noutfits; i++) { if(outfit_isMod(pilot->outfits[i].outfit)) { q = (double) pilot->outfits[i].quantity; o = pilot->outfits[i].outfit; // Movement. pilot->thrust += o->u.mod.thrust * q; pilot->turn += o->u.mod.turn * q; pilot->speed += o->u.mod.speed * q; // Health. pilot->armour_max += o->u.mod.armour * q; pilot->armour_regen += o->u.mod.armour_regen * q; pilot->shield_max += o->u.mod.shield * q; pilot->shield_regen += o->u.mod.shield_regen * q; pilot->energy_max += o->u.mod.energy * q; pilot->energy_regen += o->u.mod.energy_regen * q; // Fuel. pilot->fuel_max += o->u.mod.fuel * q; // Misc. pilot->cargo_free += o->u.mod.cargo * q; } else if(outfit_isAfterburner(pilot->outfits[i].outfit)) { // Set the afterburner. pilot->afterburner = &pilot->outfits[i]; } } // Give the pilot her health proportion back. pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; pilot->energy = ec * pilot->energy_max; pilot->fuel = fc * pilot->fuel_max; } // Pilot free cargo space. int pilot_freeCargo(Pilot* p) { return p->cargo_free; } // Try to add quantity of cargo to pilot, return quantity actually added. int pilot_addCargo(Pilot* pilot, Commodity* cargo, int quantity) { int i, q; q = quantity; for(i = 0; i < pilot->ncommodities; i++) if(!pilot->commodities[i].id && (pilot->commodities[i].commodity == cargo)) { if(pilot->cargo_free < quantity) q = pilot->cargo_free; pilot->commodities[i].quantity += q; pilot->cargo_free -= q; return q; } // Must add another one. pilot->commodities = realloc(pilot->commodities, sizeof(PilotCommodity) * (pilot->ncommodities+1)); pilot->commodities[pilot->ncommodities].commodity = cargo; if(pilot->cargo_free < quantity) q = pilot->cargo_free; pilot->commodities[pilot->ncommodities].id = 0; pilot->commodities[pilot->ncommodities].quantity = q; pilot->cargo_free -= q; pilot->ncommodities++; return q; } unsigned int pilot_addMissionCargo(Pilot* pilot, Commodity* cargo, int quantity) { int q; q = quantity; pilot->commodities = realloc(pilot->commodities, sizeof(PilotCommodity) * (pilot->ncommodities+1)); pilot->commodities[pilot->ncommodities].commodity = cargo; if(pilot->cargo_free < quantity) q = pilot->cargo_free; pilot->commodities[pilot->ncommodities].id = ++mission_cargo_id; pilot->commodities[pilot->ncommodities].quantity = q; pilot->cargo_free -= q; pilot->ncommodities++; return pilot->commodities[pilot->ncommodities-1].id; } int pilot_rmMissionCargo(Pilot* pilot, unsigned int cargo_id) { int i; for(i = 0; i < pilot->ncommodities; i++) if(pilot->commodities[i].id == cargo_id) break; if(i >= pilot->ncommodities) return 1; // Remove cargo. pilot->cargo_free += pilot->commodities[i].quantity; memmove(pilot->commodities+i, pilot->commodities+i+1, sizeof(PilotCommodity) * (pilot->ncommodities-i-1)); pilot->ncommodities--; pilot->commodities = realloc(pilot->commodities, sizeof(PilotCommodity) * pilot->ncommodities); return 0; } // Try to get rid of quantity cargo from pilot, // return quantity actually removed. int pilot_rmCargo(Pilot* pilot, Commodity* cargo, int quantity) { int i, q; q = quantity; for(i = 0; i < pilot->ncommodities; i++) if(!pilot->commodities[i].id && (pilot->commodities[i].commodity == cargo)) { if(quantity >= pilot->commodities[i].quantity) { q = pilot->commodities[i].quantity; // Remove cargo. memmove(pilot->commodities+i, pilot->commodities+i+1, sizeof(PilotCommodity)*(pilot->ncommodities-i)); pilot->ncommodities--; pilot->commodities = realloc(pilot->commodities, sizeof(PilotCommodity)*pilot->ncommodities); } else pilot->commodities[i].quantity -= q; pilot->cargo_free += q; return q; } return 0; } // Add a hook to the pilot. void pilot_addHook(Pilot* pilot, int type, int hook) { pilot->hook_type = type; pilot->hook = hook; } // ==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, int faction, AI_Profile* ai, const double dir, const Vec2* pos, const Vec2* vel, const int flags) { ShipOutfit* so; 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); // Initially idle. pilot->task = NULL; // Outfits. pilot->outfits = NULL; pilot->secondary = NULL; pilot->ammo = NULL; pilot->afterburner = NULL; pilot->noutfits = 0; if(!(flags & PILOT_NO_OUTFITS)) { 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(outfit_isTurret(so->data)) // Used to speed up AI a bit. pilot_setFlag(pilot, PILOT_HASTURRET); } } } // Set the pilot stats based on her ship and outfits. // Hack to have full armour/shield/energy/fuel. pilot->armour = pilot->armour_max = 1.; pilot->shield = pilot->shield_max = 1.; pilot->energy = pilot->energy_max = 1.; pilot->fuel = pilot->fuel_max = 1.; pilot_calcStats(pilot); // Cargo. pilot->credits = 0; pilot->commodities = NULL; pilot->ncommodities = 0; pilot->cargo_free = pilot->ship->cap_cargo; // Hooks. pilot->hook_type = PILOT_HOOK_NONE; pilot->hook = 0; // Set flags and functions. if(flags & PILOT_PLAYER) { pilot->think = player_think; // Players don't need to thing! :P pilot->render = NULL; // Render will be called from player_think pilot_setFlag(pilot, PILOT_PLAYER); // It's a player! // Bit of a hack. if(!(flags & PILOT_EMPTY)) { player = pilot; gui_load(pilot->ship->gui); // Load the GUI. } } else { pilot->think = ai_think; pilot->render = pilot_render; ai_create(pilot); // Will run the create function in ai. } // All update the same way. 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, int faction, AI_Profile* ai, const double dir, const Vec2* pos, const Vec2* vel, const int flags) { Pilot** tp, *dyn; 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. About 20 at a time. mpilots += PILOT_CHUNK; tp = pilot_stack; pilot_stack = realloc(pilot_stack, mpilots*sizeof(Pilot*)); if((pilot_stack != tp) && player) // Take into account possible mem move. player = pilot_stack[0]; } pilot_stack[pilots-1] = dyn; } return dyn->id; } Pilot* pilot_createEmpty(Ship* ship, char* name, int faction, AI_Profile* ai, const int flags) { Pilot* dyn; dyn = MALLOC_L(Pilot); pilot_init(dyn, ship, name, faction, ai, 0., NULL, NULL, flags | PILOT_EMPTY); return dyn; } // Copy src pilot to dest. Pilot* pilot_copy(Pilot* src) { Pilot* dest = malloc(sizeof(Pilot)); memcpy(dest, src, sizeof(Pilot)); if(src->name) dest->name = strdup(src->name); // Solid. dest->solid = malloc(sizeof(Solid)); memcpy(dest->solid, src->solid, sizeof(Solid)); // Copy outfits. dest->outfits = malloc(sizeof(PilotOutfit)*src->noutfits); memcpy(dest->outfits, src->outfits, sizeof(PilotOutfit)*src->noutfits); dest->secondary = NULL; dest->ammo = NULL; dest->afterburner = NULL; // Copy commodities. dest->commodities = malloc(sizeof(PilotCommodity)*src->ncommodities); memcpy(dest->commodities, src->commodities, sizeof(PilotCommodity)*src->ncommodities); // Ai is not copied. dest->task = NULL; // Will set afterburner and correct stats. pilot_calcStats(dest); return dest; } // Frees and cleans up a pilot. void pilot_free(Pilot* p) { if(player == p) player = NULL; solid_free(p->solid); if(p->outfits) free(p->outfits); free(p->name); if(p->commodities) free(p->commodities); 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; pilots--; while(i < pilots) { pilot_stack[i] = pilot_stack[i+1]; i++; } pilot_free(p); } // Free the prisoned pilot! void pilots_free(void) { int i; if(player) pilot_free(player); for(i = 1; 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])) { // Hyperspace gets special treatment. if(pilot_isFlag(pilot_stack[i], PILOT_HYP_PREP)) pilot_hyperspace(pilot_stack[i]); else pilot_stack[i]->think(pilot_stack[i]); } if(pilot_stack[i]->update) { if(pilot_isFlag(pilot_stack[i], PILOT_DELETE)) pilot_destroy(pilot_stack[i]); else pilot_stack[i]->update(pilot_stack[i], dt); } } } // 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]); } // 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->faction = -1; 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) WARN("Fleet '%s' missing '"s"' element", tmp->name) MELEMENT(tmp->ai==NULL, "ai"); MELEMENT(tmp->faction==-1, "faction"); MELEMENT(tmp->pilots==NULL, "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(xml_isNode(node, XML_FLEET)) { 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; }