/** * @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 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 ) { /* 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 { o->stimer = outfit_cooldown( o->outfit ); o->state = PILOT_OUTFIT_COOLDOWN; } return 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 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 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; } }
/** * @brief Recalculates the pilot's stats based on his outfits. * * @param pilot Pilot to recalculate his stats. */ void pilot_calcStats( Pilot* pilot ) { int i; Outfit* o; PilotOutfitSlot *slot; double ac, sc, ec, fc; /* temporary health coefficients to set */ ShipStats amount, *s, *default_s; /* * set up the basic stuff */ /* mass */ pilot->solid->mass = pilot->ship->mass; pilot->base_mass = pilot->solid->mass; /* cpu */ pilot->cpu = 0.; /* movement */ pilot->thrust_base = pilot->ship->thrust; pilot->turn_base = pilot->ship->turn; pilot->speed_base = pilot->ship->speed; /* crew */ pilot->crew = pilot->ship->crew; /* cargo */ pilot->cap_cargo = pilot->ship->cap_cargo; /* fuel_consumption. */ pilot->fuel_consumption = pilot->ship->fuel_consumption; /* health */ ac = (pilot->armour_max > 0.) ? pilot->armour / pilot->armour_max : 0.; sc = (pilot->shield_max > 0.) ? pilot->shield / pilot->shield_max : 0.; ec = (pilot->energy_max > 0.) ? pilot->energy / pilot->energy_max : 0.; fc = (pilot->fuel_max > 0.) ? pilot->fuel / pilot->fuel_max : 0.; pilot->armour_max = pilot->ship->armour; pilot->shield_max = pilot->ship->shield; pilot->fuel_max = pilot->ship->fuel; pilot->armour_regen = pilot->ship->armour_regen; pilot->shield_regen = pilot->ship->shield_regen; /* Absorption. */ pilot->dmg_absorb = pilot->ship->dmg_absorb; /* Energy. */ pilot->energy_max = pilot->ship->energy; pilot->energy_regen = pilot->ship->energy_regen; pilot->energy_loss = 0.; /* Initially no net loss. */ /* Stats. */ s = &pilot->stats; memcpy( s, &pilot->ship->stats_array, sizeof(ShipStats) ); memset( &amount, 0, sizeof(ShipStats) ); /* * Now add outfit changes */ pilot->mass_outfit = 0.; pilot->jamming = 0; for (i=0; i<pilot->noutfits; i++) { slot = pilot->outfits[i]; o = slot->outfit; /* Outfit must exist. */ if (o==NULL) continue; /* Modify CPU. */ pilot->cpu += outfit_cpu(o); /* Add mass. */ pilot->mass_outfit += o->mass; /* Keep a separate counter for required (core) outfits. */ if (sp_required( o->slot.spid )) pilot->base_mass += o->mass; /* Add ammo mass. */ if (outfit_ammo(o) != NULL) if (slot->u.ammo.outfit != NULL) pilot->mass_outfit += slot->u.ammo.quantity * slot->u.ammo.outfit->mass; if (outfit_isAfterburner(o)) /* Afterburner */ pilot->afterburner = pilot->outfits[i]; /* Set afterburner */ /* Active outfits must be on to affect stuff. */ if (slot->active && !(slot->state==PILOT_OUTFIT_ON)) continue; if (outfit_isMod(o)) { /* Modification */ /* Movement. */ pilot->thrust_base += o->u.mod.thrust; pilot->turn_base += o->u.mod.turn; pilot->speed_base += o->u.mod.speed; /* Health. */ pilot->dmg_absorb += o->u.mod.absorb; pilot->armour_max += o->u.mod.armour; pilot->armour_regen += o->u.mod.armour_regen; pilot->shield_max += o->u.mod.shield; pilot->shield_regen += o->u.mod.shield_regen; pilot->energy_max += o->u.mod.energy; pilot->energy_regen += o->u.mod.energy_regen; pilot->energy_loss += o->u.mod.energy_loss; /* Fuel. */ pilot->fuel_max += o->u.mod.fuel; /* Misc. */ pilot->cap_cargo += o->u.mod.cargo; pilot->mass_outfit += o->u.mod.mass_rel * pilot->ship->mass; pilot->crew += o->u.mod.crew_rel * pilot->ship->crew; /* * Stats. */ ss_statsModFromList( s, o->u.mod.stats, &amount ); } else if (outfit_isAfterburner(o)) { /* Afterburner */ pilot_setFlag( pilot, PILOT_AFTERBURNER ); /* We use old school flags for this still... */ pilot->energy_loss += pilot->afterburner->outfit->u.afb.energy; /* energy loss */ } else if (outfit_isJammer(o)) { /* Jammer */ pilot->jamming = 1; pilot->energy_loss += o->u.jam.energy; } } if (!pilot_isFlag( pilot, PILOT_AFTERBURNER )) pilot->solid->speed_max = pilot->speed; /* Slot voodoo. */ s = &pilot->stats; default_s = &pilot->ship->stats_array; /* Fire rate: * amount = p * exp( -0.15 * (n-1) ) * 1x 15% -> 15% * 2x 15% -> 25.82% * 3x 15% -> 33.33% * 6x 15% -> 42.51% */ if (amount.fwd_firerate > 0) { s->fwd_firerate = default_s->fwd_firerate + (s->fwd_firerate-default_s->fwd_firerate) * exp( -0.15 * (double)(MAX(amount.fwd_firerate-1.,0)) ); } /* Cruiser. */ if (amount.tur_firerate > 0) { s->tur_firerate = default_s->tur_firerate + (s->tur_firerate-default_s->tur_firerate) * exp( -0.15 * (double)(MAX(amount.tur_firerate-1.,0)) ); } /* * Electronic warfare setting base parameters. */ s->ew_hide = default_s->ew_hide + (s->ew_hide-default_s->ew_hide) * exp( -0.2 * (double)(MAX(amount.ew_hide-1.,0)) ); s->ew_detect = default_s->ew_detect + (s->ew_detect-default_s->ew_detect) * exp( -0.2 * (double)(MAX(amount.ew_detect-1.,0)) ); s->ew_jump_detect = default_s->ew_jump_detect + (s->ew_jump_detect-default_s->ew_jump_detect) * exp( -0.2 * (double)(MAX(amount.ew_jump_detect-1.,0)) ); /* Square the internal values to speed up comparisons. */ pilot->ew_base_hide = pow2( s->ew_hide ); pilot->ew_detect = pow2( s->ew_detect ); pilot->ew_jump_detect = pow2( s->ew_jump_detect ); /* * Relative increases. */ /* Movement. */ pilot->thrust_base *= s->thrust_mod; pilot->turn_base *= s->turn_mod; pilot->speed_base *= s->speed_mod; /* Health. */ pilot->armour_max *= s->armour_mod; pilot->armour_regen *= s->armour_regen_mod; pilot->shield_max *= s->shield_mod; pilot->shield_regen *= s->shield_regen_mod; pilot->energy_max *= s->energy_mod; pilot->energy_regen *= s->energy_regen_mod; /* cpu */ pilot->cpu_max = (int)floor((float)(pilot->ship->cpu + s->cpu_max)*s->cpu_mod); pilot->cpu += pilot->cpu_max; /* CPU is negative, this just sets it so it's based off of cpu_max. */ /* Misc. */ pilot->dmg_absorb = MAX( 0., pilot->dmg_absorb ); pilot->crew *= s->crew_mod; pilot->cap_cargo *= s->cargo_mod; s->engine_limit *= s->engine_limit_rel; /* * Flat increases. */ pilot->energy_max += s->energy_flat; pilot->energy += s->energy_flat; pilot->energy_regen -= s->energy_usage; /* Give the pilot his health proportion back */ pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; pilot->energy = ec * pilot->energy_max; pilot->fuel = fc * pilot->fuel_max; /* Set final energy tau. */ pilot->energy_tau = pilot->energy_max / pilot->energy_regen; /* Cargo has to be reset. */ pilot_cargoCalc(pilot); /* Calculate mass. */ pilot->solid->mass = s->mass_mod*pilot->ship->mass + pilot->stats.cargo_inertia*pilot->mass_cargo + pilot->mass_outfit; /* Calculate the heat. */ pilot_heatCalc( pilot ); /* Modulate by mass. */ pilot_updateMass( pilot ); /* Update GUI as necessary. */ gui_setGeneric( pilot ); }
/** * @brief Recalculates the pilot's stats based on his outfits. * * @param pilot Pilot to recalculate his stats. */ void pilot_calcStats( Pilot* pilot ) { int i; double q; Outfit* o; PilotOutfitSlot *slot; double ac, sc, ec, fc; /* temporary health coefficients to set */ double arel, srel, erel; /* relative health bonuses. */ ShipStats *s, *os; int nfirerate_turret, nfirerate_forward; int njammers; int ew_ndetect, ew_nhide; /* Comfortability. */ s = &pilot->stats; /* * set up the basic stuff */ /* mass */ pilot->solid->mass = pilot->ship->mass; /* movement */ pilot->thrust = pilot->ship->thrust; pilot->turn_base = pilot->ship->turn; pilot->speed = pilot->ship->speed; /* cpu */ pilot->cpu_max = pilot->ship->cpu; pilot->cpu = pilot->cpu_max; /* crew */ pilot->crew = pilot->ship->crew; /* health */ ac = pilot->armour / pilot->armour_max; sc = pilot->shield / pilot->shield_max; ec = pilot->energy / pilot->energy_max; fc = pilot->fuel / pilot->fuel_max; pilot->armour_max = pilot->ship->armour; pilot->shield_max = pilot->ship->shield; pilot->fuel_max = pilot->ship->fuel; pilot->armour_regen = pilot->ship->armour_regen; pilot->shield_regen = pilot->ship->shield_regen; /* Absorption. */ pilot->dmg_absorb = pilot->ship->dmg_absorb; /* Energy. */ pilot->energy_max = pilot->ship->energy; pilot->energy_regen = pilot->ship->energy_regen; /* Jamming */ pilot->jam_range = 0.; pilot->jam_chance = 0.; /* Stats. */ memcpy( s, &pilot->ship->stats, sizeof(ShipStats) ); /* cargo has to be reset */ pilot_cargoCalc(pilot); /* * now add outfit changes */ nfirerate_forward = nfirerate_turret = 0; pilot->mass_outfit = 0.; njammers = 0; ew_ndetect = 0; ew_nhide = 0; pilot->jam_range = 0.; pilot->jam_chance = 0.; arel = 0.; srel = 0.; erel = 0.; for (i=0; i<pilot->noutfits; i++) { slot = pilot->outfits[i]; o = slot->outfit; if (o==NULL) continue; q = (double) slot->quantity; /* Subtract CPU. */ pilot->cpu -= outfit_cpu(o) * q; if (outfit_cpu(o) < 0.) pilot->cpu_max -= outfit_cpu(o) * q; /* Add mass. */ pilot->mass_outfit += o->mass; if (outfit_isMod(o)) { /* Modification */ /* movement */ pilot->thrust += o->u.mod.thrust * pilot->ship->mass * q; pilot->thrust += o->u.mod.thrust_rel * pilot->ship->thrust * q; pilot->turn_base += o->u.mod.turn * q; pilot->turn_base += o->u.mod.turn_rel * pilot->ship->turn * q; pilot->speed += o->u.mod.speed * q; pilot->speed += o->u.mod.speed_rel * pilot->ship->speed * q; /* health */ pilot->armour_max += o->u.mod.armour * q; pilot->armour_regen += o->u.mod.armour_regen * q; arel += o->u.mod.armour_rel * q; pilot->shield_max += o->u.mod.shield * q; pilot->shield_regen += o->u.mod.shield_regen * q; srel += o->u.mod.shield_rel * q; pilot->energy_max += o->u.mod.energy * q; pilot->energy_regen += o->u.mod.energy_regen * q; erel += o->u.mod.energy_rel * q; /* fuel */ pilot->fuel_max += o->u.mod.fuel * q; /* misc */ pilot->cargo_free += o->u.mod.cargo * q; pilot->mass_outfit += o->u.mod.mass_rel * pilot->ship->mass * q; pilot->crew += o->u.mod.crew_rel * pilot->ship->crew * q; /* * Stats. */ os = &o->u.mod.stats; /* Freighter. */ s->jump_delay += os->jump_delay * q; s->jump_range += os->jump_range * q; s->cargo_inertia += os->cargo_inertia * q; /* Scout. */ if (os->ew_hide != 0.) { s->ew_hide += os->ew_hide * q; ew_nhide++; } if (os->ew_detect != 0.) { s->ew_detect += os->ew_detect * q; ew_ndetect++; } s->jam_range += os->jam_range * q; /* Military. */ s->heat_dissipation += os->heat_dissipation * q; /* Bomber. */ s->launch_rate += os->launch_rate * q; s->launch_range += os->launch_range * q; s->jam_counter += os->jam_counter * q; s->ammo_capacity += os->ammo_capacity * q; /* Fighter. */ s->heat_forward += os->heat_forward * q; s->damage_forward += os->damage_forward * q; s->energy_forward += os->energy_forward * q; if (os->firerate_forward != 0.) { s->firerate_forward += os->firerate_forward * q; nfirerate_forward += q; } /* Cruiser. */ s->heat_turret += os->heat_turret * q; s->damage_turret += os->damage_turret * q; s->energy_turret += os->energy_turret * q; if (os->firerate_turret != 0.) { s->firerate_turret += os->firerate_turret * q; if (os->firerate_turret > 0.) /* Only modulate bonuses. */ nfirerate_turret += q; } /* Misc. */ s->nebula_dmg_shield += os->nebula_dmg_shield * q; s->nebula_dmg_armour += os->nebula_dmg_armour * q; } else if (outfit_isAfterburner(o)) /* Afterburner */ pilot->afterburner = pilot->outfits[i]; /* Set afterburner */ else if (outfit_isJammer(o)) { /* Jammer */ pilot->jam_range += o->u.jam.range * q; pilot->jam_chance += o->u.jam.chance * q; pilot->energy_regen -= o->u.jam.energy * q; njammers += q;; } /* Add ammo mass. */ if (outfit_ammo(o) != NULL) { if (slot->u.ammo.outfit != NULL) pilot->mass_outfit += slot->u.ammo.quantity * slot->u.ammo.outfit->mass; } } /* Set final energy tau. */ pilot->energy_tau = pilot->energy_max / pilot->energy_regen; /* * Electronic warfare setting base parameters. */ s->ew_hide = 1. + s->ew_hide/100. * exp( -0.2 * (double)(MAX(ew_nhide-1,0)) ); s->ew_detect = 1. + s->ew_detect/100. * exp( -0.2 * (double)(MAX(ew_ndetect-1,0)) ); pilot->ew_base_hide = s->ew_hide; pilot->ew_detect = s->ew_detect; /* * Normalize stats. */ /* Freighter. */ s->jump_range = s->jump_range/100. + 1.; s->jump_delay = s->jump_delay/100. + 1.; s->cargo_inertia = s->cargo_inertia/100. + 1.; /* Scout. */ s->jam_range = s->jam_range/100. + 1.; /* Military. */ s->heat_dissipation = s->heat_dissipation/100. + 1.; /* Bomber. */ s->launch_rate = s->launch_rate/100. + 1.; s->launch_range = s->launch_range/100. + 1.; s->jam_counter = s->jam_counter/100. + 1.; s->ammo_capacity = s->ammo_capacity/100. + 1.; /* Fighter. */ s->heat_forward = s->heat_forward/100. + 1.; s->damage_forward = s->damage_forward/100. + 1.; s->energy_forward = s->energy_forward/100. + 1.; /* Fire rate: * amount = p * exp( -0.15 * (n-1) ) * 1x 15% -> 15% * 2x 15% -> 25.82% * 3x 15% -> 33.33% * 6x 15% -> 42.51% */ s->firerate_forward = s->firerate_forward/100.; if (nfirerate_forward > 0) s->firerate_forward *= exp( -0.15 * (double)(MAX(nfirerate_forward-1,0)) ); s->firerate_forward += 1.; /* Cruiser. */ s->heat_turret = s->heat_turret/100. + 1.; s->damage_turret = s->damage_turret/100. + 1.; s->energy_turret = s->energy_turret/100. + 1.; s->firerate_turret = s->firerate_turret/100.; if (nfirerate_turret > 0) s->firerate_turret *= exp( -0.15 * (double)(MAX(nfirerate_turret-1,0)) ); s->firerate_turret += 1.; /* Misc. */ s->nebula_dmg_shield = s->nebula_dmg_shield/100. + 1.; s->nebula_dmg_armour = s->nebula_dmg_armour/100. + 1.; /* * Calculate jammers. * * Range is averaged. * Diminishing return on chance. * chance = p * exp( -0.2 * (n-1) ) * 1x 20% -> 20% * 2x 20% -> 32% * 2x 40% -> 65% * 6x 40% -> 88% */ if (njammers > 1) { pilot->jam_range /= (double)njammers; pilot->jam_range *= s->jam_range; pilot->jam_chance *= exp( -0.2 * (double)(MAX(njammers-1,0)) ); } /* Increase health by relative bonuses. */ pilot->armour_max += arel * pilot->ship->armour; pilot->shield_max += srel * pilot->ship->shield; pilot->energy_max += erel * pilot->ship->energy; /* Give the pilot his health proportion back */ pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; pilot->energy = ec * pilot->energy_max; pilot->fuel = fc * pilot->fuel_max; /* Calculate mass. */ pilot->solid->mass = pilot->ship->mass + s->cargo_inertia*pilot->mass_cargo + pilot->mass_outfit; /* Calculate the heat. */ pilot_heatCalc( pilot ); /* Modulate by mass. */ pilot_updateMass( pilot ); /* Update GUI as necessary. */ gui_setGeneric( pilot ); }
/** * @brief Checks to see if can equip/remove an outfit from a slot. * * @return NULL if can swap, or error message if can't. */ const char* pilot_canEquip( Pilot *p, PilotOutfitSlot *s, Outfit *o, int add ) { /* Just in case. */ if ((p==NULL) || (o==NULL)) return "Nothing selected."; /* Check slot type. */ if ((s != NULL) && !outfit_fitsSlot( o, &s->slot )) return "Does not fit slot."; /* Adding outfit. */ if (add) { if ((outfit_cpu(o) > 0) && (p->cpu < outfit_cpu(o))) return "Insufficient CPU"; /* Can't add more than one afterburner. */ if (outfit_isAfterburner(o) && (p->afterburner != NULL)) return "Already have an afterburner"; /* Must not drive some things negative. */ if (outfit_isMod(o)) { /* * Movement. */ if (((o->u.mod.thrust + o->u.mod.thrust_rel * p->ship->thrust) < 0) && (fabs(o->u.mod.thrust + o->u.mod.thrust_rel * p->ship->thrust) > p->thrust)) return "Insufficient thrust"; if (((o->u.mod.speed + o->u.mod.speed_rel * p->ship->speed) < 0) && (fabs(o->u.mod.speed + o->u.mod.speed_rel * p->ship->speed) > p->speed)) return "Insufficient speed"; if (((o->u.mod.turn + o->u.mod.turn_rel * p->ship->turn * p->ship->mass/p->solid->mass) < 0) && (fabs(o->u.mod.turn + o->u.mod.turn_rel * p->ship->turn * p->ship->mass/p->solid->mass) > p->turn_base)) return "Insufficient turn"; /* * Health. */ /* Max. */ if ((o->u.mod.armour < 0) && (fabs(o->u.mod.armour) > p->armour_max)) return "Insufficient armour"; if ((o->u.mod.armour_rel < 0.) && (fabs(o->u.mod.armour_rel * p->ship->armour) > p->armour_max)) return "Insufficient armour"; if ((o->u.mod.shield < 0) && (fabs(o->u.mod.shield) > p->shield_max)) return "Insufficient shield"; if ((o->u.mod.shield_rel < 0.) && (fabs(o->u.mod.shield_rel * p->ship->shield) > p->shield_max)) return "Insufficient shield"; if ((o->u.mod.energy < 0) && (fabs(o->u.mod.energy) > p->armour_max)) return "Insufficient energy"; if ((o->u.mod.energy_rel < 0.) && (fabs(o->u.mod.energy_rel * p->ship->energy) > p->energy_max)) return "Insufficient energy"; /* Regen. */ if ((o->u.mod.armour_regen < 0) && (fabs(o->u.mod.armour_regen) > p->armour_regen)) return "Insufficient energy regeneration"; if ((o->u.mod.shield_regen < 0) && (fabs(o->u.mod.shield_regen) > p->shield_regen)) return "Insufficient shield regeneration"; if ((o->u.mod.energy_regen < 0) && (fabs(o->u.mod.energy_regen) > p->energy_regen)) return "Insufficient energy regeneration"; /* * Misc. */ if ((o->u.mod.fuel < 0) && (fabs(o->u.mod.fuel) > p->fuel_max)) return "Insufficient fuel"; if ((o->u.mod.cargo < 0) && (fabs(o->u.mod.cargo) > p->cargo_free)) return "Insufficient cargo space"; } } /* Removing outfit. */ else { if ((outfit_cpu(o) < 0) && (p->cpu < fabs(outfit_cpu(o)))) return "Lower CPU usage first"; /* Must not drive some things negative. */ if (outfit_isMod(o)) { /* * Movement. */ if (((o->u.mod.thrust + o->u.mod.thrust_rel * p->ship->thrust) > 0) && (o->u.mod.thrust + o->u.mod.thrust_rel * p->ship->thrust > p->thrust)) return "Increase thrust first"; if (((o->u.mod.speed + o->u.mod.speed_rel * p->ship->speed) > 0) && (o->u.mod.speed + o->u.mod.speed_rel * p->ship->speed > p->speed)) return "Increase speed first"; if (((o->u.mod.turn + o->u.mod.turn_rel * p->ship->turn * p->ship->mass/p->solid->mass) > 0) && (fabs(o->u.mod.turn + o->u.mod.turn_rel * p->ship->turn * p->ship->mass/p->solid->mass) > p->turn_base)) return "Increase turn first"; /* * Health. */ /* Max. */ if ((o->u.mod.armour > 0) && (o->u.mod.armour > p->armour_max)) return "Increase armour first"; if ((o->u.mod.shield > 0) && (o->u.mod.shield > p->shield_max)) return "Increase shield first"; if ((o->u.mod.energy > 0) && (o->u.mod.energy > p->energy_max)) return "Increase energy first"; /* Regen. */ if ((o->u.mod.armour_regen > 0) && (o->u.mod.armour_regen > p->armour_regen)) return "Lower energy usage first"; if ((o->u.mod.shield_regen > 0) && (o->u.mod.shield_regen > p->shield_regen)) return "Lower shield usage first"; if ((o->u.mod.energy_regen > 0) && (o->u.mod.energy_regen > p->energy_regen)) return "Lower energy usage first"; /* * Misc. */ if ((o->u.mod.fuel > 0) && (o->u.mod.fuel > p->fuel_max)) return "Increase fuel first"; if ((o->u.mod.cargo > 0) && (o->u.mod.cargo > p->cargo_free)) return "Increase free cargo space first"; } else if (outfit_isFighterBay(o)) { if ((s!=NULL) && (s->u.ammo.deployed > 0)) return "Recall the fighters first"; } } /* Can equip. */ return NULL; }
/** * @brief Recalculates the pilot's stats based on his outfits. * * @param pilot Pilot to recalculate his stats. */ void pilot_calcStats( Pilot* pilot ) { int i; Outfit* o; PilotOutfitSlot *slot; double ac, sc, ec, fc; /* temporary health coefficients to set */ double arel, srel, erel; /* relative health bonuses. */ ShipStats amount, *s; /* @TODO remove old school PILOT_AFTERBURN flags. */ pilot_rmFlag( pilot, PILOT_AFTERBURNER ); /* * set up the basic stuff */ /* mass */ pilot->solid->mass = pilot->ship->mass; /* movement */ pilot->thrust = pilot->ship->thrust; pilot->turn_base = pilot->ship->turn; pilot->speed = pilot->ship->speed; /* cpu */ pilot->cpu_max = pilot->ship->cpu; pilot->cpu = pilot->cpu_max; /* crew */ pilot->crew = pilot->ship->crew; /* health */ ac = pilot->armour / pilot->armour_max; sc = pilot->shield / pilot->shield_max; ec = pilot->energy / pilot->energy_max; fc = pilot->fuel / pilot->fuel_max; pilot->armour_max = pilot->ship->armour; pilot->shield_max = pilot->ship->shield; pilot->fuel_max = pilot->ship->fuel; pilot->armour_regen = pilot->ship->armour_regen; pilot->shield_regen = pilot->ship->shield_regen; /* Absorption. */ pilot->dmg_absorb = pilot->ship->dmg_absorb; /* Energy. */ pilot->energy_max = pilot->ship->energy; pilot->energy_regen = pilot->ship->energy_regen; pilot->energy_loss = 0.; /* Initially no net loss. */ /* Stats. */ memcpy( &pilot->stats, &pilot->ship->stats_array, sizeof(ShipStats) ); memset( &amount, 0, sizeof(ShipStats) ); /* cargo has to be reset */ pilot_cargoCalc(pilot); /* Slot voodoo. */ s = &pilot->stats; /* * Electronic warfare setting base parameters. * @TODO ew_hide and ew_detect should be squared so XML-sourced values are linear. */ s->ew_hide = 1. + (s->ew_hide-1.) * exp( -0.2 * (double)(MAX(amount.ew_hide-1,0)) ); s->ew_detect = 1. + (s->ew_detect-1.) * exp( -0.2 * (double)(MAX(amount.ew_detect-1,0)) ); s->ew_jumpDetect = 1. + (s->ew_jumpDetect-1.) * exp( -0.2 * (double)(MAX(amount.ew_jumpDetect-1,0)) ); pilot->ew_base_hide = s->ew_hide; pilot->ew_detect = s->ew_detect; pilot->ew_jumpDetect = pow2(s->ew_jumpDetect); /* * now add outfit changes */ pilot->mass_outfit = 0.; pilot->jamming = 0; arel = 0.; srel = 0.; erel = 0.; for (i=0; i<pilot->noutfits; i++) { slot = pilot->outfits[i]; o = slot->outfit; /* Outfit must exist. */ if (o==NULL) continue; /* Subtract CPU. */ pilot->cpu -= outfit_cpu(o); if (outfit_cpu(o) < 0.) pilot->cpu_max -= outfit_cpu(o); /* Add mass. */ pilot->mass_outfit += o->mass; /* Add ammo mass. */ if (outfit_ammo(o) != NULL) if (slot->u.ammo.outfit != NULL) pilot->mass_outfit += slot->u.ammo.quantity * slot->u.ammo.outfit->mass; /* Set afterburner. */ if (outfit_isAfterburner(o)) pilot->afterburner = pilot->outfits[i]; /* Active outfits must be on to affect stuff. */ if (slot->active && !(slot->state==PILOT_OUTFIT_ON)) continue; if (outfit_isMod(o)) { /* Modification */ /* movement */ pilot->thrust += o->u.mod.thrust * pilot->ship->mass; pilot->thrust += o->u.mod.thrust_rel * pilot->ship->thrust; pilot->turn_base += o->u.mod.turn; pilot->turn_base += o->u.mod.turn_rel * pilot->ship->turn; pilot->speed += o->u.mod.speed; pilot->speed += o->u.mod.speed_rel * pilot->ship->speed; /* health */ pilot->armour_max += o->u.mod.armour; pilot->armour_regen += o->u.mod.armour_regen; arel += o->u.mod.armour_rel; pilot->shield_max += o->u.mod.shield; pilot->shield_regen += o->u.mod.shield_regen; srel += o->u.mod.shield_rel; pilot->energy_max += o->u.mod.energy; pilot->energy_regen += o->u.mod.energy_regen; erel += o->u.mod.energy_rel; /* fuel */ pilot->fuel_max += o->u.mod.fuel; /* misc */ pilot->cargo_free += o->u.mod.cargo; pilot->mass_outfit += o->u.mod.mass_rel * pilot->ship->mass; pilot->crew += o->u.mod.crew_rel * pilot->ship->crew; pilot->ew_base_hide += o->u.mod.hide_rel * pilot->ew_base_hide; /* * Stats. */ ss_statsModFromList( &pilot->stats, o->u.mod.stats, &amount ); } else if (outfit_isAfterburner(o)) { /* Afterburner */ pilot_setFlag( pilot, PILOT_AFTERBURNER ); /* We use old school flags for this still... */ pilot->energy_loss += pilot->afterburner->outfit->u.afb.energy; /* energy loss */ pilot->solid->speed_max = pilot->speed + pilot->speed * pilot->afterburner->outfit->u.afb.speed * MIN( 1., pilot->afterburner->outfit->u.afb.mass_limit/pilot->solid->mass); } else if (outfit_isJammer(o)) { /* Jammer */ pilot->jamming = 1; pilot->energy_loss += o->u.jam.energy; } } if (!pilot_isFlag( pilot, PILOT_AFTERBURNER )) pilot->solid->speed_max = pilot->speed; /* Set final energy tau. */ pilot->energy_tau = pilot->energy_max / pilot->energy_regen; /* Fire rate: * amount = p * exp( -0.15 * (n-1) ) * 1x 15% -> 15% * 2x 15% -> 25.82% * 3x 15% -> 33.33% * 6x 15% -> 42.51% */ if (amount.fwd_firerate > 0) { s->fwd_firerate = 1. + (s->fwd_firerate-1.) * exp( -0.15 * (double)(MAX(amount.fwd_firerate-1,0)) ); } /* Cruiser. */ if (amount.tur_firerate > 0) { s->tur_firerate = 1. + (s->tur_firerate-1.) * exp( -0.15 * (double)(MAX(amount.tur_firerate-1,0)) ); } /* Increase health by relative bonuses. */ pilot->armour_max += arel * pilot->ship->armour; pilot->armour_max *= pilot->stats.armour_mod; pilot->shield_max += srel * pilot->ship->shield; pilot->shield_max *= pilot->stats.shield_mod; pilot->energy_max += erel * pilot->ship->energy; /* pilot->energy_max *= pilot->stats.energy_mod; */ /* Give the pilot his health proportion back */ pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; pilot->energy = ec * pilot->energy_max; pilot->fuel = fc * pilot->fuel_max; /* Calculate the heat. */ pilot_heatCalc( pilot ); /* Modulate by mass. */ pilot_updateMass( pilot ); /* Update GUI as necessary. */ gui_setGeneric( pilot ); }
/** * @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; }