diff --git a/bin/Makefile b/bin/Makefile index b6b9e0e..7787544 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -24,9 +24,9 @@ CVORBIS = CGL = CFLAGS = $(CLUA) $(CSDL) $(CXML) $(CTTF) $(CGL) $(CAL) $(CVORBIS) $(VERSION) ifdef DEBUG -CFLAGS += -W -Wall -g3 -DDEBUG -DLUA_USE_APICHECK +CFLAGS += -W -Wall -g3 -DDEBUG -DLUA_USE_APICHECK -std=c99 else -CFLAGS += -O2 -funroll-loops -pipe +CFLAGS += -O2 -funroll-loops -pipe -std=c99 endif # LDFLAGS. diff --git a/dat/outfit.xml b/dat/outfit.xml index c306f39..8e9db07 100644 --- a/dat/outfit.xml +++ b/dat/outfit.xml @@ -40,8 +40,9 @@ missile missile - ExmM + ExpM 5 + 1 1200 200 600 diff --git a/dat/ship.xml b/dat/ship.xml index 0675c5e..0121e95 100644 --- a/dat/ship.xml +++ b/dat/ship.xml @@ -82,6 +82,8 @@ Laser + Missile Launcher + Missile diff --git a/src/outfit.c b/src/outfit.c index 386ffaf..02193d3 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -74,6 +74,32 @@ int outfit_isAmmo(const Outfit* o) { } +// Get the outfit graphics. +glTexture* outfit_gfx(const Outfit* o) { + if(outfit_isWeapon(o)) return o->u.wpn.gfx_space; + else if(outfit_isAmmo(o)) return o->u.amm.gfx_space; + return NULL; +} + +// Get the outfit spfx if applicable. +int outfit_spfx(const Outfit* o) { + if(outfit_isWeapon(o)) return o->u.wpn.spfx; + else if(outfit_isAmmo(o)) return o->u.amm.spfx; + return -1; +} + +double outfit_dmgShield(const Outfit* o) { + if(outfit_isWeapon(o)) return o->u.wpn.damage_armour; + else if (outfit_isAmmo(o)) return o->u.amm.damage_armour; + return -1; +} + +double outfit_dmgArmour(const Outfit* o) { + if(outfit_isWeapon(o)) return o->u.wpn.damage_shield; + else if(outfit_isAmmo(o)) return o->u.amm.damage_shield; + return -1; +} + const char* outfit_typename[] = { "NULL", "Bolt Cannon", @@ -103,36 +129,38 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { do { // Load all the things. - if(xml_isNode(node, "speed")) tmp->speed = xml_getFloat(node); - else if(xml_isNode(node, "delay")) tmp->delay = xml_getInt(node); - else if(xml_isNode(node, "range")) tmp->range = xml_getFloat(node); - else if(xml_isNode(node, "accuracy")) tmp->accuracy = xml_getFloat(node); + if(xml_isNode(node, "speed")) tmp->u.wpn.speed = xml_getFloat(node); + else if(xml_isNode(node, "delay")) tmp->u.wpn.delay = xml_getInt(node); + else if(xml_isNode(node, "range")) tmp->u.wpn.range = xml_getFloat(node); + else if(xml_isNode(node, "accuracy")) tmp->u.wpn.accuracy = 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); + tmp->u.wpn.gfx_space = gl_newSprite(str, 6, 6); } else if(xml_isNode(node, "spfx")) - tmp->spfx = spfx_get(xml_get(node)); + tmp->u.wpn.spfx = spfx_get(xml_get(node)); else if(xml_isNode(node, "sound")) - tmp->sound = sound_get(xml_get(node)); + tmp->u.wpn.sound = sound_get(xml_get(node)); 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); + if(xml_isNode(cur, "armour")) + tmp->u.wpn.damage_armour = xml_getFloat(cur); + else if(xml_isNode(cur, "shield")) + tmp->u.wpn.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) + if(tmp->u.wpn.gfx_space == NULL) WARN("Outfit '%s' missing 'gfx' element", tmp->name); - MELEMENT(tmp->sound, "sound"); - MELEMENT(tmp->delay, "delay"); - MELEMENT(tmp->speed, "speed"); - MELEMENT(tmp->range, "range"); - MELEMENT(tmp->accuracy, "accuracy"); - MELEMENT(tmp->damage_armour, "armour' from element 'damage"); - MELEMENT(tmp->damage_shield, "shield' from element 'damage"); + MELEMENT(tmp->u.wpn.sound, "sound"); + MELEMENT(tmp->u.wpn.delay, "delay"); + MELEMENT(tmp->u.wpn.speed, "speed"); + MELEMENT(tmp->u.wpn.range, "range"); + MELEMENT(tmp->u.wpn.accuracy, "accuracy"); + MELEMENT(tmp->u.wpn.damage_armour, "armour' from element 'damage"); + MELEMENT(tmp->u.wpn.damage_shield, "shield' from element 'damage"); #undef MELEMENT } @@ -143,13 +171,13 @@ static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent) { 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)); + if(xml_isNode(node, "delay")) tmp->u.lau.delay = xml_getInt(node); + else if(xml_isNode(node, "ammo")) tmp->u.lau.ammo = strdup(xml_get(node)); } while((node = node->next)); #define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name) - MELEMENT(tmp->ammo == NULL, "ammo"); - MELEMENT(tmp->delay==0, "delay"); + MELEMENT(tmp->u.lau.ammo == NULL, "ammo"); + MELEMENT(tmp->u.lau.delay==0, "delay"); #undef MELEMENT } @@ -161,36 +189,40 @@ static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) { 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); + if(xml_isNode(node, "thrust")) tmp->u.amm.thrust = xml_getFloat(node); + else if(xml_isNode(node, "turn")) tmp->u.amm.turn = xml_getFloat(node); + else if(xml_isNode(node, "speed")) tmp->u.amm.speed = xml_getFloat(node); + else if(xml_isNode(node, "duration")) + tmp->u.amm.duration = (unsigned int)1000.*xml_getFloat(node); + else if(xml_isNode(node, "lockon")) + tmp->u.amm.lockon = (unsigned int)1000.*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); + tmp->u.amm.gfx_space = gl_newSprite(str, 6, 6); } else if(xml_isNode(node, "spfx")) - tmp->spfx = spfx_get(xml_get(node)); + tmp->u.amm.spfx = spfx_get(xml_get(node)); else if(xml_isNode(node, "sound")) - tmp->sound = sound_get(xml_get(node)); + tmp->u.amm.sound = sound_get(xml_get(node)); 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); + if(xml_isNode(cur, "armour")) tmp->u.amm.damage_armour = xml_getFloat(cur); + else if(xml_isNode(cur, "shield")) tmp->u.amm.damage_shield = xml_getFloat(cur); } while((cur = cur->next)); } } while((node = node->next)); #define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name) - MELEMENT(tmp->gfx_space == NULL, "gfx"); - MELEMENT((sound_lock != NULL) && (tmp->sound == 0), "sound"); - MELEMENT(tmp->thrust==0, "thrust"); - MELEMENT(tmp->turn==0, "turn"); - MELEMENT(tmp->speed==0, "speed"); - MELEMENT(tmp->range==0, "duration"); - MELEMENT(tmp->damage_armour==0, "armour' from element 'damage"); - MELEMENT(tmp->damage_shield==0, "shield' from element 'damage"); + MELEMENT(tmp->u.amm.gfx_space == NULL, "gfx"); + MELEMENT((sound_lock != NULL) && (tmp->u.amm.sound == 0), "sound"); + MELEMENT(tmp->u.amm.thrust==0, "thrust"); + MELEMENT(tmp->u.amm.turn==0, "turn"); + MELEMENT(tmp->u.amm.speed==0, "speed"); + MELEMENT(tmp->u.amm.duration==0, "duration"); + MELEMENT(tmp->u.amm.lockon==0, "lockon"); + MELEMENT(tmp->u.amm.damage_armour==0, "armour' from element 'damage"); + MELEMENT(tmp->u.amm.damage_shield==0, "shield' from element 'damage"); #undef MELEMENT } @@ -295,10 +327,13 @@ void outfit_free(void) { int i; for(i = 0; i < outfits; i++) { // Free graphics. - if(outfit_stack[i].gfx_space) gl_freeTexture(outfit_stack[i].gfx_space); + if(outfit_isWeapon(&outfit_stack[i]) && outfit_stack[i].u.wpn.gfx_space) + gl_freeTexture(outfit_stack[i].u.wpn.gfx_space); + else if(outfit_isAmmo(&outfit_stack[i]) && outfit_stack[i].u.amm.gfx_space) + gl_freeTexture(outfit_stack[i].u.amm.gfx_space); - if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].ammo) - free(outfit_stack[i].ammo); + if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].u.lau.ammo) + free(outfit_stack[i].u.lau.ammo); free(outfit_stack[i].name); } diff --git a/src/outfit.h b/src/outfit.h index b27233d..a65822d 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -49,21 +49,24 @@ typedef struct Outfit_ { glTexture* gfx_space; ALuint sound; // Sound to play. int spfx; // Special effect on hit. - }; + } wpn; struct { // Launcher. - //unsigned int delay; // Delay between shots. + unsigned int delay; // Delay between shots. char* ammo; - }; + } lau; struct { // Ammo. - //double speed; // Max speed. + unsigned int duration; // Duration. + double speed; // Max speed. double turn; // Turn vel. double thrust; // Acceleration. - unsigned int duration; // Duration. - //double damage_armour, damage_shield; // Damage. + double damage_armour, damage_shield; // Damage. - //glTexture* gfx_space; - }; - }; + glTexture* gfx_space; + ALuint sound; // Sound to play. + int spfx; // Special effect on hit. + unsigned int lockon; // Time taken to lock on the target. + } amm; + } u; } Outfit; Outfit* outfit_get(const char* name); @@ -74,6 +77,12 @@ int outfit_isAmmo(const Outfit* o); const char* outfit_getType(const Outfit* o); const char* outfit_getTypeBroad(const Outfit* o); +// Get data from outfit. +glTexture* outfit_gfx(const Outfit* o); +int outfit_spfx(const Outfit* o); +double outfit_dmgShield(const Outfit* o); +double outfit_dmgArmour(const Outfit* o); + // Load/free outfit stack. int outfit_load(void); void outfit_free(void); diff --git a/src/pilot.c b/src/pilot.c index 4e178d4..428508d 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -146,12 +146,14 @@ void pilot_shoot(Pilot* p, const unsigned int target, const int secondary) { } static void pilot_shootWeapon(Pilot* p, PilotOutfit* w, const unsigned int t) { + int quantity, delay; // WElll... Trying to shoot when you have no ammo?? FUUU - int quantity = (outfit_isAmmo(w->outfit) && p->secondary) ? + quantity = (outfit_isAmmo(w->outfit) && p->secondary) ? p->secondary->quantity : w->quantity; - // Check to see if weapon is ready. - if((SDL_GetTicks() - w->timer) < (w->outfit->delay / quantity)) return; + delay = (outfit_isWeapon(w->outfit)) ? w->outfit->u.wpn.delay : w->outfit->u.lau.delay; + // Check to see if weapon is ready. + if((SDL_GetTicks() - w->timer) < (unsigned int)(delay/quantity)) return; // Regular weapons. if(outfit_isWeapon(w->outfit)) { // Different weapons. @@ -228,7 +230,7 @@ void pilot_setAmmo(Pilot* p) { return; } - name = p->secondary->outfit->ammo; + name = p->secondary->outfit->u.lau.ammo; for(i = 0; i < p->noutfits; i++) if(strcmp(p->outfits[i].outfit->name, name)==0) { diff --git a/src/weapon.c b/src/weapon.c index 80b1f13..d50d7be 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -106,22 +106,26 @@ void weapons_unpause(void) { } static void think_seeker(Weapon* w) { + double diff; 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; + // Ammo isn't locked on yet.. + if(SDL_GetTicks() > (w->timer + w->outfit->u.amm.lockon)) { + diff = angle_diff(w->solid->dir, vect_angle(&w->solid->pos, &p->solid->pos)); + w->solid->dir_vel = 10 * diff * w->outfit->u.amm.turn; + // Face the target. + if(w->solid->dir_vel > w->outfit->u.amm.turn) w->solid->dir_vel = w->outfit->u.amm.turn; + else if(w->solid->dir_vel < -w->outfit->u.amm.turn) w->solid->dir_vel = -w->outfit->u.amm.turn; + } - vect_pset(&w->solid->force, w->outfit->thrust, w->solid->dir); + vect_pset(&w->solid->force, w->outfit->u.amm.thrust, w->solid->dir); - if(VMOD(w->solid->vel) > w->outfit->speed) + if(VMOD(w->solid->vel) > w->outfit->u.amm.speed) // We should not go any faster. - vect_pset(&w->solid->vel, w->outfit->speed, VANGLE(w->solid->vel)); + vect_pset(&w->solid->vel, w->outfit->u.amm.speed, VANGLE(w->solid->vel)); } // Update all the weapon layers. @@ -151,19 +155,21 @@ static void weapons_updateLayer(const double dt, const WeaponLayer layer) { w = wlayer[i]; switch(wlayer[i]->outfit->type) { case OUTFIT_TYPE_MISSILE_SEEK_AMMO: - if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->duration)) { + if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->u.amm.duration)) { weapon_destroy(wlayer[i], layer); continue; } break; - default: + case OUTFIT_TYPE_BOLT: // Check see if it exceeds distance. if(SDL_GetTicks() > (wlayer[i]->timer + 1000*(unsigned int) - wlayer[i]->outfit->range/wlayer[i]->outfit->speed)) { + wlayer[i]->outfit->u.wpn.range/wlayer[i]->outfit->u.wpn.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. @@ -194,17 +200,24 @@ void weapons_render(const WeaponLayer layer) { // Render the weapons. static void weapon_render(const Weapon* w) { int sx, sy; + glTexture* gfx; + + gfx = outfit_gfx(w->outfit); // Get the sprite corresponding to the direction facing. - gl_getSpriteFromDir(&sx, &sy, w->outfit->gfx_space, w->solid->dir); + gl_getSpriteFromDir(&sx, &sy, gfx, w->solid->dir); - gl_blitSprite(w->outfit->gfx_space, w->solid->pos.x, w->solid->pos.y, sx, sy, NULL); + gl_blitSprite(gfx, w->solid->pos.x, w->solid->pos.y, sx, sy, NULL); } // Update the weapon. static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { int i, wsx, wsy, psx, psy; - gl_getSpriteFromDir(&wsx, &wsy, w->outfit->gfx_space, w->solid->dir); + glTexture* gfx; + + gfx = outfit_gfx(w->outfit); + + gl_getSpriteFromDir(&wsx, &wsy, gfx, w->solid->dir); for(i = 0; i < pilots; i++) { gl_getSpriteFromDir(&psx, &psy, pilot_stack[i]->ship->gfx_space, @@ -212,14 +225,14 @@ static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { if(w->parent == pilot_stack[i]->id) continue; // Hey! That's you. if((weapon_isSmart(w)) && (pilot_stack[i]->id == w->target) && - CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos, + CollideSprite(gfx, 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, + CollideSprite(gfx, 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; @@ -239,15 +252,15 @@ 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); - spfx_add(w->outfit->spfx, &w->solid->pos, &p->solid->vel, SPFX_LAYER_BACK); + spfx_add(outfit_spfx(w->outfit), &w->solid->pos, &p->solid->vel, SPFX_LAYER_BACK); } else - spfx_add(w->outfit->spfx, &w->solid->pos, &p->solid->vel, SPFX_LAYER_FRONT); + spfx_add(outfit_spfx(w->outfit), &w->solid->pos, &p->solid->vel, SPFX_LAYER_FRONT); 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->solid, w->parent, w->outfit->damage_shield, w->outfit->damage_armour); + pilot_hit(p, w->solid, w->parent, outfit_dmgShield(w->outfit), outfit_dmgShield(w->outfit)); // We don't need the weapon particle any longer. weapon_destroy(w, layer); } @@ -268,14 +281,14 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* switch(outfit->type) { case OUTFIT_TYPE_BOLT: // Need accuracy and speed based on player. -- Another contribution from VLack. - rdir += RNG(-outfit->accuracy/2., outfit->accuracy/2.)/180.*M_PI; + rdir += RNG(-outfit->u.wpn.accuracy/2., outfit->u.wpn.accuracy/2.)/180.*M_PI; if((rdir > 2.*M_PI) || (rdir < 0.)) rdir = fmod(rdir, 2.*M_PI); vectcpy(&v, vel); - vect_cadd(&v, outfit->speed*cos(rdir), outfit->speed*sin(rdir)); + vect_cadd(&v, outfit->u.wpn.speed*cos(rdir), outfit->u.wpn.speed*sin(rdir)); w->solid = solid_create(mass, rdir, pos, &v); w->voice = sound_addVoice(VOICE_PRIORITY_BOLT, w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y, w->outfit->sound, 0); + w->solid->vel.x, w->solid->vel.y, w->outfit->u.wpn.sound, 0); break; case OUTFIT_TYPE_MISSILE_SEEK_AMMO: mass = w->outfit->mass; @@ -283,7 +296,7 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* w->think = think_seeker; // Eeek!!! w->voice = sound_addVoice(VOICE_PRIORITY_AMMO, w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y, w->outfit->sound, 0); + w->solid->vel.x, w->solid->vel.y, w->outfit->u.amm.sound, 0); break; default: