/** * @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 Activate the afterburner. */ void pilot_afterburn (Pilot *p) { double afb_mod; if (p == NULL) return; if (pilot_isFlag(p, PILOT_HYP_PREP) || pilot_isFlag(p, PILOT_HYPERSPACE) || pilot_isFlag(p, PILOT_LANDING) || pilot_isFlag(p, PILOT_TAKEOFF) || pilot_isDisabled(p) || pilot_isFlag(p, PILOT_COOLDOWN)) return; /* Not under manual control if is player. */ if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isFlag( p, PILOT_PLAYER )) return; /** @todo fancy effect? */ if (p->afterburner == NULL) return; /* The afterburner only works if its efficiency is high enough. */ if (pilot_heatEfficiencyMod( p->afterburner->heat_T, p->afterburner->outfit->u.afb.heat_base, p->afterburner->outfit->u.afb.heat_cap ) < 0.3) return; if (p->afterburner->state == PILOT_OUTFIT_OFF) { p->afterburner->state = PILOT_OUTFIT_ON; p->afterburner->stimer = outfit_duration( p->afterburner->outfit ); pilot_setFlag(p,PILOT_AFTERBURNER); pilot_calcStats( p ); /* @todo Make this part of a more dynamic activated outfit sound system. */ sound_playPos(p->afterburner->outfit->u.afb.sound_on, p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y); } if (pilot_isPlayer(p)) { afb_mod = MIN( 1., player.p->afterburner->outfit->u.afb.mass_limit / player.p->solid->mass ); spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble * SHAKE_MAX ); } }
/** * @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 Handles a weapon set press. * * @param p Pilot the weapon set belongs to. * @param id ID of the weapon set. * @param type Is +1 if it's a press or -1 if it's a release. */ void pilot_weapSetPress( Pilot* p, int id, int type ) { int i, l, on, n; PilotWeaponSet *ws; ws = pilot_weapSet(p,id); /* Case no outfits. */ if (ws->slots == NULL) return; /* Handle fire groups. */ switch (ws->type) { case WEAPSET_TYPE_CHANGE: /* On press just change active weapon set to whatever is available. */ if (type > 0) { if (id != p->active_set) pilot_weapSetUpdateOutfits( p, ws ); p->active_set = id; } break; case WEAPSET_TYPE_WEAPON: /* Activation philosophy here is to turn on while pressed and off * when it's not held anymore. */ if (type > 0) ws->active = 1; else if (type < 0) ws->active = 0; break; case WEAPSET_TYPE_ACTIVE: /* The behaviour here is more complex. What we do is consider a group * to be entirely off if not all outfits are either on or cooling down. * In the case it's deemed to be off, all outfits that are off get turned * on, otherwise all outfits that are on are turrned to cooling down. */ /* Only care about presses. */ if (type < 0) break; /* Must not be disabled or cooling down. */ if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN))) return; /* Decide what to do. */ on = 1; l = array_size(ws->slots); for (i=0; i<l; i++) { if (ws->slots[i].slot->state == PILOT_OUTFIT_OFF) { on = 0; break; } } /* Turn them off. */ n = 0; if (on) { for (i=0; i<l; i++) { if (ws->slots[i].slot->state != PILOT_OUTFIT_ON) continue; n += pilot_outfitOff( p, ws->slots[i].slot ); } } /* Turn them on. */ else { for (i=0; i<l; i++) { if (ws->slots[i].slot->state != PILOT_OUTFIT_OFF) continue; if (outfit_isAfterburner(ws->slots[i].slot->outfit)) pilot_afterburn( p ); else { ws->slots[i].slot->state = PILOT_OUTFIT_ON; ws->slots[i].slot->stimer = outfit_duration( ws->slots[i].slot->outfit ); } n++; } } /* Must recalculate stats. */ if (n > 0) pilot_calcStats( p ); break; } }