/** * @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 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 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 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. * @param add Whether or not to consider it's being added or removed. * @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->sslot->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 outfit of the same type if the outfit type is limited. */ if ((o->limit != NULL) && pilot_hasOutfitLimit( p, o->limit )) return "Already have an outfit of this type installed"; /* Must not drive some things negative. */ if (outfit_isMod(o)) { /* * Movement. */ /* TODO fix this to work with ship stats. CHECK_STAT_R( o->u.mod.thrust, o->u.mod.thrust_rel, p->ship->thrust, "Insufficient thrust" ); CHECK_STAT_R( o->u.mod.turn, o->u.mod.turn_rel, p->ship->turn, "Insufficient turn" ); CHECK_STAT_R( o->u.mod.speed, o->u.mod.speed_rel, p->ship->speed, "Insufficient speed" ); */ /* * Health. */ /* Max. */ /* TODO fix this to work with ship stats. CHECK_STAT_R( o->u.mod.armour, o->u.mod.armour_rel, p->armour_max, "Insufficient armour" ); CHECK_STAT_R( o->u.mod.shield, o->u.mod.shield_rel, p->shield_max, "Insufficient shield" ); CHECK_STAT_R( o->u.mod.energy, o->u.mod.energy_rel, p->energy_max, "Insufficient energy" ); */ /* Regen. */ /* TODO fix this to work with ship stats. CHECK_STAT( o->u.mod.armour_regen, p->armour_regen, "Insufficient armour regeneration" ); CHECK_STAT( o->u.mod.shield_regen, p->shield_regen, "Insufficient shield regeneration" ); CHECK_STAT( o->u.mod.energy_regen, p->energy_regen, "Insufficient energy regeneration" ); */ /* * Misc. */ CHECK_STAT( o->u.mod.fuel, p->fuel_max, "Insufficient fuel" ); CHECK_STAT( o->u.mod.cargo, p->cargo_free, "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. */ /* TODO fix this to work with ship stats. 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. */ /* TODO fix this to work with ship stats. 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. */ /* TODO fix this to work with ship stats. 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; 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 Dumps the modification data to csv. * * Note that this function is primarily intended for balancing core outfits. * * To that end, it omits a number of seldom-used properties, and is primarily * concerned with those that are common to most types of modifications, or at * least one category of core outfit. */ void dout_csvMod( const char *path ) { Outfit *o, *o_all; int i, n, l; SDL_RWops *rw; char buf[ 1024 ]; ShipStats base, stats; /* 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," "thrust,turn,speed,fuel,energy_usage," "armour,armour_regen," "shield,shield_regen," "energy,energy_regen," "absorb,cargo,ew_hide\n" ); SDL_RWwrite( rw, buf, l, 1 ); ss_statsInit( &base ); o_all = outfit_getAll( &n ); for (i=0; i<n; i++) { o = &o_all[i]; /* Only handle modifications. */ if (!outfit_isMod(o)) continue; memcpy( &stats, &base, sizeof(ShipStats) ); ss_statsModFromList( &stats, o->u.mod.stats, NULL ); l = nsnprintf( buf, sizeof(buf), "%s,%s,%s,%s," "%s,%f,%"CREDITS_PRI",%f," "%f,%f,%f,%f,%f," "%f,%f," "%f,%f," "%f,%f," "%f,%f,%f\n", o->name, outfit_getType(o), outfit_slotName(o), outfit_slotSize(o), o->license, o->mass, o->price, o->cpu, o->u.mod.thrust, o->u.mod.turn * 180. / M_PI, o->u.mod.speed, o->u.mod.fuel, stats.energy_usage, o->u.mod.armour, o->u.mod.armour_regen, o->u.mod.shield, o->u.mod.shield_regen, o->u.mod.energy, o->u.mod.energy_regen, o->u.mod.absorb,o->u.mod.cargo, stats.ew_hide - 1. ); SDL_RWwrite( rw, buf, l, 1 ); } /* Close file. */ SDL_RWclose( rw ); }
/** * @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; }