diff --git a/dat/fleet.xml b/dat/fleet.xml index e5c1322..90954cd 100644 --- a/dat/fleet.xml +++ b/dat/fleet.xml @@ -18,10 +18,8 @@ merchant Merchant + Merchant Ship Merchant Ship - Merchant Ship - Merchant Ship - Merchant Ship diff --git a/dat/outfit.xml b/dat/outfit.xml index b368d2e..baae407 100644 --- a/dat/outfit.xml +++ b/dat/outfit.xml @@ -1,13 +1,12 @@ - + 5 2 - 1 + 5 - - laser + lasergreen 500 550 @@ -19,4 +18,33 @@ + + + 2 + 4 + 10 + + + Missile + 1200 + + + + + 60 + 2 + 1 + + + missile + 5 + 1200 + 200 + 600 + + 25 + 20 + + + diff --git a/dat/ship.xml b/dat/ship.xml index 7be7fb1..f3d9123 100644 --- a/dat/ship.xml +++ b/dat/ship.xml @@ -24,7 +24,7 @@ 25 - laser + Laser @@ -51,7 +51,7 @@ 10 - laser + Laser @@ -78,7 +78,9 @@ 40 - laser + Laser + Missile Launcher + Missile diff --git a/gfx/outfit/missile.png b/gfx/outfit/missile.png new file mode 100644 index 0000000..10430af Binary files /dev/null and b/gfx/outfit/missile.png differ diff --git a/src/ai.c b/src/ai.c index a5b1d4a..758f08f 100644 --- a/src/ai.c +++ b/src/ai.c @@ -48,8 +48,10 @@ // Call the AI function with name f. #define AI_LCALL(f) (lua_getglobal(L, f), lua_pcall(L, 0, 0, 0)) -// Register a number constant n to name s (syntax is just like lua_register). +// Register a number constant n to name s (syntax is just like lua_regfunc). #define lua_regnumber(l,s,n) (lua_pushnumber(l,n), lua_setglobal(l,s)) +// Registers a C function. +#define lua_regfunc(l,s,f) (lua_pushcfunction(l,f), lua_setglobal(L,s)) // L state, void* buf, int n size, char* s identifier. #define luaL_dobuffer(L,b,n,s) (luaL_loadbuffer(L,b,n,s) || lua_pcall(L, 0, LUA_MULTRET, 0)) @@ -60,6 +62,13 @@ #define MAX_DIR_ERR 0.1*M_PI/180. #define MIN_VEL_ERR 0.5 +// Ai flags. +#define ai_setFlag(f) (pilot_flags |= f) +#define ai_isFlag(f) (pilot_flags & f) +// Flags. +#define AI_PRIMARY (1<<0) // Firing primary weapon. +#define AI_SECONDARY (1<<1) // Firing secondary weapon. + // file info. #define AI_PREFIX "../scripts/ai/" #define AI_SUFFIX ".lua" @@ -114,6 +123,7 @@ static int ai_getrndplanet(lua_State* L); // pointer getrndplanet() static int ai_hyperspace(lua_State* L); // [number] hyperspace() // Combat. static int ai_combat(lua_State* L); // combat(number) +static int ai_settarget(lua_State* L); // settarget(number) static int ai_shoot(lua_State* L); // shoot(number) number = 1,2,3. static int ai_getenemy(lua_State* L); // number getenemy(). static int ai_hostile(lua_State* L); // hostile(number). @@ -130,7 +140,8 @@ static int ai_rng(lua_State* L); // rng(number, number) static Pilot* cur_pilot = NULL; static double pilot_acc = 0.; static double pilot_turn = 0.; -static int pilot_primary = 0; +static int pilot_flags = 0; +static int pilot_target = 0; // Destroy the AI part of the pilot. void ai_destroy(Pilot* p) { @@ -190,47 +201,48 @@ static int ai_loadProfile(char* filename) { // Register C funstions in Lua. // Tasks. - lua_register(L, "pushtask", ai_pushtask); - lua_register(L, "poptask", ai_poptask); - lua_register(L, "taskname", ai_taskname); + lua_regfunc(L, "pushtask", ai_pushtask); + lua_regfunc(L, "poptask", ai_poptask); + lua_regfunc(L, "taskname", ai_taskname); // Consult. - lua_register(L, "gettarget", ai_gettarget); - lua_register(L, "gettargetid", ai_gettargetid); - lua_register(L, "armour", ai_armour); - lua_register(L, "shield", ai_shield); - lua_register(L, "parmour", ai_parmour); - lua_register(L, "pshield", ai_pshield); - lua_register(L, "getdist", ai_getdistance); - lua_register(L, "getpos", ai_getpos); - lua_register(L, "minbrakedist", ai_minbrakedist); + lua_regfunc(L, "gettarget", ai_gettarget); + lua_regfunc(L, "gettargetid", ai_gettargetid); + lua_regfunc(L, "armour", ai_armour); + lua_regfunc(L, "shield", ai_shield); + lua_regfunc(L, "parmour", ai_parmour); + lua_regfunc(L, "pshield", ai_pshield); + lua_regfunc(L, "getdist", ai_getdistance); + lua_regfunc(L, "getpos", ai_getpos); + lua_regfunc(L, "minbrakedist", ai_minbrakedist); // Boolean. - lua_register(L, "exists", ai_exists); - lua_register(L, "ismaxvel", ai_ismaxvel); - lua_register(L, "isstopped", ai_isstopped); - lua_register(L, "isenemy", ai_isenemy); - lua_register(L, "isally", ai_isally); - lua_register(L, "incombat", ai_incombat); + lua_regfunc(L, "exists", ai_exists); + lua_regfunc(L, "ismaxvel", ai_ismaxvel); + lua_regfunc(L, "isstopped", ai_isstopped); + lua_regfunc(L, "isenemy", ai_isenemy); + lua_regfunc(L, "isally", ai_isally); + lua_regfunc(L, "incombat", ai_incombat); // Movement. - lua_register(L, "accel", ai_accel); - lua_register(L, "turn", ai_turn); - lua_register(L, "face", ai_face); - lua_register(L, "brake", ai_brake); - lua_register(L, "getnearestplanet", ai_getnearestplanet); - lua_register(L, "getrndplanet", ai_getrndplanet); - lua_register(L, "hyperspace", ai_hyperspace); + lua_regfunc(L, "accel", ai_accel); + lua_regfunc(L, "turn", ai_turn); + lua_regfunc(L, "face", ai_face); + lua_regfunc(L, "brake", ai_brake); + lua_regfunc(L, "getnearestplanet", ai_getnearestplanet); + lua_regfunc(L, "getrndplanet", ai_getrndplanet); + lua_regfunc(L, "hyperspace", ai_hyperspace); // Combat. - lua_register(L, "combat", ai_combat); - lua_register(L, "shoot", ai_shoot); - lua_register(L, "getenemy", ai_getenemy); - lua_register(L, "hostile", ai_hostile); + lua_regfunc(L, "combat", ai_combat); + lua_regfunc(L, "settarget", ai_settarget); + lua_regfunc(L, "shoot", ai_shoot); + lua_regfunc(L, "getenemy", ai_getenemy); + lua_regfunc(L, "hostile", ai_hostile); // Timers. - lua_register(L, "settimer", ai_settimer); - lua_register(L, "timeup", ai_timeup); + lua_regfunc(L, "settimer", ai_settimer); + lua_regfunc(L, "timeup", ai_timeup); // Misc. - lua_register(L, "createvect", ai_createvect); - lua_register(L, "comm", ai_comm); - lua_register(L, "broadcast", ai_broadcast); - lua_register(L, "rng", ai_rng); + lua_regfunc(L, "createvect", ai_createvect); + lua_regfunc(L, "comm", ai_comm); + lua_regfunc(L, "broadcast", ai_broadcast); + lua_regfunc(L, "rng", ai_rng); // Now load the file, since all the functions have been previously loaded. @@ -277,7 +289,8 @@ void ai_think(Pilot* pilot) { // Clean up some variables. pilot_acc = pilot_turn = 0.; - pilot_primary = 0; + pilot_flags = 0; + pilot_target = 0; // Control function if pilot is idle or tick is up. if((cur_pilot->tcontrol < SDL_GetTicks()) || (cur_pilot->task == NULL)) { @@ -298,8 +311,10 @@ void ai_think(Pilot* pilot) { if(pilot_turn) // Set the turning velocity. cur_pilot->solid->dir_vel -= cur_pilot->ship->turn * pilot_turn; vect_pset(&cur_pilot->solid->force, cur_pilot->ship->thrust * pilot_acc, cur_pilot->solid->dir); - - if(pilot_primary) pilot_shoot(pilot, 0); // AMG, he's gunna shoot! + + // Fire weapons if needs be. + if(ai_isFlag(AI_PRIMARY)) pilot_shoot(pilot, pilot_target, 0); // Primary. + if(ai_isFlag(AI_SECONDARY)) pilot_shoot(pilot, pilot_target, 1); // Secondary. } // Pilot is attacked. @@ -676,14 +691,22 @@ static int ai_combat(lua_State* L) { return 0; } +// Set the pilots target. +static int ai_settarget(lua_State* L) { + MIN_ARGS(1); + + if(lua_isnumber(L,1)) pilot_target = (int)lua_tonumber(L,1); + return 0; +} + // Pew pew.. Says the pilot. static int ai_shoot(lua_State* L) { int n = 1; if(lua_isnumber(L, 1)) n = (int)lua_tonumber(L,1); - if(n == 1) pilot_primary = 1; - //else if(n == 2) pilot_secondary = 1; - //else if(n == 3) pilot_primary = pilot_secondary = 1; + if(n == 1) ai_setFlag(AI_PRIMARY); + else if(n == 2) ai_setFlag(AI_SECONDARY); + else if(n == 3) ai_setFlag(AI_PRIMARY | AI_SECONDARY); return 0; } diff --git a/src/outfit.c b/src/outfit.c index 14b8fbd..edbc24a 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -7,6 +7,8 @@ #include "xml.h" #include "outfit.h" +#define outfit_setProp(o,p) ((o)->properties |= p) + #define XML_OUTFIT_ID "Outfits" #define XML_OUTFIT_TAG "outfit" @@ -18,6 +20,8 @@ static int outfits = 0; static Outfit* outfit_parse(const xmlNodePtr parent); static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent); +static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent); +static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent); // Return an outfit. Outfit* outfit_get(const char* name) { @@ -98,7 +102,8 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { } } while((node = node->next)); #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) - MELEMENT(tmp->accuracy, "tech"); + if(tmp->gfx_space == NULL) + WARN("Outfit '%s' missing 'gfx' element", tmp->name); MELEMENT(tmp->delay, "delay"); MELEMENT(tmp->speed, "speed"); MELEMENT(tmp->range, "range"); @@ -108,6 +113,60 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { #undef MELEMENT } +// Parse the specific area for a launcher and loads it into Outfit. +static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent) { + xmlNodePtr node; + node = parent->xmlChildrenNode; + + do { + // Load the dataz. + if(xml_isNode(node, "delay")) tmp->delay = xml_getInt(node); + else if(xml_isNode(node, "ammo")) tmp->ammo = strdup(xml_get(node)); + } while((node = node->next)); + +#define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) + if(tmp->ammo == NULL) WARN("Outfit '%s' missing 'ammo' element", tmp->name); + MELEMENT(tmp->delay, "delay"); +#undef MELEMENT +} + +// Parse the specific area for a weapon and load it into Outfit. +static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) { + xmlNodePtr cur, node; + node = parent->xmlChildrenNode; + + char str[PATH_MAX] = "\0"; + + do { + if(xml_isNode(node, "thrust")) tmp->thrust = xml_getFloat(node); + else if(xml_isNode(node, "turn")) tmp->turn = xml_getFloat(node); + else if(xml_isNode(node, "speed")) tmp->speed = xml_getFloat(node); + else if(xml_isNode(node, "duration")) tmp->duration = 1000*(unsigned int)xml_getFloat(node); + else if(xml_isNode(node, "gfx")) { + snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+4, + OUTFIT_GFX"%s.png", xml_get(node)); + tmp->gfx_space = gl_newSprite(str, 6, 6); + } + else if(xml_isNode(node, "damage")) { + cur = node->children; + do { + if(xml_isNode(cur, "armour")) tmp->damage_armour = xml_getFloat(cur); + else if(xml_isNode(cur, "shield")) tmp->damage_shield = xml_getFloat(cur); + } while((cur = cur->next)); + } + } while((node = node->next)); +#define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) + if(tmp->gfx_space == NULL) + WARN("Outfit '%s' missing 'gfx' element", tmp->name); + MELEMENT(tmp->thrust, " thrust"); + MELEMENT(tmp->turn, "turn"); + MELEMENT(tmp->speed, "speed"); + MELEMENT(tmp->range, "duration"); + MELEMENT(tmp->damage_armour, "armour' from element 'damage"); + MELEMENT(tmp->damage_shield, "shield' from element 'damage"); +#undef MELEMENT +} + // Parse and return Outfits from parent node. static Outfit* outfit_parse(const xmlNodePtr parent) { Outfit* tmp = CALLOC_L(Outfit); @@ -130,26 +189,33 @@ static Outfit* outfit_parse(const xmlNodePtr parent) { } else if(xml_isNode(node, "specific")) { // Has to be processed seperately. + + // Get the type. prop = xml_nodeProp(node, "type"); if(prop == NULL) ERR("Outfit '%s' element 'specific' missing property 'type'", tmp->name); tmp->type = atoi(prop); free(prop); - switch(tmp->type) { - case OUTFIT_TYPE_NULL: - WARN("Outfit '%s' is of type NONE", tmp->name); - break; - case OUTFIT_TYPE_BOLT: - outfit_parseSWeapon(tmp, node); - break; - default: - break; + + // Is this the secondary weapon? + prop = xml_nodeProp(node, "secondary"); + if(prop != NULL) { + if((int)atoi(prop)) outfit_setProp(tmp, OUTFIT_PROP_WEAP_SECONDARY); + free(prop); } + if(tmp->type == OUTFIT_TYPE_NULL) + WARN("Outfit '%s' is of type NONE", tmp->name); + else if(outfit_isWeapon(tmp)) + outfit_parseSWeapon(tmp, node); + else if(outfit_isLauncher(tmp)) + outfit_parseSLauncher(tmp, node); + else if(outfit_isAmmo(tmp)) + outfit_parseSAmmo(tmp, node); } } while((node = node->next)); #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) MELEMENT(tmp->max, "max"); - MELEMENT(tmp->tech, "tech"); + MELEMENT(tmp->tech, "tech"); MELEMENT(tmp->mass, "mass"); MELEMENT(tmp->type, "type"); #undef MELEMENT @@ -202,6 +268,10 @@ void outfit_free(void) { for(i = 0; i < outfits; i++) { if(outfit_isWeapon(&outfit_stack[i]) && outfit_stack[i].gfx_space) gl_freeTexture(outfit_stack[i].gfx_space); + + if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].ammo) + free(outfit_stack[i].ammo); + free(outfit_stack[i].name); } free(outfit_stack); diff --git a/src/outfit.h b/src/outfit.h index d1f93aa..b6c5038 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -1,8 +1,9 @@ #pragma once #include "opengl.h" -#define OUTFIT_PROP_WEAP_PRIMARY (1<<0) -#define OUTFIT_PROP_WEAP_SECONDARY (1<<1) +#define outfit_isProp(o,p) ((o)->properties & p) +// Property flags. +#define OUTFIT_PROP_WEAP_SECONDARY (1<<0) // Outfit types. typedef enum { @@ -48,12 +49,14 @@ typedef struct { }; struct { // Launcher. //unsigned int delay; // Delay between shots. + char* ammo; }; struct { // Ammo. //double speed; // Max speed. - //double turn; // Turn vel. - //double thrust; // Acceleration. - //double damage_armour, damage_shield; + double turn; // Turn vel. + double thrust; // Acceleration. + unsigned int duration; // Duration. + //double damage_armour, damage_shield; // Damage. //gl_texture* gfx_space; }; diff --git a/src/pilot.c b/src/pilot.c index fc48b95..15553ff 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -64,7 +64,7 @@ unsigned int pilot_getNearest(const Pilot* p) { 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)) { + if(!pilot_isDisabled(pilot_stack[i]) && ((!tp) || (td < d))) { d = td; tp = pilot_stack[i]->id; } @@ -106,27 +106,40 @@ Pilot* pilot_get(const unsigned int id) { } // Mkay, this is how we shoot. Listen up. -void pilot_shoot(Pilot* p, const int secondary) { +void pilot_shoot(Pilot* p, const unsigned int target, 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)) + if(outfit_isWeapon(p->outfits[i].outfit)) // Are we a weapon? // 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(); + &p->solid->vel, p->id, target, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG); + p->outfits[i].timer = SDL_GetTicks(); // Let's not try this again for a while. break; default: break; } + } else { + if(!p->secondary) return; // No secondary weapon. + + if(outfit_isLauncher(p->secondary->outfit)) { + if(((SDL_GetTicks()-p->secondary->timer) > + (p->secondary->outfit->delay/p->secondary->quantity)) && + p->ammo && (p->ammo->quantity > 0)) { + weapon_add(p->ammo->outfit, p->solid->dir, &p->solid->pos, &p->solid->vel, + p->id, target, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG); + + p->secondary->timer = SDL_GetTicks(); // Let's not try this again for a while. + p->ammo->quantity -= 1; // No getting this one back. + } + } } } @@ -145,6 +158,27 @@ void pilot_hit(Pilot* p, const double damage_shield, const double damage_armour) 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; @@ -231,6 +265,8 @@ void pilot_init(Pilot* pilot, Ship* ship, char* name, Faction* faction, AI_Profi // Outfits. pilot->outfits = NULL; + pilot->secondary = NULL; + pilot->ammo = NULL; ShipOutfit* so; if(ship->outfit) { pilot->noutfits = 0; @@ -316,7 +352,7 @@ void pilots_free(void) { void pilots_update(double dt) { int i; for(i = 0; i < pilots; i++) { - if(pilot_stack[i]->think && !pilot_isFlag(pilot_stack[i], PILOT_DISABLED)) + 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); diff --git a/src/pilot.h b/src/pilot.h index 10c6c8e..dc3407d 100644 --- a/src/pilot.h +++ b/src/pilot.h @@ -25,6 +25,7 @@ #define PILOT_DISABLED (1<<4) // Pilot is disabled. // Just makes life simpler. #define pilot_isPlayer(p) ((p)->flags & PILOT_PLAYER) +#define pilot_isDisabled(p) ((p)->flags & PILOT_DISABLED) typedef struct { Outfit* outfit; // Associated outfit. @@ -54,6 +55,8 @@ typedef struct Pilot { // Outfit management. PilotOutfit* outfits; int noutfits; + PilotOutfit* secondary; // Secondary weapon. + PilotOutfit* ammo; // Secondary ammo (if needed). unsigned int flags; // Used for AI etc. @@ -90,8 +93,9 @@ unsigned int pilot_getHostile(void); // Only for the player. Fleet* fleet_get(const char* name); // MISC. -void pilot_shoot(Pilot* p, const int secondary); +void pilot_shoot(Pilot* p, const unsigned int target, const int secondary); void pilot_hit(Pilot* p, const double damage_shield, const double damage_armour); +void pilot_setAmmo(Pilot* p); // Creation. void pilot_init(Pilot* dest, Ship* ship, char* name, Faction* faction, AI_Profile* ai, diff --git a/src/player.c b/src/player.c index 9d66fea..827d626 100644 --- a/src/player.c +++ b/src/player.c @@ -26,6 +26,8 @@ #define PLAYER_TURN_LEFT (1<<0) // Player is turning left. #define PLAYER_TURN_RIGHT (1<<1) // Player is turning right. #define PLAYER_FACE (1<<2) // Player is facing target. +#define PLAYER_PRIMARY (1<<3) // Player is shooting primary weapon. +#define PLAYER_SECONDARY (1<<4) // Player is shooting secondary weapon. // Flag functions. #define player_isFlag(f) (player_flags & f) @@ -46,19 +48,17 @@ static Keybind** player_input; // Contains the players keybindings. // Name of each keybinding. const char* keybindNames[] = { "accel", "left", "right", // Movement. "primary", "target", "target_nearest", "face", "board", // Combat. + "secondary", "secondary_next", // Secondary weapons. "mapzoomin", "mapzoomout", "screenshot", "end" }; // Misc. // Player stuff. Pilot* player = NULL; // extern in pilot.h unsigned int credits = 0; -static int player_flags = 0; // Player flags. +static unsigned int player_flags = 0; // Player flags. 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. -//static int player_secondary = 0; // layer is shooting secondary weapon. static unsigned int player_target = PLAYER_ID; // Targetted pilot. static int planet_target = -1; // Targetted planet. -static int weapon = -1; // Secondary weapon is in use. // Pilot stuff for GUI. extern Pilot** pilot_stack; @@ -148,6 +148,7 @@ static void gui_renderBar(const glColour* c, const Vec2* p, const Rect* r, const // Keybinds. static void player_board(void); +static void player_secondaryNext(void); static void player_screenshot(void); // Create a new player. @@ -254,7 +255,7 @@ void player_render(void) { if(player_target != PLAYER_ID) { p = pilot_get(player_target); - if(pilot_isFlag(p, PILOT_DISABLED)) c = &cInert; + if(pilot_isDisabled(p)) c = &cInert; else if(pilot_isFlag(p, PILOT_HOSTILE)) c = &cHostile; else c = &cNeutral; @@ -340,7 +341,7 @@ void player_render(void) { gui_renderBar(&cEnergy, &gui.pos_energy, &gui.energy, player->energy / player->energy_max); // Weapon. - if(weapon == -1) { + if(player->secondary == NULL) { i = gl_printWidth(NULL, "Secondary"); vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 5); gl_print(NULL, &v, &cConsole, "Secondary"); @@ -348,7 +349,21 @@ void player_render(void) { vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 10 - gl_defFont.h); gl_print(&gui.smallFont, &v, &cGrey, "None"); } else { - + if(player->ammo == NULL) { + i = gl_printWidth(&gui.smallFont, "%s", player->secondary->outfit->name); + vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., + VY(gui.pos_weapon) - (gui.weapon.h - gui.smallFont.h)/2.); + gl_print(&gui.smallFont, &v, &cConsole, "%s", player->secondary->outfit->name); + } else { + i = gl_printWidth(&gui.smallFont, "%s", player->secondary->outfit->name); + vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., + VY(gui.pos_weapon) - 5); + gl_print(&gui.smallFont, &v, NULL, "%s", player->ammo->outfit->name); + i = gl_printWidth(NULL, "%d", player->ammo->quantity); + vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., + VY(gui.pos_weapon) - 10 - gl_defFont.h); + gl_print(NULL, &v, &cConsole, "%d", player->ammo->quantity); + } } // Target. @@ -362,7 +377,7 @@ void player_render(void) { gl_print(&gui.smallFont, &gui.pos_target_faction, NULL, "%s", p->faction->name); // Target status. - if(pilot_isFlag(p, PILOT_DISABLED)) + if(pilot_isDisabled(p)) // Disable the pilot. gl_print(&gui.smallFont, &gui.pos_target_health, NULL, "Disabled"); else if(p->shield > p->shield_max / 100.) @@ -433,7 +448,7 @@ static void gui_renderPilot(const Pilot* p) { glBegin(GL_QUADS); // Colours. if(p->id == player_target) COLOUR(cRadar_targ); - else if(pilot_isFlag(p, PILOT_DISABLED)) COLOUR(cInert); + else if(pilot_isDisabled(p)) COLOUR(cInert); else if(pilot_isFlag(p, PILOT_HOSTILE)) COLOUR(cHostile); else COLOUR(cNeutral); @@ -750,7 +765,9 @@ void player_think(Pilot* player) { if(player_turn) player->solid->dir_vel -= player->ship->turn * player_turn; - if(player_primary) pilot_shoot(player, 0); + if(player_isFlag(PLAYER_PRIMARY)) pilot_shoot(player, 0, 0); + if(player_isFlag(PLAYER_SECONDARY) && (player_target != PLAYER_ID)) // Needs a target. + pilot_shoot(player, player_target, 1); vect_pset(&player->solid->force, player->ship->thrust * player_acc, player->solid->dir); } @@ -764,7 +781,7 @@ void player_board(void) { } p = pilot_get(player_target); - if(!pilot_isFlag(p, PILOT_DISABLED)) { + if(!pilot_isDisabled(p)) { player_message("You cannot board a ship that isn't disabled!"); return; } @@ -778,7 +795,32 @@ void player_board(void) { player_message("You are going too fact to board the ship"); return; } - player_message("Ship boarding isn't implemented yet! HAHA"); + player_message("It's a shame Allanis hasn't added boarding yet, right?!"); +} + +// Get the next secondary weapon. +static void player_secondaryNext(void) { + int i = 0; + + // Get the current secondary weapon pos. + if(player->secondary != NULL) + for(i = 0; i < player->noutfits; i++) + if(&player->outfits[i] == player->secondary) { + i++; + break; + } + // Get the next secondary weapon. + for(; i < player->noutfits; i++) + if(outfit_isProp(player->outfits[i].outfit, OUTFIT_PROP_WEAP_SECONDARY)) { + player->secondary = player->outfits + i; + break; + } + // We didn't find an outfit. + if(i >= player->noutfits) + player->secondary = NULL; + + // Set ammo. + pilot_setAmmo(player); } // Take a screenshot. @@ -795,17 +837,19 @@ static void player_screenshot(void) { // Set the default input keys. void input_setDefault(void) { - 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); - input_setKeybind("target", KEYBIND_KEYBOARD, SDLK_TAB, 0); - input_setKeybind("target_nearest", KEYBIND_KEYBOARD, SDLK_r, 0); - input_setKeybind("face", KEYBIND_KEYBOARD, SDLK_f, 0); - input_setKeybind("board", KEYBIND_KEYBOARD, SDLK_b, 0); - input_setKeybind("mapzoomin", KEYBIND_KEYBOARD, SDLK_UP, 0); - input_setKeybind("mapzoomout", KEYBIND_KEYBOARD, SDLK_DOWN, 0); - input_setKeybind("screenshot", KEYBIND_KEYBOARD, SDLK_F12, 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); + input_setKeybind("target", KEYBIND_KEYBOARD, SDLK_TAB, 0); + input_setKeybind("target_nearest", KEYBIND_KEYBOARD, SDLK_r, 0); + input_setKeybind("face", KEYBIND_KEYBOARD, SDLK_f, 0); + input_setKeybind("board", KEYBIND_KEYBOARD, SDLK_b, 0); + input_setKeybind("secondary", KEYBIND_KEYBOARD, SDLK_LSHIFT, 0); + input_setKeybind("secondary_next", KEYBIND_KEYBOARD, SDLK_q, 0); + input_setKeybind("mapzoomin", KEYBIND_KEYBOARD, SDLK_UP, 0); + input_setKeybind("mapzoomout", KEYBIND_KEYBOARD, SDLK_DOWN, 0); + input_setKeybind("screenshot", KEYBIND_KEYBOARD, SDLK_F12, 0); } // Initialization/exit functions (does not assign keys). @@ -881,8 +925,8 @@ static void input_key(int keynum, double value, int abs) { } // 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; + if(value == KEY_PRESS) player_setFlag(PLAYER_PRIMARY); + else if(value == KEY_RELEASE) player_rmFlag(PLAYER_PRIMARY); } // Targetting. else if(strcmp(player_input[keynum]->name, "target")==0) { @@ -907,16 +951,26 @@ static void input_key(int keynum, double value, int abs) { else if(strcmp(player_input[keynum]->name, "board")==0) { if(value == KEY_PRESS) player_board(); } + // Shooting secondary weapon. + else if(strcmp(player_input[keynum]->name, "secondary")==0) { + if(value == KEY_PRESS) player_setFlag(PLAYER_SECONDARY); + else if(value == KEY_RELEASE) player_rmFlag(PLAYER_SECONDARY); + } + // Selecting secondary weapon. + else if(strcmp(player_input[keynum]->name, "secondary_next")==0) { + if(value == KEY_PRESS) player_secondaryNext(); + } // Zoom in. else if(strcmp(player_input[keynum]->name, "mapzoomin")==0) { - if(value == KEY_PRESS && gui.radar.res < RADAR_RES_MAX) + if((value == KEY_PRESS) && (gui.radar.res < RADAR_RES_MAX)) gui.radar.res += RADAR_RES_INTERVAL; } // Zoom out. else if(strcmp(player_input[keynum]->name, "mapzoomout")==0) { - if(value == KEY_PRESS && gui.radar.res > RADAR_RES_MIN) + if((value == KEY_PRESS) && (gui.radar.res > RADAR_RES_MIN)) gui.radar.res -= RADAR_RES_INTERVAL; } + // Take a screenshot. else if(strcmp(player_input[keynum]->name, "screenshot")==0) { if(value == KEY_PRESS) player_screenshot(); } diff --git a/src/ship.c b/src/ship.c index 0ee4738..c482317 100644 --- a/src/ship.c +++ b/src/ship.c @@ -117,7 +117,7 @@ static Ship* ship_parse(xmlNodePtr parent) { if((ocur = tmp->outfit) == NULL) tmp->outfit = otmp; else { - while(ocur->next); + while(ocur->next) ocur = ocur->next; ocur->next = otmp; } } diff --git a/src/weapon.c b/src/weapon.c index bea42cf..2520fa0 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -12,6 +12,8 @@ #include "player.h" #include "weapon.h" +#define weapon_isSmart(w) (w->think) + // Some stuff from pilot. extern Pilot** pilot_stack; extern int pilots; @@ -23,6 +25,7 @@ typedef struct Weapon { Solid* solid; // Actually has its own solid. :D unsigned int parent; // The pilot that just shot at you! + unsigned int target; // Target to hit. Only used by seeking stuff. const Outfit* outfit; // Related outfit that fired. unsigned int timer; // Mainly used to see when the weapon was fired. @@ -42,12 +45,16 @@ static int mwfrontLayer = 0; // Allocated memory size. static Weapon* weapon_create(const Outfit* outfit, const double dir, - const Vec2* pos, const Vec2* vel, unsigned int parent); + const Vec2* pos, const Vec2* vel, const unsigned int parent, + const unsigned int target); static void weapon_render(const Weapon* w); static void weapon_update(Weapon* w, const double dt, WeaponLayer layer); +static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer); static void weapon_destroy(Weapon* w, WeaponLayer layer); static void weapon_free(Weapon* w); +// Think. +static void think_seeker(Weapon* w); // Draw the minimap weapons (player.c). #define PIXEL(x,y) if((shape == RADAR_RECT && ABS(x) < w/2. && ABS(y)target == w->parent) return; // HEY! Self harm is not allowed. + + Pilot* p = pilot_get(w->target); + if(p == NULL) return; // Can't do anything with no pilots. + + double diff = angle_diff(w->solid->dir, vect_angle(&w->solid->pos, &p->solid->pos)); + w->solid->dir_vel = 10 * diff * w->outfit->turn; + // Face the target. + if(w->solid->dir_vel > w->outfit->turn) w->solid->dir_vel = w->outfit->turn; + else if(w->solid->dir_vel < -w->outfit->turn) w->solid->dir_vel = -w->outfit->turn; + + vect_pset(&w->solid->force, w->outfit->thrust, w->solid->dir); + + if(VMOD(w->solid->vel) > w->outfit->speed) + // We should not go any faster. + vect_pset(&w->solid->vel, w->outfit->speed, VANGLE(w->solid->vel)); +} + // Update all weapons in the layer. void weapons_update(const double dt, WeaponLayer layer) { Weapon** wlayer; @@ -92,15 +118,20 @@ void weapons_update(const double dt, WeaponLayer layer) { for(i = 0; i < (*nlayer); i++) { w = wlayer[i]; switch(wlayer[i]->outfit->type) { - case OUTFIT_TYPE_BOLT: + case OUTFIT_TYPE_MISSILE_SEEK_AMMO: + if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->duration)) { + weapon_destroy(wlayer[i], layer); + continue; + } + break; + default: + // Check see if it exceeds distance. if(SDL_GetTicks() > (wlayer[i]->timer + 1000*(unsigned int) wlayer[i]->outfit->range/wlayer[i]->outfit->speed)) { weapon_destroy(wlayer[i],layer); continue; } break; - default: - break; } weapon_update(wlayer[i], dt, layer); // If the weapon has been deleted we are going to have to hold back one. @@ -126,38 +157,51 @@ static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { for(i = 0; i < pilots; i++) { gl_getSpriteFromDir(&psx, &psy, pilot_stack[i]->ship->gfx_space, pilot_stack[i]->solid->dir); + if(w->parent == pilot_stack[i]->id) continue; // Hey! That's you. - if((w->parent != pilot_stack[i]->id) && // The pilot hasn't shoot it. + if((weapon_isSmart(w)) && (pilot_stack[i]->id == w->target) && + CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos, + pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) { + weapon_hit(w, pilot_stack[i], layer); + return; + } + else if(!weapon_isSmart(w) && !areAllies(pilot_get(w->parent)->faction, pilot_stack[i]->faction) && CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos, - pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) { - - if(!pilot_isPlayer(pilot_stack[i])) - // Inform the ai it has been attacked. Useless if we are player. - ai_attacked(pilot_stack[i], w->parent); - if(w->parent == PLAYER_ID) // Make hostile to player. - pilot_setFlag(pilot_stack[i], PILOT_HOSTILE); - - // Inform the ship that it should take some damage. - pilot_hit(pilot_stack[i], w->outfit->damage_shield, w->outfit->damage_armour); - // No need for the weapon particle anymore. - weapon_destroy(w, layer); + pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) { + weapon_hit(w, pilot_stack[i], layer); return; } } - if(w->think) (*w->think)(w); + if(weapon_isSmart(w)) (*w->think)(w); (*w->solid->update)(w->solid, dt); weapon_render(w); } +// Good shot. +static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer) { + // Someone should let the ai know it's been attacked. + if(!pilot_isPlayer(p)) + ai_attacked(p, w->parent); + if(w->parent == PLAYER_ID) + // Make hostile to player. + pilot_setFlag(p, PILOT_HOSTILE); + + // Let the ship know that is should take some kind of damage. + pilot_hit(p, w->outfit->damage_shield, w->outfit->damage_armour); + // We don't need the weapon particle any longer. + weapon_destroy(w, layer); +} + static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos, - const Vec2* vel, unsigned int parent) { + const Vec2* vel, unsigned int parent, const unsigned int target) { Vec2 v; double mass = 1; // Presumer lasers have a mass of 1. double rdir = dir; // Real direction (accuracy). Weapon* w = MALLOC_L(Weapon); w->parent = parent; // Non-Changeable. + w->target = target; // Non-Changeable. w->outfit = outfit; // Non-Changeable. w->update = weapon_update; w->timer = SDL_GetTicks(); @@ -172,6 +216,11 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* vect_cadd(&v, outfit->speed*cos(rdir), outfit->speed*sin(rdir)); w->solid = solid_create(mass, rdir, pos, &v); break; + case OUTFIT_TYPE_MISSILE_SEEK_AMMO: + mass = w->outfit->mass; + w->solid = solid_create(mass, dir, pos, vel); + w->think = think_seeker; // Eeek!!! + break; default: // Just dump it where the player is. w->solid = solid_create(mass, dir, pos, vel); @@ -182,13 +231,14 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* // Add a new weapon. void weapon_add(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, - unsigned int parent, WeaponLayer layer) { - if(!outfit_isWeapon(outfit)) { + unsigned int parent, unsigned int target, WeaponLayer layer) { + + if(!outfit_isWeapon(outfit) && !outfit_isAmmo(outfit)) { ERR("Trying to create a weapon from a non-Weapon type Outfit"); return; } - Weapon* w = weapon_create(outfit, dir, pos, vel, parent); + Weapon* w = weapon_create(outfit, dir, pos, vel, parent, target); // Set the propper layer. Weapon** curLayer = NULL; diff --git a/src/weapon.h b/src/weapon.h index 9fe79a8..f13c16f 100644 --- a/src/weapon.h +++ b/src/weapon.h @@ -5,7 +5,8 @@ typedef enum { WEAPON_LAYER_BG, WEAPON_LAYER_FG } WeaponLayer; void weapon_add(const Outfit* outfit, const double dir, - const Vec2* pos, const Vec2* vel, unsigned int parent, WeaponLayer layer); + const Vec2* pos, const Vec2* vel, unsigned int parent, + const unsigned int target, WeaponLayer layer); void weapons_update(const double dt, WeaponLayer layer);