#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; 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; }