diff --git a/.gitignore b/.gitignore index 37a7842..1efab61 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,6 @@ *bin/Lephisto *bin/data *pack +*core diff --git a/bin/Makefile b/bin/Makefile index 5869fef..a22f0ac 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -48,5 +48,5 @@ data: pack $(DATAFILES) ../src/pack.c clean: @echo -e "\tRemoving data.." - rm -rf $(OBJS) $(APPNAME) $(DATA) pack + rm -rf $(OBJS) $(APPNAME) $(DATA) pack core diff --git a/dat/outfit.xml b/dat/outfit.xml index 76b3a2c..8604419 100644 --- a/dat/outfit.xml +++ b/dat/outfit.xml @@ -9,7 +9,9 @@ laser.wav lasergreen.png - 450 + 500 + 550 + 300 30 20 diff --git a/src/ai.c b/src/ai.c index 935c3f4..075d98f 100644 --- a/src/ai.c +++ b/src/ai.c @@ -89,7 +89,8 @@ static double pilot_turn = 0.; // Destroy the AI part of the pilot. void ai_destroy(Pilot* p) { - ai_freetask(p->task); + if(p->task) + ai_freetask(p->task); } // Init the AI stuff. Which is basically Lua. diff --git a/src/main.c b/src/main.c index c8df506..27e304c 100644 --- a/src/main.c +++ b/src/main.c @@ -18,9 +18,11 @@ #include "rng.h" #include "ai.h" #include "outfit.h" +#include "pack.h" +#include "weapon.h" #include "pilot.h" -#define WINDOW_CAPTION "Lephisto" +#define APPNAME "Lephisto" #define CONF_FILE "conf" #define MINIMUM_FPS 0.5 @@ -31,14 +33,13 @@ static unsigned int time = 0; // Calculate FPS and movement. #define DATA_DEF "data" char* data = NULL; - static int show_fps = 1; // Default - True. +static int max_fps = 0; // Prototypes. static void print_usage(char** argv); static void display_fps(const double dt); - // Update. static void update_all(void); @@ -47,7 +48,7 @@ static void print_usage(char** argv) { LOG("USAGE: %s [OPTION]", argv[0]); LOG("Options are:"); LOG("\t-f, --fullscreen - Fullscreen"); - LOG("\t-F, --fps - Toggle frames per second"); + LOG("\t-F, --fps - Limit frames per second"); LOG("\t-d s, --data s - Set the data file to be s"); LOG("\t-j n, --joystick n - Use joystick (n)"); LOG("\t-J s, --joystick s - Use joystick whose name contains (s)"); @@ -57,6 +58,10 @@ static void print_usage(char** argv) { int main(int argc, char** argv) { int i; + + // Print the version. + LOG(""APPNAME" v%d.%d.%d", VMAJOR, VMINOR, VREV); + // Initialize SDL for possible warnings. SDL_Init(0); // Default values.. @@ -71,9 +76,10 @@ int main(int argc, char** argv) { // input. input_init(); - input_setKeybind("accel", KEYBIND_KEYBOARD, SDLK_w, 0); - input_setKeybind("left", KEYBIND_KEYBOARD, SDLK_a, 0); - input_setKeybind("right", KEYBIND_KEYBOARD, SDLK_d, 0); + input_setKeybind("accel", KEYBIND_KEYBOARD, SDLK_w, 0); + input_setKeybind("left", KEYBIND_KEYBOARD, SDLK_a, 0); + input_setKeybind("right", KEYBIND_KEYBOARD, SDLK_d, 0); + input_setKeybind("primary", KEYBIND_KEYBOARD, SDLK_SPACE, 0); // Use Lua to parse configuration file. lua_State* L = luaL_newstate(); @@ -96,7 +102,7 @@ int main(int argc, char** argv) { lua_getglobal(L, "fps"); if(lua_isnumber(L, -1)) - show_fps = (int)lua_tonumber(L, -1); + max_fps = (int)lua_tonumber(L, -1); // Joystick. lua_getglobal(L, "joystick"); @@ -153,7 +159,7 @@ int main(int argc, char** argv) { // Parse arguments. static struct option long_options[] = { { "fullscreen", no_argument, 0, 'f' }, - { "fps", optional_argument, 0, 'F' }, + { "fps", required_argument, 0, 'F' }, { "data", required_argument, 0, 'd' }, { "joystick", required_argument, 0, 'j' }, { "joystick", required_argument, 0, 'J' }, @@ -163,14 +169,14 @@ int main(int argc, char** argv) { }; int option_index = 0; int c = 0; - while((c = getopt_long(argc, argv, "fFd:J:j:hv", long_options, &option_index)) != -1) { + while((c = getopt_long(argc, argv, "fF:d:J:j:hv", long_options, &option_index)) != -1) { switch(c) { case 'f': gl_screen.fullscreen = 1; break; case 'F': if(optarg != NULL) show_fps = atoi(optarg); - else show_fps = !show_fps; + else max_fps = !show_fps; break; case 'd': data = strdup(optarg); @@ -195,11 +201,21 @@ int main(int argc, char** argv) { if(gl_init()) { // Initializes video output. WARN("Error initializing video output, exiting..."); + SDL_Quit(); + exit(EXIT_FAILURE); + } + + // See if the data file is valid. + if(pack_check(data)) { + ERR("Data file '%s' not found", data); + WARN("You can specify what data file you want to use with '-d'"); + WARN("See -h or -- help for more infoamtion."); + SDL_Quit(); exit(EXIT_FAILURE); } // Window. - SDL_WM_SetCaption(WINDOW_CAPTION, NULL); + SDL_WM_SetCaption(APPNAME, NULL); // Input. if(indjoystick >= 0 || namjoystick != NULL) { @@ -225,11 +241,11 @@ int main(int argc, char** argv) { space_load(); // Testing. - pilot_create(get_ship("Ship"), "Player", NULL, NULL, PILOT_PLAYER); + pilot_create(get_ship("Ship"), "Player", 0., NULL, NULL, PILOT_PLAYER); gl_bindCamera(&player->solid->pos); space_init("SaraSys"); - pilot_create(get_ship("Test"), NULL, NULL, NULL, 0); + pilot_create(get_ship("Test"), NULL, 2., NULL, NULL, 0); time = SDL_GetTicks(); @@ -268,19 +284,23 @@ int main(int argc, char** argv) { // // BG | Stars and planets. // | Background particles. +// | Back layer weapons. // X // N | NPC ships. // | Normal layer particles (above ships). +// | Front layer weapons. // X // FG | Player. // | Foreground particles. // | Text and GUI. // ======================================================== +static double fps_dt = 1.; static void update_all(void) { // dt in us. double dt = (double)(SDL_GetTicks() - time) / 1000.; time = SDL_GetTicks(); + // TODO: This could use some work. if(dt > MINIMUM_FPS) { Vec2 pos; vect_csetmin(&pos, 10., (double)(gl_screen.h-40)); @@ -288,20 +308,27 @@ static void update_all(void) { SDL_GL_SwapBuffers(); return; } + // If FPS is limited. + else if(max_fps != 0 && dt < 1./max_fps) { + double delay = 1./max_fps - dt; + SDL_Delay(delay); + fps_dt += delay; // Make sure it displays the propper FPS. + } glClear(GL_COLOR_BUFFER_BIT); - + +// -- // BG. space_render(dt); planets_render(); - - // N + weapons_update(dt, WEAPON_LAYER_BG); + // N. pilots_update(dt); - - // FG. - player_renderGUI(); - + weapons_update(dt, WEAPON_LAYER_FG); + // FG. + player_render(); display_fps(dt); +// -- SDL_GL_SwapBuffers(); } @@ -310,7 +337,6 @@ static void update_all(void) { // Spit this out on display. static double fps = 0.; static double fps_cur = 0.; -static double fps_dt = 1.; static void display_fps(const double dt) { fps_dt += dt; fps_cur += 1.; diff --git a/src/outfit.c b/src/outfit.c index 5187d6d..a70d521 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -4,6 +4,7 @@ #include "main.h" #include "log.h" +#include "pack.h" #include "outfit.h" #define XML_NODE_START 1 @@ -31,8 +32,27 @@ Outfit* outfit_get(const char* name) { } // Return 1 if outfit is a weapon. -int outfit_isweapon(const Outfit* o) { - return (o->type > OUTFIT_TYPE_NULL && o->type <= OUTFIT_TYPE_MISSILE_SWARM_SMART); +int outfit_isWeapon(const Outfit* o) { + return ((o->type == OUTFIT_TYPE_BOLT) || (o->type == OUTFIT_TYPE_BEAM)); +} + +// Return 1 if outfit is a launcher. +int outfit_isLauncher(const Outfit* o) { + return((o->type == OUTFIT_TYPE_MISSILE_DUMB) || + (o->type == OUTFIT_TYPE_MISSILE_SEEK) || + (o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART) || + (o->type == OUTFIT_TYPE_MISSILE_SWARM) || + (o->type == OUTFIT_TYPE_MISSILE_SWARM_SMART)); +} + +// Return 1 if outfit is weapon ammunition. +int outfit_isAmmo(const Outfit* o) { + return((o->type == OUTFIT_TYPE_MISSILE_DUMB_AMMO) || + (o->type == OUTFIT_TYPE_MISSILE_SEEK_AMMO) || + (o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO) || + (o->type == OUTFIT_TYPE_MISSILE_SWARM_AMMO) || + (o->type == OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO)); + } const char* outfit_typename[] = { @@ -40,10 +60,15 @@ const char* outfit_typename[] = { "Bolt Cannon", "Beam Cannon", "Dumb Missile", + "Dumb Missile Ammunition", "Seeker Missile", - "Smart Missile", + "Seeker Missile Ammunition", + "Smart Seeker Missile", + "Smart Seeker Missile Ammunition", "Swam Missile", - "Smart Swarm Missile" + "Swarm Missile Ammunition Pack", + "Smart Swarm Missile", + "Smart Swarm Missile Ammunition Pack" }; const char* outfit_getType(const Outfit* o) { @@ -61,6 +86,8 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { // Load all the things. if(strcmp((char*)node->name, "speed")==0) tmp->speed = (double)atoi((char*)node->children->content); + else if(strcmp((char*)node->name, "delay")==0) + tmp->delay = atoi((char*)node->children->content); else if(strcmp((char*)node->name, "accuracy")==0) tmp->accuracy = atof((char*)node->children->content)*M_PI/180.; // to rad. else if(strcmp((char*)node->name, "gfx")==0) { @@ -80,7 +107,8 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { } #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) MELEMENT(tmp->speed, "speed"); - MELEMENT(tmp->accuracy, "tech"); + MELEMENT(tmp->accuracy, "tech"); + MELEMENT(tmp->delay, "delay"); MELEMENT(tmp->damage_armor, "armor' from element 'damage"); MELEMENT(tmp->damage_shield, "shield' from element 'damage"); #undef MELEMENT @@ -181,7 +209,7 @@ int outfit_load(void) { void outfit_free(void) { int i; for(i = 0; i < outfits; i++) { - if(outfit_isweapon(&outfit_stack[i]) && outfit_stack[i].gfx_space) + if(outfit_isWeapon(&outfit_stack[i]) && outfit_stack[i].gfx_space) gl_freeTexture(outfit_stack[i].gfx_space); free(outfit_stack[i].name); } diff --git a/src/outfit.h b/src/outfit.h index 350d0cb..235f674 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -1,42 +1,70 @@ #pragma once #include "opengl.h" +#define OUTFIT_PROP_WEAP_PRIMARY (1<<0) +#define OUTFIT_PROP_WEAP_SECONDARY (1<<1) + // Outfit types. typedef enum { - OUTFIT_TYPE_NULL, + OUTFIT_TYPE_NULL = 0, OUTFIT_TYPE_BOLT, OUTFIT_TYPE_BEAM, OUTFIT_TYPE_MISSILE_DUMB, + OUTFIT_TYPE_MISSILE_DUMB_AMMO, OUTFIT_TYPE_MISSILE_SEEK, + OUTFIT_TYPE_MISSILE_SEEK_AMMO, OUTFIT_TYPE_MISSILE_SEEK_SMART, + OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO, OUTFIT_TYPE_MISSILE_SWARM, - OUTFIT_TYPE_MISSILE_SWARM_SMART + OUTFIT_TYPE_MISSILE_SWARM_AMMO, + OUTFIT_TYPE_MISSILE_SWARM_SMART, + OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO } OutfitType; // An outfit depends a lot on the type. typedef struct { char* name; + // General specs. int max; int tech; int mass; - gl_texture gfx_store; + gl_texture gfx_store; // Store graphic. + + int properties; // Properties stored bitwise. + // Type dependant. OutfitType type; union { - struct { - double speed; - double accuracy; - double damage_armor, damage_shield; + struct { // Beam/Bolt. + unsigned int delay; // Delay between shots. + double speed; // Speed of shot. (not applicable to beam. + double range; + double accuracy; // Desviation accuracy. + double damage_armor, damage_shield; // Damage. gl_texture* gfx_space; }; + struct { // Launcher. + //unsigned int delay; // Delay between shots. + }; + struct { // Ammo. + //double speed; // Max speed. + //double turn; // Turn vel. + //double thrust; // Acceleration. + //double damage_armor, damage_shield; + + //gl_texture* gfx_space; + }; }; } Outfit; Outfit* outfit_get(const char* name); -int outfit_isweapon(const Outfit* o); +// Outfit types. +int outfit_isWeapon(const Outfit* o); +int outfit_isLauncher(const Outfit* o); +int outfit_isAmmo(const Outfit* o); const char* outfit_getType(const Outfit* o); // Load/free outfit stack. diff --git a/src/physics.c b/src/physics.c index 8e3c103..b112647 100644 --- a/src/physics.c +++ b/src/physics.c @@ -175,11 +175,11 @@ static void rk4_update(Solid* obj, const double dt) { } // Initialize a new solid. -void solid_init(Solid* dest, const double mass, const Vec2* vel, const Vec2* pos) { +void solid_init(Solid* dest, const double mass, const double dir, const Vec2* pos, const Vec2* vel) { dest->mass = mass; vect_cset(&dest->force, 0., 0.); - dest->dir = 0.; + dest->dir = dir; if(vel == NULL) vectnull(&dest->vel); else vectcpy(&dest->vel, vel); @@ -191,10 +191,10 @@ void solid_init(Solid* dest, const double mass, const Vec2* vel, const Vec2* pos } // Create a new solid. -Solid* solid_create(const double mass, const Vec2* vel, const Vec2* pos) { +Solid* solid_create(const double mass, const double dir, const Vec2* pos, const Vec2* vel) { Solid* dyn = MALLOC_L(Solid); assert(dyn != NULL); - solid_init(dyn, mass, vel, pos); + solid_init(dyn, mass, dir, pos, vel); return dyn; } diff --git a/src/physics.h b/src/physics.h index 1940ecd..30b82fa 100644 --- a/src/physics.h +++ b/src/physics.h @@ -37,7 +37,9 @@ struct Solid { typedef struct Solid Solid; // Solid manipulation. -void solid_init(Solid* dest, const double mass, const Vec2* vel, const Vec2* pos); -Solid* solid_create(const double mass, const Vec2* vel, const Vec2* pos); +void solid_init(Solid* dest, const double mass, const double dir, + const Vec2* pos, const Vec2* vel); +Solid* solid_create(const double mass, const double dir, + const Vec2* pos, const Vec2* vel); void solid_free(Solid* src); diff --git a/src/pilot.c b/src/pilot.c index 9c2f110..7a9b057 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -5,6 +5,7 @@ #include "main.h" #include "log.h" +#include "weapon.h" #include "pilot.h" // Stack of pilot id's to assure uniqueness. @@ -13,6 +14,7 @@ static unsigned int pilot_id = 0; // Stack of pilots - yes, they come in stacks now. static Pilot** pilot_stack; static int pilots = 0; +extern Pilot* player; // External. extern void ai_destroy(Pilot* p); // Ai. @@ -20,7 +22,8 @@ extern void player_think(Pilot* pilot); // Player.c extern void ai_think(Pilot* pilot); // Ai.c // Internal. static void pilot_update(Pilot* pilot, const double dt); -static void pilot_render(Pilot* pilot); +void pilot_render(Pilot* pilot); +static void pilot_free(Pilot* p); // Pull a pilot out of the pilot_stack based on id. Pilot* get_pilot(unsigned int id) { @@ -31,6 +34,7 @@ Pilot* get_pilot(unsigned int id) { return pilot_stack[i]; return NULL; #endif + if(id == 0) return player; // Dichotomical search. int i, n; for(i = 0, n = pilots/2; n > 0; n /= 2) @@ -38,15 +42,40 @@ Pilot* get_pilot(unsigned int id) { return (pilot_stack[i]->id == id) ? pilot_stack[i] : NULL; } +// Mkay, this is how we shoot. Listen up. +void pilot_shoot(Pilot* p, int secondary) { + int i; + if(!secondary) { + // Primary weapons. + if(!p->outfits) return; // No outfits. + + for(i = 0; p->outfits[i].outfit; 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) + // 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==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG); + p->outfits[i].timer = SDL_GetTicks(); + break; + default: + break; + } + } +} + // Render the pilot. -static void pilot_render(Pilot* pilot) { +void pilot_render(Pilot* p) { int sprite; - gl_texture* texture = pilot->ship->gfx_ship; + gl_texture* t = p->ship->gfx_ship; // Get the sprite corresponding to the direction facing. - sprite = (int)(pilot->solid->dir / (2.0*M_PI / (texture->sy * texture->sx))); + sprite = (int)(p->solid->dir / (2.0*M_PI / (t->sy * t->sx))); - gl_blitSprite(texture, &pilot->solid->pos, sprite % (int)texture->sx, sprite / (int)texture->sy); + gl_blitSprite(t, &p->solid->pos, sprite % (int)t->sx, sprite / (int)t->sy); } // Update the pilot. @@ -68,17 +97,23 @@ static void pilot_update(Pilot* pilot, const double dt) { // ==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, const Vec2* vel, const Vec2* pos, const int flags) { - pilot->id = ++pilot_id; // New unique pilot id based on pilot_id, Can't be 0. +void pilot_init(Pilot* pilot, Ship* ship, char* name, const double dir, const Vec2* pos, + const Vec2* vel, const int flags) { + + if(flags & PILOT_PLAYER) // Player is ID 0 + pilot->id = 0; + 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); - pilot->solid = solid_create(ship->mass, vel, pos); + pilot->solid = solid_create(ship->mass, dir, pos, vel); // Max shields armor. pilot->armor = ship->armor; @@ -88,39 +123,73 @@ void pilot_init(Pilot* pilot, Ship* ship, char* name, const Vec2* vel, const Vec // Initially idle. pilot->task = NULL; + // Outfits. + pilot->outfits = NULL; + ShipOutfit* so; + if(ship->outfit) { + int noutfits = 0; + for(so = ship->outfit; so; so = so->next) { + pilot->outfits = realloc(pilot->outfits, (noutfits+1)*sizeof(PilotOutfit)); + pilot->outfits[noutfits].outfit = so->data; + pilot->outfits[noutfits].quantity = so->quantity; + pilot->outfits[noutfits].timer = 0; + noutfits++; + } + // Sentinal. + pilot->outfits = realloc(pilot->outfits, (noutfits+1)*sizeof(PilotOutfit)); + pilot->outfits[noutfits].outfit = NULL; + pilot->outfits[noutfits].quantity = 0; + pilot->outfits[noutfits].timer = 0; + } + if(flags & PILOT_PLAYER) { pilot->think = player_think; // Players don't need to thing! :P + pilot->render = NULL; pilot->properties |= PILOT_PLAYER; player = pilot; - } else + } else { pilot->think = ai_think; - - pilot->update = pilot_update; + 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, const Vec2* vel, const Vec2* pos, const int flags) { +unsigned int pilot_create(Ship* ship, char* name, 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, vel, pos, flags); + pilot_init(dyn, ship, name, dir, pos, vel, flags); - // Add to the stack. - pilot_stack = realloc(pilot_stack, ++pilots*sizeof(Pilot*)); - pilot_stack[pilots-1] = dyn; + 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. -void pilot_destroy(Pilot* p) { - int i; - +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; @@ -128,27 +197,27 @@ void pilot_destroy(Pilot* p) { pilot_stack[i] = pilot_stack[i+1]; i++; } - free(p); + pilot_free(p); } // Free the prisoned pilot! void pilots_free(void) { int i; - for(i = 0; i < pilots; i++) { - solid_free(pilot_stack[i]->solid); - free(pilot_stack[i]->name); - free(pilot_stack[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 = pilots-1; i >= 0; i--) { - if(pilot_stack[i]->think != NULL) + for(i = 0; i < pilots; i++) { + if(pilot_stack[i]->think) pilot_stack[i]->think(pilot_stack[i]); - pilot_stack[i]->update(pilot_stack[i], dt); + 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]); } } diff --git a/src/pilot.h b/src/pilot.h index cc89013..e8b0efb 100644 --- a/src/pilot.h +++ b/src/pilot.h @@ -2,10 +2,17 @@ #include "main.h" #include "physics.h" #include "ai.h" +#include "outfit.h" #include "ship.h" #define PILOT_PLAYER 1 // Pilot is a player. +typedef struct { + Outfit* outfit; // Associated outfit. + unsigned int quantity; // Number of outfits of this type that the pilot has. + unsigned int timer; // Used to store last used weapon time. +} PilotOutfit; + // Primary pilot structure. typedef struct Pilot { unsigned int id; // Pilots id. @@ -18,24 +25,30 @@ typedef struct Pilot { // Current health. double armor, shield, energy; + void (*think)(struct Pilot*); // AI thinking for the pilot. void (*update)(struct Pilot*, const double); // Update the pilot. + void (*render)(struct Pilot*); // Rendering the pilot. + + // Outfit management. + PilotOutfit* outfits; unsigned int properties; // Used for AI etc. // AI. - void (*think)(struct Pilot*); // Ai thinking for the pilot. Task* task; // Current action. } Pilot; extern Pilot* player; // The player. Pilot* get_pilot(unsigned int id); -// Creation. -void pilot_init(Pilot* dest, Ship* ship, char* name, - const Vec2* vel, const Vec2* pos, const int flags); +void pilot_shoot(Pilot* p, int secondary); -unsigned int pilot_create(Ship* ship, char* name, const Vec2* vel, - const Vec2* pos, const int flags); +// Creation. +void pilot_init(Pilot* dest, Ship* ship, char* name, const double dir, + const Vec2* pos, const Vec2* vel, const int flags); + +unsigned int pilot_create(Ship* ship, char* name, const double dir, + const Vec2* pos, const Vec2* vel, const int flags); // Cleanup. void pilot_destroy(Pilot* p); diff --git a/src/player.c b/src/player.c index 5f797d7..9c86f28 100644 --- a/src/player.c +++ b/src/player.c @@ -16,11 +16,21 @@ typedef struct { } Keybind; static Keybind** player_input; // Contains the players keybindings. // Name of each keybinding. -const char* keybindNames[] = { "accel", "left", "right" }; +const char* keybindNames[] = { "accel", "left", "right", "primary" }; Pilot* player = NULL; // extern in pilot.h static double player_turn = 0.; // Turn velocity from input. static double player_acc = 0.; // Accel velocity from input. +static int player_primary = 0; // Player is shooting primary weapon. + +extern void pilot_render(Pilot* pilot); // Extern is in Pilot.* +static void player_renderGUI(void); + +// Render the player. +void player_render(void) { + pilot_render(player); + player_renderGUI(); +} // Used in pilot.c // Basically uses keyboard input instead of AI input. @@ -29,13 +39,15 @@ void player_think(Pilot* player) { if(player_turn) player->solid->dir_vel -= player->ship->turn * player_turn; + //if(player_primary) pilot_shoot(player, 0); + vect_pset(&player->solid->force, player->ship->thrust * player_acc, player->solid->dir); } // ================ // GUI! // ================ -void player_renderGUI(void) { +static void player_renderGUI(void) { } @@ -99,6 +111,11 @@ static void input_key(int keynum, double value, int abs) { if(abs) player_turn = value; else player_turn += value; } + // Shoot primary weapon. BOOM BOOM. + else if(strcmp(player_input[keynum]->name, "primary")==0) { + if(value==KEY_PRESS) player_primary = 1; + else if(value == KEY_RELEASE) player_primary = 0; + } //Make sure values are sane. player_acc = ABS(player_acc); diff --git a/src/player.h b/src/player.h index dc248d5..9f23ed9 100644 --- a/src/player.h +++ b/src/player.h @@ -1,10 +1,12 @@ #pragma once #include +#include "pilot.h" + +extern Pilot* pilot; typedef enum { KEYBIND_NULL, KEYBIND_KEYBOARD, KEYBIND_JAXIS, KEYBIND_JBUTTON } KeybindType; -// GUI. -void player_renderGUI(void); +void player_render(void); int player_isFlag(unsigned int flag); void player_setFlag(unsigned int flag); diff --git a/src/ship.c b/src/ship.c index af8be5f..5afec1e 100644 --- a/src/ship.c +++ b/src/ship.c @@ -6,8 +6,6 @@ #include "pack.h" #include "ship.h" -#define MAX_PATH_NAME 30 // Maximum size of the path. - #define XML_NODE_START 1 #define XML_NODE_TEXT 3 @@ -20,6 +18,8 @@ static Ship* ship_stack = NULL; static int ships = 0; +static Ship* ship_parse(xmlNodePtr parent); + // Get a ship based on it's name. Ship* get_ship(const char* name) { Ship* tmp = ship_stack; @@ -33,11 +33,13 @@ Ship* get_ship(const char* name) { return tmp+i; } -Ship* ship_parse(xmlNodePtr parent) { +static Ship* ship_parse(xmlNodePtr parent) { xmlNodePtr cur, node; Ship* tmp = CALLOC_L(Ship); + ShipOutfit* otmp, *ocur; - char str[MAX_PATH_NAME] = "\0"; + char str[PATH_MAX] = "\0"; + xmlChar* xstr; tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); @@ -45,18 +47,12 @@ Ship* ship_parse(xmlNodePtr parent) { while((node = node->next)) { // Load all the data. if(strcmp((char*)node->name, "GFX")==0) { - cur = node->children; - if(strcmp((char*)cur->name, "text")==0) { - snprintf(str, strlen((char*)cur->content)+sizeof(SHIP_GFX), - SHIP_GFX"%s", (char*)cur->content); - tmp->gfx_ship = gl_newSprite(str, 6, 6); - } - } - else if(strcmp((char*)node->name, "class")==0) { - cur = node->children; - if(strcmp((char*)cur->name, "text")==0) - tmp->class = atoi((char*)cur->content); + snprintf(str, strlen((char*)node->children->content)+sizeof(SHIP_GFX), + SHIP_GFX"%s", (char*)node->children->content); + tmp->gfx_ship = gl_newSprite(str, 6, 6); } + else if(strcmp((char*)node->name, "class")==0) + tmp->class = atoi((char*)node->children->content); else if(strcmp((char*)node->name, "movement")==0) { cur = node->children; while((cur = cur->next)) { @@ -66,10 +62,10 @@ Ship* ship_parse(xmlNodePtr parent) { tmp->turn = atoi((char*)cur->children->content); else if(strcmp((char*)cur->name, "speed")==0) tmp->speed = atoi((char*)cur->children->content); + } } - } - else if(strcmp((char*)node->name, "health")==0) { - cur = node->children; + else if(strcmp((char*)node->name, "health")==0) { + cur = node->children; while((cur = cur->next)) { if(strcmp((char*)cur->name, "armor")==0) tmp->armor = (double)atoi((char*)cur->children->content); @@ -98,7 +94,28 @@ Ship* ship_parse(xmlNodePtr parent) { tmp->cap_cargo = atoi((char*)cur->children->content); } } - } + else if(strcmp((char*)node->name, "outfits")==0) { + cur = node->children; + while((cur = cur->next)) { + if(strcmp((char*)cur->name, "outfit")==0) + otmp = MALLOC_L(ShipOutfit); + otmp->data = outfit_get((char*)cur->children->content); + xstr = xmlGetProp(cur, (xmlChar*)"qunatity"); + if(!xstr) + WARN("Ship '%s' is missing tag 'quantity for outfit '%s'", tmp->name, otmp->data->name); + otmp->quantity = atoi((char*)xstr); + free(xstr); + otmp->next = NULL; + + if((ocur = tmp->outfit) == NULL) tmp->outfit = otmp; + else { + while(ocur->next); + ocur->next = otmp; + } + } + } + } + tmp->thrust *= tmp->mass; // Helps keep number sane. #define MELEMENT(o,s) if(o == 0) WARN("Ship '%s' missing '"s"' element", tmp->name) diff --git a/src/ship.h b/src/ship.h index e998a45..ec4b355 100644 --- a/src/ship.h +++ b/src/ship.h @@ -1,10 +1,25 @@ #pragma once #include "main.h" +#include "outfit.h" #include "opengl.h" -enum ship_class { SHIP_CLASS_NULL, SHIP_CLASS_CIVILIAN }; +enum ship_class { + SHIP_CLASS_NULL, + SHIP_CLASS_CIV_LIGHT, + SHIP_CLASS_CIV_MEDIUM, + SHIP_CLASS_CIV_HEAVY +}; typedef enum ship_class ship_class; +// Small wrapper for the outfits. +typedef struct ShipOutfit { + struct ShipOutfit* next; // Linked list. + Outfit* data; // Data itself. + int quantity; +} ShipOutfit; + + +// Ship structure. typedef struct { char* name; // Ship name. ship_class class; // Ship class. @@ -26,6 +41,9 @@ typedef struct { // Capacity. int cap_cargo, cap_weapon; + + // Outfits + ShipOutfit* outfit; } Ship; int ships_load(void); diff --git a/src/weapon.c b/src/weapon.c new file mode 100644 index 0000000..6e2bb2b --- /dev/null +++ b/src/weapon.c @@ -0,0 +1,158 @@ +#include +#include +#include + +#include "outfit.h" +#include "physics.h" +#include "main.h" +#include "log.h" +#include "weapon.h" + +typedef struct Weapon { + Solid* solid; // Actually has its own solid. :D + + const Outfit* outfit; // Related outfit that fired. + unsigned int timer; // Mainly used to see when the weapon was fired. + + void(*update)(struct Weapon*, const double); // Position update and render. + void(*think)(struct Weapon*); // Some missiles need to be inteligent. +} Weapon; + +// Behind Pilot layer. +static Weapon** backLayer = NULL; // Behind pilots. +static int nbackLayer = 0; // Number of elements. +static int mbackLayer = 0; // Allocated memory size. +// Behind player layer. +static Weapon** frontLayer = NULL; // Behind pilots. +static int nfrontLayer = 0; // Number of elements. +static int mfrontLayer = 0; // Allocated memory size. + + +static Weapon* weapon_create(const Outfit* outfit, const double dir, + const Vec2* pos, const Vec2* vel); + +static void weapon_render(const Weapon* w); +static void weapon_update(Weapon* w, const double dt); +static void weapon_free(Weapon* w); + +// Update all weapons in the layer. +void weapons_update(const double dt, WeaponLayer layer) { + Weapon** wlayer; + int nlayer; + + switch(layer) { + case WEAPON_LAYER_BG: + wlayer = backLayer; + nlayer = nbackLayer; + break; + case WEAPON_LAYER_FG: + wlayer = frontLayer; + nlayer = nfrontLayer; + break; + } + int i; + for(i = 0; i < nlayer; i++) + weapon_update(wlayer[i], dt); +} + +// Render the weapons. +static void weapon_render(const Weapon* w) { + int sprite; + gl_texture* t = w->outfit->gfx_space; + + // Get the sprite corresponding to the direction facing. + sprite = (int)(w->solid->dir / (2.0*M_PI / (t->sy*t->sx))); + + gl_blitSprite(t, &w->solid->pos, sprite % (int)t->sx, sprite / (int)t->sy); +} + +// Update the weapon. +static void weapon_update(Weapon* w, const double dt) { + if(w->think) + (*w->think)(w); + (*w->solid->update)(w->solid, dt); + + weapon_render(w); +} + +static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel) { + Vec2 v; + double mass = 1; // Presumer lasers have a mass of 1. + Weapon* w = MALLOC_L(Weapon); + w->outfit = outfit; // Non-Changeable. + w->update = weapon_update; + w->think = NULL; + + switch(outfit->type) { + case OUTFIT_TYPE_BOLT: + vect_cset(&v, VY(*vel)+outfit->speed*cos(dir), VANGLE(*vel)+outfit->speed*sin(dir)); + w->solid = solid_create(mass, dir, pos, &v); + break; + default: + break; + } + return w; +} + +// Add a new weapon. +void weapon_add(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, WeaponLayer layer) { + if(!outfit_isWeapon(outfit)) { + ERR("Trying to create a weapon from a non-Weapon type Outfit"); + return; + } + + Weapon* w = weapon_create(outfit, dir, pos, vel); + + // Set the propper layer. + Weapon** curLayer = NULL; + int* mLayer = NULL; + int* nLayer = NULL; + switch(layer) { + case WEAPON_LAYER_BG: + curLayer = backLayer; + nLayer = &nbackLayer; + mLayer = &mbackLayer; + break; + case WEAPON_LAYER_FG: + curLayer = frontLayer; + nLayer = &nfrontLayer; + mLayer = &mfrontLayer; + 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 = backLayer = realloc(curLayer, (++(*mLayer))*sizeof(Weapon)); + break; + case WEAPON_LAYER_FG: + curLayer = frontLayer = realloc(curLayer, (++(*mLayer))*sizeof(Weapon*)); + break; + } + curLayer[(*nLayer)++] = w; + } +} + +// 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 < nbackLayer; i++) + weapon_free(backLayer[i]); + nbackLayer = 0; + backLayer = NULL; + for(i = 0; i < nfrontLayer; i++) + weapon_free(frontLayer[i]); + nfrontLayer = 0; + frontLayer = NULL; +} + diff --git a/src/weapon.h b/src/weapon.h new file mode 100644 index 0000000..6d6df73 --- /dev/null +++ b/src/weapon.h @@ -0,0 +1,11 @@ +#pragma once +#include "outfit.h" +#include "physics.h" + +typedef enum { WEAPON_LAYER_BG, WEAPON_LAYER_FG } WeaponLayer; + +void weapon_add(const Outfit* outfit, const double dir, + const Vec2* pos, const Vec2* vel, WeaponLayer layer); + +void weapons_update(const double dt, WeaponLayer layer); +