From d17521c8b9085a91d08fecfd0b51bbbf7b1dccac Mon Sep 17 00:00:00 2001 From: "milo24x7@gmail.com" Date: Sun, 7 Jul 2013 22:08:49 +0000 Subject: Updated open source license declaration and fixed some formatting issues. --- Stars45/Weapon.cpp | 2392 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1208 insertions(+), 1184 deletions(-) (limited to 'Stars45/Weapon.cpp') diff --git a/Stars45/Weapon.cpp b/Stars45/Weapon.cpp index 4aa402e..68b249b 100644 --- a/Stars45/Weapon.cpp +++ b/Stars45/Weapon.cpp @@ -1,1184 +1,1208 @@ -/* Project Starshatter 4.5 - Destroyer Studios LLC - Copyright © 1997-2004. All Rights Reserved. - - SUBSYSTEM: Stars.exe - FILE: Weapon.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Weapon class -*/ - -#include "MemDebug.h" -#include "Weapon.h" -#include "Shot.h" -#include "Drone.h" -#include "Contact.h" -#include "Ship.h" -#include "Sim.h" -#include "SimEvent.h" -#include "Random.h" - -#include "NetGame.h" -#include "NetUtil.h" - -#include "Game.h" -#include "Solid.h" - -// +----------------------------------------------------------------------+ - -Weapon::Weapon(WeaponDesign* d, int nmuz, Vec3* muzzles, double az, double el) -: System(WEAPON, d->type, d->name, d->value, -d->capacity, d->capacity, d->recharge_rate), -design(d), group(d->group), ammo(-1), ripple_count(0), -aim_azimuth((float) az), aim_elevation((float) el), -old_azimuth(0.0f), old_elevation(0.0f), aim_time(0), -enabled(true), refire(0.0f), -mass(d->carry_mass), resist(d->carry_resist), -guided(d->guided), shot_speed(d->speed), -active_barrel(0), locked(false), centered(false), firing(false), blocked(false), -index(0), target(0), subtarget(0), beams(0), orders(MANUAL), -control(SINGLE_FIRE), sweep(SWEEP_TIGHT), turret(0), turret_base(0) -{ - ZeroMemory(visible_stores, sizeof(visible_stores)); - - if (design->primary) - abrv = Game::GetText("sys.weapon.primary.abrv"); - else - abrv = Game::GetText("sys.weapon.secondary.abrv"); - - nbarrels = nmuz; - - if (nbarrels > MAX_BARRELS) - nbarrels = MAX_BARRELS; - - if (nbarrels == 0 && design->nbarrels > 0) { - nbarrels = design->nbarrels; - - for (int i = 0; i < nbarrels; i++) - muzzle_pts[i] = rel_pts[i] = design->muzzle_pts[i] * design->scale; - - ammo = design->ammo * nbarrels; - } - else if (nbarrels == 1 && design->nstores > 0) { - nbarrels = design->nstores; - - for (int i = 0; i < nbarrels; i++) - muzzle_pts[i] = rel_pts[i] = (muzzles[0] + design->attachments[i]); - - ammo = nbarrels; - } - else { - for (int i = 0; i < nbarrels; i++) - muzzle_pts[i] = rel_pts[i] = muzzles[i]; - - ammo = design->ammo * nbarrels; - } - - if (design->syncro) - active_barrel = -1; - - emcon_power[0] = 0; - emcon_power[1] = 0; - emcon_power[2] = 100; - - aim_az_max = design->aim_az_max; - aim_az_min = design->aim_az_min; - aim_az_rest = design->aim_az_rest; - - aim_el_max = design->aim_el_max; - aim_el_min = design->aim_el_min; - aim_el_rest = design->aim_el_rest; -} - -// +----------------------------------------------------------------------+ - -Weapon::Weapon(const Weapon& w) -: System(w), design(w.design), ammo(-1), ripple_count(0), -enabled(true), refire(0.0f), -mass(w.mass), resist(w.resist), -aim_azimuth(w.aim_azimuth), aim_elevation(w.aim_elevation), -old_azimuth(0.0f), old_elevation(0.0f), aim_time(0), -guided(w.guided), shot_speed(w.shot_speed), -active_barrel(0), locked(false), centered(false), firing(false), blocked(false), -target(0), subtarget(0), beams(0), orders(MANUAL), -control(SINGLE_FIRE), sweep(SWEEP_TIGHT), group(w.group), -aim_az_max(w.aim_az_max), aim_az_min(w.aim_az_min), aim_az_rest(w.aim_az_rest), -aim_el_max(w.aim_el_max), aim_el_min(w.aim_el_min), aim_el_rest(w.aim_el_rest), -turret(0), turret_base(0) -{ - Mount(w); - ZeroMemory(visible_stores, sizeof(visible_stores)); - - nbarrels = w.nbarrels; - - for (int i = 0; i < nbarrels; i++) { - muzzle_pts[i] = rel_pts[i] = w.muzzle_pts[i]; - } - - ammo = design->ammo * nbarrels; - - if (design->syncro) - active_barrel = -1; - - if (design->beam) { - beams = new(__FILE__,__LINE__) Shot* [nbarrels]; - ZeroMemory(beams, sizeof(Shot*) * nbarrels); - } - - if (aim_az_rest >= 2*PI) - aim_az_rest = design->aim_az_rest; - - if (aim_el_rest >= 2*PI) - aim_el_rest = design->aim_el_rest; -} - -// +--------------------------------------------------------------------+ - -Weapon::~Weapon() -{ - if (beams) { - for (int i = 0; i < nbarrels; i++) { - if (beams[i]) { - Ignore(beams[i]); - delete beams[i]; - beams[i] = 0; - } - } - - delete [] beams; - } - - GRAPHIC_DESTROY(turret); - GRAPHIC_DESTROY(turret_base); - - for (int i = 0; i < MAX_BARRELS; i++) - GRAPHIC_DESTROY(visible_stores[i]); -} - -// +--------------------------------------------------------------------+ - -bool -Weapon::IsPrimary() const -{ - return design->primary; -} - -bool -Weapon::IsDrone() const -{ - return design->drone; -} - -bool -Weapon::IsDecoy() const -{ - return design->decoy_type != 0; -} - -bool -Weapon::IsProbe() const -{ - return design->probe != 0; -} - -bool -Weapon::IsMissile() const -{ - return !design->primary; -} - -bool -Weapon::IsBeam() const -{ - return design->beam; -} - -// +--------------------------------------------------------------------+ - -Shot* -Weapon::GetBeam(int i) -{ - if (beams && i >= 0 && i < nbarrels) - return beams[i]; - - return 0; -} - -// +--------------------------------------------------------------------+ - -void -Weapon::SetOwner(Ship* s) -{ - ship = s; - - if (design->turret_model) { - Solid* t = new(__FILE__,__LINE__) Solid; - t->UseModel(design->turret_model); - turret = t; - } - - if (design->turret_base_model) { - Solid* t = new(__FILE__,__LINE__) Solid; - t->UseModel(design->turret_base_model); - turret_base = t; - } - - if (!design->primary && - design->visible_stores && - ammo == nbarrels && - design->shot_model != 0) - { - for (int i = 0; i < nbarrels; i++) { - Solid* s = new(__FILE__,__LINE__) Solid; - s->UseModel(design->shot_model); - - visible_stores[i] = s; - } - } -} - -Solid* -Weapon::GetTurret() -{ - return turret; -} - -Solid* -Weapon::GetTurretBase() -{ - return turret_base; -} - -Solid* -Weapon::GetVisibleStore(int i) -{ - if (i >= 0 && i < MAX_BARRELS) - return visible_stores[i]; - - return 0; -} - -void -Weapon::SetAmmo(int a) -{ - if (a >= 0) { - if (active_barrel >= 0 && design->visible_stores) { - while (a < ammo) { - if (active_barrel >= nbarrels) - active_barrel = 0; - - if (visible_stores[active_barrel]) { - GRAPHIC_DESTROY(visible_stores[active_barrel]); - active_barrel++; - ammo--; - } - } - } - - ammo = a; - } -} - -// +--------------------------------------------------------------------+ - -void -Weapon::ExecFrame(double seconds) -{ - System::ExecFrame(seconds); - - if (refire > 0) - refire -= (float) seconds; - - locked = false; - centered = false; - - if (!ship) - return; - - if (orders == POINT_DEFENSE && enabled) - SelectTarget(); - - if (beams && !target) { - for (int i = 0; i < nbarrels; i++) { - if (beams[i]) { - // aim beam straight: - Aim(); - SetBeamPoints(false); - return; - } - } - } - - if (design->self_aiming) { - Track(target, subtarget); - } - else if (turret) { - ZeroAim(); - } - - if (ship->CheckFire()) - return; - - // aim beam at target: - bool aim_beams = false; - - if (beams) { - for (int i = 0; i < nbarrels; i++) { - if (beams[i]) { - aim_beams = true; - SetBeamPoints(true); - break; - } - } - } - - if (!aim_beams) { - if (ripple_count > 0) { - if (Fire()) - ripple_count--; - } - - else if (locked && !blocked) { - if (!ship->IsHostileTo(target)) - return; - - if (orders == AUTO && centered) { - if (energy >= design->charge && - (ammo < 0 || target && target->Integrity() >= 1) && - objective.length() < design->max_range) - Fire(); - } - - else if (orders == POINT_DEFENSE) { - if (energy >= design->min_charge && - (ammo < 0 || target && target->Integrity() >= 1) && - objective.length() < design->max_range) - Fire(); - } - } - } -} - -// +----------------------------------------------------------------------+ - -void -Weapon::Distribute(double delivered_energy, double seconds) -{ - if (UsesWatts()) { - if (seconds < 0.01) - seconds = 0.01; - - // convert Joules to Watts: - energy = (float) (delivered_energy/seconds); - } - - else if (!Game::Paused()) { - energy += (float) (delivered_energy * 1.25); - - if (energy > capacity) - energy = capacity; - - else if (energy < 0) - energy = 0.0f; - } -} - - -// +--------------------------------------------------------------------+ - -bool -Weapon::Update(SimObject* obj) -{ - if (obj == target) { - target = 0; - } - - else if (beams) { - for (int i = 0; i < nbarrels; i++) - if (obj == beams[i]) - beams[i] = 0; - } - - return SimObserver::Update(obj); -} - -const char* -Weapon::GetObserverName() const -{ - static char name[256]; - sprintf_s(name, "Weapon %s", design->name.data()); - return name; -} - -// +--------------------------------------------------------------------+ - -void -Weapon::SetFiringOrders(int o) -{ - if (o >= MANUAL && o <= POINT_DEFENSE) - orders = o; -} - -void -Weapon::SetControlMode(int m) -{ - if (m >= SINGLE_FIRE && m <= SALVO_FIRE) - control = m; -} - -void -Weapon::SetSweep(int s) -{ - if (s >= SWEEP_NONE && s <= SWEEP_WIDE) - sweep = s; -} - -// +--------------------------------------------------------------------+ - -bool -Weapon::CanTarget(DWORD classification) const -{ - return (design->target_type & classification) ? true : false; -} - -// +--------------------------------------------------------------------+ - -void -Weapon::SetTarget(SimObject* targ, System* sub) -{ - // check self targeting: - if (targ == (SimObject*) ship) - return; - - // check target class filter: - if (targ) { - switch (targ->Type()) { - case SimObject::SIM_SHIP: { - Ship* tgt_ship = (Ship*) targ; - - if ((tgt_ship->Class() & design->target_type) == 0) - return; - } - break; - - case SimObject::SIM_SHOT: - return; - - case SimObject::SIM_DRONE: { - if ((design->target_type & Ship::DRONE) == 0) - return; - } - break; - - default: - return; - } - } - - // if ok, target this object: - if (target != targ) { - target = targ; - - if (target) - Observe(target); - } - - subtarget = sub; -} - -// +--------------------------------------------------------------------+ - -void -Weapon::SelectTarget() -{ - bool select_locked = false; - SimObject* targ = 0; - double dist = 1e9; - double az = 0; - double el = 0; - - if (ammo && enabled && (availability > crit_level)) { - ZeroAim(); - - ListIter contact = ship->ContactList(); - - // lock onto any threatening shots first (if we can): - if (design->target_type & Ship::DRONE) { - while (++contact) { - Shot* c_shot = contact->GetShot(); - - if (c_shot && contact->Threat(ship)) { - - // distance from self to target: - double distance = Point(c_shot->Location() - muzzle_pts[0]).length(); - - if (distance > design->min_range && - distance < design->max_range && - distance < dist) { - // check aim basket: - select_locked = CanLockPoint(c_shot->Location(), az, el); - - if (select_locked) { - targ = c_shot; - dist = distance; - } - } - } - } - } - - // lock onto a threatening ship only if it is (much) closer: - dist *= 0.2; - contact.reset(); - while (++contact) { - Ship* c_ship = contact->GetShip(); - - if (!c_ship) continue; - - // can we lock onto this target? - if ((c_ship->IsRogue() || c_ship->GetIFF() > 0 && c_ship->GetIFF() != ship->GetIFF()) && - (c_ship->Class() & design->target_type) && - c_ship->Weapons().size() > 0) { - // distance from self to target: - double distance = Point(c_ship->Location() - muzzle_pts[0]).length(); - - if (distance < design->max_range && distance < dist) { - // check aim basket: - select_locked = CanLockPoint(c_ship->Location(), az, el); - - if (select_locked) { - targ = c_ship; - dist = distance; - } - } - } - } - } - - if (!ammo || !enabled) { - SetTarget(0,0); - locked = false; - } - - else { - SetTarget(targ, 0); - } -} - -// +--------------------------------------------------------------------+ - -int -Weapon::Track(SimObject* targ, System* sub) -{ - if (ammo && enabled && (availability > crit_level)) { - firing = 0; - Aim(); - } - else if (turret) { - ZeroAim(); - } - - return locked; -} - -// +--------------------------------------------------------------------+ - -Shot* -Weapon::Fire() -{ - if (Game::Paused()) - return 0; - - if (ship && ship->CheckFire()) - return 0; - - if (ship->IsStarship() && target && !centered) - return 0; - - if (beams && active_barrel >= 0 && active_barrel < nbarrels && beams[active_barrel]) - return 0; - - Shot* shot = 0; - - if (ammo && enabled && - (refire <= 0) && (energy > design->min_charge) && - (availability > crit_level)) { - - refire = design->refire_delay; - - NetGame* net_game = NetGame::GetInstance(); - bool net_client = net_game ? net_game->IsClient() : false; - - // all barrels - if (active_barrel < 0) { - if (net_client || IsPrimary()) - NetUtil::SendWepTrigger(this, nbarrels); - - if (!net_game || IsPrimary()) { - for (int i = 0; i < nbarrels; i++) - shot = FireBarrel(i); - } - - else if (net_game && net_game->IsServer() && IsMissile()) { - for (int i = 0; i < nbarrels; i++) { - shot = FireBarrel(i); - NetUtil::SendWepRelease(this, shot); - } - } - } - - // single barrel - else { - if (net_client || IsPrimary()) - NetUtil::SendWepTrigger(this, nbarrels); - - if (!net_game || IsPrimary()) { - shot = FireBarrel(active_barrel++); - } - - else if (net_game && net_game->IsServer() && IsMissile()) { - shot = FireBarrel(active_barrel++); - NetUtil::SendWepRelease(this, shot); - } - - if (active_barrel >= nbarrels) { - active_barrel = 0; - refire += design->salvo_delay; - } - } - - if (design->ripple_count > 0 && ripple_count <= 0) - ripple_count = design->ripple_count-1; - - if (status != NOMINAL) - refire *= 2; - } - - return shot; -} - -// +--------------------------------------------------------------------+ - -Shot* -Weapon::NetFirePrimary(SimObject* tgt, System* sub, int count) -{ - Shot* shot = 0; - - if (!IsPrimary() || Game::Paused()) - return shot; - - if (active_barrel < 0 || active_barrel >= nbarrels) - active_barrel = 0; - - target = tgt; - subtarget = sub; - aim_time = 0; - - for (int i = 0; i < count; i++) { - shot = FireBarrel(active_barrel++); - - if (active_barrel >= nbarrels) { - active_barrel = 0; - refire += design->salvo_delay; - } - } - - if (target) - Observe(target); - - return shot; -} - -Shot* -Weapon::NetFireSecondary(SimObject* tgt, System* sub, DWORD objid) -{ - Shot* shot = 0; - - if (IsPrimary() || Game::Paused()) - return shot; - - if (active_barrel < 0 || active_barrel >= nbarrels) - active_barrel = 0; - - target = tgt; - subtarget = sub; - aim_time = 0; - - shot = FireBarrel(active_barrel++); - - if (active_barrel >= nbarrels) { - active_barrel = 0; - refire += design->salvo_delay; - } - - if (shot) - shot->SetObjID(objid); - - if (target) - Observe(target); - - return shot; -} - -// +--------------------------------------------------------------------+ - -Shot* -Weapon::FireBarrel(int n) -{ - const Point& base_vel = ship->Velocity(); - Shot* shot = 0; - SimRegion* region = ship->GetRegion(); - - if (!region || n < 0 || n >= nbarrels || Game::Paused()) - return 0; - - firing = 1; - Aim(); - - Camera rail_cam; - rail_cam.Clone(aim_cam); - - Point shotpos = muzzle_pts[n]; - if (design->length > 0) - shotpos = shotpos + aim_cam.vpn() * design->length; - - // guns may be slewed towards target: - if (design->primary) { - shot = CreateShot(shotpos, aim_cam, design, ship); - - if (shot) { - shot->SetVelocity(shot->Velocity() + base_vel); - } - } - - // missiles always launch in rail direction: - else { - // unless they are on a mobile launcher - if (turret && design->self_aiming) { - shot = CreateShot(shotpos, aim_cam, design, ship); - shot->SetVelocity(base_vel); - } - else { - shot = CreateShot(shotpos, rail_cam, design, ship); - if (shot /* && !turret */) { - Matrix orient = ship->Cam().Orientation(); - if (aim_azimuth != 0) orient.Yaw(aim_azimuth); - if (aim_elevation != 0) orient.Pitch(aim_elevation); - - Point eject = design->eject * orient; - shot->SetVelocity(base_vel + eject); - } - } - - if (shot && visible_stores[n]) { - GRAPHIC_DESTROY(visible_stores[n]); - } - } - - if (shot) { - if (ammo > 0) - ammo--; - - if (guided && target) - shot->SeekTarget(target, subtarget); - - float shot_load; - if (energy > design->charge) - shot_load = design->charge; - else - shot_load = energy; - - energy -= shot_load; - shot->SetCharge(shot_load * availability); - - if (target && design->flak && !design->guided) { - double speed = shot->Velocity().length(); - double range = (target->Location() - shot->Location()).length(); - - if (range > design->min_range && range < design->max_range) { - shot->SetFuse(range / speed); - } - } - - region->InsertObject(shot); - - if (beams) { - beams[n] = shot; - Observe(beams[n]); - - // aim beam at target: - SetBeamPoints(true); - } - - if (ship) { - ShipStats* stats = ShipStats::Find(ship->Name()); - - if (design->primary) - stats->AddGunShot(); - else if (design->decoy_type == 0 && design->damage > 0) - stats->AddMissileShot(); - } - } - - return shot; -} - -Shot* -Weapon::CreateShot(const Point& loc, const Camera& cam, WeaponDesign* dsn, const Ship* own) -{ - if (dsn->drone) - return new(__FILE__,__LINE__) Drone(loc, cam, dsn, own); - - else - return new(__FILE__,__LINE__) Shot(loc, cam, dsn, own); -} - -// +--------------------------------------------------------------------+ - -void -Weapon::Orient(const Physical* rep) -{ - System::Orient(rep); - - const Matrix& orientation = rep->Cam().Orientation(); - Point loc = rep->Location(); - - // align graphics with camera: - if (turret) { - if (!design->self_aiming) - ZeroAim(); - - const Matrix& aim = aim_cam.Orientation(); - - turret->MoveTo(mount_loc); - turret->SetOrientation(aim); - - if (turret_base) { - Matrix base = orientation; - - if (design->turret_axis == 1) { - base.Pitch(aim_elevation); - base.Pitch(old_elevation); - } - else { - base.Yaw(aim_azimuth); - base.Yaw(old_azimuth); - } - - turret_base->MoveTo(mount_loc); - turret_base->SetOrientation(base); - } - - for (int i = 0; i < nbarrels; i++) { - muzzle_pts[i] = (rel_pts[i] * aim) + mount_loc; - - if (visible_stores[i]) { - visible_stores[i]->SetOrientation(aim); - visible_stores[i]->MoveTo(muzzle_pts[i]); - } - } - } - else { - for (int i = 0; i < nbarrels; i++) { - muzzle_pts[i] = (rel_pts[i] * orientation) + loc; - - if (visible_stores[i]) { - visible_stores[i]->SetOrientation(orientation); - visible_stores[i]->MoveTo(muzzle_pts[i]); - } - } - } -} - -// +--------------------------------------------------------------------+ - -void -Weapon::SetBeamPoints(bool aim) -{ - for (int i = 0; i < nbarrels; i++) { - if (beams && beams[i]) { - Point from = muzzle_pts[i]; - Point to; - double len = design->length; - - if (len < 1 || len > 1e7) - len = design->max_range; - - if (len < 1) - len = 3e5; - - else if (len > 1e7) - len = 1e7; - - // always use the aim cam, to enforce slew_rate - to = from + aim_cam.vpn() * len; - - beams[i]->SetBeamPoints(from, to); - } - } -} - -// +--------------------------------------------------------------------+ - -void -Weapon::Aim() -{ - locked = false; - centered = false; - - FindObjective(); - - if (target) { - double az = 0; - double el = 0; - - locked = CanLockPoint(obj_w, az, el, &objective); - - double spread_az = design->spread_az; - double spread_el = design->spread_el; - - // beam sweep target: - if (design->beam) { - double factor = 0; - double az_phase = 0; - double el_phase = 0; - - if (target->Type() == SimObject::SIM_SHIP) { - Ship* s = (Ship*) target; - - if (s->IsStarship()) { - switch (sweep) { - default: - case SWEEP_NONE: factor = 0; break; - case SWEEP_TIGHT: factor = 1; break; - case SWEEP_WIDE: factor = 2; break; - } - } - } - - if (factor > 0) { - factor *= atan2(target->Radius(), (double) objective.z); - - for (int i = 0; i < nbarrels; i++) { - if (beams && beams[i]) { - az_phase = sin(beams[i]->Life() * 0.4 * PI); - el_phase = sin(beams[i]->Life() * 1.0 * PI); - break; - } - } - - az += factor * spread_az * az_phase; - el += factor * spread_el * el_phase * 0.25; - } - } - - else if (!design->beam) { - if (spread_az > 0) - az += Random(-spread_az, spread_az); - - if (spread_el > 0) - el += Random(-spread_el, spread_el); - } - - AimTurret(az, -el); - - // check range for guided weapons: - if (locked && guided) { - double range = objective.length(); - - if (range > design->max_track) - locked = false; - - else if (range > design->max_range) { - if (firing) { - if (RandomChance(1,4)) // 1 in 4 chance of locking anyway - locked = false; - } - else { - locked = false; - } - } - } - - if (locked) { - Point tloc = target->Location(); - tloc = Transform(tloc); - - if (tloc.z > 1) { - az = atan2(fabs(tloc.x), tloc.z); - el = atan2(fabs(tloc.y), tloc.z); - - double firing_cone = 10*DEGREES; - - if (orders == MANUAL) - firing_cone = 30*DEGREES; - - if (az < firing_cone && el < firing_cone) - centered = true; - } - } - } - else { - AimTurret(aim_az_rest, -aim_el_rest); - } -} - -void -Weapon::FindObjective() -{ - ZeroAim(); - - if (!target || !design->self_aiming) { - objective = Point(); - return; - } - - obj_w = target->Location(); - - if (subtarget) { - obj_w = subtarget->MountLocation(); - } - else if (target->Type() == SimObject::SIM_SHIP) { - Ship* tgt_ship = (Ship*) target; - - if (tgt_ship->IsGroundUnit()) - obj_w += Point(0,150,0); - } - - if (!design->beam && shot_speed > 0) { - // distance from self to target: - double distance = Point(obj_w - muzzle_pts[0]).length(); - - // TRUE shot speed is relative to ship speed: - Point eff_shot_vel = ship->Velocity() + aim_cam.vpn() * shot_speed - target->Velocity(); - double eff_shot_speed = eff_shot_vel.length(); - - // time to reach target: - double time = distance / eff_shot_speed; - - // where the target will be when the shot reaches it: - obj_w += (target->Velocity() - ship->Velocity()) * time + - target->Acceleration() * 0.25 * time * time; - } - - // transform into camera coords: - objective = Transform(obj_w); -} - -Point -Weapon::Transform(const Point& pt) -{ - Point result; - - Point obj_t = pt - aim_cam.Pos(); - result.x = obj_t * aim_cam.vrt(); - result.y = obj_t * aim_cam.vup(); - result.z = obj_t * aim_cam.vpn(); - - return result; -} - -bool -Weapon::CanLockPoint(const Point& test, double& az, double& el, Point* obj) -{ - Point pt = Transform(test); - bool locked = true; - - // first compute az: - if (fabs(pt.z) < 0.1) pt.z = 0.1; - az = atan(pt.x / pt.z); - if (pt.z < 0) az -= PI; - if (az < -PI) az += 2*PI; - - // then, rotate target into az-coords to compute el: - Camera tmp; - tmp.Clone(aim_cam); - aim_cam.Yaw(az); - pt = Transform(test); - aim_cam.Clone(tmp); - - if (fabs(pt.z) < 0.1) pt.z = 0.1; - el = atan(pt.y / pt.z); - - if (obj) *obj = pt; - - // is the target in the basket? - // clamp if necessary: - - if (az > aim_az_max) { - az = aim_az_max; - locked = false; - } - else if (az < aim_az_min) { - az = aim_az_min; - locked = false; - } - - if (el > aim_el_max) { - el = aim_el_max; - locked = false; - } - else if (el < aim_el_min) { - el = aim_el_min; - locked = false; - } - - if (IsDrone() && guided) { - double firing_cone = 10*DEGREES; - - if (orders == MANUAL) - firing_cone = 20*DEGREES; - - if (az < firing_cone && el < firing_cone) - locked = true; - } - - return locked; -} - -// +--------------------------------------------------------------------+ - -void -Weapon::AimTurret(double az, double el) -{ - double seconds = (Game::GameTime() - aim_time) / 1000.0; - - // don't let the weapon turn faster than turret slew rate: - double max_turn = design->slew_rate * seconds; - - if (fabs(az-old_azimuth) > max_turn) { - if (az > old_azimuth) - az = old_azimuth + max_turn; - else - az = old_azimuth - max_turn; - } - - if (fabs(el-old_elevation) > PI/2 * seconds) { - if (el > old_elevation) - el = old_elevation + max_turn; - else - el = old_elevation - max_turn; - } - - aim_cam.Yaw(az); - aim_cam.Pitch(el); - - old_azimuth = (float) az; - old_elevation = (float) el; - - aim_time = Game::GameTime(); -} - -void -Weapon::ZeroAim() -{ - aim_cam.Clone(ship->Cam()); - if (aim_azimuth != 0) aim_cam.Yaw(aim_azimuth); - if (aim_elevation != 0) aim_cam.Pitch(aim_elevation); - - aim_cam.MoveTo(muzzle_pts[0]); -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: Stars.exe + FILE: Weapon.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Weapon class +*/ + +#include "MemDebug.h" +#include "Weapon.h" +#include "Shot.h" +#include "Drone.h" +#include "Contact.h" +#include "Ship.h" +#include "Sim.h" +#include "SimEvent.h" +#include "Random.h" + +#include "NetGame.h" +#include "NetUtil.h" + +#include "Game.h" +#include "Solid.h" + +// +----------------------------------------------------------------------+ + +Weapon::Weapon(WeaponDesign* d, int nmuz, Vec3* muzzles, double az, double el) + : System(WEAPON, d->type, d->name, d->value, d->capacity, d->capacity, + d->recharge_rate), + design(d), group(d->group), ammo(-1), ripple_count(0), + aim_azimuth((float) az), aim_elevation((float) el), + old_azimuth(0.0f), old_elevation(0.0f), aim_time(0), + enabled(true), refire(0.0f), + mass(d->carry_mass), resist(d->carry_resist), + guided(d->guided), shot_speed(d->speed), + active_barrel(0), locked(false), centered(false), firing(false), blocked(false), + index(0), target(0), subtarget(0), beams(0), orders(MANUAL), + control(SINGLE_FIRE), sweep(SWEEP_TIGHT), turret(0), turret_base(0) +{ + ZeroMemory(visible_stores, sizeof(visible_stores)); + + if (design->primary) + abrv = Game::GetText("sys.weapon.primary.abrv"); + else + abrv = Game::GetText("sys.weapon.secondary.abrv"); + + nbarrels = nmuz; + + if (nbarrels > MAX_BARRELS) + nbarrels = MAX_BARRELS; + + if (nbarrels == 0 && design->nbarrels > 0) { + nbarrels = design->nbarrels; + + for (int i = 0; i < nbarrels; i++) + muzzle_pts[i] = rel_pts[i] = design->muzzle_pts[i] * design->scale; + + ammo = design->ammo * nbarrels; + } + else if (nbarrels == 1 && design->nstores > 0) { + nbarrels = design->nstores; + + for (int i = 0; i < nbarrels; i++) + muzzle_pts[i] = rel_pts[i] = (muzzles[0] + design->attachments[i]); + + ammo = nbarrels; + } + else { + for (int i = 0; i < nbarrels; i++) + muzzle_pts[i] = rel_pts[i] = muzzles[i]; + + ammo = design->ammo * nbarrels; + } + + if (design->syncro) + active_barrel = -1; + + emcon_power[0] = 0; + emcon_power[1] = 0; + emcon_power[2] = 100; + + aim_az_max = design->aim_az_max; + aim_az_min = design->aim_az_min; + aim_az_rest = design->aim_az_rest; + + aim_el_max = design->aim_el_max; + aim_el_min = design->aim_el_min; + aim_el_rest = design->aim_el_rest; +} + +// +----------------------------------------------------------------------+ + +Weapon::Weapon(const Weapon& w) + : System(w), design(w.design), ammo(-1), ripple_count(0), + enabled(true), refire(0.0f), + mass(w.mass), resist(w.resist), + aim_azimuth(w.aim_azimuth), aim_elevation(w.aim_elevation), + old_azimuth(0.0f), old_elevation(0.0f), aim_time(0), + guided(w.guided), shot_speed(w.shot_speed), + active_barrel(0), locked(false), centered(false), firing(false), blocked(false), + target(0), subtarget(0), beams(0), orders(MANUAL), + control(SINGLE_FIRE), sweep(SWEEP_TIGHT), group(w.group), + aim_az_max(w.aim_az_max), aim_az_min(w.aim_az_min), aim_az_rest(w.aim_az_rest), + aim_el_max(w.aim_el_max), aim_el_min(w.aim_el_min), aim_el_rest(w.aim_el_rest), + turret(0), turret_base(0) +{ + Mount(w); + ZeroMemory(visible_stores, sizeof(visible_stores)); + + nbarrels = w.nbarrels; + + for (int i = 0; i < nbarrels; i++) { + muzzle_pts[i] = rel_pts[i] = w.muzzle_pts[i]; + } + + ammo = design->ammo * nbarrels; + + if (design->syncro) + active_barrel = -1; + + if (design->beam) { + beams = new(__FILE__,__LINE__) Shot* [nbarrels]; + ZeroMemory(beams, sizeof(Shot*) * nbarrels); + } + + if (aim_az_rest >= 2*PI) + aim_az_rest = design->aim_az_rest; + + if (aim_el_rest >= 2*PI) + aim_el_rest = design->aim_el_rest; +} + +// +--------------------------------------------------------------------+ + +Weapon::~Weapon() +{ + if (beams) { + for (int i = 0; i < nbarrels; i++) { + if (beams[i]) { + Ignore(beams[i]); + delete beams[i]; + beams[i] = 0; + } + } + + delete [] beams; + } + + GRAPHIC_DESTROY(turret); + GRAPHIC_DESTROY(turret_base); + + for (int i = 0; i < MAX_BARRELS; i++) + GRAPHIC_DESTROY(visible_stores[i]); +} + +// +--------------------------------------------------------------------+ + +bool +Weapon::IsPrimary() const +{ + return design->primary; +} + +bool +Weapon::IsDrone() const +{ + return design->drone; +} + +bool +Weapon::IsDecoy() const +{ + return design->decoy_type != 0; +} + +bool +Weapon::IsProbe() const +{ + return design->probe != 0; +} + +bool +Weapon::IsMissile() const +{ + return !design->primary; +} + +bool +Weapon::IsBeam() const +{ + return design->beam; +} + +// +--------------------------------------------------------------------+ + +Shot* +Weapon::GetBeam(int i) +{ + if (beams && i >= 0 && i < nbarrels) + return beams[i]; + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Weapon::SetOwner(Ship* s) +{ + ship = s; + + if (design->turret_model) { + Solid* t = new(__FILE__,__LINE__) Solid; + t->UseModel(design->turret_model); + turret = t; + } + + if (design->turret_base_model) { + Solid* t = new(__FILE__,__LINE__) Solid; + t->UseModel(design->turret_base_model); + turret_base = t; + } + + if (!design->primary && + design->visible_stores && + ammo == nbarrels && + design->shot_model != 0) + { + for (int i = 0; i < nbarrels; i++) { + Solid* s = new(__FILE__,__LINE__) Solid; + s->UseModel(design->shot_model); + + visible_stores[i] = s; + } + } +} + +Solid* +Weapon::GetTurret() +{ + return turret; +} + +Solid* +Weapon::GetTurretBase() +{ + return turret_base; +} + +Solid* +Weapon::GetVisibleStore(int i) +{ + if (i >= 0 && i < MAX_BARRELS) + return visible_stores[i]; + + return 0; +} + +void +Weapon::SetAmmo(int a) +{ + if (a >= 0) { + if (active_barrel >= 0 && design->visible_stores) { + while (a < ammo) { + if (active_barrel >= nbarrels) + active_barrel = 0; + + if (visible_stores[active_barrel]) { + GRAPHIC_DESTROY(visible_stores[active_barrel]); + active_barrel++; + ammo--; + } + } + } + + ammo = a; + } +} + +// +--------------------------------------------------------------------+ + +void +Weapon::ExecFrame(double seconds) +{ + System::ExecFrame(seconds); + + if (refire > 0) + refire -= (float) seconds; + + locked = false; + centered = false; + + if (!ship) + return; + + if (orders == POINT_DEFENSE && enabled) + SelectTarget(); + + if (beams && !target) { + for (int i = 0; i < nbarrels; i++) { + if (beams[i]) { + // aim beam straight: + Aim(); + SetBeamPoints(false); + return; + } + } + } + + if (design->self_aiming) { + Track(target, subtarget); + } + else if (turret) { + ZeroAim(); + } + + if (ship->CheckFire()) + return; + + // aim beam at target: + bool aim_beams = false; + + if (beams) { + for (int i = 0; i < nbarrels; i++) { + if (beams[i]) { + aim_beams = true; + SetBeamPoints(true); + break; + } + } + } + + if (!aim_beams) { + if (ripple_count > 0) { + if (Fire()) + ripple_count--; + } + + else if (locked && !blocked) { + if (!ship->IsHostileTo(target)) + return; + + if (orders == AUTO && centered) { + if (energy >= design->charge && + (ammo < 0 || target && target->Integrity() >= 1) && + objective.length() < design->max_range) + Fire(); + } + + else if (orders == POINT_DEFENSE) { + if (energy >= design->min_charge && + (ammo < 0 || target && target->Integrity() >= 1) && + objective.length() < design->max_range) + Fire(); + } + } + } +} + +// +----------------------------------------------------------------------+ + +void +Weapon::Distribute(double delivered_energy, double seconds) +{ + if (UsesWatts()) { + if (seconds < 0.01) + seconds = 0.01; + + // convert Joules to Watts: + energy = (float) (delivered_energy/seconds); + } + + else if (!Game::Paused()) { + energy += (float) (delivered_energy * 1.25); + + if (energy > capacity) + energy = capacity; + + else if (energy < 0) + energy = 0.0f; + } +} + + +// +--------------------------------------------------------------------+ + +bool +Weapon::Update(SimObject* obj) +{ + if (obj == target) { + target = 0; + } + + else if (beams) { + for (int i = 0; i < nbarrels; i++) + if (obj == beams[i]) + beams[i] = 0; + } + + return SimObserver::Update(obj); +} + +const char* +Weapon::GetObserverName() const +{ + static char name[256]; + sprintf_s(name, "Weapon %s", design->name.data()); + return name; +} + +// +--------------------------------------------------------------------+ + +void +Weapon::SetFiringOrders(int o) +{ + if (o >= MANUAL && o <= POINT_DEFENSE) + orders = o; +} + +void +Weapon::SetControlMode(int m) +{ + if (m >= SINGLE_FIRE && m <= SALVO_FIRE) + control = m; +} + +void +Weapon::SetSweep(int s) +{ + if (s >= SWEEP_NONE && s <= SWEEP_WIDE) + sweep = s; +} + +// +--------------------------------------------------------------------+ + +bool +Weapon::CanTarget(DWORD classification) const +{ + return (design->target_type & classification) ? true : false; +} + +// +--------------------------------------------------------------------+ + +void +Weapon::SetTarget(SimObject* targ, System* sub) +{ + // check self targeting: + if (targ == (SimObject*) ship) + return; + + // check target class filter: + if (targ) { + switch (targ->Type()) { + case SimObject::SIM_SHIP: { + Ship* tgt_ship = (Ship*) targ; + + if ((tgt_ship->Class() & design->target_type) == 0) + return; + } + break; + + case SimObject::SIM_SHOT: + return; + + case SimObject::SIM_DRONE: { + if ((design->target_type & Ship::DRONE) == 0) + return; + } + break; + + default: + return; + } + } + + // if ok, target this object: + if (target != targ) { + target = targ; + + if (target) + Observe(target); + } + + subtarget = sub; +} + +// +--------------------------------------------------------------------+ + +void +Weapon::SelectTarget() +{ + bool select_locked = false; + SimObject* targ = 0; + double dist = 1e9; + double az = 0; + double el = 0; + + if (ammo && enabled && (availability > crit_level)) { + ZeroAim(); + + ListIter contact = ship->ContactList(); + + // lock onto any threatening shots first (if we can): + if (design->target_type & Ship::DRONE) { + while (++contact) { + Shot* c_shot = contact->GetShot(); + + if (c_shot && contact->Threat(ship)) { + + // distance from self to target: + double distance = Point(c_shot->Location() - muzzle_pts[0]).length(); + + if (distance > design->min_range && + distance < design->max_range && + distance < dist) { + // check aim basket: + select_locked = CanLockPoint(c_shot->Location(), az, el); + + if (select_locked) { + targ = c_shot; + dist = distance; + } + } + } + } + } + + // lock onto a threatening ship only if it is (much) closer: + dist *= 0.2; + contact.reset(); + while (++contact) { + Ship* c_ship = contact->GetShip(); + + if (!c_ship) continue; + + // can we lock onto this target? + if ((c_ship->IsRogue() || c_ship->GetIFF() > 0 && c_ship->GetIFF() != ship->GetIFF()) && + (c_ship->Class() & design->target_type) && + c_ship->Weapons().size() > 0) { + // distance from self to target: + double distance = Point(c_ship->Location() - muzzle_pts[0]).length(); + + if (distance < design->max_range && distance < dist) { + // check aim basket: + select_locked = CanLockPoint(c_ship->Location(), az, el); + + if (select_locked) { + targ = c_ship; + dist = distance; + } + } + } + } + } + + if (!ammo || !enabled) { + SetTarget(0,0); + locked = false; + } + + else { + SetTarget(targ, 0); + } +} + +// +--------------------------------------------------------------------+ + +int +Weapon::Track(SimObject* targ, System* sub) +{ + if (ammo && enabled && (availability > crit_level)) { + firing = 0; + Aim(); + } + else if (turret) { + ZeroAim(); + } + + return locked; +} + +// +--------------------------------------------------------------------+ + +Shot* +Weapon::Fire() +{ + if (Game::Paused()) + return 0; + + if (ship && ship->CheckFire()) + return 0; + + if (ship->IsStarship() && target && !centered) + return 0; + + if (beams && active_barrel >= 0 && active_barrel < nbarrels && beams[active_barrel]) + return 0; + + Shot* shot = 0; + + if (ammo && enabled && + (refire <= 0) && (energy > design->min_charge) && + (availability > crit_level)) { + + refire = design->refire_delay; + + NetGame* net_game = NetGame::GetInstance(); + bool net_client = net_game ? net_game->IsClient() : false; + + // all barrels + if (active_barrel < 0) { + if (net_client || IsPrimary()) + NetUtil::SendWepTrigger(this, nbarrels); + + if (!net_game || IsPrimary()) { + for (int i = 0; i < nbarrels; i++) + shot = FireBarrel(i); + } + + else if (net_game && net_game->IsServer() && IsMissile()) { + for (int i = 0; i < nbarrels; i++) { + shot = FireBarrel(i); + NetUtil::SendWepRelease(this, shot); + } + } + } + + // single barrel + else { + if (net_client || IsPrimary()) + NetUtil::SendWepTrigger(this, nbarrels); + + if (!net_game || IsPrimary()) { + shot = FireBarrel(active_barrel++); + } + + else if (net_game && net_game->IsServer() && IsMissile()) { + shot = FireBarrel(active_barrel++); + NetUtil::SendWepRelease(this, shot); + } + + if (active_barrel >= nbarrels) { + active_barrel = 0; + refire += design->salvo_delay; + } + } + + if (design->ripple_count > 0 && ripple_count <= 0) + ripple_count = design->ripple_count-1; + + if (status != NOMINAL) + refire *= 2; + } + + return shot; +} + +// +--------------------------------------------------------------------+ + +Shot* +Weapon::NetFirePrimary(SimObject* tgt, System* sub, int count) +{ + Shot* shot = 0; + + if (!IsPrimary() || Game::Paused()) + return shot; + + if (active_barrel < 0 || active_barrel >= nbarrels) + active_barrel = 0; + + target = tgt; + subtarget = sub; + aim_time = 0; + + for (int i = 0; i < count; i++) { + shot = FireBarrel(active_barrel++); + + if (active_barrel >= nbarrels) { + active_barrel = 0; + refire += design->salvo_delay; + } + } + + if (target) + Observe(target); + + return shot; +} + +Shot* +Weapon::NetFireSecondary(SimObject* tgt, System* sub, DWORD objid) +{ + Shot* shot = 0; + + if (IsPrimary() || Game::Paused()) + return shot; + + if (active_barrel < 0 || active_barrel >= nbarrels) + active_barrel = 0; + + target = tgt; + subtarget = sub; + aim_time = 0; + + shot = FireBarrel(active_barrel++); + + if (active_barrel >= nbarrels) { + active_barrel = 0; + refire += design->salvo_delay; + } + + if (shot) + shot->SetObjID(objid); + + if (target) + Observe(target); + + return shot; +} + +// +--------------------------------------------------------------------+ + +Shot* +Weapon::FireBarrel(int n) +{ + const Point& base_vel = ship->Velocity(); + Shot* shot = 0; + SimRegion* region = ship->GetRegion(); + + if (!region || n < 0 || n >= nbarrels || Game::Paused()) + return 0; + + firing = 1; + Aim(); + + Camera rail_cam; + rail_cam.Clone(aim_cam); + + Point shotpos = muzzle_pts[n]; + if (design->length > 0) + shotpos = shotpos + aim_cam.vpn() * design->length; + + // guns may be slewed towards target: + if (design->primary) { + shot = CreateShot(shotpos, aim_cam, design, ship); + + if (shot) { + shot->SetVelocity(shot->Velocity() + base_vel); + } + } + + // missiles always launch in rail direction: + else { + // unless they are on a mobile launcher + if (turret && design->self_aiming) { + shot = CreateShot(shotpos, aim_cam, design, ship); + shot->SetVelocity(base_vel); + } + else { + shot = CreateShot(shotpos, rail_cam, design, ship); + if (shot /* && !turret */) { + Matrix orient = ship->Cam().Orientation(); + if (aim_azimuth != 0) orient.Yaw(aim_azimuth); + if (aim_elevation != 0) orient.Pitch(aim_elevation); + + Point eject = design->eject * orient; + shot->SetVelocity(base_vel + eject); + } + } + + if (shot && visible_stores[n]) { + GRAPHIC_DESTROY(visible_stores[n]); + } + } + + if (shot) { + if (ammo > 0) + ammo--; + + if (guided && target) + shot->SeekTarget(target, subtarget); + + float shot_load; + if (energy > design->charge) + shot_load = design->charge; + else + shot_load = energy; + + energy -= shot_load; + shot->SetCharge(shot_load * availability); + + if (target && design->flak && !design->guided) { + double speed = shot->Velocity().length(); + double range = (target->Location() - shot->Location()).length(); + + if (range > design->min_range && range < design->max_range) { + shot->SetFuse(range / speed); + } + } + + region->InsertObject(shot); + + if (beams) { + beams[n] = shot; + Observe(beams[n]); + + // aim beam at target: + SetBeamPoints(true); + } + + if (ship) { + ShipStats* stats = ShipStats::Find(ship->Name()); + + if (design->primary) + stats->AddGunShot(); + else if (design->decoy_type == 0 && design->damage > 0) + stats->AddMissileShot(); + } + } + + return shot; +} + +Shot* +Weapon::CreateShot(const Point& loc, const Camera& cam, WeaponDesign* dsn, const Ship* own) +{ + if (dsn->drone) + return new(__FILE__,__LINE__) Drone(loc, cam, dsn, own); + + else + return new(__FILE__,__LINE__) Shot(loc, cam, dsn, own); +} + +// +--------------------------------------------------------------------+ + +void +Weapon::Orient(const Physical* rep) +{ + System::Orient(rep); + + const Matrix& orientation = rep->Cam().Orientation(); + Point loc = rep->Location(); + + // align graphics with camera: + if (turret) { + if (!design->self_aiming) + ZeroAim(); + + const Matrix& aim = aim_cam.Orientation(); + + turret->MoveTo(mount_loc); + turret->SetOrientation(aim); + + if (turret_base) { + Matrix base = orientation; + + if (design->turret_axis == 1) { + base.Pitch(aim_elevation); + base.Pitch(old_elevation); + } + else { + base.Yaw(aim_azimuth); + base.Yaw(old_azimuth); + } + + turret_base->MoveTo(mount_loc); + turret_base->SetOrientation(base); + } + + for (int i = 0; i < nbarrels; i++) { + muzzle_pts[i] = (rel_pts[i] * aim) + mount_loc; + + if (visible_stores[i]) { + visible_stores[i]->SetOrientation(aim); + visible_stores[i]->MoveTo(muzzle_pts[i]); + } + } + } + else { + for (int i = 0; i < nbarrels; i++) { + muzzle_pts[i] = (rel_pts[i] * orientation) + loc; + + if (visible_stores[i]) { + visible_stores[i]->SetOrientation(orientation); + visible_stores[i]->MoveTo(muzzle_pts[i]); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +Weapon::SetBeamPoints(bool aim) +{ + for (int i = 0; i < nbarrels; i++) { + if (beams && beams[i]) { + Point from = muzzle_pts[i]; + Point to; + double len = design->length; + + if (len < 1 || len > 1e7) + len = design->max_range; + + if (len < 1) + len = 3e5; + + else if (len > 1e7) + len = 1e7; + + // always use the aim cam, to enforce slew_rate + to = from + aim_cam.vpn() * len; + + beams[i]->SetBeamPoints(from, to); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Weapon::Aim() +{ + locked = false; + centered = false; + + FindObjective(); + + if (target) { + double az = 0; + double el = 0; + + locked = CanLockPoint(obj_w, az, el, &objective); + + double spread_az = design->spread_az; + double spread_el = design->spread_el; + + // beam sweep target: + if (design->beam) { + double factor = 0; + double az_phase = 0; + double el_phase = 0; + + if (target->Type() == SimObject::SIM_SHIP) { + Ship* s = (Ship*) target; + + if (s->IsStarship()) { + switch (sweep) { + default: + case SWEEP_NONE: factor = 0; break; + case SWEEP_TIGHT: factor = 1; break; + case SWEEP_WIDE: factor = 2; break; + } + } + } + + if (factor > 0) { + factor *= atan2(target->Radius(), (double) objective.z); + + for (int i = 0; i < nbarrels; i++) { + if (beams && beams[i]) { + az_phase = sin(beams[i]->Life() * 0.4 * PI); + el_phase = sin(beams[i]->Life() * 1.0 * PI); + break; + } + } + + az += factor * spread_az * az_phase; + el += factor * spread_el * el_phase * 0.25; + } + } + + else if (!design->beam) { + if (spread_az > 0) + az += Random(-spread_az, spread_az); + + if (spread_el > 0) + el += Random(-spread_el, spread_el); + } + + AimTurret(az, -el); + + // check range for guided weapons: + if (locked && guided) { + double range = objective.length(); + + if (range > design->max_track) + locked = false; + + else if (range > design->max_range) { + if (firing) { + if (RandomChance(1,4)) // 1 in 4 chance of locking anyway + locked = false; + } + else { + locked = false; + } + } + } + + if (locked) { + Point tloc = target->Location(); + tloc = Transform(tloc); + + if (tloc.z > 1) { + az = atan2(fabs(tloc.x), tloc.z); + el = atan2(fabs(tloc.y), tloc.z); + + double firing_cone = 10*DEGREES; + + if (orders == MANUAL) + firing_cone = 30*DEGREES; + + if (az < firing_cone && el < firing_cone) + centered = true; + } + } + } + else { + AimTurret(aim_az_rest, -aim_el_rest); + } +} + +void +Weapon::FindObjective() +{ + ZeroAim(); + + if (!target || !design->self_aiming) { + objective = Point(); + return; + } + + obj_w = target->Location(); + + if (subtarget) { + obj_w = subtarget->MountLocation(); + } + else if (target->Type() == SimObject::SIM_SHIP) { + Ship* tgt_ship = (Ship*) target; + + if (tgt_ship->IsGroundUnit()) + obj_w += Point(0,150,0); + } + + if (!design->beam && shot_speed > 0) { + // distance from self to target: + double distance = Point(obj_w - muzzle_pts[0]).length(); + + // TRUE shot speed is relative to ship speed: + Point eff_shot_vel = ship->Velocity() + aim_cam.vpn() * shot_speed - target->Velocity(); + double eff_shot_speed = eff_shot_vel.length(); + + // time to reach target: + double time = distance / eff_shot_speed; + + // where the target will be when the shot reaches it: + obj_w += (target->Velocity() - ship->Velocity()) * time + + target->Acceleration() * 0.25 * time * time; + } + + // transform into camera coords: + objective = Transform(obj_w); +} + +Point +Weapon::Transform(const Point& pt) +{ + Point result; + + Point obj_t = pt - aim_cam.Pos(); + result.x = obj_t * aim_cam.vrt(); + result.y = obj_t * aim_cam.vup(); + result.z = obj_t * aim_cam.vpn(); + + return result; +} + +bool +Weapon::CanLockPoint(const Point& test, double& az, double& el, Point* obj) +{ + Point pt = Transform(test); + bool locked = true; + + // first compute az: + if (fabs(pt.z) < 0.1) pt.z = 0.1; + az = atan(pt.x / pt.z); + if (pt.z < 0) az -= PI; + if (az < -PI) az += 2*PI; + + // then, rotate target into az-coords to compute el: + Camera tmp; + tmp.Clone(aim_cam); + aim_cam.Yaw(az); + pt = Transform(test); + aim_cam.Clone(tmp); + + if (fabs(pt.z) < 0.1) pt.z = 0.1; + el = atan(pt.y / pt.z); + + if (obj) *obj = pt; + + // is the target in the basket? + // clamp if necessary: + + if (az > aim_az_max) { + az = aim_az_max; + locked = false; + } + else if (az < aim_az_min) { + az = aim_az_min; + locked = false; + } + + if (el > aim_el_max) { + el = aim_el_max; + locked = false; + } + else if (el < aim_el_min) { + el = aim_el_min; + locked = false; + } + + if (IsDrone() && guided) { + double firing_cone = 10*DEGREES; + + if (orders == MANUAL) + firing_cone = 20*DEGREES; + + if (az < firing_cone && el < firing_cone) + locked = true; + } + + return locked; +} + +// +--------------------------------------------------------------------+ + +void +Weapon::AimTurret(double az, double el) +{ + double seconds = (Game::GameTime() - aim_time) / 1000.0; + + // don't let the weapon turn faster than turret slew rate: + double max_turn = design->slew_rate * seconds; + + if (fabs(az-old_azimuth) > max_turn) { + if (az > old_azimuth) + az = old_azimuth + max_turn; + else + az = old_azimuth - max_turn; + } + + if (fabs(el-old_elevation) > PI/2 * seconds) { + if (el > old_elevation) + el = old_elevation + max_turn; + else + el = old_elevation - max_turn; + } + + aim_cam.Yaw(az); + aim_cam.Pitch(el); + + old_azimuth = (float) az; + old_elevation = (float) el; + + aim_time = Game::GameTime(); +} + +void +Weapon::ZeroAim() +{ + aim_cam.Clone(ship->Cam()); + if (aim_azimuth != 0) aim_cam.Yaw(aim_azimuth); + if (aim_elevation != 0) aim_cam.Pitch(aim_elevation); + + aim_cam.MoveTo(muzzle_pts[0]); +} -- cgit v1.1