From 3c487c5cd69c53d6fea948643c0a76df03516605 Mon Sep 17 00:00:00 2001 From: Aki Date: Fri, 1 Apr 2022 21:23:39 +0200 Subject: Moved Stars45 to StarsEx --- StarsEx/Weapon.cpp | 1184 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1184 insertions(+) create mode 100644 StarsEx/Weapon.cpp (limited to 'StarsEx/Weapon.cpp') diff --git a/StarsEx/Weapon.cpp b/StarsEx/Weapon.cpp new file mode 100644 index 0000000..b5a0fd3 --- /dev/null +++ b/StarsEx/Weapon.cpp @@ -0,0 +1,1184 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Weapon class +*/ + +#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 "Clock.h" +#include "ContentBundle.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 = ContentBundle::GetInstance()->GetText("sys.weapon.primary.abrv"); + else + abrv = ContentBundle::GetInstance()->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 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 Solid; + t->UseModel(design->turret_model); + turret = t; + } + + if (design->turret_base_model) { + Solid* t = new 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 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::GetInstance()->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::GetInstance()->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::GetInstance()->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::GetInstance()->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::GetInstance()->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 Drone(loc, cam, dsn, own); + + else + return new 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 = (Clock::GetInstance()->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 = Clock::GetInstance()->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