/** * @brief Have pilot stop shooting his weapon. * * Only really deals with beam weapons. * * @param p Pilot that was shooting. * @param level Level of the shot. */ void pilot_shootStop( Pilot* p, int level ) { int i; PilotWeaponSet *ws; /* Get active set. */ ws = pilot_weapSet( p, p->active_set ); /* Case no outfits. */ if (ws->slots == NULL) return; /* Stop all beams. */ for (i=0; i<array_size(ws->slots); i++) { /* Must have assosciated outfit. */ if (ws->slots[i].slot->outfit == NULL) continue; /* Must match level. */ if ((level != -1) && (ws->slots[i].level != level)) continue; /* Only handle beams. */ if (!outfit_isBeam(ws->slots[i].slot->outfit)) continue; /* Stop beam. */ if (ws->slots[i].slot->u.beamid > 0) { beam_end( p->id, ws->slots[i].slot->u.beamid ); ws->slots[i].slot->u.beamid = 0; } } }
/** * @brief Disables a given active outfit. * * @param p Pilot whose outfit we are disabling. * @return Whether the outfit was actually disabled. */ int pilot_outfitOff( Pilot *p, PilotOutfitSlot *o ) { double c; /* Must not be disabled or cooling down. */ if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN))) return 0; if (outfit_isAfterburner( o->outfit )) /* Afterburners */ pilot_afterburnOver( p ); else if (outfit_isBeam( o->outfit )) { /* Beams use stimer to represent minimum time until shutdown. */ o->stimer = -1; } else { c = outfit_cooldown( o->outfit ); if (o->stimer != INFINITY) o->stimer = c - (c * o->stimer / outfit_duration( o->outfit )); else o->stimer = c; o->state = PILOT_OUTFIT_COOLDOWN; } return 1; }
/** * @brief Gets the outfit's graphic effect. * @param o Outfit to get information from. */ glTexture* outfit_gfx( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.gfx_space; else if (outfit_isBeam(o)) return o->u.bem.gfx; else if (outfit_isAmmo(o)) return o->u.amm.gfx_space; return NULL; }
/** * @brief Gets the outfit's energy usage. * @param o Outfit to get information from. */ double outfit_energy( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.energy; else if (outfit_isBeam(o)) return o->u.bem.energy; else if (outfit_isAmmo(o)) return o->u.amm.energy; return -1.; }
/** * @brief Gets the outfit's range. * @param o Outfit to get information from. */ double outfit_range( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.range; else if (outfit_isBeam(o)) return o->u.bem.range; else if (outfit_isAmmo(o)) return 0.8*o->u.amm.speed*o->u.amm.duration; return -1.; }
/** * @brief Gets the outfit's damage type. * @param o Outfit to get information from. */ DamageType outfit_damageType( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.dtype; else if (outfit_isBeam(o)) return o->u.bem.dtype; else if (outfit_isAmmo(o)) return o->u.amm.dtype; return DAMAGE_TYPE_NULL; }
/** * @brief Gets the outfit's damage. * @param o Outfit to get information from. */ double outfit_damage( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.damage; else if (outfit_isBeam(o)) return o->u.bem.damage; else if (outfit_isAmmo(o)) return o->u.amm.damage; return -1.; }
/** * @brief Gets the outfit's sound effect. * @param o Outfit to get information from. */ int outfit_spfxShield( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.spfx_shield; else if (outfit_isBeam(o)) return o->u.bem.spfx_shield; else if (outfit_isAmmo(o)) return o->u.amm.spfx_shield; return -1; }
/** * @brief Gets the outfit's sound effect. * @param o Outfit to get information from. */ int outfit_spfxArmour( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.spfx_armour; else if (outfit_isBeam(o)) return o->u.bem.spfx_armour; else if (outfit_isAmmo(o)) return o->u.amm.spfx_armour; return -1; }
/** * @brief Computes an estimation of ammo flying time * * @param w the weapon that shoot * @param parent Parent of the weapon * @param target Target of the weapon */ double pilot_weapFlyTime( Outfit *o, Pilot *parent, Vector2d *pos, Vector2d *vel) { Vector2d approach_vector, relative_location, orthoradial_vector; double speed, radial_speed, orthoradial_speed, dist, t; dist = vect_dist( &parent->solid->pos, pos ); /* Beam weapons */ if (outfit_isBeam(o)) { if (dist > o->u.bem.range) return INFINITY; return 0.; } /* A bay doesn't have range issues */ if (outfit_isFighterBay(o)) return 0.; /* Rockets use absolute velocity while bolt use relative vel */ if (outfit_isLauncher(o)) vect_cset( &approach_vector, - vel->x, - vel->y ); else vect_cset( &approach_vector, VX(parent->solid->vel) - vel->x, VY(parent->solid->vel) - vel->y ); speed = outfit_speed(o); /* Get the vector : shooter -> target */ vect_cset( &relative_location, pos->x - VX(parent->solid->pos), pos->y - VY(parent->solid->pos) ); /* Get the orthogonal vector */ vect_cset(&orthoradial_vector, VY(parent->solid->pos) - pos->y, pos->x - VX(parent->solid->pos) ); radial_speed = vect_dot( &approach_vector, &relative_location ); radial_speed = radial_speed / VMOD(relative_location); orthoradial_speed = vect_dot(&approach_vector, &orthoradial_vector); orthoradial_speed = orthoradial_speed / VMOD(relative_location); if( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0) t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) / (speed*speed - VMOD(approach_vector)*VMOD(approach_vector)); else return INFINITY; /* if t < 0, try the other solution */ if (t < 0) t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) / (speed*speed - VMOD(approach_vector)*VMOD(approach_vector)); /* if t still < 0, no solution */ if (t < 0) return INFINITY; return t; }
/** * @brief Gets the outfit's delay. * @param o Outfit to get information from. */ int outfit_delay( const Outfit* o ) { if (outfit_isBolt(o)) return o->u.blt.delay; else if (outfit_isBeam(o)) return o->u.bem.delay; else if (outfit_isLauncher(o)) return o->u.lau.delay; else if (outfit_isFighterBay(o)) return o->u.bay.delay; return -1; }
/** * @brief Have pilot stop shooting their weapon. * * Only really deals with beam weapons. * * @param p Pilot that was shooting. * @param level Level of the shot. */ void pilot_shootStop( Pilot* p, int level ) { int i, recalc; PilotWeaponSet *ws; PilotOutfitSlot *slot; /* Get active set. */ ws = pilot_weapSet( p, p->active_set ); /* Case no outfits. */ if (ws->slots == NULL) return; /* Stop all beams. */ recalc = 0; for (i=0; i<array_size(ws->slots); i++) { slot = ws->slots[i].slot; /* Must have associated outfit. */ if (ws->slots[i].slot->outfit == NULL) continue; /* Must match level. */ if ((level != -1) && (ws->slots[i].level != level)) continue; /* Only handle beams. */ if (!outfit_isBeam(slot->outfit)) { /* Turn off the state. */ if (outfit_isMod( slot->outfit )) { slot->state = PILOT_OUTFIT_OFF; recalc = 1; } continue; } /* Stop beam. */ if (ws->slots[i].slot->u.beamid > 0) { /* Enforce minimum duration if set. */ if (slot->outfit->u.bem.min_duration > 0.) { slot->stimer = slot->outfit->u.bem.min_duration - (slot->outfit->u.bem.duration - slot->timer); if (slot->stimer > 0.) continue; } beam_end( p->id, slot->u.beamid ); pilot_stopBeam(p, slot); } } /* Must recalculate. */ if (recalc) pilot_calcStats( p ); }
/** * @brief Dumps the beam weapon data to csv. */ void dout_csvBeam( const char *path ) { Outfit *o, *o_all; int i, n, l; SDL_RWops *rw; char buf[ 1024 ]; Damage *dmg; /* File to output to. */ rw = SDL_RWFromFile( path, "w" ); if (rw == NULL) { WARN("Unable to open '%s' for writing: %s", path, SDL_GetError()); return; } /* Write "header" */ l = nsnprintf( buf, sizeof(buf), "name,type,slot,size," "license,mass,price,cpu," "delay,warmup,duration,min_duration," "range,turn,energy,heatup," "penetrate,dtype,damage,disable\n" ); SDL_RWwrite( rw, buf, l, 1 ); o_all = outfit_getAll( &n ); for (i=0; i<n; i++) { o = &o_all[i]; /* Only handle bolt weapons. */ if (!outfit_isBeam(o)) continue; dmg = &o->u.bem.dmg; l = nsnprintf( buf, sizeof(buf), "%s,%s,%s,%s," "%s,%f,%"CREDITS_PRI",%f," "%f,%f,%f,%f," "%f,%f,%f,%f," "%f,%s,%f,%f\n", o->name, outfit_getType(o), outfit_slotName(o), outfit_slotSize(o), o->license, o->mass, o->price, o->cpu, o->u.bem.delay, o->u.bem.warmup, o->u.bem.duration, o->u.bem.min_duration, o->u.bem.range, o->u.bem.turn * 180. / M_PI, o->u.bem.energy, o->u.bem.heatup, dmg->penetration*100, dtype_damageTypeToStr(dmg->type), dmg->damage, dmg->disable ); SDL_RWwrite( rw, buf, l, 1 ); } /* Close file. */ SDL_RWclose( rw ); }
/** * @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 Gets the outfit's broad type. * * @param o Outfit to get the type of. * @return The outfit's broad type in human readable form. */ const char* outfit_getTypeBroad( const Outfit* o ) { if (outfit_isBolt(o)) return "Bolt Weapon"; else if (outfit_isBeam(o)) return "Beam Weapon"; else if (outfit_isLauncher(o)) return "Launcher"; else if (outfit_isAmmo(o)) return "Ammo"; else if (outfit_isTurret(o)) return "Turret"; else if (outfit_isMod(o)) return "Modification"; else if (outfit_isAfterburner(o)) return "Afterburner"; else if (outfit_isJammer(o)) return "Jammer"; else if (outfit_isFighterBay(o)) return "Fighter Bay"; else if (outfit_isFighter(o)) return "Fighter"; else if (outfit_isMap(o)) return "Map"; else if (outfit_isLicense(o)) return "License"; else return "Unknown"; }
/** * @brief Adds an outfit to a weapon set. * * @param p Pilot to manipulate. * @param id ID of the weapon set. * @param o Outfit to add. * @param level Level of the trigger. */ void pilot_weapSetAdd( Pilot* p, int id, PilotOutfitSlot *o, int level ) { PilotWeaponSet *ws; PilotWeaponSetOutfit *slot; Outfit *oo; int i; ws = pilot_weapSet(p,id); /* Make sure outfit is valid. */ oo = o->outfit; if (oo == NULL) return; /* Make sure outfit type is weapon (or usable). */ if (!(outfit_isBeam(oo) || outfit_isBolt(oo) || outfit_isLauncher(oo) || outfit_isFighterBay(oo))) return; /* Create if needed. */ if (ws->slots == NULL) ws->slots = array_create( PilotWeaponSetOutfit ); /* Check if already there. */ for (i=0; i<array_size(ws->slots); i++) { if (ws->slots[i].slot == o) { ws->slots[i].level = level; /* Update if needed. */ if (id == p->active_set) pilot_weapSetUpdateOutfits( p, ws ); return; } } /* Add it. */ slot = &array_grow( &ws->slots ); slot->level = level; slot->slot = o; /* Update range. */ pilot_weapSetUpdateRange( ws ); /* Update if needed. */ if (id == p->active_set) pilot_weapSetUpdateOutfits( p, ws ); }
/** * @brief Adds an outfit to the pilot, ignoring CPU or other limits. * * @note Does not call pilot_calcStats(). * * @param pilot Pilot to add the outfit to. * @param outfit Outfit to add to the pilot. * @param s Slot to add ammo to. * @return 0 on success. */ int pilot_addOutfitRaw( Pilot* pilot, Outfit* outfit, PilotOutfitSlot *s ) { Outfit *o; /* Set the outfit. */ s->outfit = outfit; /* Set some default parameters. */ s->timer = 0.; /* Some per-case scenarios. */ if (outfit_isFighterBay(outfit)) { s->u.ammo.outfit = NULL; s->u.ammo.quantity = 0; s->u.ammo.deployed = 0; } if (outfit_isTurret(outfit)) /* used to speed up AI */ pilot->nturrets++; else if (outfit_isBolt(outfit)) pilot->ncannons++; else if (outfit_isJammer(outfit)) pilot->njammers++; else if (outfit_isAfterburner(outfit)) pilot->nafterburners++; if (outfit_isBeam(outfit)) { /* Used to speed up some calculations. */ s->u.beamid = 0; pilot->nbeams++; } if (outfit_isLauncher(outfit)) { s->u.ammo.outfit = NULL; s->u.ammo.quantity = 0; s->u.ammo.deployed = 0; /* Just in case. */ } /* Check if active. */ o = s->outfit; s->active = outfit_isActive(o); /* Update heat. */ pilot_heatCalcSlot( s ); return 0; }
/** * @brief Adds an outfit to the pilot, ignoring CPU or other limits. * * @note Does not call pilot_calcStats(). * * @param pilot Pilot to add the outfit to. * @param outfit Outfit to add to the pilot. * @param s Slot to add ammo to. * @return 0 on success. */ int pilot_addOutfitRaw( Pilot* pilot, Outfit* outfit, PilotOutfitSlot *s ) { Outfit *o; /* Set the outfit. */ s->outfit = outfit; s->quantity = 1; /* Sort of pointless, but hey. */ /* Set some default parameters. */ s->timer = 0.; /* Some per-case scenarios. */ if (outfit_isFighterBay(outfit)) { s->u.ammo.outfit = NULL; s->u.ammo.quantity = 0; s->u.ammo.deployed = 0; } if (outfit_isTurret(outfit)) /* used to speed up AI */ pilot_setFlag(pilot, PILOT_HASTURRET); if (outfit_isBeam(outfit)) { /* Used to speed up some calculations. */ s->u.beamid = -1; pilot_setFlag(pilot, PILOT_HASBEAMS); } if (outfit_isLauncher(outfit)) { s->u.ammo.outfit = NULL; s->u.ammo.quantity = 0; s->u.ammo.deployed = 0; /* Just in case. */ } /* Check if active. */ o = s->outfit; if (outfit_isForward(o) || outfit_isTurret(o) || outfit_isLauncher(o) || outfit_isFighterBay(o)) s->active = 1; else s->active = 0; /* Update heat. */ pilot_heatCalcSlot( s ); return 0; }
/** * @brief Removes an outfit from the pilot without doing any checks. * * @note Does not run pilot_calcStats(). * * @param pilot Pilot to remove the outfit from. * @param s Slot to remove. * @return 0 on success. */ int pilot_rmOutfitRaw( Pilot* pilot, PilotOutfitSlot *s ) { int ret; /* Decrement counters if necessary. */ if (s->outfit != NULL) { if (outfit_isTurret(s->outfit)) pilot->nturrets--; else if (outfit_isBolt(s->outfit)) pilot->ncannons--; if (outfit_isBeam(s->outfit)) pilot->nbeams--; } /* Remove the outfit. */ ret = (s->outfit==NULL); s->outfit = NULL; /* Remove secondary and such if necessary. */ if (pilot->afterburner == s) pilot->afterburner = NULL; 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 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 Actually handles the shooting, how often the player.p can shoot and such. * * @param p Pilot that is shooting. * @param w Pilot's outfit to shoot. * @param time Expected flight time. * @return 0 if nothing was shot and 1 if something was shot. */ static int pilot_shootWeapon( Pilot* p, PilotOutfitSlot* w, double time ) { Vector2d vp, vv; double rate_mod, energy_mod; double energy; int j; /* Make sure weapon has outfit. */ if (w->outfit == NULL) return 0; /* Reset beam shut-off if needed. */ if (outfit_isBeam(w->outfit) && w->outfit->u.bem.min_duration) w->stimer = INFINITY; /* check to see if weapon is ready */ if (w->timer > 0.) return 0; /* Calculate rate modifier. */ pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit ); /* Get weapon mount position. */ pilot_getMount( p, w, &vp ); vp.x += p->solid->pos.x; vp.y += p->solid->pos.y; /* Modify velocity to take into account the rotation. */ vect_cset( &vv, p->solid->vel.x + vp.x*p->solid->dir_vel, p->solid->vel.y + vp.y*p->solid->dir_vel ); /* * regular bolt weapons */ if (outfit_isBolt(w->outfit)) { /* enough energy? */ if (outfit_energy(w->outfit)*energy_mod > p->energy) return 0; energy = outfit_energy(w->outfit)*energy_mod; p->energy -= energy; pilot_heatAddSlot( p, w ); weapon_add( w->outfit, w->heat_T, p->solid->dir, &vp, &p->solid->vel, p, p->target, time ); } /* * Beam weapons. */ else if (outfit_isBeam(w->outfit)) { /* Don't fire if the existing beam hasn't been destroyed yet. */ if (w->u.beamid > 0) return 0; /* Check if enough energy to last a second. */ if (outfit_energy(w->outfit)*energy_mod > p->energy) return 0; /** @todo Handle warmup stage. */ w->state = PILOT_OUTFIT_ON; w->u.beamid = beam_start( w->outfit, p->solid->dir, &vp, &p->solid->vel, p, p->target, w ); w->timer = w->outfit->u.bem.duration; return 1; /* Return early due to custom timer logic. */ } /* * missile launchers * * must be a secondary weapon */ else if (outfit_isLauncher(w->outfit)) { /* Shooter can't be the target - sanity check for the player.p */ if ((w->outfit->u.lau.ammo->u.amm.ai != AMMO_AI_DUMB) && (p->id==p->target)) return 0; /* Must have ammo left. */ if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0)) return 0; /* enough energy? */ if (outfit_energy(w->u.ammo.outfit)*energy_mod > p->energy) return 0; energy = outfit_energy(w->u.ammo.outfit)*energy_mod; p->energy -= energy; pilot_heatAddSlot( p, w ); weapon_add( w->outfit, w->heat_T, p->solid->dir, &vp, &p->solid->vel, p, p->target, time ); w->u.ammo.quantity -= 1; /* we just shot it */ p->mass_outfit -= w->u.ammo.outfit->mass; p->solid->mass -= w->u.ammo.outfit->mass; pilot_updateMass( p ); /* If last ammo was shot, update the range */ if (w->u.ammo.quantity <= 0) { for (j=0; j<PILOT_WEAPON_SETS; j++) pilot_weapSetUpdateRange( &p->weapon_sets[j] ); } } /* * Fighter bays. */ else if (outfit_isFighterBay(w->outfit)) { /* Must have ammo left. */ if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0)) return 0; /* Create the escort. */ escort_create( p, w->u.ammo.outfit->u.fig.ship, &vp, &p->solid->vel, p->solid->dir, ESCORT_TYPE_BAY, 1 ); w->u.ammo.quantity -= 1; /* we just shot it */ p->mass_outfit -= w->u.ammo.outfit->mass; w->u.ammo.deployed += 1; /* Mark as deployed. */ pilot_updateMass( p ); } else WARN(_("Shooting unknown weapon type: %s"), w->outfit->name); /* Reset timer. */ w->timer += rate_mod * outfit_delay( w->outfit ); return 1; }
/** * @brief Updates an individual weapon. * * @param w Weapon to update. * @param dt Current delta tick. * @param layer Layer to which the weapon belongs. */ static void weapon_update( Weapon* w, const double dt, WeaponLayer layer ) { int i, psx,psy; glTexture *gfx; Vector2d crash[2]; Pilot *p; /* Get the sprite direction to speed up calculations. */ if (!outfit_isBeam(w->outfit)) { gfx = outfit_gfx(w->outfit); gl_getSpriteFromDir( &w->sx, &w->sy, gfx, w->solid->dir ); } for (i=0; i<pilot_nstack; i++) { p = pilot_stack[i]; psx = pilot_stack[i]->tsx; psy = pilot_stack[i]->tsy; if (w->parent == pilot_stack[i]->id) continue; /* pilot is self */ /* Beam weapons have special collisions. */ if (outfit_isBeam(w->outfit)) { /* Check for collision. */ if (weapon_checkCanHit(w,p) && CollideLineSprite( &w->solid->pos, w->solid->dir, w->outfit->u.bem.range, p->ship->gfx_space, psx, psy, &p->solid->pos, crash)) { weapon_hitBeam( w, p, layer, crash, dt ); /* No return because beam can still think, it's not * destroyed like the other weapons.*/ } } /* smart weapons only collide with their target */ else if (weapon_isSmart(w)) { if ((pilot_stack[i]->id == w->target) && (w->status != WEAPON_STATUS_OK) && /* Must not be locking on. */ weapon_checkCanHit(w,p) && CollideSprite( gfx, w->sx, w->sy, &w->solid->pos, p->ship->gfx_space, psx, psy, &p->solid->pos, &crash[0] )) { weapon_hit( w, p, layer, &crash[0] ); return; /* Weapon is destroyed. */ } } /* dumb weapons hit anything not of the same faction */ else { if (weapon_checkCanHit(w,p) && CollideSprite( gfx, w->sx, w->sy, &w->solid->pos, p->ship->gfx_space, psx, psy, &p->solid->pos, &crash[0] )) { weapon_hit( w, p, layer, &crash[0] ); return; /* Weapon is destroyed. */ } } } /* smart weapons also get to think their next move */ if (weapon_isSmart(w)) (*w->think)(w,dt); /* Update the solid position. */ (*w->solid->update)(w->solid, dt); /* Update the sound. */ sound_updatePos(w->voice, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); }
/** * @brief Starts a beam weaapon. * * @param outfit Outfit which spawns the weapon. * @param dir Direction of the shooter. * @param pos Position of the shooter. * @param vel Velocity of the shooter. * @param parent Pilot ID of the shooter. * @param target Target ID that is getting shot. * @param mount Mount on the ship. * @return The identifier of the beam weapon. * * @sa beam_end */ int beam_start( const Outfit* outfit, const double dir, const Vector2d* pos, const Vector2d* vel, const unsigned int parent, const unsigned int target, const PilotOutfitSlot *mount ) { WeaponLayer layer; Weapon *w; Weapon **curLayer; int *mLayer, *nLayer; GLsizei size; if (!outfit_isBeam(outfit)) { ERR("Trying to create a Beam Weapon from a non-beam outfit."); return -1; } layer = (parent==PLAYER_ID) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG; w = weapon_create( outfit, dir, pos, vel, parent, target ); w->ID = ++beam_idgen; w->mount = mount; /* set the proper layer */ switch (layer) { case WEAPON_LAYER_BG: curLayer = wbackLayer; nLayer = &nwbackLayer; mLayer = &mwbacklayer; break; case WEAPON_LAYER_FG: curLayer = wfrontLayer; nLayer = &nwfrontLayer; mLayer = &mwfrontLayer; break; default: ERR("Invalid WEAPON_LAYER specified"); return -1; } if (*mLayer > *nLayer) /* more memory alloced than needed */ curLayer[(*nLayer)++] = w; else { /* need to allocate more memory */ switch (layer) { case WEAPON_LAYER_BG: (*mLayer) += WEAPON_CHUNK; curLayer = wbackLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*)); break; case WEAPON_LAYER_FG: (*mLayer) += WEAPON_CHUNK; curLayer = wfrontLayer = realloc(curLayer, (*mLayer)*sizeof(Weapon*)); break; } curLayer[(*nLayer)++] = w; /* Grow the vertex stuff. */ weapon_vboSize = mwfrontLayer + mwbacklayer; size = sizeof(GLfloat) * (2+4) * weapon_vboSize; weapon_vboData = realloc( weapon_vboData, size ); if (weapon_vbo == NULL) weapon_vbo = gl_vboCreateStream( size, NULL ); } return w->ID; }
/** * @brief Actually handles the shooting, how often the player.p can shoot and such. * * @param p Pilot that is shooting. * @param w Pilot's outfit to shoot. * @return 0 if nothing was shot and 1 if something was shot. */ static int pilot_shootWeapon( Pilot* p, PilotOutfitSlot* w ) { Vector2d vp, vv; double rate_mod, energy_mod; double energy; /* Make sure weapon has outfit. */ if (w->outfit == NULL) return 0; /* check to see if weapon is ready */ if (w->timer > 0.) return 0; /* Calculate rate modifier. */ pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit ); /* Get weapon mount position. */ pilot_getMount( p, w, &vp ); vp.x += p->solid->pos.x; vp.y += p->solid->pos.y; /* Modify velocity to take into account the rotation. */ vect_cset( &vv, p->solid->vel.x + vp.x*p->solid->dir_vel, p->solid->vel.y + vp.y*p->solid->dir_vel ); /* * regular bolt weapons */ if (outfit_isBolt(w->outfit)) { /* enough energy? */ if (outfit_energy(w->outfit)*energy_mod > p->energy) return 0; energy = outfit_energy(w->outfit)*energy_mod; p->energy -= energy; pilot_heatAddSlot( p, w ); weapon_add( w->outfit, w->heat_T, p->solid->dir, &vp, &p->solid->vel, p, p->target ); } /* * Beam weapons. */ else if (outfit_isBeam(w->outfit)) { /* Check if enough energy to last a second. */ if (outfit_energy(w->outfit)*energy_mod > p->energy) return 0; /** @todo Handle warmup stage. */ w->state = PILOT_OUTFIT_ON; w->u.beamid = beam_start( w->outfit, p->solid->dir, &vp, &p->solid->vel, p, p->target, w ); } /* * missile launchers * * must be a secondary weapon */ else if (outfit_isLauncher(w->outfit)) { /* Shooter can't be the target - sanity check for the player.p */ if ((w->outfit->u.lau.ammo->u.amm.ai > 0) && (p->id==p->target)) return 0; /* Must have ammo left. */ if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0)) return 0; /* enough energy? */ if (outfit_energy(w->u.ammo.outfit)*energy_mod > p->energy) return 0; energy = outfit_energy(w->u.ammo.outfit)*energy_mod; p->energy -= energy; pilot_heatAddSlot( p, w ); weapon_add( w->outfit, w->heat_T, p->solid->dir, &vp, &p->solid->vel, p, p->target ); w->u.ammo.quantity -= 1; /* we just shot it */ p->mass_outfit -= w->u.ammo.outfit->mass; pilot_updateMass( p ); } /* * Fighter bays. */ else if (outfit_isFighterBay(w->outfit)) { /* Must have ammo left. */ if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0)) return 0; /* Create the escort. */ escort_create( p, w->u.ammo.outfit->u.fig.ship, &vp, &p->solid->vel, p->solid->dir, ESCORT_TYPE_BAY, 1 ); w->u.ammo.quantity -= 1; /* we just shot it */ p->mass_outfit -= w->u.ammo.outfit->mass; w->u.ammo.deployed += 1; /* Mark as deployed. */ pilot_updateMass( p ); } else WARN("Shooting unknown weapon type: %s", w->outfit->name); /* Reset timer. */ w->timer += rate_mod * outfit_delay( w->outfit ); return 1; }
/** * @brief Calculates and shoots the appropriate weapons in a weapon set matching an outfit. */ static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, Outfit *o, int level, double time ) { int i, ret; int is_launcher, is_bay; double rate_mod, energy_mod; PilotOutfitSlot *w; int maxp, minh; double q, maxt; /* Store number of shots. */ ret = 0; /** @TODO Make beams not fire all at once. */ if (outfit_isBeam(o)) { for (i=0; i<array_size(ws->slots); i++) if (ws->slots[i].slot->outfit == o) ret += pilot_shootWeapon( p, ws->slots[i].slot, 0 ); return ret; } /* Stores if it is a launcher. */ is_launcher = outfit_isLauncher(o); is_bay = outfit_isFighterBay(o); /* Calculate rate modifier. */ pilot_getRateMod( &rate_mod, &energy_mod, p, o ); /* Find optimal outfit, coolest that can fire. */ minh = -1; maxt = 0.; maxp = -1; q = 0.; for (i=0; i<array_size(ws->slots); i++) { /* Only matching outfits. */ if (ws->slots[i].slot->outfit != o) continue; /* Only match levels. */ if ((level != -1) && (ws->slots[i].level != level)) continue; /* Simplicity. */ w = ws->slots[i].slot; /* Launcher only counts with ammo. */ if ((is_launcher || is_bay) && ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0))) continue; /* Get coolest that can fire. */ if (w->timer <= 0.) { if (is_launcher) { if ((minh < 0) || (ws->slots[minh].slot->u.ammo.quantity < w->u.ammo.quantity)) minh = i; } else { if ((minh < 0) || (ws->slots[minh].slot->heat_T > w->heat_T)) minh = i; } } /* Save some stuff. */ if ((maxp < 0) || (w->timer > maxt)) { maxp = i; maxt = w->timer; } q += 1.; } /* No weapon can fire. */ if (minh < 0) return 0; /* Only fire if the last weapon to fire fired more than (q-1)/q ago. */ if (maxt > rate_mod * outfit_delay(o) * ((q-1.) / q)) return 0; /* Shoot the weapon. */ ret += pilot_shootWeapon( p, ws->slots[minh].slot, time ); return ret; }
/** * @brief Parses and returns Outfit from parent node. * * @param temp Outfit to load into. * @param parent Parent node to parse outfit from. * @return 0 on success. */ static int outfit_parse( Outfit* temp, const xmlNodePtr parent ) { xmlNodePtr cur, node; char *prop; /* Clear data. */ memset( temp, 0, sizeof(Outfit) ); temp->name = xml_nodeProp(parent,"name"); /* already mallocs */ if (temp->name == NULL) WARN("Outfit in "OUTFIT_DATA" has invalid or no name"); node = parent->xmlChildrenNode; do { /* load all the data */ if (xml_isNode(node,"general")) { cur = node->children; do { xmlr_int(cur,"max",temp->max); xmlr_int(cur,"tech",temp->tech); xmlr_strd(cur,"license",temp->license); xmlr_int(cur,"mass",temp->mass); xmlr_int(cur,"price",temp->price); xmlr_strd(cur,"description",temp->description); if (xml_isNode(cur,"gfx_store")) { temp->gfx_store = xml_parseTexture( cur, OUTFIT_GFX"store/%s.png", 1, 1, 0 ); } } while (xml_nextNode(cur)); } 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'",temp->name); temp->type = outfit_strToOutfitType(prop); free(prop); /* is secondary weapon? */ prop = xml_nodeProp(node,"secondary"); if (prop != NULL) { if ((int)atoi(prop)) outfit_setProp(temp, OUTFIT_PROP_WEAP_SECONDARY); free(prop); } if (temp->type==OUTFIT_TYPE_NULL) WARN("Outfit '%s' is of type NONE", temp->name); else if (outfit_isBolt(temp)) outfit_parseSBolt( temp, node ); else if (outfit_isBeam(temp)) outfit_parseSBeam( temp, node ); else if (outfit_isLauncher(temp)) outfit_parseSLauncher( temp, node ); else if (outfit_isAmmo(temp)) outfit_parseSAmmo( temp, node ); else if (outfit_isMod(temp)) outfit_parseSMod( temp, node ); else if (outfit_isAfterburner(temp)) outfit_parseSAfterburner( temp, node ); else if (outfit_isJammer(temp)) outfit_parseSJammer( temp, node ); else if (outfit_isFighterBay(temp)) outfit_parseSFighterBay( temp, node ); else if (outfit_isFighter(temp)) outfit_parseSFighter( temp, node ); else if (outfit_isMap(temp)) outfit_parseSMap( temp, node ); else if (outfit_isLicense(temp)) outfit_parseSLicense( temp, node ); } } while (xml_nextNode(node)); #define MELEMENT(o,s) \ if (o) WARN("Outfit '%s' missing/invalid '"s"' element", temp->name) /**< Define to help check for data errors. */ MELEMENT(temp->name==NULL,"name"); MELEMENT(temp->max==0,"max"); MELEMENT(temp->tech==0,"tech"); MELEMENT(temp->gfx_store==NULL,"gfx_store"); /*MELEMENT(temp->mass==0,"mass"); Not really needed */ MELEMENT(temp->type==0,"type"); MELEMENT(temp->price==0,"price"); MELEMENT(temp->description==NULL,"description"); #undef MELEMENT return 0; }