From 36f1936667d7cabc211e0a77c611db9d54b73981 Mon Sep 17 00:00:00 2001 From: Allanis <allanis@saracraft.net> Date: Fri, 15 Feb 2013 07:31:59 +0000 Subject: [PATCH] [Add] Secondary weapons. [Add] Preliminary seeker missile. --- dat/fleet.xml | 4 +- dat/outfit.xml | 36 ++++++++++++-- dat/ship.xml | 8 +-- gfx/outfit/missile.png | Bin 0 -> 13108 bytes src/ai.c | 109 +++++++++++++++++++++++++---------------- src/outfit.c | 92 +++++++++++++++++++++++++++++----- src/outfit.h | 13 +++-- src/pilot.c | 50 ++++++++++++++++--- src/pilot.h | 6 ++- src/player.c | 108 ++++++++++++++++++++++++++++++---------- src/ship.c | 2 +- src/weapon.c | 94 ++++++++++++++++++++++++++--------- src/weapon.h | 3 +- 13 files changed, 397 insertions(+), 128 deletions(-) create mode 100644 gfx/outfit/missile.png 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 @@ <ai>merchant</ai> <faction>Merchant</faction> <pilots> + <pilot chance='100'>Merchant Ship</pilot> <pilot chance='80'>Merchant Ship</pilot> - <pilot chance='80'>Merchant Ship</pilot> - <pilot chance='60'>Merchant Ship</pilot> - <pilot chance='60'>Merchant Ship</pilot> </pilots> </fleet> <fleet name="Pirate"> 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 @@ <?xml version="1.0" encoding="UTF-8"?> <Outfits> - <outfit name="laser"> + <outfit name="Laser"> <general> <max>5</max> <tech>2</tech> - <mass>1</mass> + <mass>5</mass> </general> - <specific type = "1"> - <sound>laser</sound> + <specific type="1"> <gfx>lasergreen</gfx> <delay>500</delay> <speed>550</speed> @@ -19,4 +18,33 @@ </damage> </specific> </outfit> + <outfit name="Missile Launcher"> + <general> + <max>2</max> + <tech>4</tech> + <mass>10</mass> + </general> + <specific type="5" secondary="1"> + <ammo>Missile</ammo> + <delay>1200</delay> + </specific> + </outfit> + <outfit name="Missile"> + <general> + <max>60</max> + <tech>2</tech> + <mass>1</mass> + </general> + <specific type="6"> + <gfx>missile</gfx> + <duration>5</duration> + <thrust>1200</thrust> + <turn>200</turn> + <speed>600</speed> + <damage> + <armour>25</armour> + <shield>20</shield> + </damage> + </specific> + </outfit> </Outfits> 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 @@ <cap_cargo>25</cap_cargo> </characteristics> <outfits> - <outfit quantity='1'>laser</outfit> + <outfit quantity='1'>Laser</outfit> </outfits> </ship> <ship name = "Leapard"> @@ -51,7 +51,7 @@ <cap_cargo>10</cap_cargo> </characteristics> <outfits> - <outfit quantity ='2'>laser</outfit> + <outfit quantity ='2'>Laser</outfit> </outfits> </ship> <ship name="Lancer"> @@ -78,7 +78,9 @@ <cap_cargo>40</cap_cargo> </characteristics> <outfits> - <outfit quantity='4'>laser</outfit> + <outfit quantity='3'>Laser</outfit> + <outfit quantity='2'>Missile Launcher</outfit> + <outfit quantity='20'>Missile</outfit> </outfits> </ship> </Ships> diff --git a/gfx/outfit/missile.png b/gfx/outfit/missile.png new file mode 100644 index 0000000000000000000000000000000000000000..10430afd71ab19118cd7b2d41262cf5c726517cf GIT binary patch literal 13108 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 YdjCV%n79;-Hw5D4i`U|XqWb>-2iDrW@Bjb+ literal 0 HcmV?d00001 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)<h/2.) || \ @@ -72,6 +79,25 @@ void weapon_minimap(const double res, const double w, const double h, const Rada } #undef PIXEL +static void think_seeker(Weapon* w) { + if(w->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);