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);