diff --git a/BUK-M2/BUK-M2-set.xml b/BUK-M2/BUK-M2-set.xml index 2e3fae9..d75e1b4 100644 --- a/BUK-M2/BUK-M2-set.xml +++ b/BUK-M2/BUK-M2-set.xml @@ -123,6 +123,24 @@ 0 + + + + + + true + + + + nasal + + + + + + @@ -200,23 +218,38 @@ 1576 145 900 - 0.025 + 0.15 ‪1.312336‬ 2 0.35 120 3000 - 15000 + 0 2.0 200 true 5.5 false + 30 A - Grumble + Bird away + + +
+ + 0 + + + 0 + + + 0 + +
+
@@ -225,6 +258,15 @@ Aircraft/BUK-M2/Nasal/damage.nas + + Aircraft/BUK-M2/Nasal/radar-logic.nas + + + Aircraft/BUK-M2/Nasal/guided-missiles.nas + + + Aircraft/BUK-M2/Nasal/fire-control.nas + diff --git a/BUK-M2/Nasal/damage.nas b/BUK-M2/Nasal/damage.nas index 5398f58..232630e 100644 --- a/BUK-M2/Nasal/damage.nas +++ b/BUK-M2/Nasal/damage.nas @@ -7,14 +7,15 @@ var hp = 80; var hp_max = hp; var cannon_types = { - " M70 rocket hit": 40.00, #Viggen rocket - " M55 cannon shell hit": 3.50, #Viggen gun - " KCA cannon shell hit": 7.00, #Viggen gunpod - " Gun Splash On ": 3.0, #Mirage - " M61A1 shell hit": 2.50, #f-14/15 - " GAU-8/A hit": 10.00, #a-10 - " BK27 cannon hit": 3.50, #typhoon - " GSh-30 hit": 3.00, #mig-21 + " M70 rocket hit": 0.30, + " M55 cannon shell hit": 0.20, + " KCA cannon shell hit": 0.20, + " Gun Splash On ": 0.30, + " M61A1 shell hit": 0.20, + " GAU-8/A hit": 0.30, + " BK27 cannon hit": 0.20, + " GSh-30 hit": 0.20, + " 7.62 hit": 2.50, #UH-1 " 50 BMG hit": 3.00, #p-47 }; @@ -60,7 +61,8 @@ var warhead_lbs = { "R-60": 6.60, "R-27R1": 85.98, "R-27T1": 85.98, - "FAB-500": 564.00, + "FAB-500": 564.00, + "M71R": 200.00, }; var incoming_listener = func { diff --git a/BUK-M2/Nasal/fire-control.nas b/BUK-M2/Nasal/fire-control.nas new file mode 100644 index 0000000..c5641e5 --- /dev/null +++ b/BUK-M2/Nasal/fire-control.nas @@ -0,0 +1,245 @@ +################### GLOBALS + +var false = 0; +var true = 1; + +var radar_update_time = 2; +var launch_update_time = 0.3; + +var missile_delay_time = 0; +var ciws_delay_time = 0; + +var AIR = 0; +var MARINE = 1; +var SURFACE = 2; +var ORDNANCE = 3; + +var ACTIVE_MISSILE = 0; +var NUM_MISSILES = 3; # total carried minus 1 +var ROUNDS = 30; +var RELOAD_TIME = 600; + +################### MISSILE INFO + +var missile_name = "M317"; +var missile_brevity = "Grizzly"; +var missile_max_distance = 31; #max distance in nm +var missile_min_distance = 2; #minimum distance in nm +var lockon_time = 12; #time in seconds it takes to lock on to a target +var launch_in_progress = 0; + +################### IFF + +var target = { + new: func (callsign) { + var m = { parents: [target] }; + m.callsign = callsign; + m.fired = false; + return m; + }, +}; + +var targets = { + "pinto": target.new("pinto"), + "Leto": target.new("Leto"), + "YV-187": target.new("YV-187"), + "swamp": target.new("swamp"), + "S": target.new("S"), + "G-UNTER": target.new("G-UNTER"), + "J-Mav16": target.new("J-Mav16"), + "Raider1": target.new("Raider1"), + "AF-MA13": target.new("AF-MA13"), + "KOL24M": target.new("KOL24M"), + "SNOWY1": target.new("SNOWY1"), + "Evading-target": target.new("Evading-target"),# for testing with AI targets + "Slow-evading-target": target.new("Slow-evading-target"), +}; + +################### MAIN LOOP + +var scan = func() { + if ( getprop("/carrier/sunk") == 1 ) { + return; + } + + #### ITERATE THROUGH MP LIST #### + var my_pos = geo.aircraft_position(); + foreach(var mp; props.globals.getNode("/ai/models").getChildren("multiplayer")){#change to "aircraft" to shoot at scenario AI planes. + #### DO WE HAVE FIRING SOLUTION... #### + # is plane in range, do we still have missiles, and is a missile already inbound, and has it been 4 seconds since the last missile launch? + var trigger = fire_control(mp, my_pos); + #print("dist to target = " ~ dist_to_target); + #### ... FOR THE MISSILE #### + if ( launch_in_progress == 0 and trigger == true and targets[mp.getNode("callsign").getValue()].fired == false and ACTIVE_MISSILE <= NUM_MISSILES and ( systime() - missile_delay_time > 7 ) ) { # + #print("callsign " ~ cs ~ " found at " ~ dist_to_target); + missile_delay_time = systime(); + targets[mp.getNode("callsign").getValue()].fired = true; + mp.getNode("unique",1).setValue(rand()); + armament.contact = radar_logic.Contact.new(mp, AIR); + missile_launch(mp, systime(), my_pos); + launch_in_progress = 1; + #### ... FOR THE CIWS + # disabled + } + } + settimer(scan,radar_update_time); +} + +################## FIRE CONTROL +################## ALL AI LOGIC RELATED TO FIGURING OUT IF A TARGET SHOULD BE SHOT AT SHOULD GO HERE. + +var fire_control = func(mp, my_pos) { + #gather some data about the target + var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048)); + var target_distance = my_pos.direct_distance_to(ufo_pos) * .000539957; #in nautical miles + var target_heading = mp.getNode("orientation/true-heading-deg").getValue(); + var target_bearing = my_pos.course_to(ufo_pos); + var relative_bearing = math.abs(ufo_pos.course_to(my_pos) - target_heading); + var target_altitude = mp.getNode("position/altitude-ft").getValue(); + var target_airspeed = mp.getNode("velocities/true-airspeed-kt").getValue(); + + # can the radar see it? + if ( mp.getNode("valid").getValue() == false ) { return false; } + if ( radar_logic.isNotBehindTerrain(mp) == false ) { return false; } + if ( target_airspeed < 40 ) { return false; } + if ( target_altitude - props.globals.getNode("/position/altitude-ft").getValue() < 150 ) { return false; } + if ( target_altitude > 70000 ) { return false; } + if ( target_distance > missile_max_distance ) { return false; } + # is this plane a friend or foe? + if ( targets[mp.getNode("callsign").getValue()] == nil ) { return false; } + + #should we shoot? using linear interpolation, with minimum probability of 0.01 to 0.10 and maximum probability of 1 + var distance_probability = 1 + (0.01 - 1) * (( target_distance - missile_min_distance ) / ( missile_max_distance - missile_min_distance )); + var bearing_probability = 1 + (0.05 - 1) * (( relative_bearing ) / ( 180 )); + var altitude_probability = 1 + (0.05 - 1) * (( target_altitude - 150 ) / ( 70000 - 150 )); + var speed_probability = distance_probability / 2.5 + (1 - distance_probability / 2.5) * (( target_airspeed - 1000 ) / ( 40 - 1000 )); + + var distance_weight = 0.4; + var bearing_weight = 0.3; + var altitude_weight = 0.2; + var speed_weight = 0.1; + + var fire_probability = (distance_probability * distance_weight) + (bearing_probability * bearing_weight) + (altitude_probability * altitude_weight) + (speed_probability * speed_weight); + var the_dice = rand(); + + print("probability for " ~ mp.getNode("callsign").getValue() ~ ": "~fire_probability); + + if ( fire_probability > 0.60 and fire_probability > the_dice ) { + return true; + } else { + return false; + } +} + +################### MISSILE CONTROL + +### missile reload + +var reload = func() { + #figure out how many to add + for ( var i = 0; i <= NUM_MISSILES; i = i + 1 ) { + if (armament.AIM.new(i,missile_name,missile_brevity) != -1) { + #if statement just in case reload was called before all missiles were fired. Cause avoid calling search() on same missile twice. + armament.AIM.active[i].status = 0; + armament.AIM.active[i].search(); + } + } + ACTIVE_MISSILE = 0; + print("SAM reloaded with "~(NUM_MISSILES+1)~" missiles."); +} + +### missile launch + +var missile_launch = func(mp, launchtime, my_pos) { + var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048)); + var target_bearing = my_pos.course_to(ufo_pos); + setprop("/orientation/heading-deg",target_bearing); + if ( armament.AIM.active[ACTIVE_MISSILE].status == 1 and systime() - launchtime > lockon_time and radar_logic.isNotBehindTerrain(mp) == true ) { + var brevity = armament.AIM.active[ACTIVE_MISSILE].brevity; + armament.AIM.active[ACTIVE_MISSILE].release(); + defeatSpamFilter(brevity ~ " at: " ~ mp.getNode("callsign").getValue()); + ACTIVE_MISSILE = ACTIVE_MISSILE + 1; + print("Fired "~missile_name~" #" ~ ACTIVE_MISSILE ~ " at: " ~ mp.getNode("callsign").getValue()); + launch_in_progress = 0; + return; + } elsif ((systime() - launchtime) > (lockon_time*3) or radar_logic.isNotBehindTerrain(mp) == false) { + # launch cancelled so it dont forever goes in this loop and dont allow for other firings. + launch_in_progress = 0; + return; + } + settimer( func { missile_launch(mp, launchtime, my_pos); },launch_update_time); +} + +### missile set launched to false for target + +var incoming_listener = func { + var history = getprop("/sim/multiplay/chat-history"); + var hist_vector = split("\n", history); + if (size(hist_vector) > 0) { + var last = hist_vector[size(hist_vector)-1]; + var last_vector = split(":", last); + var author = last_vector[0]; + var callsign = getprop("sim/multiplay/callsign"); + if (size(last_vector) > 1 and author == callsign) { + var last1 = split(" ", last_vector[1]); + print(last1); + if(size(last1) > 2 and (last1[size(last1)-1] == "exploded" or last1[size(last1)-1] == "disarmed") ) { + print("missile hit"); + if (size(last_vector) > 3) { + #print("that someone is me!"); + var type = last1[1]; + #callsign = target, type = missile + last_vector[3] = right(last_vector[3],size(last_vector[3]) - 1); + if ( targets[last_vector[3]] != nil and type == missile_name ) { + targets[last_vector[3]].fired = false; + } + } + } elsif(size(last1) > 2 and last1[size(last1)-2] == "missed" ) { + print("missile missed"); + var target = last1[size(last1)-1]; + var type = last1[1]; + #callsign = target, type = missile + last_vector[3] = right(last_vector[3],size(last_vector[3]) - 1); + if (targets[target] != nil and type == missile_name) { + targets[target].fired = false; + } + } + } + } +} + + +################### MISC + +var spams = 0; +var spamList = []; + +var defeatSpamFilter = func (str) { + spams += 1; + if (spams == 15) { + spams = 1; + } + str = str~":"; + for (var i = 1; i <= spams; i+=1) { + str = str~"."; + } + var newList = [str]; + for (var i = 0; i < size(spamList); i += 1) { + append(newList, spamList[i]); + } + spamList = newList; +} + +var spamLoop = func { + var spam = pop(spamList); + if (spam != nil) { + setprop("/sim/multiplay/chat", spam); + } + settimer(spamLoop, 1.20); +} + +spamLoop(); + +reload(); +scan(); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file diff --git a/BUK-M2/Nasal/guided-missiles.nas b/BUK-M2/Nasal/guided-missiles.nas new file mode 100644 index 0000000..7a6c528 --- /dev/null +++ b/BUK-M2/Nasal/guided-missiles.nas @@ -0,0 +1,2222 @@ +################################################################################# +####### +####### Guided/Cruise missiles, rockets and dumb/glide bombs code for Flightgear. +####### +####### License: GPL 2 +####### +####### Authors: +####### Alexis Bory, Fabien Barbier, Justin Nicholson, Nikolai V. Chr. +####### +####### In addition, some code is derived from work by: +####### David Culp, Vivian Meazza, M. Franz +####### +################################################################################## + +# Some notes about making weapons: +# +# Firstly make sure you read the comments (line 190+) below for the properties. +# For laser/gps guided gravity bombs make sure to set the max G very low, like 0.5G, to simulate them slowly adjusting to hit the target. +# Remember for air to air missiles the speed quoted in literature is normally the speed above the launch platform. I usually fly at the typical max usage +# regime for that missile, so for example for AIM-7 it would be at 40000 ft, +# there I make sure it can reach approx the max relative speed. For older missiles the max speed quoted is sometimes absolute speed though, so beware. +# If it quotes aerodynamic speed then its the absolute speed. Speeds quoted in in unofficial sources can be any of them, +# but if its around mach 5 for A/A its a good bet its absolute, only very few A/A missiles are likely hypersonic. (probably due to heat or fuel limitations) +# If you cannot find fuel weight in literature, you probably wont go far off with a value that is 1/4 to 1/3 of total launch weight for a A/A missile. +# Stage durations is allowed to be 0, so can thrust values. If there is no second stage, instead of just setting stage 2 thrust to 0, +# set stage 2 duration to 0 also. For unpowered munitions, set all thrusts to 0. +# For very low sea skimming missiles, be sure to set terrain following to false, you cannot have it both ways. +# Since if it goes very low (below 100ft), it cannot navigate terrain reliable. +# The property terrain following only goes into effect, if a cruise altitude is set below 10000ft and not set to 0. +# Cruise missiles against ground targets will always terrain follow, no matter that property. +# If literature quotes a max distance for a weapon, its a good bet it is under the condition that the target +# is approaching the launch platform with high speed and does not evade, and also if the launch platform is an aircraft, +# that it also is approaching the target with high speed. In other words, high closing rate. For example the AIM-7, which can hit bombers out at 32 NM, +# will often have to be within 3 NM of an escaping target to hit it (source). Missiles typically have significantly less range against an evading +# or escaping target than what is commonly believed. I typically fly at 40000 ft at mach 2, approach a target flying head-on with same speed and altitude, +# to test max range. +# When you test missiles against aircraft, be sure to do it with a framerate of 25+, else they will not hit very good, especially high speed missiles like +# Amraam or Phoenix. Also notice they generally not hit so close against Scenario/AI objects compared to MP aircraft due to the way these are updated. +# Laser and semi-radar guided munitions need the target to be painted to keep lock. Notice gps guided munition that are all aspect will never lose lock, +# whether they can 'see' the target or not. +# Remotely controlled navigation is not implemented, but the way it flies can be simulated by setting direct navigation with semi-radar or laser guidance. +# +# +# Usage: +# +# To create a weapon call AIM.new(pylon, type, description). The pylon is an integer from 0 or higher. When its launched it will read the pylon position in +# controls/armament/station[pylon+1]/offsets, where the position properties must be x-m, y-m and z-m. The type is just a string, the description is a string +# that is exposed in its radar properties under AI/models during flight. +# The model that is loaded and shown is located in the aircraft folder at the value of property payload/armament/models in a subfolder with same name as type. +# Inside the subfolder the xml file is called [lowercase type]-[pylon].xml +# To start making the missile try to get a lock, set its status to MISSILE_SEARCH and call search(), the missile will then keep trying to get a lock on 'contact'. +# 'contact' can be set to nil at any time or changed. To stop the search, just set its status to MISSILE_STANDBY. To resume the search you again have to set +# the status and call search(). +# To release the munition at a target call release(), do this only after the missile has set its own status to MISSILE_LOCK. +# When using weapons without target, call releaseAtNothing() instead of release(), search() does not need to have been called beforehand. +# To then find out where it hit the ground check the impact report in AI/models. The impact report will contain warhead weight, but that will be zero if +# the weapon did not have time to arm before hitting ground. +# To drop the munition, without arming it nor igniting its engine, call eject(). +# +# +# Limitations: +# +# The weapons use a simplified flight model that does not have AoA or sideslip. Mass balance, rotational inertia, wind is also not implemented. They also do not roll. +# If you fire a weapon and have HoT enabled in flightgear, they likely will not hit very precise. +# The weapons are highly dependant on framerate, so low frame rate will make them hit imprecise. +# APN does not take target sideslip and AoA into account when considering the targets acceleration. It assumes the target flies in the direction its pointed. +# The drag curves are tailored for sizable munitions, so it does not work well will bullet or cannon sized munition, submodels are better suited for that. +# +# +# Future features: +# +# Make ground hitting weapons hit all nearby targets, not just what its locked on. +# Chaff interaction for radar guided weapons. +# ECM disturbance of getting radar lock. +# Lock on jam. (advanced feature) +# After FG gets HLA: stop using MP chat for hit messages. +# Allow firing only if certain conditions are met. Like not being inverted when firing dropped weapons. +# Remote controlled guidance (advanced feature and probably not very practical in FG..yet) +# Ground launched rails/tubes that rotate towards target before firing. +# Make weapon unreliable by design, to simulate weapons which were unreliable, like Phoenix. +# Sub munitions that have their own guidance/FDM. (advanced) +# GPS guided munitions could have waypoints added. +# Specify terminal manouvres and preferred impact aspect. +# Limit guiding if needed so that the missile don't lose sight of target. +# Change flare to use helicopter property double. +# Make check for seeker FOV round instead of square. +# Consider to average the closing speed in proportional navigation. So get it between second last positions and current, instead of last to current. +# Drag coeff reduction due to exhaust plume. +# Proportional navigation should use vector math instead decomposition horizontal/vertical navigation. +# If closing speed is negative, consider to switch to pure pursuit from proportional navigation, the target might turn back into missile. +# +# +# Please report bugs and features to Nikolai V. Chr. | ForumUser: Necolatis | Callsign: Leto + +var AcModel = props.globals.getNode("payload"); +var OurHdg = props.globals.getNode("orientation/heading-deg"); +var OurRoll = props.globals.getNode("orientation/roll-deg"); +var OurPitch = props.globals.getNode("orientation/pitch-deg"); +var OurAlpha = props.globals.getNode("orientation/alpha-deg"); +var OurBeta = props.globals.getNode("orientation/side-slip-deg"); +var deltaSec = props.globals.getNode("sim/time/delta-sec"); +var speedUp = props.globals.getNode("sim/speed-up"); +var noseAir = props.globals.getNode("velocities/uBody-fps"); +var belowAir = props.globals.getNode("velocities/wBody-fps"); +var HudReticleDev = props.globals.getNode("payload/armament/hud/reticle-total-deviation", 1);#polar coords +var HudReticleDeg = props.globals.getNode("payload/armament/hud/reticle-total-angle", 1); +var update_loop_time = 0.000; + +var SIM_TIME = 0; +var REAL_TIME = 1; + +var TRUE = 1; +var FALSE = 0; + +var use_fg_default_hud = FALSE; + +var MISSILE_STANDBY = -1; +var MISSILE_SEARCH = 0; +var MISSILE_LOCK = 1; +var MISSILE_FLYING = 2; + +var AIR = 0; +var MARINE = 1; +var SURFACE = 2; +var ORDNANCE = 3; + +var g_fps = 9.80665 * M2FT; +var slugs_to_lbm = 32.1740485564; +var const_e = 2.71828183; + +var first_in_air = FALSE;# first missile is in the air, other missiles should not write to blade[x]. + +# +# The radar will make sure to keep this variable updated. +# Whatever is targeted and ready to be fired upon, should be set here. +# +var contact = nil; +# +# Contact should implement the following interface: +# +# get_type() - (AIR, MARINE, SURFACE or ORDNANCE) +# getUnique() - Used when comparing 2 targets to each other and determining if they are the same target. +# isValid() - If this target is valid +# getElevation() +# get_bearing() +# get_Callsign() +# get_range() +# get_Coord() +# get_Latitude() +# get_Longitude() +# get_altitude() +# get_Pitch() +# get_heading() +# getFlareNode() - Used for flares. +# isPainted() - Tells if this target is still being tracked by the launch platform, only used in semi-radar and laser guided missiles. + +var AIM = { + #done + new : func (p, type = "AIM-9", sign = "Sidewinder") { + if(AIM.active[p] != nil) { + #do not make new missile logic if one exist for this pylon. + return -1; + } + var m = { parents : [AIM]}; + # Args: p = Pylon. + + m.type_lc = string.lc(type); + m.type = type; + + m.deleted = FALSE; + + m.status = MISSILE_STANDBY; # -1 = stand-by, 0 = searching, 1 = locked, 2 = fired. + m.free = 0; # 0 = status fired with lock, 1 = status fired but having lost lock. + m.trackWeak = 1; + + m.prop = AcModel.getNode("armament/"~m.type_lc~"/").getChild("msl", 0, 1); + m.SwSoundOnOff = AcModel.getNode("armament/"~m.type_lc~"/sound-on-off"); + m.SwSoundVol = AcModel.getNode("armament/"~m.type_lc~"/sound-volume"); + m.PylonIndex = m.prop.getNode("pylon-index", 1).setValue(p); + m.ID = p; + m.pylon_prop = props.globals.getNode("controls/armament").getChild("station", p+1); + m.Tgt = nil; + m.callsign = "Unknown"; + m.update_track_time = 0; + m.direct_dist_m = nil; + m.speed_m = 0; + + # AIM specs: + m.fcs_fov = getprop("payload/armament/"~m.type_lc~"/FCS-field-deg") / 2; # fire control system total field of view + m.max_detect_rng = getprop("payload/armament/"~m.type_lc~"/max-fire-range-nm"); # max range that the FCS allows firing + m.max_seeker_dev = getprop("payload/armament/"~m.type_lc~"/seeker-field-deg") / 2; # missiles own seekers total FOV + m.force_lbf_1 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-1"); # stage 1 thrust, set both stages to zero to simulate gravity bomb, set them to 1 to simulate glide bomb + m.force_lbf_2 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-2"); # stage 2 thrust + m.stage_1_duration = getprop("payload/armament/"~m.type_lc~"/stage-1-duration-sec"); # stage 1 duration + m.stage_2_duration = getprop("payload/armament/"~m.type_lc~"/stage-2-duration-sec"); # stage 2 duration + m.weight_launch_lbm = getprop("payload/armament/"~m.type_lc~"/weight-launch-lbs"); # total weight of armament, including fuel and warhead. + m.weight_whead_lbm = getprop("payload/armament/"~m.type_lc~"/weight-warhead-lbs"); # warhead weight + m.weight_fuel_lbm = getprop("payload/armament/"~m.type_lc~"/weight-fuel-lbm"); # fuel weight [optional]. If this property is not present, it won't lose weight as the fuel is used. + m.Cd_base = getprop("payload/armament/"~m.type_lc~"/drag-coeff"); # drag coefficient + m.eda = getprop("payload/armament/"~m.type_lc~"/drag-area"); # normally is crosssection area of munition (without fins) + m.max_g = getprop("payload/armament/"~m.type_lc~"/max-g"); # max G-force the missile can pull at sealevel + m.arming_time = getprop("payload/armament/"~m.type_lc~"/arming-time-sec"); # time for weapon to arm + m.min_speed_for_guiding = getprop("payload/armament/"~m.type_lc~"/min-speed-for-guiding-mach"); # minimum speed before the missile steers, before it reaches this speed it will fly straight + m.selfdestruct_time = getprop("payload/armament/"~m.type_lc~"/self-destruct-time-sec"); # time before selfdestruct + m.guidance = getprop("payload/armament/"~m.type_lc~"/guidance"); # heat/radar/semi-radar/laser/gps/vision/unguided + m.navigation = getprop("payload/armament/"~m.type_lc~"/navigation"); # direct/PN/APN (use direct for bombs, use PN for very old missiles, use APN for modern missiles) + m.all_aspect = getprop("payload/armament/"~m.type_lc~"/all-aspect"); # set to false if missile only locks on reliably to rear of target aircraft + m.vol_search = getprop("payload/armament/"~m.type_lc~"/vol-search"); # sound volume when searcing + m.vol_track = getprop("payload/armament/"~m.type_lc~"/vol-track"); # sound volume when having lock + m.vol_track_weak = getprop("payload/armament/"~m.type_lc~"/vol-track-weak"); # sound volume before getting solid lock + m.angular_speed = getprop("payload/armament/"~m.type_lc~"/seeker-angular-speed-dps"); # only for heat/vision seeking missiles. Max angular speed that the target can move as seen from seeker, before seeker loses lock. + m.sun_lock = getprop("payload/armament/"~m.type_lc~"/lock-on-sun-deg"); # only for heat seeking missiles. If it looks at sun within this angle, it will lose lock on target. + m.loft_alt = getprop("payload/armament/"~m.type_lc~"/loft-altitude"); # if 0 then no snap up. Below 10000 then cruise altitude above ground. Above 10000 max altitude it will snap up to. + m.follow = getprop("payload/armament/"~m.type_lc~"/terrain-follow"); # used for anti-ship missiles that should be able to terrain follow instead of purely sea skimming. + m.min_dist = getprop("payload/armament/"~m.type_lc~"/min-fire-range-nm"); # it wont get solid lock before the target has this range + m.rail = getprop("payload/armament/"~m.type_lc~"/rail"); # if the weapon is rail or tube fired set to true. If dropped 7ft before ignited set to false. + m.rail_dist_m = getprop("payload/armament/"~m.type_lc~"/rail-length-m"); # length of tube/rail + m.rail_forward = getprop("payload/armament/"~m.type_lc~"/rail-point-forward"); # true for rail, false for rail/tube with a pitch + m.rail_pitch_deg = getprop("payload/armament/"~m.type_lc~"/rail-pitch-deg"); # Only used when rail is not forward. 90 for vertical tube. + m.class = getprop("payload/armament/"~m.type_lc~"/class"); # put in letters here that represent the types the missile can fire at. A=air, M=marine, G=ground + m.brevity = getprop("payload/armament/"~m.type_lc~"/fire-msg"); # what the pilot will call out over the comm when he fires this weapon + m.reportDist = getprop("payload/armament/"~m.type_lc~"/max-report-distance"); # max distance from target the missile will report that it has exploded, instead of just passed. + + + m.weapon_model = getprop("payload/armament/models")~type~"/"~m.type_lc~"-"; + m.elapsed_last = 0; + + m.target_air = find("A", m.class)==-1?FALSE:TRUE; + m.target_sea = find("M", m.class)==-1?FALSE:TRUE;#use M for marine, since S can be confused with surface. + m.target_gnd = find("G", m.class)==-1?FALSE:TRUE; + + if (m.navigation == nil) { + m.navigation = "APN"; + } + + # Find the next index for "models/model" and create property node. + # Find the next index for "ai/models/aim-9" and create property node. + # (M. Franz, see Nasal/tanker.nas) + var n = props.globals.getNode("models", 1); + var i = 0; + for (i = 0; 1==1; i += 1) { + if (n.getChild("model", i, 0) == nil) { + break; + } + } + m.model = n.getChild("model", i, 1); + + n = props.globals.getNode("ai/models", 1); + for (i = 0; 1==1; i += 1) { + if (n.getChild(m.type_lc, i, 0) == nil) { + break; + } + } + m.ai = n.getChild(m.type_lc, i, 1); + + m.ai.getNode("valid", 1).setBoolValue(1); + m.ai.getNode("name", 1).setValue(type); + m.ai.getNode("sign", 1).setValue(sign); + #m.model.getNode("collision", 1).setBoolValue(0); + #m.model.getNode("impact", 1).setBoolValue(0); + var id_model = m.weapon_model ~ m.ID ~ ".xml"; + m.model.getNode("path", 1).setValue(id_model); + m.life_time = 0; + + # Create the AI position and orientation properties. + m.latN = m.ai.getNode("position/latitude-deg", 1); + m.lonN = m.ai.getNode("position/longitude-deg", 1); + m.altN = m.ai.getNode("position/altitude-ft", 1); + m.hdgN = m.ai.getNode("orientation/true-heading-deg", 1); + m.pitchN = m.ai.getNode("orientation/pitch-deg", 1); + m.rollN = m.ai.getNode("orientation/roll-deg", 1); + + m.ac = nil; + + m.coord = geo.Coord.new().set_latlon(0, 0, 0); + m.last_coord = nil; + m.before_last_coord = nil; + m.t_coord = geo.Coord.new().set_latlon(0, 0, 0); + m.last_t_coord = m.t_coord; + m.before_last_t_coord = nil; + + m.speed_down_fps = nil; + m.speed_east_fps = nil; + m.speed_north_fps = nil; + m.alt_ft = nil; + m.pitch = nil; + m.hdg = nil; + + # Nikolai V. Chr. + # The more variables here instead of declared locally, the better for performance. + # Due to garbage collector. + # + + + m.density_alt_diff = 0; + m.max_g_current = m.max_g; + m.old_speed_horz_fps = nil; + m.paused = 0; + m.old_speed_fps = 0; + m.dt = 0; + m.g = 0; + + # navigation and guidance + m.last_deviation_e = nil; + m.last_deviation_h = nil; + m.last_track_e = 0; + m.last_track_h = 0; + m.guiding = TRUE; + m.t_alt = 0; + m.dist_curr = 0; + m.dist_curr_direct = 0; + m.t_elev_deg = 0; + m.t_course = 0; + m.dist_last = nil; + m.dist_direct_last = nil; + m.last_t_course = nil; + m.last_t_elev_deg = nil; + m.last_cruise_or_loft = FALSE; + m.last_t_norm_speed = nil; + m.last_t_elev_norm_speed = nil; + m.last_dt = 0; + m.dive_token = FALSE; + m.raw_steer_signal_elev = 0; + m.raw_steer_signal_head = 0; + m.cruise_or_loft = FALSE; + m.curr_deviation_e = 0; + m.curr_deviation_h = 0; + m.track_signal_e = 0; + m.track_signal_h = 0; + + # cruise-missiles + m.nextGroundElevation = 0; # next Ground Elevation + m.nextGroundElevationMem = [-10000, -1]; + + #rail + m.drop_time = 0; + m.rail_passed = FALSE; + m.x = 0; + m.y = 0; + m.z = 0; + m.rail_pos = 0; + m.rail_speed_into_wind = 0; + + # stats + m.maxMach = 0; + m.energyBleedKt = 0; + + m.lastFlare = 0; + m.fooled = FALSE; + m.explodeSound = TRUE; + m.first = FALSE; + + # these 3 is used for limiting spam to console: + m.heatLostLock = FALSE; + m.semiLostLock = FALSE; + m.tooLowSpeed = FALSE; + + m.SwSoundOnOff.setBoolValue(FALSE); + m.SwSoundVol.setDoubleValue(m.vol_search); + #me.trackWeak = 1; + + return AIM.active[m.ID] = m; + }, + + del: func {#GCD (garbage collection optimization done) + #print("deleted"); + if (me.first == TRUE) { + me.resetFirst(); + } + me.model.remove(); + me.ai.remove(); + if (me.status == MISSILE_FLYING) { + delete(AIM.flying, me.flyID); + } else { + delete(AIM.active, me.ID); + } + me.SwSoundVol.setDoubleValue(0); + me.deleted = TRUE; + }, + + getGPS: func(x, y, z, pitch) {#GCD + # + # get Coord from body position. x,y,z must be in meters. + # derived from Vivian's code in AIModel/submodel.cxx. + # + me.ac = geo.aircraft_position(); + + if(x == 0 and y==0 and z==0) { + return geo.Coord.new(me.ac); + } + + me.ac_roll = OurRoll.getValue(); + me.ac_pitch = pitch; + me.ac_hdg = OurHdg.getValue(); + + me.in = [0,0,0]; + me.trans = [[0,0,0],[0,0,0],[0,0,0]]; + me.out = [0,0,0]; + + me.in[0] = -x * M2FT; + me.in[1] = y * M2FT; + me.in[2] = z * M2FT; + # Pre-process trig functions: + me.cosRx = math.cos(-me.ac_roll * D2R); + me.sinRx = math.sin(-me.ac_roll * D2R); + me.cosRy = math.cos(-me.ac_pitch * D2R); + me.sinRy = math.sin(-me.ac_pitch * D2R); + me.cosRz = math.cos(me.ac_hdg * D2R); + me.sinRz = math.sin(me.ac_hdg * D2R); + # Set up the transform matrix: + me.trans[0][0] = me.cosRy * me.cosRz; + me.trans[0][1] = -1 * me.cosRx * me.sinRz + me.sinRx * me.sinRy * me.cosRz ; + me.trans[0][2] = me.sinRx * me.sinRz + me.cosRx * me.sinRy * me.cosRz; + me.trans[1][0] = me.cosRy * me.sinRz; + me.trans[1][1] = me.cosRx * me.cosRz + me.sinRx * me.sinRy * me.sinRz; + me.trans[1][2] = -1 * me.sinRx * me.cosRx + me.cosRx * me.sinRy * me.sinRz; + me.trans[2][0] = -1 * me.sinRy; + me.trans[2][1] = me.sinRx * me.cosRy; + me.trans[2][2] = me.cosRx * me.cosRy; + # Multiply the input and transform matrices: + me.out[0] = me.in[0] * me.trans[0][0] + me.in[1] * me.trans[0][1] + me.in[2] * me.trans[0][2]; + me.out[1] = me.in[0] * me.trans[1][0] + me.in[1] * me.trans[1][1] + me.in[2] * me.trans[1][2]; + me.out[2] = me.in[0] * me.trans[2][0] + me.in[1] * me.trans[2][1] + me.in[2] * me.trans[2][2]; + # Convert ft to degrees of latitude: + me.out[0] = me.out[0] / (366468.96 - 3717.12 * math.cos(me.ac.lat() * D2R)); + # Convert ft to degrees of longitude: + me.out[1] = me.out[1] / (365228.16 * math.cos(me.ac.lat() * D2R)); + # Set submodel initial position: + me.mlat = me.ac.lat() + me.out[0]; + me.mlon = me.ac.lon() + me.out[1]; + me.malt = (me.ac.alt() * M2FT) + me.out[2]; + + me.c = geo.Coord.new(); + me.c.set_latlon(me.mlat, me.mlon, me.malt * FT2M); + + return me.c; + }, + + eject: func () {#GCD + me.stage_1_duration = 0; + me.force_lbf_1 = 0; + me.stage_2_duration = 0; + me.force_lbf_2 = 0; + me.arming_time = 5000; + me.rail = FALSE; + me.releaseAtNothing(); + }, + + releaseAtNothing: func() {#GCD + me.Tgt = nil; + me.release(); + }, + + release: func() {#GCn + # Release missile/bomb from its pylon/rail/tube and send it away. + # + me.status = MISSILE_FLYING; + me.flyID = rand(); + AIM.flying[me.flyID] = me; + delete(AIM.active, me.ID); + me.animation_flags_props(); + + # Get the A/C position and orientation values. + me.ac = geo.aircraft_position(); + me.ac_init = geo.Coord.new(me.ac); + var ac_roll = OurRoll.getValue();# positive is banking right + var ac_pitch = OurPitch.getValue(); + var ac_hdg = OurHdg.getValue(); + + if (me.rail == TRUE) { + if (me.rail_forward == FALSE) { + ac_pitch = ac_pitch + me.rail_pitch_deg; + } + } + + # Compute missile initial position relative to A/C center + me.x = me.pylon_prop.getNode("offsets/x-m").getValue(); + me.y = me.pylon_prop.getNode("offsets/y-m").getValue(); + me.z = me.pylon_prop.getNode("offsets/z-m").getValue(); + var init_coord = me.getGPS(me.x, me.y, me.z, ac_pitch); + + # Set submodel initial position: + var mlat = init_coord.lat(); + var mlon = init_coord.lon(); + var malt = init_coord.alt() * M2FT; + me.latN.setDoubleValue(mlat); + me.lonN.setDoubleValue(mlon); + me.altN.setDoubleValue(malt); + me.hdgN.setDoubleValue(ac_hdg); + + if (me.rail == FALSE) { + # drop distance in time + me.drop_time = math.sqrt(2*7/g_fps);# time to fall 7 ft to clear aircraft + } + + me.pitchN.setDoubleValue(ac_pitch); + me.rollN.setDoubleValue(0); + + me.coord = geo.Coord.new(init_coord); + + me.model.getNode("latitude-deg-prop", 1).setValue(me.latN.getPath()); + me.model.getNode("longitude-deg-prop", 1).setValue(me.lonN.getPath()); + me.model.getNode("elevation-ft-prop", 1).setValue(me.altN.getPath()); + me.model.getNode("heading-deg-prop", 1).setValue(me.hdgN.getPath()); + me.model.getNode("pitch-deg-prop", 1).setValue(me.pitchN.getPath()); + me.model.getNode("roll-deg-prop", 1).setValue(me.rollN.getPath()); + var loadNode = me.model.getNode("load", 1); + loadNode.setBoolValue(1); + + # Get initial velocity vector (aircraft): + me.speed_down_fps = getprop("velocities/speed-down-fps"); + me.speed_east_fps = getprop("velocities/speed-east-fps"); + me.speed_north_fps = getprop("velocities/speed-north-fps"); + if (me.rail == TRUE) { + if (me.rail_forward == FALSE) { + if (me.rail_pitch_deg == 90) { + # rail is actually a tube pointing upward + me.rail_speed_into_wind = -getprop("velocities/wBody-fps");# wind from below + } else { + #does not account for incoming airstream, yet. + me.rail_speed_into_wind = 0; + } + } else { + # rail is pointing forward + me.rail_speed_into_wind = getprop("velocities/uBody-fps");# wind from nose + } + } + + me.alt_ft = malt; + me.pitch = ac_pitch; + me.hdg = ac_hdg; + + if (getprop("sim/flight-model") == "jsb") { + # currently not supported in Yasim + me.density_alt_diff = getprop("fdm/jsbsim/atmosphere/density-altitude") - me.ac.alt()*M2FT; + } + + # setup lofting and cruising + me.snapUp = me.loft_alt > 10000; + me.rotate_token = FALSE; + #if (me.Tgt != nil and me.snapUp == TRUE) { + #var dst = me.coord.distance_to(me.Tgt.get_Coord()) * M2NM; + # + #f(x) = y1 + ((x - x1) / (x2 - x1)) * (y2 - y1) +# me.loft_alt = me.loft_alt - ((me.max_detect_rng - 10) - (dst - 10))*500; original code +# me.loft_alt = 0+((dst-38)/(me.max_detect_rng-38))*(me.loft_alt-36000); originally for phoenix missile +# me.loft_alt = 0+((dst-10)/(me.max_detect_rng-10))*(me.loft_alt-0); also doesn't really work +# me.loft_alt = me.clamp(me.loft_alt, 0, 200000); + #printf("Loft to max %5d ft.", me.loft_alt); + #} + + + me.SwSoundVol.setDoubleValue(0); + me.trackWeak = 1; + #settimer(func { HudReticleDeg.setValue(0) }, 2); + #interpolate(HudReticleDev, 0, 2); + + me.startMach = getprop("velocities/mach"); + me.startAlt = getprop("position/altitude-ft"); + me.startDist = 0; + me.maxAlt = me.startAlt; + if (me.Tgt != nil) { + me.startDist = me.ac_init.direct_distance_to(me.Tgt.get_Coord()); + } + printf("Launch %s at %s.", me.type, me.callsign); + + me.weight_current = me.weight_launch_lbm; + me.mass = me.weight_launch_lbm / slugs_to_lbm; + + # find the fuel consumption - lbm/sec + if (me.weight_fuel_lbm == nil) { + me.weight_fuel_lbm = 0; + } + var energy1 = me.force_lbf_1 * me.stage_1_duration; + var energy2 = me.force_lbf_2 * me.stage_2_duration; + var energyT = energy1 + energy2; + var fuel_per_energy = me.weight_fuel_lbm / energyT; + me.fuel_per_sec_1 = (fuel_per_energy * energy1) / me.stage_1_duration; + me.fuel_per_sec_2 = (fuel_per_energy * energy2) / me.stage_2_duration; + + # find the sun: + if(me.guidance == "heat") { + var sun_x = getprop("ephemeris/sun/local/x"); + var sun_y = getprop("ephemeris/sun/local/x"); + var sun_z = getprop("ephemeris/sun/local/x"); + me.sun_power = getprop("/rendering/scene/diffuse/red"); + me.sun = geo.Coord.new(me.ac_init); + me.sun.set_xyz(me.sun.x()+sun_x*200000, me.sun.y()+sun_y*200000, me.sun.z()+sun_z*200000);#heat seeking missiles don't fly far, so setting it 200Km away is fine. + } + me.lock_on_sun = FALSE; + + me.flight(); + loadNode.remove(); + }, + + drag: func (mach) {#GCD + # Nikolai V. Chr.: Made the drag calc more in line with big missiles as opposed to small bullets. + # + # The old equations were based on curves for a conventional shell/bullet (no boat-tail), + # and derived from Davic Culps code in AIBallistic. + me.Cd = 0; + if (mach < 0.7) { + me.Cd = (0.0125 * mach + 0.20) * 5 * me.Cd_base; + } elsif (mach < 1.2 ) { + me.Cd = (0.3742 * math.pow(mach, 2) - 0.252 * mach + 0.0021 + 0.2 ) * 5 * me.Cd_base; + } else { + me.Cd = (0.2965 * math.pow(mach, -1.1506) + 0.2) * 5 * me.Cd_base; + } + + return me.Cd; + }, + + maxG: func (rho, max_g_sealevel) {#GCD + # Nikolai V. Chr.: A function to determine max G-force depending on air density. + # + # density for 0ft and 50kft: + #print("0:"~rho_sndspeed(0)[0]); = 0.0023769 + #print("50k:"~rho_sndspeed(50000)[0]); = 0.00036159 + # + # Fact: An aim-9j can do 22G at sealevel, 13G at 50Kft + # 13G = 22G * 0.5909 + # + # extra/inter-polation: + # f(x) = y1 + ((x - x1) / (x2 - x1)) * (y2 - y1) + # calculate its performance at current air density: + return max_g_sealevel+((rho-0.0023769)/(0.00036159-0.0023769))*(max_g_sealevel*0.5909-max_g_sealevel); + }, + + thrust: func () {#GCD + # Determine the thrust at this moment. + # + # If dropped, then ignited after fall time of what is the equivalent of 7ft. + # If the rocket is 2 stage, then ignite the second stage when 1st has burned out. + # + me.thrust_lbf = 0;# pounds force (lbf) + if (me.life_time > me.drop_time) { + me.thrust_lbf = me.force_lbf_1; + } + if (me.life_time > me.stage_1_duration + me.drop_time) { + me.thrust_lbf = me.force_lbf_2; + } + if (me.life_time > (me.drop_time + me.stage_1_duration + me.stage_2_duration)) { + me.thrust_lbf = 0; + } + if (me.thrust_lbf < 1) { + me.smoke_prop.setBoolValue(0); + } else { + me.smoke_prop.setBoolValue(1); + } + return me.thrust_lbf; + }, + + speedChange: func (thrust_lbf, rho, Cd) {#GCD + # Calculate speed change from last update. + # + # Acceleration = thrust/mass - drag/mass; + + me.acc = thrust_lbf / me.mass; + me.q = 0.5 * rho * me.old_speed_fps * me.old_speed_fps;# dynamic pressure + me.drag_acc = (me.Cd * me.q * me.eda) / me.mass; + + # get total new speed change (minus gravity) + return me.acc*me.dt - me.drag_acc*me.dt; + }, + + energyBleed: func (gForce, altitude) {#GCD + # Bleed of energy from pulling Gs. + # This is very inaccurate, but better than nothing. + # + # First we get the speedloss due to normal drag: + me.b300 = me.bleed32800at0g(); + me.b000 = me.bleed0at0g(); + # + # We then subtract the normal drag from the loss due to G and normal drag. + me.b325 = me.bleed32800at25g()-me.b300; + me.b025 = me.bleed0at25g()-me.b000; + me.b300 = 0; + me.b000 = 0; + # + # We now find what the speedloss will be at sealevel and 32800 ft. + me.speedLoss32800 = me.b300 + ((gForce-0)/(25-0))*(me.b325 - me.b300); + me.speedLoss0 = me.b000 + ((gForce-0)/(25-0))*(me.b025 - me.b000); + # + # We then inter/extra-polate that to the currect density-altitude. + me.speedLoss = me.speedLoss0 + ((altitude-0)/(32800-0))*(me.speedLoss32800-me.speedLoss0); + # + # For good measure the result is clamped to below zero. + me.speedLoss = me.clamp(me.speedLoss, -100000, 0); + me.energyBleedKt += me.speedLoss * FPS2KT; + return me.speedLoss; + }, + + bleed32800at0g: func () {#GCD + me.loss_fps = 0 + ((me.last_dt - 0)/(15 - 0))*(-330 - 0); + return me.loss_fps*M2FT; + }, + + bleed32800at25g: func () {#GCD + me.loss_fps = 0 + ((me.last_dt - 0)/(3.5 - 0))*(-240 - 0); + return me.loss_fps*M2FT; + }, + + bleed0at0g: func () {#GCD + me.loss_fps = 0 + ((me.last_dt - 0)/(22 - 0))*(-950 - 0); + return me.loss_fps*M2FT; + }, + + bleed0at25g: func () {#GCD + me.loss_fps = 0 + ((me.last_dt - 0)/(7 - 0))*(-750 - 0); + return me.loss_fps*M2FT; + }, + + flight: func {#GCD + if (me.Tgt != nil and me.Tgt.isValid() == FALSE) { + print(me.type~": Target went away, deleting missile."); + me.del(); + return; + } + me.dt = deltaSec.getValue();#TODO: time since last time nasal timers were called + if (me.dt == 0) { + #FG is likely paused + me.paused = 1; + settimer(func me.flight(), 0.00); + return; + } + #if just called from release() then dt is almost 0 (cannot be zero as we use it to divide with) + # It can also not be too small, then the missile will lag behind aircraft and seem to be fired from behind the aircraft. + #dt = dt/2; + me.elapsed = systime(); + if (me.paused == 1) { + # sim has been unpaused lets make sure dt becomes very small to let elapsed time catch up. + me.paused = 0; + me.elapsed_last = me.elapsed-0.02; + } + me.init_launch = 0; + if (me.elapsed_last != 0) { + #if (getprop("sim/speed-up") == 1) { + me.dt = (me.elapsed - me.elapsed_last)*speedUp.getValue(); + #} else { + # dt = getprop("sim/time/delta-sec")*getprop("sim/speed-up"); + #} + me.init_launch = 1; + if(me.dt <= 0) { + # to prevent pow floating point error in line:cdm = 0.2965 * math.pow(me.speed_m, -1.1506) + me.cd; + # could happen if the OS adjusts the clock backwards + me.dt = 0.00001; + } + } + me.elapsed_last = me.elapsed; + + + me.life_time += me.dt; + + + me.thrust_lbf = me.thrust();# pounds force (lbf) + + + # Get total old speed, thats what we will use in next loop. + me.old_speed_horz_fps = math.sqrt((me.speed_east_fps*me.speed_east_fps)+(me.speed_north_fps*me.speed_north_fps)); + me.old_speed_fps = math.sqrt((me.old_speed_horz_fps*me.old_speed_horz_fps)+(me.speed_down_fps*me.speed_down_fps)); + + me.setRadarProperties(me.old_speed_fps); + + + + # Get air density and speed of sound (fps): + me.rs = me.rho_sndspeed(me.altN.getValue() + me.density_alt_diff); + me.rho = me.rs[0]; + me.sound_fps = me.rs[1]; + + me.max_g_current = me.maxG(me.rho, me.max_g); + + me.speed_m = me.old_speed_fps / me.sound_fps; + + if (me.speed_m > me.maxMach) { + me.maxMach = me.speed_m; + } + + me.Cd = me.drag(me.speed_m); + + me.speed_change_fps = me.speedChange(me.thrust_lbf, me.rho, me.Cd); + + + if (me.last_dt != 0) { + me.speed_change_fps = me.speed_change_fps + me.energyBleed(me.g, me.altN.getValue() + me.density_alt_diff); + } + + # Get target position. + if (me.Tgt != nil) { + me.t_coord = me.Tgt.get_Coord(); + } + + ################### + #### Guidance.##### + ################### + if (me.Tgt != nil and me.free == FALSE and me.guidance != "unguided" + and (me.rail == FALSE or me.rail_passed == TRUE)) { + # + # Here we figure out how to guide, navigate and steer. + # + me.guide(); + me.limitG(); + + me.pitch += me.track_signal_e; + me.hdg += me.track_signal_h; + #printf("%.1f deg elevation command done, new pitch: %.1f deg", me.track_signal_e, pitch_deg); + #printf("%.1f deg bearing command done, new heading: %.1f", me.last_track_h, hdg_deg); + } else { + me.track_signal_e = 0; + me.track_signal_h = 0; + } + me.last_track_e = me.track_signal_e; + me.last_track_h = me.track_signal_h; + + me.new_speed_fps = me.speed_change_fps + me.old_speed_fps; + if (me.new_speed_fps < 0) { + # drag and bleed can theoretically make the speed less than 0, this will prevent that from happening. + me.new_speed_fps = 0.001; + } + + # Break speed change down total speed to North, East and Down components. + me.speed_down_fps = -math.sin(me.pitch * D2R) * me.new_speed_fps; + me.speed_horizontal_fps = math.cos(me.pitch * D2R) * me.new_speed_fps; + me.speed_north_fps = math.cos(me.hdg * D2R) * me.speed_horizontal_fps; + me.speed_east_fps = math.sin(me.hdg * D2R) * me.speed_horizontal_fps; + me.speed_down_fps += g_fps * me.dt; + + if (me.rail == TRUE and me.rail_passed == FALSE) { + # missile still on rail, lets calculate its speed relative to the wind coming in from the aircraft nose. + me.rail_speed_into_wind = me.rail_speed_into_wind + me.speed_change_fps; + } else { + # gravity acc makes the weapon pitch down + me.pitch = math.atan2(-me.speed_down_fps, me.speed_horizontal_fps ) * R2D; + } + + + + #printf("down_s=%.1f grav=%.1f", me.speed_down_fps*me.dt, g_fps * me.dt * !grav_bomb * me.dt); + + if (me.rail == TRUE and me.rail_passed == FALSE) { + me.u = noseAir.getValue();# airstream from nose + #var v = getprop("velocities/vBody-fps");# airstream from side + me.w = belowAir.getValue();# airstream from below + + if (me.rail_forward == TRUE) { + me.pitch = OurPitch.getValue(); + me.opposing_wind = me.u; + me.hdg = OurHdg.getValue(); + } else { + me.pitch = OurPitch.getValue() + me.rail_pitch_deg; + if (me.rail_pitch_deg == 90) { + me.opposing_wind = -me.w; + } else { + # no incoming airstream if not vertical tube + me.opposing_wind = 0; + } + me.hdg = me.Tgt.get_bearing(); + } + + me.speed_on_rail = me.clamp(me.rail_speed_into_wind - me.opposing_wind, 0, 1000000); + me.movement_on_rail = me.speed_on_rail * me.dt; + + me.rail_pos = me.rail_pos + me.movement_on_rail; + if (me.rail_forward == TRUE) { + me.x = me.x - (me.movement_on_rail * FT2M);# negative cause positive is rear in body coordinates + } elsif (me.rail_pitch_deg == 90) { + me.z = me.z + (me.movement_on_rail * FT2M);# positive cause positive is up in body coordinates + } else { + me.x = me.x - (me.movement_on_rail * FT2M); + } + } + + if (me.rail == FALSE or me.rail_passed == TRUE) { + # misssile not on rail, lets move it to next waypoint + me.alt_ft = me.alt_ft - (me.speed_down_fps * me.dt); + me.dist_h_m = me.speed_horizontal_fps * me.dt * FT2M; + me.coord.apply_course_distance(me.hdg, me.dist_h_m); + me.coord.set_alt(me.alt_ft * FT2M); + } else { + # missile on rail, lets move it on the rail + if (me.rail_pitch_deg == 90 or me.rail_forward == TRUE) { + me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); + } else { + # kind of a hack, but work + me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()+me.rail_pitch_deg); + } + me.alt_ft = me.coord.alt() * M2FT; + # find its speed, for used in calc old speed + me.speed_down_fps = -math.sin(me.pitch * D2R) * me.rail_speed_into_wind; + me.speed_horizontal_fps = math.cos(me.pitch * D2R) * me.rail_speed_into_wind; + me.speed_north_fps = math.cos(me.hdg * D2R) * me.speed_horizontal_fps; + me.speed_east_fps = math.sin(me.hdg * D2R) * me.speed_horizontal_fps; + } + if (me.alt_ft > me.maxAlt) { + me.maxAlt = me.alt_ft; + } + + # performance logging: + # + #var q = 0.5 * rho * me.old_speed_fps * me.old_speed_fps; + #setprop("logging/missile/dist-nm", me.ac_init.distance_to(me.coord)*M2NM); + #setprop("logging/missile/alt-m", me.alt_ft * FT2M); + #setprop("logging/missile/speed-m", me.speed_m*1000); + #setprop("logging/missile/drag-lbf", Cd * q * me.eda); + #setprop("logging/missile/thrust-lbf", thrust_lbf); + + me.setFirst(); + + me.latN.setDoubleValue(me.coord.lat()); + me.lonN.setDoubleValue(me.coord.lon()); + me.altN.setDoubleValue(me.alt_ft); + me.pitchN.setDoubleValue(me.pitch); + me.hdgN.setDoubleValue(me.hdg); + + # log missiles to unicsv for visualizing flightpath in Google Earth + # + #setprop("/logging/missile/latitude-deg", me.coord.lat()); + #setprop("/logging/missile/longitude-deg", me.coord.lon()); + #setprop("/logging/missile/altitude-ft", alt_ft); + #setprop("/logging/missile/t-latitude-deg", me.t_coord.lat()); + #setprop("/logging/missile/t-longitude-deg", me.t_coord.lon()); + #setprop("/logging/missile/t-altitude-ft", me.t_coord.alt()*M2FT); + + ############################## + #### Proximity detection.##### + ############################## + if (me.rail == FALSE or me.rail_passed == TRUE) { + if ( me.free == FALSE ) { + # check if the missile overloaded with G force. + me.g = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); + + if ( me.g > me.max_g_current and me.init_launch != 0) { + me.free = TRUE; + printf("%s: Missile attempted to pull too many G, it broke.", me.type); + } + } else { + me.g = 0; + } + + me.exploded = me.proximity_detection(); + +# +# Uncomment the following lines to check stats while flying: +# +#printf("Mach %02.2f , time %03.1f s , thrust %03.1f lbf , G-force %02.2f", me.speed_m, me.life_time, me.thrust_lbf, me.g); +#printf("Alt %05.1f ft , distance to target %02.1f NM", me.alt_ft, me.direct_dist_m*M2NM); + + if (me.exploded == TRUE) { + printf("%s max absolute %.2f Mach. Max relative %.2f Mach. Max alt %6d ft.", me.type, me.maxMach, me.maxMach-me.startMach, me.maxAlt); + printf(" Fired at %s from %.1f Mach, %5d ft at %3d NM distance. Flew %0.1f NM.", me.callsign, me.startMach, me.startAlt, me.startDist * M2NM, me.ac_init.direct_distance_to(me.coord)*M2NM); + # We exploded, and start the sound propagation towards the plane + me.sndSpeed = me.sound_fps; + me.sndDistance = 0; + me.elapsed_last = systime(); + if (me.explodeSound == TRUE) { + me.sndPropagate(); + } else { + settimer( func me.del(), 10); + } + return; + } + } else { + me.g = 0; + } + # record coords so we can give the latest nearest position for impact. + me.before_last_coord = geo.Coord.new(me.last_coord); + me.last_coord = geo.Coord.new(me.coord); + if (me.Tgt != nil) { + me.before_last_t_coord = geo.Coord.new(me.last_t_coord); + me.last_t_coord = geo.Coord.new(me.t_coord); + } + + if (me.rail_passed == FALSE and (me.rail == FALSE or me.rail_pos > me.rail_dist_m * M2FT)) { + me.rail_passed = TRUE; + #print("rail passed"); + } + + # consume fuel + if (me.life_time > (me.drop_time + me.stage_1_duration + me.stage_2_duration)) { + me.weight_current = me.weight_launch_lbm - me.weight_fuel_lbm; + } elsif (me.life_time > (me.drop_time + me.stage_1_duration)) { + me.weight_current = me.weight_current - me.fuel_per_sec_2 * me.dt; + } elsif (me.life_time > me.drop_time) { + me.weight_current = me.weight_current - me.fuel_per_sec_1 * me.dt; + } + #printf("weight %0.1f", me.weight_current); + me.mass = me.weight_current / slugs_to_lbm; + + me.last_dt = me.dt; + settimer(func me.flight(), update_loop_time, SIM_TIME); + }, + + setFirst: func() {#GCD + if (me.smoke_prop.getValue() == TRUE) { + if (me.first == TRUE or first_in_air == FALSE) { + # report position over MP for MP animation of smoke trail. + me.first = TRUE; + first_in_air = TRUE; + # using helicopter properties for reporting over MP. To mount this code on a helicopter, you best change that. + setprop("rotors/main/blade[0]/flap-deg", me.coord.lat()); + setprop("rotors/main/blade[1]/flap-deg", me.coord.lon()); + setprop("rotors/main/blade[2]/flap-deg", me.coord.alt()); + } + } elsif (me.first == TRUE and me.life_time > me.drop_time + me.stage_1_duration + me.stage_2_duration) { + # this weapon was reporting its position over MP, but now its fuel has used up. So allow for another to do that. + me.resetFirst(); + } + }, + + resetFirst: func() {#GCD + first_in_air = FALSE; + me.first = FALSE; + setprop("rotors/main/blade[0]/flap-deg", 0); + setprop("rotors/main/blade[1]/flap-deg", 0); + setprop("rotors/main/blade[2]/flap-deg", 0); + }, + + limitG: func () {#GCD + # + # Here will be set the max angle of pitch and the max angle of heading to avoid G overload + # + me.myG = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); + if(me.max_g_current < me.myG) + { + me.MyCoef = me.max_G_Rotation(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt, me.max_g_current); + me.track_signal_e = me.track_signal_e * me.MyCoef; + me.track_signal_h = me.track_signal_h * me.MyCoef; + #print(sprintf("G1 %.2f", myG)); + me.myG = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); + #print(sprintf("G2 %.2f", myG)~sprintf(" - Coeff %.2f", MyCoef)); + if (me.limitGs == FALSE) { + printf("%s: Missile pulling almost max G: %.1f G", me.type, me.myG); + } + } + if (me.limitGs == TRUE and me.myG > me.max_g_current/2) { + # Save the high performance manouving for later + me.track_signal_e = me.track_signal_e /2; + } + }, + + setRadarProperties: func (new_speed_fps) {#GCD + # + # Set missile radar properties for use in selection view, radar and HUD. + # + me.self = geo.aircraft_position(); + me.ai.getNode("radar/bearing-deg", 1).setDoubleValue(me.self.course_to(me.coord)); + me.ai.getNode("radar/elevation-deg", 1).setDoubleValue(me.getPitch(me.self, me.coord)); + me.ai.getNode("velocities/true-airspeed-kt",1).setDoubleValue(new_speed_fps * FPS2KT); + }, + + rear_aspect: func () {#GCD + # + # If is heat-seeking rear-aspect-only missile, check if it has good view on engine(s) and can keep lock. + # + me.offset = me.aspect(); + + if (me.offset < 45) { + # clear view of engine heat, keep the lock + me.rearAspect = 1; + } else { + # the greater angle away from clear engine view the greater chance of losing lock. + me.offset_away = me.offset - 45; + me.probability = me.offset_away/135; + me.probability = me.probability*2.5;# The higher the factor, the less chance to keep lock. + me.rearAspect = rand() > me.probability; + } + + #print ("RB-24J deviation from full rear-aspect: "~sprintf("%01.1f", offset)~" deg, keep IR lock on engine: "~rearAspect); + + return me.rearAspect;# 1: keep lock, 0: lose lock + }, + + aspect: func () {#GCD + me.rearAspect = 0; + + #var t_dist_m = me.coord.distance_to(me.t_coord); + #var alt_delta_m = me.coord.alt() - me.t_coord.alt(); + me.elev_deg = me.getPitch(me.t_coord, me.coord);#math.atan2( alt_delta_m, t_dist_m ) * R2D; elevation to missile from target aircraft + me.elevation_offset = me.elev_deg - me.Tgt.get_Pitch(); + + me.courseA = me.t_coord.course_to(me.coord); + me.heading_offset = me.courseA - me.Tgt.get_heading(); + + # + while (me.heading_offset < -180) { + me.heading_offset += 360; + } + while (me.heading_offset > 180) { + me.heading_offset -= 360; + } + while (me.elevation_offset < -180) { + me.elevation_offset += 360; + } + while (me.elevation_offset > 180) { + me.elevation_offset -= 360; + } + me.elevation_offset = math.abs(me.elevation_offset); + me.heading_offset = 180 - math.abs(me.heading_offset); + + me.offset = math.max(me.elevation_offset, me.heading_offset); + + return me.offset; + }, + + guide: func() {#GCD + # + # navigation and guidance + # + me.raw_steer_signal_elev = 0; + me.raw_steer_signal_head = 0; + + me.guiding = TRUE; + + # Calculate current target elevation and azimut deviation. + me.t_alt = me.t_coord.alt()*M2FT; + #var t_alt_delta_m = (me.t_alt - me.alt_ft) * FT2M; + me.dist_curr = me.coord.distance_to(me.t_coord); + me.dist_curr_direct = me.coord.direct_distance_to(me.t_coord); + me.t_elev_deg = me.getPitch(me.coord, me.t_coord);#math.atan2( t_alt_delta_m, me.dist_curr ) * R2D; + me.t_course = me.coord.course_to(me.t_coord); + me.curr_deviation_e = me.t_elev_deg - me.pitch; + me.curr_deviation_h = me.t_course - me.hdg; + + #var (t_course, me.dist_curr) = courseAndDistance(me.coord, me.t_coord); + #me.dist_curr = me.dist_curr * NM2M; + + #printf("Altitude above launch platform = %.1f ft", M2FT * (me.coord.alt()-me.ac.alt())); + + while(me.curr_deviation_h < -180) { + me.curr_deviation_h += 360; + } + while(me.curr_deviation_h > 180) { + me.curr_deviation_h -= 360; + } + + me.checkForFlare(); + + me.checkForSun(); + + me.checkForGuidance(); + + me.canSeekerKeepUp(); + + me.cruiseAndLoft(); + + me.APN();# Proportional navigation + + me.track_signal_e = me.raw_steer_signal_elev * !me.free * me.guiding; + me.track_signal_h = me.raw_steer_signal_head * !me.free * me.guiding; + + #printf("%.1f deg elevate command desired", me.track_signal_e); + #printf("%.1f deg heading command desired", me.track_signal_h); + + # record some variables for next loop: + me.dist_last = me.dist_curr; + me.dist_direct_last = me.dist_curr_direct; + me.last_t_course = me.t_course; + me.last_t_elev_deg = me.t_elev_deg; + me.last_cruise_or_loft = me.cruise_or_loft; + }, + + checkForFlare: func () {#GCD + # + # Check for being fooled by flare. + # + if (me.guidance == "heat") { + # + # TODO: Use Richards Emissary for this. + # + me.flareNode = me.Tgt.getFlareNode(); + if (me.flareNode != nil) { + me.flareNumber = me.flareNode.getValue(); + if (me.flareNumber != nil and me.flareNumber != 0) { + if (me.flareNumber != me.lastFlare) { + # target has released a new flare, lets check if it fools us + me.lastFlare = me.flareNumber; + me.aspectDeg = me.aspect() / 180; + me.fooled = rand() < (0.15 + 0.15 * me.aspectDeg); + # 15% chance to be fooled, extra up till 15% chance added if front aspect + if (me.fooled == TRUE) { + # fooled by the flare + print(me.type~": Missile fooled by flare"); + me.free = TRUE; + } else { + print(me.type~": Missile ignored flare"); + } + } + } + } + } + }, + + checkForSun: func () { + if (me.guidance == "heat" and me.sun_power > 0.6) { + # test for heat seeker locked on to sun + me.sun_dev_e = me.getPitch(me.coord, me.sun) - me.pitch; + me.sun_dev_h = me.coord.course_to(me.sun) - me.hdg; + while(me.sun_dev_h < -180) { + me.sun_dev_h += 360; + } + while(me.sun_dev_h > 180) { + me.sun_dev_h -= 360; + } + # now we check if the sun is behind the target, which is the direction the gyro seeker is pointed at: + me.sun_dev = math.sqrt((me.sun_dev_e-me.curr_deviation_e)*(me.sun_dev_e-me.curr_deviation_e)+(me.sun_dev_h-me.curr_deviation_h)*(me.sun_dev_h-me.curr_deviation_h)); + if (me.sun_dev < me.sun_lock) { + print(me.type~": Locked onto sun, lost target. "); + me.lock_on_sun = TRUE; + me.free = TRUE; + } + } + }, + + checkForGuidance: func () {#GCD + if(me.speed_m < me.min_speed_for_guiding) { + # it doesn't guide at lower speeds + me.guiding = FALSE; + if (me.tooLowSpeed == FALSE) { + print(me.type~": Not guiding (too low speed)"); + } + me.tooLowSpeed = TRUE; + } elsif ((me.guidance == "semi-radar" or me.guidance =="laser") and me.is_painted(me.Tgt) == FALSE) { + # if its semi-radar guided and the target is no longer painted + me.guiding = FALSE; + if (me.semiLostLock == FALSE) { + print(me.type~": Not guiding (lost radar reflection, trying to reaquire)"); + } + me.semiLostLock = TRUE; + } elsif ((math.abs(me.curr_deviation_e) > me.max_seeker_dev or math.abs(me.curr_deviation_h) > me.max_seeker_dev) and me.guidance != "gps") { + # target is not in missile seeker view anymore + if (me.curr_deviation_e > me.max_seeker_dev) { + me.viewLost = "Target is above seeker view."; + } elsif (me.curr_deviation_e < (-1 * me.max_seeker_dev)) { + me.viewLost = "Target is below seeker view. "~(me.dist_curr*M2NM)~" NM and "~((me.coord.alt()-me.t_coord.alt())*M2FT)~" ft diff."; + } elsif (me.curr_deviation_h > me.max_seeker_dev) { + me.viewLost = "Target is right of seeker view."; + } else { + me.viewLost = "Target is left of seeker view."; + } + print(me.type~": Target is not in missile seeker view anymore. "~me.viewLost); + me.free = TRUE; + } elsif (me.all_aspect == FALSE and me.rear_aspect() == FALSE) { + me.guiding = FALSE; + if (me.heatLostLock == FALSE) { + print(me.type~": Missile lost heat lock, attempting to reaquire.."); + } + me.heatLostLock = TRUE; + } elsif (me.life_time < me.drop_time) { + me.guiding = FALSE; + } elsif (me.semiLostLock == TRUE) { + print(me.type~": Reaquired radar reflection."); + me.semiLostLock = FALSE; + } elsif (me.heatLostLock == TRUE) { + print(me.type~": Regained heat lock."); + me.heatLostLock = FALSE; + } elsif (me.tooLowSpeed == TRUE) { + print(me.type~": Gained speed and started guiding."); + me.tooLowSpeed = FALSE; + } + }, + + canSeekerKeepUp: func () {#GCD + if (me.last_deviation_e != nil and (me.guidance == "heat" or me.guidance == "vision")) { + # calculate if the seeker can keep up with the angular change of the target + # + # missile own movement is subtracted from this change due to seeker being on gyroscope + # + me.dve_dist = me.curr_deviation_e - me.last_deviation_e + me.last_track_e; + me.dvh_dist = me.curr_deviation_h - me.last_deviation_h + me.last_track_h; + me.deviation_per_sec = math.sqrt(me.dve_dist*me.dve_dist+me.dvh_dist*me.dvh_dist)/me.dt; + + if (me.deviation_per_sec > me.angular_speed) { + #print(sprintf("last-elev=%.1f", me.last_deviation_e)~sprintf(" last-elev-adj=%.1f", me.last_track_e)); + #print(sprintf("last-head=%.1f", me.last_deviation_h)~sprintf(" last-head-adj=%.1f", me.last_track_h)); + # lost lock due to angular speed limit + printf("%s: %.1f deg/s too fast angular change for seeker head.", me.type, me.deviation_per_sec); + me.free = TRUE; + } + } + + me.last_deviation_e = me.curr_deviation_e; + me.last_deviation_h = me.curr_deviation_h; + }, + + cruiseAndLoft: func () {#GCD + # + # cruise, loft, cruise-missile + # + me.loft_angle = 15;# notice Shinobi used 26.5651 degs, but Raider1 found a source saying 10-20 degs. + me.cruise_or_loft = FALSE; + me.time_before_snap_up = me.drop_time * 3; + me.limitGs = FALSE; + + if(me.loft_alt != 0 and me.snapUp == FALSE) { + # this is for Air to ground/sea cruise-missile (SCALP, Sea-Eagle, Taurus, Tomahawk, RB-15...) + + # detect terrain for use in terrain following + me.nextGroundElevationMem[1] -= 1; + me.geoPlus2 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*5); + me.geoPlus3 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*10); + me.geoPlus4 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*20); + me.e1 = geo.elevation(me.coord.lat(), me.coord.lon());# This is done, to make sure is does not decline before it has passed obstacle. + me.e2 = geo.elevation(me.geoPlus2.lat(), me.geoPlus2.lon());# This is the main one. + me.e3 = geo.elevation(me.geoPlus3.lat(), me.geoPlus3.lon());# This is an extra, just in case there is an high cliff it needs longer time to climb. + me.e4 = geo.elevation(me.geoPlus4.lat(), me.geoPlus4.lon()); + if (me.e1 != nil) { + me.nextGroundElevation = me.e1; + } else { + print(me.type~": nil terrain, blame terrasync! Cruise-missile keeping altitude."); + } + if (me.e2 != nil and me.e2 > me.nextGroundElevation) { + me.nextGroundElevation = me.e2; + if (me.e2 > me.nextGroundElevationMem[0] or me.nextGroundElevationMem[1] < 0) { + me.nextGroundElevationMem[0] = me.e2; + me.nextGroundElevationMem[1] = 5; + } + } + if (me.nextGroundElevationMem[0] > me.nextGroundElevation) { + me.nextGroundElevation = me.nextGroundElevationMem[0]; + } + if (me.e3 != nil and me.e3 > me.nextGroundElevation) { + me.nextGroundElevation = me.e3; + } + if (me.e4 != nil and me.e4 > me.nextGroundElevation) { + me.nextGroundElevation = me.e4; + } + + me.Daground = 0;# zero for sealevel in case target is ship. Don't shoot A/S missiles over terrain. :) + if(me.Tgt.get_type() == SURFACE or me.follow == TRUE) { + me.Daground = me.nextGroundElevation * M2FT; + } + me.loft_alt_curr = me.loft_alt; + if (me.dist_curr < me.old_speed_fps * 4 * FT2M and me.dist_curr > me.old_speed_fps * 2.5 * FT2M) { + # the missile lofts a bit at the end to avoid APN to slam it into ground before target is reached. + # end here is between 2.5-4 seconds + me.loft_alt_curr = me.loft_alt*2; + } + if (me.dist_curr > me.old_speed_fps * 2.5 * FT2M) {# need to give the missile time to do final navigation + # it's 1 or 2 seconds for this kinds of missiles... + me.t_alt_delta_ft = (me.loft_alt_curr + me.Daground - me.alt_ft); + #print("var t_alt_delta_m : "~t_alt_delta_m); + if(me.loft_alt_curr + me.Daground > me.alt_ft) { + # 200 is for a very short reaction to terrain + #print("Moving up"); + me.raw_steer_signal_elev = -me.pitch + math.atan2(me.t_alt_delta_ft, me.old_speed_fps * me.dt * 5) * R2D; + } else { + # that means a dive angle of 22.5° (a bit less + # coz me.alt is in feet) (I let this alt in feet on purpose (more this figure is low, more the future pitch is high) + #print("Moving down"); + me.slope = me.clamp(me.t_alt_delta_ft / 300, -5, 0);# the lower the desired alt is, the steeper the slope. + me.raw_steer_signal_elev = -me.pitch + me.clamp(math.atan2(me.t_alt_delta_ft, me.old_speed_fps * me.dt * 5) * R2D, me.slope, 0); + } + me.cruise_or_loft = TRUE; + } elsif (me.dist_curr > 500) { + # we put 9 feets up the target to avoid ground at the + # last minute... + #print("less than 1000 m to target"); + #me.raw_steer_signal_elev = -me.pitch + math.atan2(t_alt_delta_m + 100, me.dist_curr) * R2D; + #me.cruise_or_loft = 1; + } else { + #print("less than 500 m to target"); + } + if (me.cruise_or_loft == TRUE) { + #print(" pitch "~me.pitch~" + me.raw_steer_signal_elev "~me.raw_steer_signal_elev); + } + } elsif (me.rail == TRUE and me.rail_forward == FALSE and me.rotate_token == FALSE) { + # tube launched missile turns towards target + + me.raw_steer_signal_elev = -me.pitch + me.t_elev_deg; + #print("Turning, desire "~me.t_elev_deg~" degs pitch."); + me.cruise_or_loft = TRUE; + me.limitGs = TRUE; + if (math.abs(me.curr_deviation_e) < 7.5) { + me.rotate_token = TRUE; + #print("Is last turn, snap-up/PN takes it from here..") + } + } elsif (me.snapUp == TRUE and me.t_elev_deg > -25 and me.dist_curr * M2NM > 10 + and me.t_elev_deg < me.loft_angle #and me.t_elev_deg > -7.5 + and me.dive_token == FALSE) { + # lofting: due to target is more than 10 miles out and we havent reached + # our desired cruising alt, and the elevation to target is less than lofting angle. + # The -7.5 limit, is so the seeker don't lose track of target when lofting. + if (me.life_time < me.time_before_snap_up and me.coord.alt() * M2FT < me.loft_alt) { + #print("preparing for lofting"); + me.cruise_or_loft = TRUE; + } elsif (me.coord.alt() * M2FT < me.loft_alt) { + me.raw_steer_signal_elev = -me.pitch + me.loft_angle; + me.limitGs = TRUE; + #print(sprintf("Lofting %.1f degs, dev is %.1f", me.loft_angle, me.raw_steer_signal_elev)); + } else { + me.dive_token = TRUE; + #print("Stopped lofting"); + } + me.cruise_or_loft = TRUE; + } elsif (me.snapUp == TRUE and me.coord.alt() > me.t_coord.alt() and me.last_cruise_or_loft == TRUE + and me.t_elev_deg > -25 and me.dist_curr * M2NM > 10) { + # cruising: keeping altitude since target is below and more than -45 degs down + + me.ratio = (g_fps * me.dt)/me.old_speed_fps; + me.attitude = 0; + + if (me.ratio < 1 and me.ratio > -1) { + me.attitude = math.asin(me.ratio)*R2D; + } + + me.raw_steer_signal_elev = -me.pitch + me.attitude; + #print("Cruising, desire "~me.attitude~" degs pitch."); + me.cruise_or_loft = TRUE; + me.limitGs = TRUE; + me.dive_token = TRUE; + } elsif (me.last_cruise_or_loft == TRUE and math.abs(me.curr_deviation_e) > 7.5 and me.life_time > me.time_before_snap_up) { + # after cruising, point the missile in the general direction of the target, before APN starts guiding. + #print("Rotating toward target"); + me.raw_steer_signal_elev = me.curr_deviation_e; + me.cruise_or_loft = TRUE; + #me.limitGs = TRUE; + } + }, + + APN: func () {#GCD + # + # augmented proportional navigation + # + if (me.guiding == TRUE and me.free == FALSE and me.dist_last != nil and me.last_dt != 0) { + # augmented proportional navigation for heading # + ################################################# + + if (me.navigation == "direct") { + me.raw_steer_signal_head = me.curr_deviation_h; + if (me.cruise_or_loft == FALSE) { + me.raw_steer_signal_elev = me.curr_deviation_e; + } + return; + } elsif (me.navigation == "PN") { + me.apn = 0; + } else { + me.apn = 1; + } + + me.horz_closing_rate_fps = me.clamp(((me.dist_last - me.dist_curr)*M2FT)/me.dt, 0.1, 1000000);#clamped due to cruise missiles that can fly slower than target. + #printf("Horz closing rate: %5d ft/sec", me.horz_closing_rate_fps); + me.proportionality_constant = 3; + + me.c_dv = me.t_course-me.last_t_course; + while(me.c_dv < -180) { + me.c_dv += 360; + } + while(me.c_dv > 180) { + me.c_dv -= 360; + } + + me.line_of_sight_rate_rps = (D2R*me.c_dv)/me.dt; + + #printf("LOS rate: %.4f rad/s", line_of_sight_rate_rps); + + #if (me.before_last_t_coord != nil) { + # var t_heading = me.before_last_t_coord.course_to(me.t_coord); + # var t_dist = me.before_last_t_coord.distance_to(me.t_coord); + # var t_dist_dir = me.before_last_t_coord.direct_distance_to(me.t_coord); + # var t_climb = me.t_coord.alt() - me.before_last_t_coord.alt(); + # var t_horz_speed = (t_dist*M2FT)/(me.dt+me.last_dt); + # var t_speed = (t_dist_dir*M2FT)/(me.dt+me.last_dt); + #} else { + # var t_heading = me.last_t_coord.course_to(me.t_coord); + # var t_dist = me.last_t_coord.distance_to(me.t_coord); + # var t_dist_dir = me.last_t_coord.direct_distance_to(me.t_coord); + # var t_climb = me.t_coord.alt() - me.last_t_coord.alt(); + # var t_horz_speed = (t_dist*M2FT)/me.dt; + # var t_speed = (t_dist_dir*M2FT)/me.dt; + #} + + #var t_pitch = math.atan2(t_climb,t_dist)*R2D; + + + # calculate target acc as normal to LOS line: + me.t_heading = me.Tgt.get_heading(); + me.t_pitch = me.Tgt.get_Pitch(); + me.t_speed = me.Tgt.get_Speed()*KT2FPS;#true airspeed + + #if (me.last_t_coord.direct_distance_to(me.t_coord) != 0) { + # # taking sideslip and AoA into consideration: + # me.t_heading = me.last_t_coord.course_to(me.t_coord); + # me.t_climb = me.t_coord.alt() - me.last_t_coord.alt(); + # me.t_dist = me.last_t_coord.distance_to(me.t_coord); + # me.t_pitch = math.atan2(me.t_climb, me.t_dist) * R2D; + #} elsif (me.Tgt.get_Speed() > 25) { + # target position was not updated since last loop. + # to avoid confusing the navigation, we just fly + # straight. + #print("not updated"); + # return; + #} + + + + me.t_horz_speed = math.abs(math.cos(me.t_pitch*D2R)*me.t_speed); + me.t_LOS_norm_head = me.t_course + 90; + me.t_LOS_norm_speed = math.cos((me.t_LOS_norm_head - me.t_heading)*D2R)*me.t_horz_speed; + + if (me.last_t_norm_speed == nil) { + me.last_t_norm_speed = me.t_LOS_norm_speed; + } + + me.t_LOS_norm_acc = (me.t_LOS_norm_speed - me.last_t_norm_speed)/me.dt; + + me.last_t_norm_speed = me.t_LOS_norm_speed; + + # acceleration perpendicular to instantaneous line of sight in feet/sec^2 + me.acc_sideways_ftps2 = me.proportionality_constant*me.line_of_sight_rate_rps*me.horz_closing_rate_fps+me.apn*me.proportionality_constant*me.t_LOS_norm_acc/2; + #printf("horz acc = %.1f + %.1f", proportionality_constant*line_of_sight_rate_rps*horz_closing_rate_fps, proportionality_constant*t_LOS_norm_acc/2); + + # now translate that sideways acc to an angle: + me.velocity_vector_length_fps = me.old_speed_horz_fps; + me.commanded_sideways_vector_length_fps = me.acc_sideways_ftps2*me.dt; + me.raw_steer_signal_head = math.atan2(me.commanded_sideways_vector_length_fps, me.velocity_vector_length_fps)*R2D; + + #printf("Proportional lead: %0.1f deg horz", -(me.curr_deviation_h-me.raw_steer_signal_head)); + + #print(sprintf("LOS-rate=%.2f rad/s - closing-rate=%.1f ft/s",line_of_sight_rate_rps,horz_closing_rate_fps)); + #print(sprintf("commanded-perpendicular-acceleration=%.1f ft/s^2", acc_sideways_ftps2)); + #print(sprintf("horz leading by %.1f deg, commanding %.1f deg", me.curr_deviation_h, me.raw_steer_signal_head)); + + if (me.cruise_or_loft == FALSE) {# and me.last_cruise_or_loft == FALSE + # augmented proportional navigation for elevation # + ################################################### + #print(me.navigation~" in fully control"); + me.vert_closing_rate_fps = me.clamp(((me.dist_direct_last - me.dist_curr_direct)*M2FT)/me.dt, 0.1, 1000000); + #printf("Vert closing rate: %5d ft/sec", me.vert_closing_rate_fps); + me.line_of_sight_rate_up_rps = (D2R*(me.t_elev_deg-me.last_t_elev_deg))/me.dt; + + # calculate target acc as normal to LOS line: (up acc is positive) + me.t_approach_bearing = me.t_course + 180; + + + # used to do this with trigonometry, but vector math is simpler to understand: (they give same result though) + me.t_LOS_elev_norm_speed = me.scalarProj(me.t_heading,me.t_pitch,me.t_speed,me.t_approach_bearing,me.t_elev_deg*-1 +90); + + if (me.last_t_elev_norm_speed == nil) { + me.last_t_elev_norm_speed = me.t_LOS_elev_norm_speed; + } + + me.t_LOS_elev_norm_acc = (me.t_LOS_elev_norm_speed - me.last_t_elev_norm_speed)/me.dt; + me.last_t_elev_norm_speed = me.t_LOS_elev_norm_speed; + + me.acc_upwards_ftps2 = me.proportionality_constant*me.line_of_sight_rate_up_rps*me.vert_closing_rate_fps+me.apn*me.proportionality_constant*me.t_LOS_elev_norm_acc/2; + me.velocity_vector_length_fps = me.old_speed_fps; + me.commanded_upwards_vector_length_fps = me.acc_upwards_ftps2*me.dt; + + me.raw_steer_signal_elev = math.atan2(me.commanded_upwards_vector_length_fps, me.velocity_vector_length_fps)*R2D; + + # now compensate for the predicted gravity drop of attitude: + me.attitudePN = math.atan2(-(me.speed_down_fps+g_fps * me.dt), me.speed_horizontal_fps ) * R2D; + me.gravComp = me.pitch - me.attitudePN; + #printf("Gravity compensation %0.2f degs", me.gravComp); + me.raw_steer_signal_elev += me.gravComp; + + #printf("Proportional lead: %0.1f deg elev", -(me.curr_deviation_e-me.raw_steer_signal_elev)); + } + } + }, + + scalarProj: func (head, pitch, magn, projHead, projPitch) {#GCD + head = head*D2R; + pitch = pitch*D2R; + projHead = projHead*D2R; + projPitch = projPitch*D2R; + + # Convert the 2 polar vectors to cartesian + me.ax = magn * math.cos(pitch) * math.cos(-head); + me.ay = magn * math.cos(pitch) * math.sin(-head); + me.az = magn * math.sin(pitch); + + me.bx = 1 * math.cos(projPitch) * math.cos(-projHead); + me.by = 1 * math.cos(projPitch) * math.sin(-projHead); + me.bz = 1 * math.sin(projPitch); + + # the dot product is the scalar projection. + me.result = (me.ax * me.bx + me.ay*me.by+me.az*me.bz)/1; + return me.result; + }, + + map: func (value, leftMin, leftMax, rightMin, rightMax) { + # Figure out how 'wide' each range is + var leftSpan = leftMax - leftMin; + var rightSpan = rightMax - rightMin; + + # Convert the left range into a 0-1 range (float) + var valueScaled = (value - leftMin) / leftSpan; + + # Convert the 0-1 range into a value in the right range. + return rightMin + (valueScaled * rightSpan); + }, + + proximity_detection: func {#GCD + + ####Ground interaction + me.ground = geo.elevation(me.coord.lat(), me.coord.lon()); + if(me.ground != nil) + { + if(me.ground > me.coord.alt()) { + me.event = "exploded"; + if(me.life_time < me.arming_time) { + me.event = "landed disarmed"; + } + if ((me.Tgt != nil and me.direct_dist_m != nil) or me.Tgt == nil) { + me.explode("Hit terrain.", me.event); + return TRUE; + } + } + } + + if (me.Tgt != nil) { + me.cur_dir_dist_m = me.coord.direct_distance_to(me.t_coord); + # Get current direct distance. + if ( me.direct_dist_m != nil and me.life_time > me.arming_time) { + #print("distance to target_m = "~cur_dir_dist_m~" prev_distance to target_m = "~me.direct_dist_m); + if ( me.cur_dir_dist_m > me.direct_dist_m and me.cur_dir_dist_m < 250) { + #print("passed target"); + # Distance to target increase, trigger explosion. + me.explode("Passed target."); + return TRUE; + } + if (me.life_time > me.selfdestruct_time) { + me.explode("Selfdestructed."); + return TRUE; + } + } + me.direct_dist_m = me.cur_dir_dist_m; + } + return FALSE; + }, + + explode: func (reason, event = "exploded") { + # Get missile relative position to the target at last frame. + #var t_bearing_deg = me.last_t_coord.course_to(me.last_coord); + #var t_delta_alt_m = me.last_coord.alt() - me.last_t_coord.alt(); + #var new_t_alt_m = me.t_coord.alt() + t_delta_alt_m; + #var t_dist_m = me.direct_dist_m; + + if (me.lock_on_sun == TRUE) { + reason = "Locked onto sun."; + } elsif (me.fooled == TRUE) { + reason = "Fooled by flare."; + } + + var explosion_coord = me.last_coord; + if (me.Tgt != nil) { + var min_distance = me.direct_dist_m; + + #print("min1 "~min_distance); + #print("last_t to t : "~me.last_t_coord.direct_distance_to(me.t_coord)); + #print("last to current: "~me.last_coord.direct_distance_to(me.coord)); + for (var i = 0.00; i <= 1; i += 0.025) { + var t_coord = me.interpolate(me.last_t_coord, me.t_coord, i); + var coord = me.interpolate(me.last_coord, me.coord, i); + var dist = coord.direct_distance_to(t_coord); + if (dist < min_distance) { + min_distance = dist; + explosion_coord = coord; + } + } + #print("min2 "~min_distance); + if (me.before_last_coord != nil and me.before_last_t_coord != nil) { + for (var i = 0.00; i <= 1; i += 0.025) { + var t_coord = me.interpolate(me.before_last_t_coord, me.last_t_coord, i); + var coord = me.interpolate(me.before_last_coord, me.last_coord, i); + var dist = coord.direct_distance_to(t_coord); + if (dist < min_distance) { + min_distance = dist; + explosion_coord = coord; + } + } + } + } + me.coord = explosion_coord; + #print("min3 "~min_distance); + + # Create impact coords from this previous relative position applied to target current coord. + #me.t_coord.apply_course_distance(t_bearing_deg, t_dist_m); + #me.t_coord.set_alt(new_t_alt_m); + var wh_mass = event == "exploded"?me.weight_whead_lbm:0;#will report 0 mass if did not have time to arm + #print("FOX2: me.direct_dist_m = ", me.direct_dist_m, " time ",getprop("sim/time/elapsed-sec")); + impact_report(me.coord, wh_mass, "munition", me.type); # pos, alt, mass_slug,(speed_mps) + + if (me.Tgt != nil) { + var phrase = sprintf( me.type~" "~event~": %01.1f", min_distance) ~ " meters from: " ~ me.callsign; + print(phrase~" Reason: "~reason~sprintf(" time %.1f", me.life_time)); + if (min_distance < me.reportDist) { + me.sendMessage(phrase); + } else { + me.sendMessage(me.type~" missed "~me.callsign~": "~reason); + } + } + + me.ai.getNode("valid", 1).setBoolValue(0); + if (event == "exploded") { + me.animate_explosion(); + me.explodeSound = TRUE; + } else { + me.animate_dud(); + me.explodeSound = FALSE; + } + me.Tgt = nil; + }, + + sendMessage: func (str) {#GCD + if (getprop("payload/armament/msg")) { + defeatSpamFilter(str); + } else { + setprop("/sim/messages/atc", str); + } + }, + + interpolate: func (start, end, fraction) {#GCD + me.xx = (start.x()*(1-fraction)+end.x()*fraction); + me.yy = (start.y()*(1-fraction)+end.y()*fraction); + me.zz = (start.z()*(1-fraction)+end.z()*fraction); + + me.cc = geo.Coord.new(); + me.cc.set_xyz(me.xx,me.yy,me.zz); + + return me.cc; + }, + + getPitch: func (coord1, coord2) {#GCD + #pitch from c1 to c2 + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord2.alt()); + me.d12 = coord1.direct_distance_to(coord2); + me.d32 = me.coord3.direct_distance_to(coord2); + if (me.d12 > 0.01) { + me.altDi = coord1.alt()-me.coord3.alt(); + me.yyy = R2D * math.acos((math.pow(me.d12, 2)+math.pow(me.altDi,2)-math.pow(me.d32, 2))/(2 * me.d12 * me.altDi)); + me.pitchC = -1* (90 - me.yyy); + return me.pitchC; + } else{ + # arccos wont like if the coord are the same + return 0; + } + + }, + + # aircraft searching for lock + search: func {#GCD + if ( me.status == MISSILE_FLYING ) { + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(FALSE); + return; + } elsif ( me.status == MISSILE_STANDBY ) { + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(FALSE); + me.trackWeak = 1; + return; + } elsif ( me.status > MISSILE_SEARCH) { + # Locked or fired. + return; + } elsif (me.deleted == TRUE) { + return; + } + #print("search"); + # search. + if (1==1 or contact != me.Tgt) { + #print("search2"); + if (contact != nil and contact.isValid() == TRUE and + ( (contact.get_type() == SURFACE and me.target_gnd == TRUE) + or (contact.get_type() == AIR and me.target_air == TRUE) + or (contact.get_type() == MARINE and me.target_sea == TRUE))) { + #print("search3"); + me.tagt = contact; # In the radar range and horizontal field. + me.rng = me.tagt.get_range(); + me.total_elev = deviation_normdeg(OurPitch.getValue(), me.tagt.getElevation()); # deg. + me.total_horiz = deviation_normdeg(OurHdg.getValue(), me.tagt.get_bearing()); # deg. + # Check if in range and in the (square shaped here) seeker FOV. + me.abs_total_elev = math.abs(me.total_elev); + me.abs_dev_deg = math.abs(me.total_horiz); + if (((me.guidance != "semi-radar" and me.guidance != "laser") or me.is_painted(me.tagt) == TRUE) + and me.rng < me.max_detect_rng and me.abs_total_elev < me.fcs_fov and me.abs_dev_deg < me.fcs_fov ) { + #print("search4"); + me.status = MISSILE_LOCK; + me.SwSoundOnOff.setBoolValue(TRUE); + me.SwSoundVol.setDoubleValue(me.vol_track_weak); + me.trackWeak = 1; + me.Tgt = me.tagt; + + me.callsign = me.Tgt.get_Callsign(); + + me.time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); + me.update_track_time = me.time; + + settimer(func me.update_lock(), 0.1); + return; + } else { + me.Tgt = nil; + } + } else { + me.Tgt = nil; + } + } + me.SwSoundVol.setDoubleValue(me.vol_search); + me.SwSoundOnOff.setBoolValue(TRUE); + me.trackWeak = 1; + settimer(func me.search(), 0.1); + }, + + update_lock: func() {#GCD + # + # Missile locked on target + # + if ( me.Tgt == nil or me.status == MISSILE_FLYING) { + return TRUE; + } + if (me.status == MISSILE_SEARCH) { + # Status = searching. + #print("search commanded"); + me.return_to_search(); + return TRUE; + } elsif ( me.status == MISSILE_STANDBY ) { + # Status = stand-by. + me.reset_seeker(); + me.SwSoundOnOff.setBoolValue(FALSE); + me.SwSoundVol.setDoubleValue(0); + me.trackWeak = 1; + return TRUE; + } elsif (!me.Tgt.isValid()) { + # Lost of lock due to target disapearing: + # return to search mode. + #print("invalid"); + me.return_to_search(); + return TRUE; + } elsif (me.deleted == TRUE) { + return; + } + #print("lock"); + # Time interval since lock time or last track loop. + + if (me.status == MISSILE_LOCK) { + # Status = locked. Get target position relative to our aircraft. + me.curr_deviation_e = - deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); + me.curr_deviation_h = - deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); + } + + me.time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); + + # Compute HUD reticle position. + if ( use_fg_default_hud == TRUE and me.status == MISSILE_LOCK ) { + var h_rad = (90 - me.curr_deviation_h) * D2R; + var e_rad = (90 - me.curr_deviation_e) * D2R; + var devs = develev_to_devroll(h_rad, e_rad); + var combined_dev_deg = devs[0]; + var combined_dev_length = devs[1]; + var clamped = devs[2]; + if ( clamped ) { SW_reticle_Blinker.blink();} + else { SW_reticle_Blinker.cont();} + HudReticleDeg.setDoubleValue(combined_dev_deg); + HudReticleDev.setDoubleValue(combined_dev_length); + } + if (me.status != MISSILE_STANDBY ) { + me.in_view = me.check_t_in_fov(); + if (me.in_view == FALSE) { + #print("out of view"); + me.return_to_search(); + return TRUE; + } + # We are not launched yet: update_track() loops by itself at 10 Hz. + me.dist = geo.aircraft_position().direct_distance_to(me.Tgt.get_Coord()); + if (me.time - me.update_track_time > 1 and me.dist != nil and me.dist > (me.min_dist * NM2M)) { + # after 1 second we get solid track if target is further than minimum distance. + me.SwSoundOnOff.setBoolValue(TRUE); + me.SwSoundVol.setDoubleValue(me.vol_track); + me.trackWeak = 0; + } else { + me.SwSoundOnOff.setBoolValue(TRUE); + me.SwSoundVol.setDoubleValue(me.vol_track_weak); + me.trackWeak = 1; + } + if (contact == nil or (contact.getUnique() != nil and me.Tgt.getUnique() != nil and contact.getUnique() != me.Tgt.getUnique())) { + #print("oops "); + me.return_to_search(); + return TRUE; + } + settimer(func me.update_lock(), 0.1); + } + return TRUE; + }, + + return_to_search: func {#GCD + me.status = MISSILE_SEARCH; + me.Tgt = nil; + me.SwSoundOnOff.setBoolValue(TRUE); + me.SwSoundVol.setDoubleValue(me.vol_search); + me.trackWeak = 1; + me.reset_seeker(); + settimer(func me.search(), 0.1); + }, + + check_t_in_fov: func {#GCD + me.total_elev = deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); # deg. + me.total_horiz = deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); # deg. + # Check if in range and in the (square shaped here) seeker FOV. + me.abs_total_elev = math.abs(me.total_elev); + me.abs_dev_deg = math.abs(me.total_horiz); + if (me.abs_total_elev < me.fcs_fov and me.abs_dev_deg < me.fcs_fov and me.Tgt.get_range() < me.max_detect_rng) { + return TRUE; + } + # Target out of FOV or range while still not launched, return to search loop. + return FALSE; + }, + + is_painted: func (target) {#GCD + if(target != nil and target.isPainted() != nil and target.isPainted() == TRUE) { + return TRUE; + } + return FALSE; + }, + + reset_seeker: func {#GCD + settimer(func { HudReticleDeg.setDoubleValue(0) }, 2); + interpolate(HudReticleDev, 0, 2); + }, + + clamp_min_max: func (v, mm) { + if ( v < -mm ) { + v = -mm; + } elsif ( v > mm ) { + v = mm; + } + return(v); + }, + + clamp: func(v, min, max) { v < min ? min : v > max ? max : v }, + + animation_flags_props: func { + # Create animation flags properties. + var path_base = "payload/armament/"~me.type_lc~"/flags/"; + + var msl_path = path_base~"msl-id-" ~ me.ID; + me.msl_prop = props.globals.initNode( msl_path, TRUE, "BOOL", TRUE); + me.msl_prop.setBoolValue(TRUE);# this is cause it might already exist, and so need to force value + + var smoke_path = path_base~"smoke-id-" ~ me.ID; + me.smoke_prop = props.globals.initNode( smoke_path, FALSE, "BOOL", TRUE); + + var explode_path = path_base~"explode-id-" ~ me.ID; + me.explode_prop = props.globals.initNode( explode_path, FALSE, "BOOL", TRUE); + + var explode_smoke_path = path_base~"explode-smoke-id-" ~ me.ID; + me.explode_smoke_prop = props.globals.initNode( explode_smoke_path, FALSE, "BOOL", TRUE); + + var explode_sound_path = "payload/armament/flags/explode-sound-on-" ~ me.ID;; + me.explode_sound_prop = props.globals.initNode( explode_sound_path, FALSE, "BOOL", TRUE); + + var explode_sound_vol_path = "payload/armament/flags/explode-sound-vol-" ~ me.ID;; + me.explode_sound_vol_prop = props.globals.initNode( explode_sound_vol_path, 0, "DOUBLE", TRUE); + }, + + animate_explosion: func {#GCD + # + # a last position update to where the explosion happened: + # + me.latN.setDoubleValue(me.coord.lat()); + me.lonN.setDoubleValue(me.coord.lon()); + me.altN.setDoubleValue(me.coord.alt()*M2FT); + me.pitchN.setDoubleValue(0);# this will make explosions from cluster bombs (like M90) align to ground 'sorta'. + me.msl_prop.setBoolValue(FALSE); + me.smoke_prop.setBoolValue(FALSE); + me.explode_prop.setBoolValue(TRUE); + settimer( func me.explode_prop.setBoolValue(FALSE), 0.5 ); + settimer( func me.explode_smoke_prop.setBoolValue(TRUE), 0.5 ); + settimer( func me.explode_smoke_prop.setBoolValue(FALSE), 3 ); + }, + + animate_dud: func {#GCD + # + # a last position update to where the impact happened: + # + me.latN.setDoubleValue(me.coord.lat()); + me.lonN.setDoubleValue(me.coord.lon()); + me.altN.setDoubleValue(me.coord.alt()*M2FT); + #me.pitchN.setDoubleValue(0); uncomment this to let it lie flat on ground, instead of sticking its nose in it. + me.smoke_prop.setBoolValue(FALSE); + }, + + sndPropagate: func { + var dt = deltaSec.getValue(); + if (dt == 0) { + #FG is likely paused + settimer(func me.sndPropagate(), 0.01); + return; + } + #dt = update_loop_time; + var elapsed = systime(); + if (me.elapsed_last != 0) { + dt = (elapsed - me.elapsed_last) * speedUp.getValue(); + } + me.elapsed_last = elapsed; + + me.ac = geo.aircraft_position(); + var distance = me.coord.direct_distance_to(me.ac); + + me.sndDistance = me.sndDistance + (me.sndSpeed * dt) * FT2M; + if(me.sndDistance > distance) { + var volume = math.pow(2.71828,(-.00025*(distance-1000))); + #print("explosion heard "~distance~"m vol:"~volume); + me.explode_sound_vol_prop.setDoubleValue(volume); + me.explode_sound_prop.setBoolValue(1); + settimer( func me.explode_sound_prop.setBoolValue(0), 3); + settimer( func me.del(), 4); + return; + } elsif (me.sndDistance > 5000) { + settimer(func { me.del(); }, 4 ); + } else { + settimer(func me.sndPropagate(), 0.05); + return; + } + }, + + steering_speed_G: func(steering_e_deg, steering_h_deg, s_fps, dt) {#GCD + # Get G number from steering (e, h) in deg, speed in ft/s. + me.steer_deg = math.sqrt((steering_e_deg*steering_e_deg) + (steering_h_deg*steering_h_deg)); + + # next speed vector + me.vector_next_x = math.cos(me.steer_deg*D2R)*s_fps; + me.vector_next_y = math.sin(me.steer_deg*D2R)*s_fps; + + # present speed vector + me.vector_now_x = s_fps; + me.vector_now_y = 0; + + # subtract the vectors from each other + me.dv = math.sqrt((me.vector_now_x - me.vector_next_x)*(me.vector_now_x - me.vector_next_x)+(me.vector_now_y - me.vector_next_y)*(me.vector_now_y - me.vector_next_y)); + + # calculate g-force + # dv/dt=a + me.g = (me.dv/dt) / g_fps; + + return me.g; + }, + + max_G_Rotation: func(steering_e_deg, steering_h_deg, s_fps, dt, gMax) {#GCD + me.guess = 1; + me.coef = 1; + me.lastgoodguess = 1; + + for(var i=1;i<25;i+=1){ + me.coef = me.coef/2; + me.new_g = me.steering_speed_G(steering_e_deg*me.guess, steering_h_deg*me.guess, s_fps, dt); + if (me.new_g < gMax) { + me.lastgoodguess = me.guess; + me.guess = me.guess + me.coef; + } else { + me.guess = me.guess - me.coef; + } + } + return me.lastgoodguess; + }, + + nextGeoloc: func(lat, lon, heading, speed, dt, alt=100) { + # lng & lat & heading, in degree, speed in fps + # this function should send back the futures lng lat + me.distanceN = speed * dt * FT2M; # should be a distance in meters + #print("distance ", distance); + # much simpler than trigo + me.NextGeo = geo.Coord.new().set_latlon(lat, lon, alt); + me.NextGeo.apply_course_distance(heading, me.distanceN); + return me.NextGeo; + }, + + rho_sndspeed: func(altitude) { + # Calculate density of air: rho + # at altitude (ft), using standard atmosphere, + # standard temperature T and pressure p. + + me.T = 0; + me.p = 0; + if (altitude < 36152) { + # curve fits for the troposphere + me.T = 59 - 0.00356 * altitude; + me.p = 2116 * math.pow( ((me.T + 459.7) / 518.6) , 5.256); + } elsif ( 36152 < altitude and altitude < 82345 ) { + # lower stratosphere + me.T = -70; + me.p = 473.1 * math.pow( const_e , 1.73 - (0.000048 * altitude) ); + } else { + # upper stratosphere + me.T = -205.05 + (0.00164 * altitude); + me.p = 51.97 * math.pow( ((me.T + 459.7) / 389.98) , -11.388); + } + + me.rho = me.p / (1718 * (me.T + 459.7)); + + # calculate the speed of sound at altitude + # a = sqrt ( g * R * (T + 459.7)) + # where: + # snd_speed in feet/s, + # g = specific heat ratio, which is usually equal to 1.4 + # R = specific gas constant, which equals 1716 ft-lb/slug/R + + me.snd_speed = math.sqrt( 1.4 * 1716 * (me.T + 459.7)); + return [me.rho, me.snd_speed]; + + }, + + active: {}, + flying: {}, +}; + + +# Create impact report. + +#altitde-agl-ft DOUBLE +#impact +# elevation-m DOUBLE +# heading-deg DOUBLE +# latitude-deg DOUBLE +# longitude-deg DOUBLE +# pitch-deg DOUBLE +# roll-deg DOUBLE +# speed-mps DOUBLE +# type STRING +# valid "true" BOOL +var impact_report = func(pos, mass, string, name) { + # Find the next index for "ai/models/model-impact" and create property node. + var n = props.globals.getNode("ai/models", 1); + for (var i = 0; 1; i += 1) + if (n.getChild(string, i, 0) == nil) + break; + var impact = n.getChild(string, i, 1); + + impact.getNode("impact/elevation-m", 1).setDoubleValue(pos.alt()); + impact.getNode("impact/latitude-deg", 1).setDoubleValue(pos.lat()); + impact.getNode("impact/longitude-deg", 1).setDoubleValue(pos.lon()); + impact.getNode("warhead-lbm", 1).setDoubleValue(mass); + #impact.getNode("speed-mps", 1).setValue(speed_mps); + impact.getNode("valid", 1).setBoolValue(1); + impact.getNode("impact/type", 1).setValue("terrain"); + impact.getNode("name", 1).setValue(name); + + var impact_str = "/ai/models/" ~ string ~ "[" ~ i ~ "]"; + setprop("ai/models/model-impact", impact_str); +} + +# HUD clamped target blinker +SW_reticle_Blinker = aircraft.light.new("payload/armament/hud/hud-sw-reticle-switch", [0.1, 0.1]); +setprop("payload/armament/hud/hud-sw-reticle-switch/enabled", 1); + + + + + +var eye_hud_m = 0.6;#pilot: -3.30 hud: -3.9 +var hud_radius_m = 0.100; + +#was in hud +var develev_to_devroll = func(dev_rad, elev_rad) { + var clamped = 0; + # Deviation length on the HUD (at level flight), + # 0.6686m = distance eye <-> virtual HUD screen. + var h_dev = eye_hud_m / ( math.sin(dev_rad) / math.cos(dev_rad) ); + var v_dev = eye_hud_m / ( math.sin(elev_rad) / math.cos(elev_rad) ); + # Angle between HUD center/top <-> HUD center/symbol position. + # -90° left, 0° up, 90° right, +/- 180° down. + var dev_deg = math.atan2( h_dev, v_dev ) * R2D; + # Correction with own a/c roll. + var combined_dev_deg = dev_deg - OurRoll.getValue(); + # Lenght HUD center <-> symbol pos on the HUD: + var combined_dev_length = math.sqrt((h_dev*h_dev)+(v_dev*v_dev)); + # clamp and squeeze the top of the display area so the symbol follow the egg shaped HUD limits. + var abs_combined_dev_deg = math.abs( combined_dev_deg ); + var clamp = hud_radius_m; + if ( abs_combined_dev_deg >= 0 and abs_combined_dev_deg < 90 ) { + var coef = ( 90 - abs_combined_dev_deg ) * 0.00075; + if ( coef > 0.050 ) { coef = 0.050 } + clamp -= coef; + } + if ( combined_dev_length > clamp ) { + combined_dev_length = clamp; + clamped = 1; + } + var v = [combined_dev_deg, combined_dev_length, clamped]; + return(v); +} + +#was in radar +var deviation_normdeg = func(our_heading, target_bearing) { + var dev_norm = our_heading - target_bearing; + while (dev_norm < -180) dev_norm += 360; + while (dev_norm > 180) dev_norm -= 360; + return(dev_norm); +} + +# +# this code make sure messages don't trigger the MP spam filter: + +var spams = 0; +var spamList = []; + +var defeatSpamFilter = func (str) { + spams += 1; + if (spams == 15) { + spams = 1; + } + str = str~":"; + for (var i = 1; i <= spams; i+=1) { + str = str~"."; + } + var newList = [str]; + for (var i = 0; i < size(spamList); i += 1) { + append(newList, spamList[i]); + } + spamList = newList; +} + +var spamLoop = func { + var spam = pop(spamList); + if (spam != nil) { + setprop("/sim/multiplay/chat", spam); + } + settimer(spamLoop, 1.20); +} + +spamLoop(); \ No newline at end of file diff --git a/BUK-M2/Nasal/radar-logic.nas b/BUK-M2/Nasal/radar-logic.nas new file mode 100644 index 0000000..dab71a1 --- /dev/null +++ b/BUK-M2/Nasal/radar-logic.nas @@ -0,0 +1,403 @@ +var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +var encode3bits = func(first, second, third) { + var integer = first; + integer = integer + 2 * second; + integer = integer + 4 * third; + return integer; +} + +var FALSE = 0; +var TRUE = 1; + +var AIR = 0; +var MARINE = 1; +var SURFACE = 2; +var ORDNANCE = 3; + +var Contact = { + # For now only used in guided missiles, to make it compatible with Mirage 2000-5. + new: func(c, class) { + var obj = { parents : [Contact]}; +#debug.benchmark("radar process1", func { + obj.rdrProp = c.getNode("radar"); + obj.oriProp = c.getNode("orientation"); + obj.velProp = c.getNode("velocities"); + obj.posProp = c.getNode("position"); + obj.heading = obj.oriProp.getNode("true-heading-deg"); +#}); +#debug.benchmark("radar process2", func { + obj.alt = obj.posProp.getNode("altitude-ft"); + obj.lat = obj.posProp.getNode("latitude-deg"); + obj.lon = obj.posProp.getNode("longitude-deg"); +#}); +#debug.benchmark("radar process3", func { + #As it is a geo.Coord object, we have to update lat/lon/alt ->and alt is in meters + obj.coord = geo.Coord.new(); + obj.coord.set_latlon(obj.lat.getValue(), obj.lon.getValue(), obj.alt.getValue() * FT2M); +#}); +#debug.benchmark("radar process4", func { + obj.pitch = obj.oriProp.getNode("pitch-deg"); + obj.speed = obj.velProp.getNode("true-airspeed-kt"); + obj.vSpeed = obj.velProp.getNode("vertical-speed-fps"); + obj.callsign = c.getNode("callsign", 1); + obj.shorter = c.getNode("model-shorter"); + obj.orig_callsign = obj.callsign.getValue(); + obj.name = c.getNode("name"); + obj.sign = c.getNode("sign",1); + obj.valid = c.getNode("valid"); + obj.painted = c.getNode("painted"); + obj.unique = c.getNode("unique"); + obj.validTree = 0; +#}); +#debug.benchmark("radar process5", func { + #obj.transponderID = c.getNode("instrumentation/transponder/transmitted-id"); +#}); +#debug.benchmark("radar process6", func { + obj.acType = c.getNode("sim/model/ac-type"); + obj.type = c.getName(); + obj.index = c.getIndex(); + obj.string = "ai/models/" ~ obj.type ~ "[" ~ obj.index ~ "]"; + obj.shortString = obj.type ~ "[" ~ obj.index ~ "]"; +#}); +#debug.benchmark("radar process7", func { + obj.range = obj.rdrProp.getNode("range-nm"); + obj.bearing = obj.rdrProp.getNode("bearing-deg"); + obj.elevation = obj.rdrProp.getNode("elevation-deg"); +#}); + obj.deviation = nil; + + obj.node = c; + obj.class = class; + + obj.polar = [0,0]; + obj.cartesian = [0,0]; + + return obj; + }, + + isValid: func () { + var valid = me.valid.getValue(); + if (valid == nil) { + valid = FALSE; + } + if (me.callsign.getValue() != me.orig_callsign) { + valid = FALSE; + } + return valid; + }, + + isPainted: func () { + if (me.painted == nil) { + me.painted = me.node.getNode("painted"); + } + if (me.painted == nil) { + return nil; + } + var p = me.painted.getValue(); + return p; + }, + + getUnique: func () { + if (me.unique == nil) { + me.unique = me.node.getNode("unique"); + } + if (me.unique == nil) { + return nil; + } + var u = me.unique.getValue(); + return u; + }, + + getElevation: func() { + var e = 0; + e = me.elevation.getValue(); + if(e == nil or e == 0) { + # AI/MP has no radar properties + var self = geo.aircraft_position(); + me.get_Coord(); + var angleInv = clamp(self.distance_to(me.coord)/self.direct_distance_to(me.coord), -1, 1); + e = (self.alt()>me.coord.alt()?-1:1)*math.acos(angleInv)*R2D; + } + return e; + }, + + getNode: func () { + return me.node; + }, + + getFlareNode: func () { + return me.node.getNode("sim/multiplay/generic/string[10]"); + }, + + setPolar: func(dist, angle) { + me.polar = [dist,angle]; + }, + + setCartesian: func(x, y) { + me.cartesian = [x,y]; + }, + + remove: func(){ + if(me.validTree != 0){ + me.validTree.setValue(0); + } + }, + + get_Coord: func(){ + me.coord.set_latlon(me.lat.getValue(), me.lon.getValue(), me.alt.getValue() * FT2M); + var TgTCoord = geo.Coord.new(me.coord); + return TgTCoord; + }, + + get_Callsign: func(){ + var n = me.callsign.getValue(); + if(n != "" and n != nil) { + return n; + } + if (me.name == nil) { + me.name = me.getNode().getNode("name"); + } + if (me.name == nil) { + n = ""; + } else { + n = me.name.getValue(); + } + if(n != "" and n != nil) { + return n; + } + n = me.sign.getValue(); + if(n != "" and n != nil) { + return n; + } + return "UFO"; + }, + + get_model: func(){ + var n = ""; + if (me.shorter == nil) { + me.shorter = me.node.getNode("model-shorter"); + } + if (me.shorter != nil) { + n = me.shorter.getValue(); + } + if(n != "" and n != nil) { + return n; + } + n = me.sign.getValue(); + if(n != "" and n != nil) { + return n; + } + if (me.name == nil) { + me.name = me.getNode().getNode("name"); + } + if (me.name == nil) { + n = ""; + } else { + n = me.name.getValue(); + } + if(n != "" and n != nil) { + return n; + } + return me.get_Callsign(); + }, + + get_Speed: func(){ + # return true airspeed + var n = me.speed.getValue(); + return n; + }, + + get_Longitude: func(){ + var n = me.lon.getValue(); + return n; + }, + + get_Latitude: func(){ + var n = me.lat.getValue(); + return n; + }, + + get_Pitch: func(){ + var n = me.pitch.getValue(); + return n; + }, + + get_heading : func(){ + var n = me.heading.getValue(); + if(n == nil) + { + n = 0; + } + return n; + }, + + get_bearing: func(){ + var n = 0; + n = me.bearing.getValue(); + if(n == nil or n == 0) { + # AI/MP has no radar properties + n = me.get_bearing_from_Coord(geo.aircraft_position()); + } + return n; + }, + + get_bearing_from_Coord: func(MyAircraftCoord){ + me.get_Coord(); + var myBearing = 0; + if(me.coord.is_defined()) { + myBearing = MyAircraftCoord.course_to(me.coord); + } + return myBearing; + }, + + get_reciprocal_bearing: func(){ + return geo.normdeg(me.get_bearing() + 180); + }, + + get_deviation: func(true_heading_ref, coord){ + me.deviation = - deviation_normdeg(true_heading_ref, me.get_bearing_from_Coord(coord)); + return me.deviation; + }, + + get_altitude: func(){ + #Return Alt in feet + return me.alt.getValue(); + }, + + get_Elevation_from_Coord: func(MyAircraftCoord) { + me.get_Coord(); + var value = (me.coord.alt() - MyAircraftCoord.alt()) / me.coord.direct_distance_to(MyAircraftCoord); + if (math.abs(value) > 1) { + # warning this else will fail if logged in as observer and see aircraft on other side of globe + return 0; + } + var myPitch = math.asin(value) * R2D; + return myPitch; + }, + + get_total_elevation_from_Coord: func(own_pitch, MyAircraftCoord){ + var myTotalElevation = - deviation_normdeg(own_pitch, me.get_Elevation_from_Coord(MyAircraftCoord)); + return myTotalElevation; + }, + + get_total_elevation: func(own_pitch) { + me.deviation = - deviation_normdeg(own_pitch, me.getElevation()); + return me.deviation; + }, + + get_range: func() { + var r = 0; + if(me.range == nil or me.range.getValue() == nil or me.range.getValue() == 0) { + # AI/MP has no radar properties + me.get_Coord(); + r = me.coord.direct_distance_to(geo.aircraft_position()) * M2NM; + } else { + r = me.range.getValue(); + } + return r; + }, + + get_range_from_Coord: func(MyAircraftCoord) { + var myCoord = me.get_Coord(); + var myDistance = 0; + if(myCoord.is_defined()) { + myDistance = MyAircraftCoord.direct_distance_to(myCoord) * M2NM; + } + return myDistance; + }, + + get_type: func () { + return me.class; + }, + + get_cartesian: func() { + return me.cartesian; + }, + + get_polar: func() { + return me.polar; + }, +}; + +var isNotBehindTerrain = func( mp ) { + +########### + var pos = mp.getNode("position"); + var alt = pos.getNode("altitude-ft").getValue(); + var lat = pos.getNode("latitude-deg").getValue(); + var lon = pos.getNode("longitude-deg").getValue(); + if(alt == nil or lat == nil or lon == nil) { + return isVisible = 0; + } + var aircraftPos = geo.Coord.new().set_latlon(lat, lon, alt*0.3048); +################# + + + var isVisible = 0; + var MyCoord = geo.aircraft_position(); + + # Because there is no terrain on earth that can be between these 2 + if(MyCoord.alt() < 8900 and aircraftPos.alt() < 8900) + { + # Temporary variable + # A (our plane) coord in meters + var a = MyCoord.x(); + var b = MyCoord.y(); + var c = MyCoord.z(); + # B (target) coord in meters + var d = aircraftPos.x(); + var e = aircraftPos.y(); + var f = aircraftPos.z(); + var difa = d - a; + var difb = e - b; + var difc = f - c; + # direct Distance in meters + var myDistance = math.sqrt( math.pow((d-a),2) + math.pow((e-b),2) + math.pow((f-c),2)); #calculating distance ourselves to avoid another call to geo.nas (read: speed, probably). + var Aprime = geo.Coord.new(); + + # Here is to limit FPS drop on very long distance + var L = 500; + if(myDistance > 50000) + { + L = myDistance / 15; + } + var step = L; + var maxLoops = int(myDistance / L); + + isVisible = 1; + # This loop will make travel a point between us and the target and check if there is terrain + for(var i = 1 ; i <= maxLoops ; i += 1) + { + #calculate intermediate step + #basically dividing the line into maxLoops number of steps, and checking at each step + #to ascii-art explain it: + # |us|----------|step 1|-----------|step 2|--------|step 3|----------|them| + #there will be as many steps as there is i + #every step will be equidistant + + #also, if i == 0 then the first step will be our plane + + var x = ((difa/(maxLoops+1))*i)+a; + var y = ((difb/(maxLoops+1))*i)+b; + var z = ((difc/(maxLoops+1))*i)+c; + #print("i:" ~ i ~ "|x,y,z | " ~ x ~ "," ~ y ~ "," ~ z); + Aprime.set_xyz(x,y,z); + var AprimeTerrainAlt = geo.elevation(Aprime.lat(), Aprime.lon()); + if(AprimeTerrainAlt == nil) + { + AprimeTerrainAlt = 0; + } + + if(AprimeTerrainAlt > Aprime.alt()) + { + #print("behind terrain"); + return 0; + } + } + } + else + { + isVisible = 1; + } + return isVisible; +} \ No newline at end of file diff --git a/Frigate/Nasal/damage.nas b/Frigate/Nasal/damage.nas index 63262c9..be4c468 100644 --- a/Frigate/Nasal/damage.nas +++ b/Frigate/Nasal/damage.nas @@ -7,14 +7,15 @@ var hp = 200; var hp_max = hp; var cannon_types = { - " M70 rocket hit": 40.00, #Viggen rocket - " M55 cannon shell hit": 3.50, #Viggen gun - " KCA cannon shell hit": 7.00, #Viggen gunpod - " Gun Splash On ": 3.0, #Mirage - " M61A1 shell hit": 2.50, #f-14/15 - " GAU-8/A hit": 10.00, #a-10 - " BK27 cannon hit": 3.50, #typhoon - " GSh-30 hit": 3.00, #mig-21 + " M70 rocket hit": 0.30, + " M55 cannon shell hit": 0.20, + " KCA cannon shell hit": 0.20, + " Gun Splash On ": 0.30, + " M61A1 shell hit": 0.20, + " GAU-8/A hit": 0.30, + " BK27 cannon hit": 0.20, + " GSh-30 hit": 0.20, + " 7.62 hit": 2.50, #UH-1 " 50 BMG hit": 3.00, #p-47 }; @@ -60,7 +61,8 @@ var warhead_lbs = { "R-60": 6.60, "R-27R1": 85.98, "R-27T1": 85.98, - "FAB-500": 564.00, + "FAB-500": 564.00, + "M71R": 200.00, }; var incoming_listener = func { diff --git a/depot/Nasal/damage.nas b/depot/Nasal/damage.nas index 38bd05d..f08c516 100644 --- a/depot/Nasal/damage.nas +++ b/depot/Nasal/damage.nas @@ -21,14 +21,15 @@ var FALSE = 0; var cannon_types = { - " M70 rocket hit": 40.00, #Viggen rocket - " M55 cannon shell hit": 3.50, #Viggen gun - " KCA cannon shell hit": 7.00, #Viggen gunpod - " Gun Splash On ": 3.0, #Mirage - " M61A1 shell hit": 2.50, #f-14/15 - " GAU-8/A hit": 10.00, #a-10 - " BK27 cannon hit": 3.50, #typhoon - " GSh-30 hit": 3.00, #mig-21 + " M70 rocket hit": 0.30, + " M55 cannon shell hit": 0.20, + " KCA cannon shell hit": 0.20, + " Gun Splash On ": 0.30, + " M61A1 shell hit": 0.20, + " GAU-8/A hit": 0.30, + " BK27 cannon hit": 0.20, + " GSh-30 hit": 0.20, + " 7.62 hit": 2.50, #UH-1 " 50 BMG hit": 3.00, #p-47 }; @@ -74,7 +75,8 @@ var warhead_lbs = { "R-60": 6.60, "R-27R1": 85.98, "R-27T1": 85.98, - "FAB-500": 564.00, + "FAB-500": 564.00, + "M71R": 200.00, }; var incoming_listener = func { diff --git a/groundtarget/Nasal/damage.nas b/groundtarget/Nasal/damage.nas index 42003bd..980896e 100644 --- a/groundtarget/Nasal/damage.nas +++ b/groundtarget/Nasal/damage.nas @@ -7,14 +7,15 @@ var hp = 2500; var hp_max = hp; var cannon_types = { - " M70 rocket hit": 40.00, #Viggen rocket - " M55 cannon shell hit": 3.50, #Viggen gun - " KCA cannon shell hit": 7.00, #Viggen gunpod - " Gun Splash On ": 3.0, #Mirage - " M61A1 shell hit": 2.50, #f-14/15 - " GAU-8/A hit": 10.00, #a-10 - " BK27 cannon hit": 3.50, #typhoon - " GSh-30 hit": 3.00, #mig-21 + " M70 rocket hit": 0.30, + " M55 cannon shell hit": 0.20, + " KCA cannon shell hit": 0.20, + " Gun Splash On ": 0.30, + " M61A1 shell hit": 0.20, + " GAU-8/A hit": 0.30, + " BK27 cannon hit": 0.20, + " GSh-30 hit": 0.20, + " 7.62 hit": 2.50, #UH-1 " 50 BMG hit": 3.00, #p-47 }; @@ -60,7 +61,8 @@ var warhead_lbs = { "R-60": 6.60, "R-27R1": 85.98, "R-27T1": 85.98, - "FAB-500": 564.00, + "FAB-500": 564.00, + "M71R": 200.00, }; var incoming_listener = func { diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-0.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-0.xml new file mode 100644 index 0000000..d08efe6 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-0.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-0 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-0 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-1.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-1.xml new file mode 100644 index 0000000..9438b1c --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-1.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-1 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-1 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-2.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-2.xml new file mode 100644 index 0000000..6832fd5 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-2.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-2 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-2 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-3.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-3.xml new file mode 100644 index 0000000..4cd81e8 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-3.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-3 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-3 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-4.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-4.xml new file mode 100644 index 0000000..c6f31bf --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-4.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-4 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-4 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-5.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-5.xml index 9561d08..7c607f3 100644 --- a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-5.xml +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-5.xml @@ -8,7 +8,7 @@ false - payload/armament/test/flags/explode-id-5 + payload/armament/kn-06/flags/explode-id-5 world @@ -86,26 +86,6 @@ - - RB-99 missile 1 explosion - explosion-bounds.xml - payload/armament/test/flags/explode-id-5 - 0.0 - false - 0.00 - -1 - 0.00003 - 0.00136354 - 0 - true - sim/ai/aircraft/collision/bullet - true - sim/ai/aircraft/impact/bomb - false - 0 - 0.5 - - explosion-smoke smoke.png @@ -114,7 +94,7 @@ false - payload/armament/test/flags/explode-smoke-id-5 + payload/armament/kn-06/flags/explode-smoke-id-5 world diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-6.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-6.xml new file mode 100644 index 0000000..99e6c65 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-6.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-6 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-6 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/explosion-7.xml b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-7.xml new file mode 100644 index 0000000..821925d --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/explosion-7.xml @@ -0,0 +1,175 @@ + + + + explosion + smoke.png + + true + false + + + payload/armament/kn-06/flags/explode-id-7 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 80 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 30 + 10 + + + + billboard + + + + + 1.0 + 1.0 + 0.4 + 1 + + + 2 + + + + + + 0.8 + 0.5 + 0.2 + 0.1 + + + 55 + + + + + 0.5 + + + 5 + 0.5 + + + + air + true + true + + + + + + explosion-smoke + smoke.png + + false + false + + + payload/armament/kn-06/flags/explode-smoke-id-7 + + + world + + + point + + + + -180 + 180 + -180 + 180 + + 500 + 60 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 50 + 10 + + + + billboard + + + + + 0.3 + 0.3 + 0.25 + 0.5 + + + 10 + + + + + + 0.4 + 0.4 + 0.4 + 0.04 + + + 60 + + + + + 40 + + + 5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-0.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-0.xml new file mode 100644 index 0000000..23fde8e --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-0.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-0 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-0.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-0.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-1.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-1.xml new file mode 100644 index 0000000..86735dc --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-1.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-1 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-1.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-1.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-2.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-2.xml new file mode 100644 index 0000000..b5a715d --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-2.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-2 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-2.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-2.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-3.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-3.xml new file mode 100644 index 0000000..c44328c --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-3.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-3 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-3.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-3.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-4.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-4.xml new file mode 100644 index 0000000..846fbd0 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-4.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-4 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-4.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-4.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-5.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-5.xml new file mode 100644 index 0000000..aac2845 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-5.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-5 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-5.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-5.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-6.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-6.xml new file mode 100644 index 0000000..6f4e80c --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-6.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-6 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-6.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-6.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-7.xml b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-7.xml new file mode 100644 index 0000000..a495487 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/kn-06-7.xml @@ -0,0 +1,42 @@ + + + + + + + aim-120 + aim-120.ac + + + select + aim-120 + + payload/armament/kn-06/flags/msl-id-7 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-7.xml + + 1.83937 + 0 + 0 + + + + + explosion + Aircraft/missile-frigate/Models/Armament/Weapons/KN-06/explosion-7.xml + + 0 + 0 + 0 + + + + + aim-120 + false + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-0.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-0.xml new file mode 100644 index 0000000..feb59eb --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-0.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-0 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-0 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-1.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-1.xml new file mode 100644 index 0000000..ebb87d4 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-1.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-1 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-1 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-2.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-2.xml new file mode 100644 index 0000000..6ae883d --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-2.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-2 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-2 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-3.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-3.xml new file mode 100644 index 0000000..38e2579 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-3.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-3 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-3 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-4.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-4.xml new file mode 100644 index 0000000..4c82c52 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-4.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-4 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-4 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-5.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-5.xml index 6aff45a..f52a322 100644 --- a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-5.xml +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-5.xml @@ -8,7 +8,7 @@ false - payload/armament/test/flags/smoke-id-5 + payload/armament/kn-06/flags/smoke-id-5 world @@ -44,7 +44,7 @@ - 50 + 100 0 @@ -100,7 +100,7 @@ false - payload/armament/test/flags/smoke-id-5 + payload/armament/kn-06/flags/smoke-id-5 world diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-6.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-6.xml new file mode 100644 index 0000000..55e73c8 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-6.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-6 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-6 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-7.xml b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-7.xml new file mode 100644 index 0000000..c4c8cb7 --- /dev/null +++ b/missile-frigate/Models/Armament/Weapons/KN-06/motor-smoke-7.xml @@ -0,0 +1,187 @@ + + + + smoke + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-7 + + + world + + + point + + + + 7 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1 + 1 + 1 + 1 + + + 1.0 + + + + + + 1 + 1 + 1 + 1 + + + 10 + + + + + 200 + + + 0.5 + 0.5 + + + + air + false + false + + + + + + exhaust + smoke.png + + true + false + + + payload/armament/kn-06/flags/smoke-id-7 + + + world + + + point + + + + 0.0 + 0 + 0 + + + + -1.5 + 1.5 + -1.5 + 1.5 + + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 100 + 0 + + + + billboard + + + + + 1.0 + 0.7 + 0.2 + 0.7 + + + 1.0 + + + + + + 0.9 + 0.6 + 0.2 + 0.1 + + + 1.5 + + + + + 0.15 + + + 0.5 + 0.5 + + + + air + true + true + + + + + diff --git a/missile-frigate/Models/missile_frigate.xml b/missile-frigate/Models/missile_frigate.xml index 6d7765e..665e70f 100644 --- a/missile-frigate/Models/missile_frigate.xml +++ b/missile-frigate/Models/missile_frigate.xml @@ -6,7 +6,7 @@ early-production LPD17.ac - + loopid += 1; diff --git a/missile-frigate/Nasal/damage.nas b/missile-frigate/Nasal/damage.nas index 43f7875..e374305 100644 --- a/missile-frigate/Nasal/damage.nas +++ b/missile-frigate/Nasal/damage.nas @@ -7,14 +7,15 @@ var hp = 200; var hp_max = hp; var cannon_types = { - " M70 rocket hit": 40.00, #Viggen rocket - " M55 cannon shell hit": 3.50, #Viggen gun - " KCA cannon shell hit": 7.00, #Viggen gunpod - " Gun Splash On ": 3.0, #Mirage - " M61A1 shell hit": 2.50, #f-14/15 - " GAU-8/A hit": 10.00, #a-10 - " BK27 cannon hit": 3.50, #typhoon - " GSh-30 hit": 3.00, #mig-21 + " M70 rocket hit": 0.30, + " M55 cannon shell hit": 0.20, + " KCA cannon shell hit": 0.20, + " Gun Splash On ": 0.30, + " M61A1 shell hit": 0.20, + " GAU-8/A hit": 0.30, + " BK27 cannon hit": 0.20, + " GSh-30 hit": 0.20, + " 7.62 hit": 2.50, #UH-1 " 50 BMG hit": 3.00, #p-47 }; @@ -60,7 +61,8 @@ var warhead_lbs = { "R-60": 6.60, "R-27R1": 85.98, "R-27T1": 85.98, - "FAB-500": 564.00, + "FAB-500": 564.00, + "M71R": 200.00, }; var incoming_listener = func { diff --git a/missile-frigate/missile-frigate-set.xml b/missile-frigate/missile-frigate-set.xml index a3b054f..03ac6ef 100644 --- a/missile-frigate/missile-frigate-set.xml +++ b/missile-frigate/missile-frigate-set.xml @@ -69,7 +69,7 @@ KPN - Aircraft/missile-frigate/Models/missile-frigate.xml + Aircraft/missile-frigate/Models/missile_frigate.xml true @@ -123,16 +123,227 @@ 0 + + + + + true + + + + nasal + + + + + + - + + 0 + false + + + 4.15986 + -2.70311 + -0.67645 + + false + + + + -0.02697 + -0.98148 + -0.94307 + + false + + + + 4.15986 + 2.70311 + -0.67645 + + false + + + + -0.02697 + 0.98148 + -0.94307 + + false + + + + 4.05216 + -3.94417 + -0.73722 + + false + + + + 4.05216 + 3.94417 + -0.73722 + + false + + + + 4.05216 + 3.94417 + -0.73722 + + false + + + + 4.05216 + 3.94417 + -0.73722 + + false + + + + + Main Store + + none + + KN-06 + 50 + + + + + Aircraft/missile-frigate/Models/Armament/Weapons/ + true + true + + false + 0.0 + 0.00 + 0.15 + 0.1 + radar + APN + true + 85 + 360 + 360 + 22 + 100000 + 80000 + 0.25 + 12.0 + 3920 + 315 + 2800 + 0.15 + 2.11349 + 2 + 0.25 + 160 + 3000 + 75000 + 0.85 + 200 + true + 7.5 + false + 90 + A + Bird away + + + + false + 0.0 + 0.00 + radar + true + 38.8 + 360 + 360 + 30 + 9000 + 1500 + 4.0 + 15.0 + 291 + 44 + 0.5 + 0.2739 + 3.0 + 1.6 + 0.9 + 90 + 30 + 0 + 0.35 + true + 5 + false + A/A + Chickenbeater + + + + + +
+ + 0 + + + 0 + + + 0 + +
+