/** * @brief Destroys a weapon. * * @param w Weapon to destroy. * @param layer Layer to which the weapon belongs. */ static void weapon_destroy( Weapon* w, WeaponLayer layer ) { int i; Weapon** wlayer; int *nlayer; Pilot *pilot_target; /* Decrement target lockons if needed */ if (outfit_isSeeker(w->outfit)) { pilot_target = pilot_get( w->target ); if (pilot_target != NULL) pilot_target->lockons--; } /* Stop playing sound if beam weapon. */ if (outfit_isBeam(w->outfit)) { sound_stop( w->voice ); sound_playPos(w->outfit->u.bem.sound_off, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); } switch (layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; default: WARN("Unknown weapon layer!"); } for (i=0; (wlayer[i] != w) && (i < *nlayer); i++); /* get to the curent position */ if (i >= *nlayer) { WARN("Trying to destroy weapon not found in stack!"); return; } weapon_free(wlayer[i]); wlayer[i] = NULL; (*nlayer)--; for ( ; i < (*nlayer); i++) wlayer[i] = wlayer[i+1]; }
/** * @brief Clears pilot's missile lockon timers. * * @param p Pilot to clear missile lockon timers. */ void pilot_lockClear( Pilot *p ) { int i; PilotOutfitSlot *o; for (i=0; i<p->noutfits; i++) { o = p->outfits[i]; if (o->outfit == NULL) continue; if (!outfit_isSeeker(o->outfit)) continue; /* Clear timer. */ o->u.ammo.lockon_timer = o->outfit->u.lau.lockon; /* Clear arc. */ o->u.ammo.in_arc = 0; } }
/** * @brief Fires a weapon set. * * @param p Pilot firing weaponsets. * @param ws Weapon set to fire. * @param level Level of the firing weapon set. */ static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level ) { int i, j, ret, s; Pilot *pt; AsteroidAnchor *field; Asteroid *ast; double time; Outfit *o; /* Case no outfits. */ if (ws->slots == NULL) return 0; /* Fire. */ ret = 0; for (i=0; i<array_size(ws->slots); i++) { o = ws->slots[i].slot->outfit; /* Ignore NULL outfits. */ if (o == NULL) continue; /* Only "active" outfits. */ if ((level != -1) && (ws->slots[i].level != level)) continue; /* Only run once for each weapon type in the group. */ s = 0; for (j=0; j<i; j++) { /* Only active outfits. */ if ((level != -1) && (ws->slots[j].level != level)) continue; /* Found a match. */ if (ws->slots[j].slot->outfit == o) { s = 1; break; } } if (s!=0) continue; /* Only "locked on" outfits. */ if (outfit_isSeeker(o) && (ws->slots[i].slot->u.ammo.lockon_timer > 0.)) continue; /* If inrange is set we only fire at targets in range. */ time = INFINITY; /* With no target we just set time to infinity. */ if (p->target != p->id){ pt = pilot_get( p->target ); if (pt != NULL) time = pilot_weapFlyTime( o, p, &pt->solid->pos, &pt->solid->vel); } /* Looking for a closer targeted asteroid */ if (p->nav_asteroid != -1){ field = &cur_system->asteroids[p->nav_anchor]; ast = &field->asteroids[p->nav_asteroid]; time = MIN( time, pilot_weapFlyTime( o, p, &ast->pos, &ast->vel) ); } /* Only "inrange" outfits. */ if ( ws->inrange && outfit_duration(o) < time) continue; /* Shoot the weapon of the weaponset. */ ret += pilot_shootWeaponSetOutfit( p, ws, o, level, time ); } return ret; }
/** * @brief Tries to automatically set and create the pilot's weapon set. * * Weapon set 0 is for all weapons. <br /> * Weapon set 1 is for forward weapons. Ammo using weapons are secondaries. <br /> * Weapon set 2 is for turret weapons. Ammo using weapons are secondaries. <br /> * Weapon set 3 is for all weapons. Forwards are primaries and turrets are secondaries. <br /> * Weapon set 4 is for seeking weapons. High payload variants are secondaries. <br /> * Weapon set 5 is for fighter bays. <br /> * * @param p Pilot to automagically generate weapon lists. */ void pilot_weaponAuto( Pilot *p ) { PilotOutfitSlot *slot; Outfit *o; int i, level, id; /* Clear weapons. */ pilot_weaponClear( p ); /* Set modes. */ pilot_weapSetType( p, 0, WEAPSET_TYPE_CHANGE ); pilot_weapSetType( p, 1, WEAPSET_TYPE_CHANGE ); pilot_weapSetType( p, 2, WEAPSET_TYPE_CHANGE ); pilot_weapSetType( p, 3, WEAPSET_TYPE_CHANGE ); pilot_weapSetType( p, 4, WEAPSET_TYPE_WEAPON ); pilot_weapSetType( p, 5, WEAPSET_TYPE_WEAPON ); pilot_weapSetType( p, 6, WEAPSET_TYPE_ACTIVE ); pilot_weapSetType( p, 7, WEAPSET_TYPE_ACTIVE ); pilot_weapSetType( p, 8, WEAPSET_TYPE_ACTIVE ); pilot_weapSetType( p, 9, WEAPSET_TYPE_ACTIVE ); /* All should be inrange. */ if (!pilot_isPlayer(p)) for (i=0; i<PILOT_WEAPON_SETS; i++){ pilot_weapSetInrange( p, i, 1 ); /* Update range and speed (at 0)*/ pilot_weapSetUpdateRange( &p->weapon_sets[i] ); } /* Iterate through all the outfits. */ for (i=0; i<p->noutfits; i++) { slot = p->outfits[i]; o = slot->outfit; /* Must be non-empty, and a weapon or active outfit. */ if ((o == NULL) || !outfit_isActive(o)) { slot->level = -1; /* Clear level. */ slot->weapset = -1; continue; } /* Manually defined group preempts others. */ if (o->group) { id = o->group; } /* Bolts and beams. */ else if (outfit_isBolt(o) || outfit_isBeam(o) || (outfit_isLauncher(o) && !outfit_isSeeker(o->u.lau.ammo))) { id = outfit_isTurret(o) ? 2 : 1; } /* Seekers. */ else if (outfit_isLauncher(o) && outfit_isSeeker(o->u.lau.ammo)) { id = 4; } /* Fighter bays. */ else if (outfit_isFighterBay(o)) { id = 5; } /* Ignore rest. */ else { slot->level = -1; continue; } /* Set level based on secondary flag. */ level = outfit_isSecondary(o); /* Add to its base group. */ pilot_weapSetAdd( p, id, slot, level ); /* Also add another copy to another group. */ if (id == 1) { /* Forward. */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ pilot_weapSetAdd( p, 3, slot, 0 ); /* Also get added to 'Fwd/Tur'. */ } else if (id == 2) { /* Turrets. */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ pilot_weapSetAdd( p, 3, slot, 1 ); /* Also get added to 'Fwd/Tur'. */ } else if (id == 4) /* Seekers */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ } /* Update active weapon set. */ pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] ); }
/** * @brief Updates the lockons on the pilot's launchers * * @param p Pilot being updated. * @param o Slot being updated. * @param t Pilot that is currently the target of p (or NULL if not applicable). * @param a Angle to update if necessary. Should be initialized to -1 before the loop. * @param dt Current delta tick. */ void pilot_lockUpdateSlot( Pilot *p, PilotOutfitSlot *o, Pilot *t, double *a, double dt ) { double max, old; double x,y, ang, arc; int locked; /* No target. */ if (t == NULL) return; /* Nota seeker. */ if (!outfit_isSeeker(o->outfit)) return; /* Check arc. */ arc = o->outfit->u.lau.arc; if (arc > 0.) { /* We use an external variable to set and update the angle if necessary. */ if (*a < 0.) { x = t->solid->pos.x - p->solid->pos.x; y = t->solid->pos.y - p->solid->pos.y; ang = ANGLE( x, y ); *a = fabs( angle_diff( ang, p->solid->dir ) ); } /* Decay if not in arc. */ if (*a > arc) { /* Limit decay to the lockon time for this launcher. */ max = o->outfit->u.lau.lockon; /* When a lock is lost, immediately gain half the lock timer. * This is meant as an incentive for the aggressor not to lose the lock, * and for the target to try and break the lock. */ old = o->u.ammo.lockon_timer; o->u.ammo.lockon_timer += dt; if ((old <= 0.) && (o->u.ammo.lockon_timer > 0.)) o->u.ammo.lockon_timer += o->outfit->u.lau.lockon / 2.; /* Cap at max. */ if (o->u.ammo.lockon_timer > max) o->u.ammo.lockon_timer = max; /* Out of arc. */ o->u.ammo.in_arc = 0; return; } } /* In arc. */ o->u.ammo.in_arc = 1; locked = (o->u.ammo.lockon_timer < 0.); /* Lower timer. When the timer reaches zero, the lock is established. */ max = -o->outfit->u.lau.lockon/3.; if (o->u.ammo.lockon_timer > max) { /* Compensate for enemy hide factor. */ o->u.ammo.lockon_timer -= dt * (o->outfit->u.lau.ew_target2 / t->ew_hide); /* Cap at -max/3. */ if (o->u.ammo.lockon_timer < max) o->u.ammo.lockon_timer = max; /* Trigger lockon hook. */ if (!locked && (o->u.ammo.lockon_timer < 0.)) pilot_runHook( p, PILOT_HOOK_LOCKON ); } }
/** * @brief Draws the minimap weapons (used in player.c). * * @param res Minimap resolution. * @param w Width of minimap. * @param h Height of minimap. * @param shape Shape of the minimap. * @param alpha Alpha to draw points at. */ void weapon_minimap( const double res, const double w, const double h, const RadarShape shape, double alpha ) { int i, rc, p; double x, y; Weapon *wp; glColour *c; GLsizei offset; /* Get offset. */ p = 0; offset = weapon_vboSize; if (shape==RADAR_CIRCLE) rc = (int)(w*w); /* Draw the points for weapons on all layers. */ for (i=0; i<nwbackLayer; i++) { wp = wbackLayer[i]; /* Make sure is in range. */ if (!pilot_inRange( player, wp->solid->pos.x, wp->solid->pos.y )) continue; /* Get radar position. */ x = (wp->solid->pos.x - player->solid->pos.x) / res; y = (wp->solid->pos.y - player->solid->pos.y) / res; /* Make sure in range. */ if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.)) continue; if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc)) continue; /* Choose colour based on if it'll hit player. */ if (outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID)) c = &cNeutral; else if ((wp->target == PLAYER_ID) || !areAllies(FACTION_PLAYER, wp->faction)) c = &cHostile; else c = &cNeutral; /* Set the colour. */ weapon_vboData[ offset + 4*p + 0 ] = c->r; weapon_vboData[ offset + 4*p + 1 ] = c->g; weapon_vboData[ offset + 4*p + 2 ] = c->b; weapon_vboData[ offset + 4*p + 3 ] = alpha; /* Put the pixel. */ weapon_vboData[ 2*p + 0 ] = x; weapon_vboData[ 2*p + 1 ] = y; /* "Add" pixel. */ p++; } for (i=0; i<nwfrontLayer; i++) { wp = wfrontLayer[i]; /* Make sure is in range. */ if (!pilot_inRange( player, wp->solid->pos.x, wp->solid->pos.y )) continue; /* Get radar position. */ x = (wp->solid->pos.x - player->solid->pos.x) / res; y = (wp->solid->pos.y - player->solid->pos.y) / res; /* Make sure in range. */ if (shape==RADAR_RECT && (ABS(x)>w/2. || ABS(y)>h/2.)) continue; if (shape==RADAR_CIRCLE && (((x)*(x)+(y)*(y)) > rc)) continue; /* Choose colour based on if it'll hit player. */ if (outfit_isSeeker(wp->outfit) && (wp->target != PLAYER_ID)) c = &cNeutral; else if ((wp->target == PLAYER_ID) || !areAllies(FACTION_PLAYER, wp->faction)) c = &cHostile; else c = &cNeutral; /* Set the colour. */ weapon_vboData[ offset + 4*p + 0 ] = c->r; weapon_vboData[ offset + 4*p + 1 ] = c->g; weapon_vboData[ offset + 4*p + 2 ] = c->b; weapon_vboData[ offset + 4*p + 3 ] = alpha; /* Put the pixel. */ weapon_vboData[ 2*p + 0 ] = x; weapon_vboData[ 2*p + 1 ] = y; /* "Add" pixel. */ p++; } /* Only render with something to draw. */ if (p > 0) { /* Upload data changes. */ gl_vboSubData( weapon_vbo, 0, sizeof(GLfloat) * 2*p, weapon_vboData ); gl_vboSubData( weapon_vbo, offset * sizeof(GLfloat), sizeof(GLfloat) * 4*p, &weapon_vboData[offset] ); /* Activate VBO. */ gl_vboActivateOffset( weapon_vbo, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( weapon_vbo, GL_COLOR_ARRAY, offset * sizeof(GLfloat), 4, GL_FLOAT, 0 ); /* Render VBO. */ glDrawArrays( GL_POINTS, 0, p ); /* Disable VBO. */ gl_vboDeactivate(); } }
/** * @brief Fires a weapon set. * * @param p Pilot firing weaponsets. * @param ws Weapon set to fire. * @param level Level of the firing weapon set. */ static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level ) { int i, j, ret, s; Pilot *pt; double dist2; Outfit *o; /* Case no outfits. */ if (ws->slots == NULL) return 0; /* If inrange is set we only fire at targets in range. */ dist2 = INFINITY; /* With no target we just set distance to infinity. */ if (ws->inrange) { if (p->target != p->id) { pt = pilot_get( p->target ); if (pt != NULL) dist2 = vect_dist2( &p->solid->pos, &pt->solid->pos ); } } /* Fire. */ ret = 0; for (i=0; i<array_size(ws->slots); i++) { o = ws->slots[i].slot->outfit; /* Ignore NULL outfits. */ if (o == NULL) continue; /* Only "active" outfits. */ if ((level != -1) && (ws->slots[i].level != level)) continue; /* Only run once for each weapon type in the group. */ s = 0; for (j=0; j<i; j++) { /* Only active outfits. */ if ((level != -1) && (ws->slots[j].level != level)) continue; /* Found a match. */ if (ws->slots[j].slot->outfit == o) { s = 1; break; } } if (s!=0) continue; /* Only "locked on" outfits. */ if (outfit_isSeeker(o) && (ws->slots[i].slot->u.ammo.lockon_timer > 0.)) continue; /* Only "inrange" outfits. */ if (!outfit_isFighterBay(o) && (ws->inrange && (dist2 > ws->slots[i].range2))) continue; /* Shoot the weapon of the weaponset. */ ret += pilot_shootWeaponSetOutfit( p, ws, o, level ); } return ret; }
/** * @brief Tries to automatically set and create the pilot's weapon set. * * Weapon set 0 is for all weapons. <br /> * Weapon set 1 is for forward weapons. Ammo using weapons are secondaries. <br /> * Weapon set 2 is for turret weapons. Ammo using weapons are secondaries. <br /> * Weapon set 3 is for all weapons. Forwards are primaries and turrets are secondaries. <br /> * Weapon set 4 is for seeking weapons. High payload variants are secondaries. <br /> * Weapon set 5 is for fighter bays. <br /> * * @param p Pilot to automagically generate weapon lists. */ void pilot_weaponAuto( Pilot *p ) { PilotOutfitSlot *slot; Outfit *o; int i, level, id; /* Clear weapons. */ pilot_weaponClear( p ); /* Set modes. */ pilot_weapSetMode( p, 0, 0 ); pilot_weapSetMode( p, 1, 0 ); pilot_weapSetMode( p, 2, 0 ); pilot_weapSetMode( p, 3, 0 ); pilot_weapSetMode( p, 4, 1 ); pilot_weapSetMode( p, 5, 1 ); pilot_weapSetMode( p, 6, 0 ); pilot_weapSetMode( p, 7, 0 ); pilot_weapSetMode( p, 8, 0 ); pilot_weapSetMode( p, 9, 0 ); /* Set names. */ pilot_weapSetNameSet( p, 0, "All" ); pilot_weapSetNameSet( p, 1, "Forward" ); pilot_weapSetNameSet( p, 2, "Turret" ); pilot_weapSetNameSet( p, 3, "Fwd/Tur" ); pilot_weapSetNameSet( p, 4, "Seekers" ); pilot_weapSetNameSet( p, 5, "Fighter Bays" ); pilot_weapSetNameSet( p, 6, "Weaponset 7" ); pilot_weapSetNameSet( p, 7, "Weaponset 8" ); pilot_weapSetNameSet( p, 8, "Weaponset 9" ); pilot_weapSetNameSet( p, 9, "Weaponset 0" ); /* Iterate through all the outfits. */ for (i=0; i<p->outfit_nweapon; i++) { slot = &p->outfit_weapon[i]; o = slot->outfit; /* Must have outfit. */ if (o == NULL) { slot->level = -1; /* Clear level. */ continue; } /* Bolts and beams. */ if (outfit_isBolt(o) || outfit_isBeam(o) || (outfit_isLauncher(o) && !outfit_isSeeker(o->u.lau.ammo))) { id = outfit_isTurret(o) ? 2 : 1; level = (outfit_ammo(o) != NULL) ? 1 : 0; } /* Seekers. */ else if (outfit_isLauncher(o) && outfit_isSeeker(o->u.lau.ammo)) { id = 4; level = 1; } /* Fighter bays. */ else if (outfit_isFighterBay(o)) { id = 5; level = 0; } /* Ignore rest. */ else continue; /* Add to it's base group. */ pilot_weapSetAdd( p, id, slot, level ); /* Also add another copy to another group. */ if (id == 1) { /* Forward. */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ pilot_weapSetAdd( p, 3, slot, 0 ); /* Also get added to 'Fwd/Tur'. */ } else if (id == 2) { /* Turrets. */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ pilot_weapSetAdd( p, 3, slot, 1 ); /* Also get added to 'Fwd/Tur'. */ } else if (id == 4) { /* Seekers */ pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */ } } }
/** * @brief Parses the specific area for a weapon and loads it into Outfit. * * @param temp Outfit to finish loading. * @param parent Outfit's parent node. */ static void outfit_parseSAmmo( Outfit* temp, const xmlNodePtr parent ) { xmlNodePtr node; char *buf; node = parent->xmlChildrenNode; /* Defaults. */ temp->u.amm.spfx_armour = -1; temp->u.amm.spfx_shield = -1; temp->u.amm.sound = -1; do { /* load all the data */ /* Basic */ xmlr_float(node,"duration",temp->u.amm.duration); xmlr_float(node,"lockon",temp->u.amm.lockon); xmlr_float(node,"resist",temp->u.amm.resist); /* Movement */ xmlr_float(node,"thrust",temp->u.amm.thrust); xmlr_float(node,"turn",temp->u.amm.turn); xmlr_float(node,"speed",temp->u.amm.speed); xmlr_float(node,"accuracy",temp->u.amm.accuracy); xmlr_float(node,"energy",temp->u.amm.energy); if (xml_isNode(node,"gfx")) { temp->u.amm.gfx_space = xml_parseTexture( node, OUTFIT_GFX"space/%s.png", 6, 6, OPENGL_TEX_MAPTRANS ); xmlr_attr(node, "spin", buf); if (buf != NULL) { outfit_setProp( temp, OUTFIT_PROP_WEAP_SPIN ); temp->u.blt.spin = atof( buf ); free(buf); } continue; } else if (xml_isNode(node,"spfx_armour")) temp->u.amm.spfx_armour = spfx_get(xml_get(node)); else if (xml_isNode(node,"spfx_shield")) temp->u.amm.spfx_shield = spfx_get(xml_get(node)); else if (xml_isNode(node,"sound")) temp->u.amm.sound = sound_get( xml_get(node) ); else if (xml_isNode(node,"damage")) outfit_parseDamage( &temp->u.amm.dtype, &temp->u.amm.damage, node ); } while (xml_nextNode(node)); /* Post-processing */ temp->u.amm.resist /= 100.; /* Set it in per one */ #define MELEMENT(o,s) \ if (o) WARN("Outfit '%s' missing/invalid '"s"' element", temp->name) /**< Define to help check for data errors. */ MELEMENT(temp->u.amm.gfx_space==NULL,"gfx"); MELEMENT(temp->u.amm.spfx_shield==-1,"spfx_shield"); MELEMENT(temp->u.amm.spfx_armour==-1,"spfx_armour"); MELEMENT((sound_disabled!=0) && (temp->u.amm.sound<0),"sound"); /* MELEMENT(temp->u.amm.thrust==0,"thrust"); */ /* Dumb missiles don't need everything */ if (outfit_isSeeker(temp)) { MELEMENT(temp->u.amm.turn==0,"turn"); MELEMENT(temp->u.amm.lockon==0,"lockon"); } MELEMENT(temp->u.amm.speed==0,"speed"); MELEMENT(temp->u.amm.duration==0,"duration"); MELEMENT(temp->u.amm.damage==0,"damage"); #undef MELEMENT }