/** * @brief Checks to see if can equip/remove an outfit from a slot. * * @param p Pilot to check if can equip. * @param s Slot being checked to see if it can equip/remove an outfit. * @param o Outfit to check (NULL if being removed). * @return NULL if can swap, or error message if can't. */ const char* pilot_canEquip( Pilot *p, PilotOutfitSlot *s, Outfit *o ) { Outfit *o_old; const char *err; double pa, ps, pe, pf; /* Just in case. */ if ((p==NULL) || (s==NULL)) return "Nothing selected."; if (o!=NULL) { /* Check slot type. */ if (!outfit_fitsSlot( o, &s->sslot->slot )) return "Does not fit slot."; /* Check outfit limit. */ if ((o->limit != NULL) && pilot_hasOutfitLimit( p, o->limit )) return "Already have an outfit of this type installed"; } else { /* Check fighter bay. */ if ((o==NULL) && (s!=NULL) && (s->u.ammo.deployed > 0)) return "Recall the fighters first"; } /* Store health. */ pa = p->armour; ps = p->shield; pe = p->energy; pf = p->fuel; /* Swap outfit. */ o_old = s->outfit; s->outfit = o; /* Check sanity. */ pilot_calcStats( p ); /* can now equip outfit even if ship won't be spaceworthy * err = pilot_checkSpaceworthy( p );*/ /* actually, this is also redundant */ if (!pilot_slotsCheckSanity(p)) err = "Does not fit in slot"; else err = NULL; /* Swap back. */ s->outfit = o_old; /* Recalc. */ pilot_calcStats( p ); /* Recover health. */ p->armour = pa; p->shield = ps; p->energy = pe; p->fuel = pf; return err; }
/** * @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 Deactivates the afterburner. */ void pilot_afterburnOver (Pilot *p) { if (p == NULL) return; if (p->afterburner == NULL) return; if (p->afterburner->state == PILOT_OUTFIT_ON) { p->afterburner->state = PILOT_OUTFIT_OFF; pilot_rmFlag(p,PILOT_AFTERBURNER); pilot_calcStats( p ); /* @todo Make this part of a more dynamic activated outfit sound system. */ sound_play(p->afterburner->outfit->u.afb.sound_off); } }
/** * @brief Adds an outfit to the pilot. * * @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_addOutfit( Pilot* pilot, Outfit* outfit, PilotOutfitSlot *s ) { int ret; /* Test to see if outfit can be added. */ ret = pilot_addOutfitTest( pilot, outfit, s, 1 ); if (ret != 0) return -1; /* Add outfit. */ ret = pilot_addOutfitRaw( pilot, outfit, s ); /* Recalculate the stats */ pilot_calcStats(pilot); return ret; }
/** * @brief Removes an outfit from the pilot. * * @param pilot Pilot to remove the outfit from. * @param s Slot to remove. * @return 0 on success. */ int pilot_rmOutfit( Pilot* pilot, PilotOutfitSlot *s ) { const char *str; int ret; str = pilot_canEquip( pilot, s, NULL ); if (str != NULL) { WARN("Pilot '%s': Trying to remove outfit but %s", pilot->name, str ); return -1; } ret = pilot_rmOutfitRaw( pilot, s ); /* recalculate the stats */ pilot_calcStats(pilot); return ret; }
/** * @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 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; } }