diff --git a/BUK-M2/Nasal/ArmamentNotification.nas b/BUK-M2/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/BUK-M2/Nasal/ArmamentNotification.nas +++ b/BUK-M2/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/BUK-M2/Nasal/GeoBridgedTransmitter.nas b/BUK-M2/Nasal/GeoBridgedTransmitter.nas index 973e4a4..2e73fbb 100644 --- a/BUK-M2/Nasal/GeoBridgedTransmitter.nas +++ b/BUK-M2/Nasal/GeoBridgedTransmitter.nas @@ -51,3 +51,33 @@ objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/BUK-M2/Nasal/damage.nas b/BUK-M2/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/BUK-M2/Nasal/damage.nas +++ b/BUK-M2/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/BUK-M2/Nasal/datalink.nas b/BUK-M2/Nasal/datalink.nas index 7b88482..0f01161 100644 --- a/BUK-M2/Nasal/datalink.nas +++ b/BUK-M2/Nasal/datalink.nas @@ -678,4 +678,4 @@ var decode_point = func(aircrafts_data, callsign, str) { return aircrafts_data; } -register_extension("point", "P", ContactPoint, encode_point, decode_point); \ No newline at end of file +register_extension("point", "P", ContactPoint, encode_point, decode_point); diff --git a/BUK-M2/Nasal/guided-missiles.nas b/BUK-M2/Nasal/guided-missiles.nas index 98f3b94..0c83c1d 100644 --- a/BUK-M2/Nasal/guided-missiles.nas +++ b/BUK-M2/Nasal/guided-missiles.nas @@ -153,19 +153,11 @@ var slugs_to_lbm = SLUGS2LBM;# since various aircraft use this from outside miss var first_in_air = 0;# first missile is in the air, other missiles should not write to MP. var first_in_air_max_sec = 30; -var versionString = getprop("sim/version/flightgear"); -var version = split(".", versionString); -var major = num(version[0]); -var minor = num(version[1]); -var pica = num(version[2]); -var pickingMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - pickingMethod = 1; -} -var offsetMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - offsetMethod = 1; -} +#var versionString = getprop("sim/version/flightgear"); +#var version = split(".", versionString); +#var major = num(version[0]); +#var minor = num(version[1]); +#var pica = num(version[2]); var wingedGuideFactor = 0.1; @@ -193,27 +185,28 @@ var contactPoint = nil; # 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() +# getElevation() - Pitch to target from launch vehicle +# get_bearing() - Bearing to target from launch vehicle +# get_Callsign() +# get_range() - NM # get_Coord() -# get_altitude() +# get_altitude() - FT # get_Pitch() # get_Speed() # get_heading() -# get_uBody() -# get_vBody() -# get_wBody() +# get_uBody() - Body velocities in ft/s. Forward component. +# get_vBody() - Right component +# get_wBody() - Down component # getLastGroundTrackBlep() - Used for sample guidance # getFlareNode() - Used for flares. # getChaffNode() - Used for chaff. # isPainted() - Tells if this target is still being radar tracked by the launch platform, only used in semi-radar guided missiles. # isLaserPainted() - Tells if this target is still being tracked by the launch platform, only used by laser guided ordnance. # isRadiating(coord) - Tell if anti-radiation missile is hit by radiation from target. coord is the weapon position. -# isCommandActive() +# isSpikingMe() - Tells if the target is STT locked on launch platform. +# isCommandActive() - If surface based launcher is still tracking (command guidance) or have a search blep (TVM) on target. # isVirtual() - Tells if the target is just a position, and should not be considered for damage. -# get_closure_rate() - closure rate in kt +# get_closure_rate() - closure rate in kt to launch platform var AIM = { lowestETA: nil, @@ -312,6 +305,8 @@ var AIM = { m.radarZ = getprop(m.nodeString~"FCS-z"); # In future I will add direction to it also, for now its center gimbal is along -x axis. m.expand_min = getprop(m.nodeString~"expand-min-fire-range"); # Bool. Default false. If min fire range should expand with closing rate. Mainly use this for A/A missiles. m.asc = getprop(m.nodeString~"attack-steering-cue-enabled");# Bool. ASC enabled. + m.powerOnRequired = getprop(m.nodeString~"requires-power-on"); # Bool. ASC enabled. + m.powerOnBatteryTime = getprop(m.nodeString~"power-on-battery-time"); # Seconds. # navigation, guiding and seekerhead m.max_seeker_dev = getprop(m.nodeString~"seeker-field-deg") / 2; # missiles own seekers total FOV diameter. m.guidance = getprop(m.nodeString~"guidance"); # heat/radar/semi-radar/tvm/laser/gps/gps-laser/vision/unguided/level/gyro-pitch/radiation/inertial/remote/remote-stable/command/sample @@ -412,6 +407,14 @@ var AIM = { m.eject_speed = 0; } + if (m.powerOnRequired == nil) { + m.powerOnRequired = 0; + } + + if (m.powerOnBatteryTime == nil) { + m.powerOnBatteryTime = 0;# infinite + } + if (m.rail_forward == 1) { m.rail_pitch_deg = 0; m.rail_head_deg = 0; @@ -720,6 +723,7 @@ var AIM = { m.contacts = [];# contacts that should be considered to lock onto. In slave it will only lock to the first. m.warm = 1;# normalized warm/cold m.ready_standby_time = 0;# time when started from standby + m.power_on_time = 0;# time when started from standby m.cooling = 0; m.slave_to_radar = m.seam_support?1:0; m.seeker_last_time = 0; @@ -729,6 +733,11 @@ var AIM = { m.cooling_last_time = 0; m.cool_total_time = 0; + m.powerOn = 0; + m.powerAvailable = m.powerOnBatteryTime; + m.powerOnPassedTime = 0; + m.powerOnLastCheck = 0; + # # Emesary damage system # @@ -1351,7 +1360,11 @@ var AIM = { # do NOT call this after launch # see also release(vect) if (me.status == MISSILE_FLYING) return; - me.contacts = vect; + me.contacts = vect; + }, + + clearTgt: func { + me.Tgt = nil; }, commandDir: func (heading_deg, pitch_deg) { @@ -1408,6 +1421,28 @@ var AIM = { return me.cooling; }, + togglePowerOn: func { + me.powerOn = !me.powerOn; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + setPowerOn: func (enable) { + me.powerOn = enable; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + isPowerOn: func { + return me.powerOn; + }, + + hasPowerEnough: func { + return !me.powerOnRequired or me.powerOnBatteryTime == 0 or me.powerAvailable > me.powerOnBatteryTime * 0.05; + }, + start: func { if (me.status == MISSILE_STANDBY) { me.status = MISSILE_STARTING; @@ -1421,9 +1456,17 @@ var AIM = { if (me.status != MISSILE_FLYING) { me.status = MISSILE_STANDBY; #printf("stop #%3d %s", me.ID, me.type); + if (me.guidance == "radiation") { + me.Tgt = nil; + me.contacts = []; + } } }, + getStatus: func { + return me.status; + }, + isCaged: func () { if (!me.seam_support) return me.status != MISSILE_FLYING; return me.caged; @@ -1484,6 +1527,9 @@ var AIM = { me.elapsed_last = systime(); me.status = MISSILE_FLYING; + me.powerOnLastCheck = 0;# switch to use me.life_time from elapsed sim time. + me.powerOn = 1;# If pilot releases while power off, we make sure it on now. + if (vect!= nil) { # sets a vector of contacts the weapons will try to lock onto @@ -1549,7 +1595,7 @@ var AIM = { me.railEnd = vector.Math.plus(me.railBegin, vector.Math.product(me.rail_dist_m, me.railvec)); } } - if (offsetMethod and (!me.rail or me.rail_forward)) { + if (!me.rail or me.rail_forward) { var pos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); init_coord = geo.Coord.new(); init_coord.set_xyz(pos.x, pos.y, pos.z); @@ -2094,6 +2140,13 @@ var AIM = { me.life_time += me.dt; + me.consumeBattery(me.life_time); + if (!me.hasPowerEnough()) { + # Smart weapon reduced to dumb weapon + me.free = 1; + me.printStats("Ran out of battery, stopped guiding"); + } + me.handleMidFlightFunc(); if (me.hasGuided and me.maddog) { @@ -2394,12 +2447,8 @@ var AIM = { # missile on rail, lets move it on the rail if (me.rail_forward) { var init_coord = nil; - if (offsetMethod) { - me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); - me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); - } else { - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); - } + me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); + me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); } else { me.coord = me.getGPS(-me.railPos[0], -me.railPos[1], me.railPos[2], OurPitch.getValue(), OurHdg.getValue()); } @@ -2723,8 +2772,13 @@ var AIM = { } else { me.remote_control_pitch = 0; } - if (me.settings["abort_midflight_function"] != nil) { + if (me.settings["seeker_fov"] != nil) { + me.max_seeker_dev = me.settings.seeker_fov; + me.printStats("Seeker FOV switched to %s",me.max_seeker_dev); + } + if (me.settings["abort_midflight_function"] == 1) { me.mfFunction = nil; + me.printStats("Midflight function finished"); } } }, @@ -2872,7 +2926,7 @@ var AIM = { } if (!me.simple_drag) { - if (me.vector_thrust and me.thrust_lbf>0) N=N*0.35; + if (me.vector_thrust and me.thrust_lbf>0) N=N*0.15; if (mach < 1.1) { me.Cdi = (me.Cd_base+me.Cd_delta*me.deploy)*N;# N = normal force in G } else { @@ -3337,7 +3391,7 @@ var AIM = { }, checkForLOS: func () { - if (pickingMethod and me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { + if (me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { me.xyz = {"x":me.coord.x(), "y":me.coord.y(), "z":me.coord.z()}; me.directionLOS = {"x":me.t_coord.x()-me.coord.x(), "y":me.t_coord.y()-me.coord.y(), "z":me.t_coord.z()-me.coord.z()}; @@ -3456,6 +3510,10 @@ var AIM = { me.printStats(me.type~": Passed minimum speed for guiding after %.1f seconds. Target %d%% inside view.", me.life_time, me.normFOV*100); } } + if (me.guidance == "gps" or me.guidance == "inertial" or me.guidance == "sample") { + # To make sure deviation_deg is updated in midflight function. + me.FOV_check(me.hdg, me.pitch, me.curr_deviation_h, me.curr_deviation_e, me.max_seeker_dev, me.myMath); + } if (me.chaffLock and (me.guidance == "command" or me.guidance == "semi-radar" or me.guidance == "tvm") and (me.life_time - me.chaffLockTime) > (me.gnd_launch?4:6)) { me.chaffLock = 0; me.printStats(me.type~": Chaff dissipated, regained track."); @@ -4285,7 +4343,7 @@ var AIM = { notifyInFlight: func (lat,lon,alt,rdar,semiRdr,typeID,typ,unique,thrustOn,callsign, heading, pitch, speed, is_deleted=0) { ## thrustON cannot be named 'thrust' as FG for some reason will then think its a function (probably fixed by the way call() now is used) - var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, 21+typeID); + var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, damage.DamageRecipient.typeID2emesaryID(typeID)); if (lat != nil) { msg.Position.set_latlon(lat,lon,alt); } else { @@ -4324,7 +4382,7 @@ var AIM = { }, notifyHit: func (RelativeAltitude, Distance, callsign, Bearing, reason, typeID, type, self) { - var msg = notifications.ArmamentNotification.new("mhit", 4, 21+typeID); + var msg = notifications.ArmamentNotification.new("mhit", 4, damage.DamageRecipient.typeID2emesaryID(typeID)); msg.RelativeAltitude = RelativeAltitude; msg.Bearing = Bearing; msg.Distance = Distance; @@ -4516,11 +4574,27 @@ var AIM = { } }, + consumeBattery: func (curr_time) { + if (me.powerOnRequired) { + me.powerOnPassedTime = curr_time-me.powerOnLastCheck; + me.powerAvailable += (me.powerOn?-1:1)*me.powerOnPassedTime; + me.powerAvailable = math.clamp(me.powerAvailable, 0, me.powerOnBatteryTime); + me.powerOnLastCheck = curr_time; + } + }, + standby: func { # looping in standby mode if (deltaSec.getValue()==0) { + # paused settimer(func me.standby(), 0.5); + return; } + me.printCode("In standby(%d)",me.status); + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4541,8 +4615,11 @@ var AIM = { # looping in starting mode #print("startup"); if (deltaSec.getValue()==0) { + # Paused settimer(func me.startup(), 0.5); + return; } + me.printCode("In startup()"); if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4550,7 +4627,11 @@ var AIM = { me.standby(); return; } - if (me.ready_standby_time != 0 and getprop("sim/time/elapsed-sec") > (me.ready_standby_time+me.ready_time)) { + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + + if (me.ready_standby_time != 0 and me.hasPowerEnough() and ((!me.powerOnRequired and me.curr_time > (me.ready_standby_time+me.ready_time)) or (me.powerOnRequired and (me.powerOn and me.curr_time > me.power_on_time + me.ready_time)))) { me.status = MISSILE_SEARCH; me.search(); return; @@ -4752,6 +4833,7 @@ var AIM = { if (deltaSec.getValue()==0) { settimer(func me.search(), 0.5); } + me.printCode("In search()"); if (me.deleted) { return; } elsif ( me.status == MISSILE_FLYING ) { @@ -4765,13 +4847,21 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif ( me.status == MISSILE_LOCK) { # Locked. me.printSearch("in search loop, but locked!"); me.return_to_search(); return; } - + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("searching"); @@ -5130,6 +5220,15 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + me.reset_seeker(); + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif (!me.Tgt.isValid()) { # Lost of lock due to target disapearing: # return to search mode. @@ -5143,6 +5242,7 @@ var AIM = { me.return_to_search(); return; } + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("lock (caged:%d slave:%d point:%s same:%d noCommon:%d)",me.caged,me.slave_to_radar,contactPoint!=nil,me.getContact() == me.Tgt,me.noCommonTarget); # Time interval since lock time or last track loop. #if (me.status == MISSILE_LOCK) { @@ -5158,13 +5258,16 @@ var AIM = { me.coolingSyst(); me.computeSeekerPos(); if (me.status != MISSILE_STANDBY ) {#TODO: should this also check for starting up? - me.in_view = me.check_t_in_fov(); - if (!me.in_view) { - me.printSearch("out of view"); - me.return_to_search(); - return; - } + # This code does not seem needed anymore: + #me.in_view = me.check_t_in_fov(); + # + #if (!me.in_view) { + # if (me.noCommonTarget and me.guidance == "radiation") me.Tgt = nil;#Hack, todo fix this + # me.printSearch("out of view"); + # me.return_to_search(); + # return; + #} if (!me.caged or me.slave_to_radar) { me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); @@ -5184,7 +5287,8 @@ var AIM = { # Notice: seeker_xxxx_target is used both for denoting where seeker should move towards and where the target is. In this case its the latter: me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); me.testSeeker(); - if (!me.inBeam or (me.guidance == "semi-radar" and !me.is_painted(me.Tgt))) { + me.tagt = me.Tgt;# Used in checkForLock + if (!me.inBeam or !me.checkForLock()) { me.printSearch("out of beam or no beam for fox 1"); me.status = MISSILE_SEARCH; me.Tgt = nil; @@ -5298,6 +5402,7 @@ var AIM = { if (me.seeMe != nil and me.seeMe) { return 1; } + return target.isSpikingMe(); } return 0; }, @@ -5319,15 +5424,6 @@ var AIM = { me.moveSeeker(); }, - 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 { diff --git a/BUK-M2/Nasal/vector.nas b/BUK-M2/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/BUK-M2/Nasal/vector.nas +++ b/BUK-M2/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/Frigate/Nasal/ArmamentNotification.nas b/Frigate/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/Frigate/Nasal/ArmamentNotification.nas +++ b/Frigate/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/Frigate/Nasal/GeoBridgedTransmitter.nas b/Frigate/Nasal/GeoBridgedTransmitter.nas index 973e4a4..2e73fbb 100644 --- a/Frigate/Nasal/GeoBridgedTransmitter.nas +++ b/Frigate/Nasal/GeoBridgedTransmitter.nas @@ -51,3 +51,33 @@ objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/Frigate/Nasal/damage.nas b/Frigate/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/Frigate/Nasal/damage.nas +++ b/Frigate/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/Frigate/Nasal/vector.nas b/Frigate/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/Frigate/Nasal/vector.nas +++ b/Frigate/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/MIM-104D/Nasal/ArmamentNotification.nas b/MIM-104D/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/MIM-104D/Nasal/ArmamentNotification.nas +++ b/MIM-104D/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/MIM-104D/Nasal/GeoBridgedTransmitter.nas b/MIM-104D/Nasal/GeoBridgedTransmitter.nas index cd69a17..2e73fbb 100644 --- a/MIM-104D/Nasal/GeoBridgedTransmitter.nas +++ b/MIM-104D/Nasal/GeoBridgedTransmitter.nas @@ -27,7 +27,7 @@ #i.e. change to [notifications.ArmamentInFlightNotification.new(nil), notifications.ArmamentNotification.new(nil)]; var geoRoutedNotifications = [notifications.ArmamentInFlightNotification.new()]; var geoBridgedTransmitter = emesary.Transmitter.new("geoOutgoingBridge"); -var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); +var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); # bridge should be tuned to be around 90% of the packet size full. geooutgoingBridge.TransmitFrequencySeconds = 0.75; @@ -38,7 +38,7 @@ emesary_mp_bridge.IncomingMPBridge.startMPBridge(geoRoutedNotifications, 18, eme #----- bridge hit (armament) notifications var hitRoutedNotifications = [notifications.ArmamentNotification.new(),notifications.StaticNotification.new()]; var hitBridgedTransmitter = emesary.Transmitter.new("armamentNotificationBridge"); -var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); +var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); hitoutgoingBridge.TransmitFrequencySeconds = 1.5; hitoutgoingBridge.MPStringMaxLen = 120; emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, emesary.GlobalTransmitter); @@ -46,8 +46,38 @@ emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, eme #----- bridge object notifications var objectRoutedNotifications = [notifications.ObjectInFlightNotification.new()]; var objectBridgedTransmitter = emesary.Transmitter.new("objectNotificationBridge"); -var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); +var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; -emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); \ No newline at end of file +emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/MIM-104D/Nasal/damage.nas b/MIM-104D/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/MIM-104D/Nasal/damage.nas +++ b/MIM-104D/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/MIM-104D/Nasal/datalink.nas b/MIM-104D/Nasal/datalink.nas index 7b88482..0f01161 100644 --- a/MIM-104D/Nasal/datalink.nas +++ b/MIM-104D/Nasal/datalink.nas @@ -678,4 +678,4 @@ var decode_point = func(aircrafts_data, callsign, str) { return aircrafts_data; } -register_extension("point", "P", ContactPoint, encode_point, decode_point); \ No newline at end of file +register_extension("point", "P", ContactPoint, encode_point, decode_point); diff --git a/MIM-104D/Nasal/guided-missiles.nas b/MIM-104D/Nasal/guided-missiles.nas index 98f3b94..0c83c1d 100644 --- a/MIM-104D/Nasal/guided-missiles.nas +++ b/MIM-104D/Nasal/guided-missiles.nas @@ -153,19 +153,11 @@ var slugs_to_lbm = SLUGS2LBM;# since various aircraft use this from outside miss var first_in_air = 0;# first missile is in the air, other missiles should not write to MP. var first_in_air_max_sec = 30; -var versionString = getprop("sim/version/flightgear"); -var version = split(".", versionString); -var major = num(version[0]); -var minor = num(version[1]); -var pica = num(version[2]); -var pickingMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - pickingMethod = 1; -} -var offsetMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - offsetMethod = 1; -} +#var versionString = getprop("sim/version/flightgear"); +#var version = split(".", versionString); +#var major = num(version[0]); +#var minor = num(version[1]); +#var pica = num(version[2]); var wingedGuideFactor = 0.1; @@ -193,27 +185,28 @@ var contactPoint = nil; # 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() +# getElevation() - Pitch to target from launch vehicle +# get_bearing() - Bearing to target from launch vehicle +# get_Callsign() +# get_range() - NM # get_Coord() -# get_altitude() +# get_altitude() - FT # get_Pitch() # get_Speed() # get_heading() -# get_uBody() -# get_vBody() -# get_wBody() +# get_uBody() - Body velocities in ft/s. Forward component. +# get_vBody() - Right component +# get_wBody() - Down component # getLastGroundTrackBlep() - Used for sample guidance # getFlareNode() - Used for flares. # getChaffNode() - Used for chaff. # isPainted() - Tells if this target is still being radar tracked by the launch platform, only used in semi-radar guided missiles. # isLaserPainted() - Tells if this target is still being tracked by the launch platform, only used by laser guided ordnance. # isRadiating(coord) - Tell if anti-radiation missile is hit by radiation from target. coord is the weapon position. -# isCommandActive() +# isSpikingMe() - Tells if the target is STT locked on launch platform. +# isCommandActive() - If surface based launcher is still tracking (command guidance) or have a search blep (TVM) on target. # isVirtual() - Tells if the target is just a position, and should not be considered for damage. -# get_closure_rate() - closure rate in kt +# get_closure_rate() - closure rate in kt to launch platform var AIM = { lowestETA: nil, @@ -312,6 +305,8 @@ var AIM = { m.radarZ = getprop(m.nodeString~"FCS-z"); # In future I will add direction to it also, for now its center gimbal is along -x axis. m.expand_min = getprop(m.nodeString~"expand-min-fire-range"); # Bool. Default false. If min fire range should expand with closing rate. Mainly use this for A/A missiles. m.asc = getprop(m.nodeString~"attack-steering-cue-enabled");# Bool. ASC enabled. + m.powerOnRequired = getprop(m.nodeString~"requires-power-on"); # Bool. ASC enabled. + m.powerOnBatteryTime = getprop(m.nodeString~"power-on-battery-time"); # Seconds. # navigation, guiding and seekerhead m.max_seeker_dev = getprop(m.nodeString~"seeker-field-deg") / 2; # missiles own seekers total FOV diameter. m.guidance = getprop(m.nodeString~"guidance"); # heat/radar/semi-radar/tvm/laser/gps/gps-laser/vision/unguided/level/gyro-pitch/radiation/inertial/remote/remote-stable/command/sample @@ -412,6 +407,14 @@ var AIM = { m.eject_speed = 0; } + if (m.powerOnRequired == nil) { + m.powerOnRequired = 0; + } + + if (m.powerOnBatteryTime == nil) { + m.powerOnBatteryTime = 0;# infinite + } + if (m.rail_forward == 1) { m.rail_pitch_deg = 0; m.rail_head_deg = 0; @@ -720,6 +723,7 @@ var AIM = { m.contacts = [];# contacts that should be considered to lock onto. In slave it will only lock to the first. m.warm = 1;# normalized warm/cold m.ready_standby_time = 0;# time when started from standby + m.power_on_time = 0;# time when started from standby m.cooling = 0; m.slave_to_radar = m.seam_support?1:0; m.seeker_last_time = 0; @@ -729,6 +733,11 @@ var AIM = { m.cooling_last_time = 0; m.cool_total_time = 0; + m.powerOn = 0; + m.powerAvailable = m.powerOnBatteryTime; + m.powerOnPassedTime = 0; + m.powerOnLastCheck = 0; + # # Emesary damage system # @@ -1351,7 +1360,11 @@ var AIM = { # do NOT call this after launch # see also release(vect) if (me.status == MISSILE_FLYING) return; - me.contacts = vect; + me.contacts = vect; + }, + + clearTgt: func { + me.Tgt = nil; }, commandDir: func (heading_deg, pitch_deg) { @@ -1408,6 +1421,28 @@ var AIM = { return me.cooling; }, + togglePowerOn: func { + me.powerOn = !me.powerOn; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + setPowerOn: func (enable) { + me.powerOn = enable; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + isPowerOn: func { + return me.powerOn; + }, + + hasPowerEnough: func { + return !me.powerOnRequired or me.powerOnBatteryTime == 0 or me.powerAvailable > me.powerOnBatteryTime * 0.05; + }, + start: func { if (me.status == MISSILE_STANDBY) { me.status = MISSILE_STARTING; @@ -1421,9 +1456,17 @@ var AIM = { if (me.status != MISSILE_FLYING) { me.status = MISSILE_STANDBY; #printf("stop #%3d %s", me.ID, me.type); + if (me.guidance == "radiation") { + me.Tgt = nil; + me.contacts = []; + } } }, + getStatus: func { + return me.status; + }, + isCaged: func () { if (!me.seam_support) return me.status != MISSILE_FLYING; return me.caged; @@ -1484,6 +1527,9 @@ var AIM = { me.elapsed_last = systime(); me.status = MISSILE_FLYING; + me.powerOnLastCheck = 0;# switch to use me.life_time from elapsed sim time. + me.powerOn = 1;# If pilot releases while power off, we make sure it on now. + if (vect!= nil) { # sets a vector of contacts the weapons will try to lock onto @@ -1549,7 +1595,7 @@ var AIM = { me.railEnd = vector.Math.plus(me.railBegin, vector.Math.product(me.rail_dist_m, me.railvec)); } } - if (offsetMethod and (!me.rail or me.rail_forward)) { + if (!me.rail or me.rail_forward) { var pos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); init_coord = geo.Coord.new(); init_coord.set_xyz(pos.x, pos.y, pos.z); @@ -2094,6 +2140,13 @@ var AIM = { me.life_time += me.dt; + me.consumeBattery(me.life_time); + if (!me.hasPowerEnough()) { + # Smart weapon reduced to dumb weapon + me.free = 1; + me.printStats("Ran out of battery, stopped guiding"); + } + me.handleMidFlightFunc(); if (me.hasGuided and me.maddog) { @@ -2394,12 +2447,8 @@ var AIM = { # missile on rail, lets move it on the rail if (me.rail_forward) { var init_coord = nil; - if (offsetMethod) { - me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); - me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); - } else { - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); - } + me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); + me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); } else { me.coord = me.getGPS(-me.railPos[0], -me.railPos[1], me.railPos[2], OurPitch.getValue(), OurHdg.getValue()); } @@ -2723,8 +2772,13 @@ var AIM = { } else { me.remote_control_pitch = 0; } - if (me.settings["abort_midflight_function"] != nil) { + if (me.settings["seeker_fov"] != nil) { + me.max_seeker_dev = me.settings.seeker_fov; + me.printStats("Seeker FOV switched to %s",me.max_seeker_dev); + } + if (me.settings["abort_midflight_function"] == 1) { me.mfFunction = nil; + me.printStats("Midflight function finished"); } } }, @@ -2872,7 +2926,7 @@ var AIM = { } if (!me.simple_drag) { - if (me.vector_thrust and me.thrust_lbf>0) N=N*0.35; + if (me.vector_thrust and me.thrust_lbf>0) N=N*0.15; if (mach < 1.1) { me.Cdi = (me.Cd_base+me.Cd_delta*me.deploy)*N;# N = normal force in G } else { @@ -3337,7 +3391,7 @@ var AIM = { }, checkForLOS: func () { - if (pickingMethod and me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { + if (me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { me.xyz = {"x":me.coord.x(), "y":me.coord.y(), "z":me.coord.z()}; me.directionLOS = {"x":me.t_coord.x()-me.coord.x(), "y":me.t_coord.y()-me.coord.y(), "z":me.t_coord.z()-me.coord.z()}; @@ -3456,6 +3510,10 @@ var AIM = { me.printStats(me.type~": Passed minimum speed for guiding after %.1f seconds. Target %d%% inside view.", me.life_time, me.normFOV*100); } } + if (me.guidance == "gps" or me.guidance == "inertial" or me.guidance == "sample") { + # To make sure deviation_deg is updated in midflight function. + me.FOV_check(me.hdg, me.pitch, me.curr_deviation_h, me.curr_deviation_e, me.max_seeker_dev, me.myMath); + } if (me.chaffLock and (me.guidance == "command" or me.guidance == "semi-radar" or me.guidance == "tvm") and (me.life_time - me.chaffLockTime) > (me.gnd_launch?4:6)) { me.chaffLock = 0; me.printStats(me.type~": Chaff dissipated, regained track."); @@ -4285,7 +4343,7 @@ var AIM = { notifyInFlight: func (lat,lon,alt,rdar,semiRdr,typeID,typ,unique,thrustOn,callsign, heading, pitch, speed, is_deleted=0) { ## thrustON cannot be named 'thrust' as FG for some reason will then think its a function (probably fixed by the way call() now is used) - var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, 21+typeID); + var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, damage.DamageRecipient.typeID2emesaryID(typeID)); if (lat != nil) { msg.Position.set_latlon(lat,lon,alt); } else { @@ -4324,7 +4382,7 @@ var AIM = { }, notifyHit: func (RelativeAltitude, Distance, callsign, Bearing, reason, typeID, type, self) { - var msg = notifications.ArmamentNotification.new("mhit", 4, 21+typeID); + var msg = notifications.ArmamentNotification.new("mhit", 4, damage.DamageRecipient.typeID2emesaryID(typeID)); msg.RelativeAltitude = RelativeAltitude; msg.Bearing = Bearing; msg.Distance = Distance; @@ -4516,11 +4574,27 @@ var AIM = { } }, + consumeBattery: func (curr_time) { + if (me.powerOnRequired) { + me.powerOnPassedTime = curr_time-me.powerOnLastCheck; + me.powerAvailable += (me.powerOn?-1:1)*me.powerOnPassedTime; + me.powerAvailable = math.clamp(me.powerAvailable, 0, me.powerOnBatteryTime); + me.powerOnLastCheck = curr_time; + } + }, + standby: func { # looping in standby mode if (deltaSec.getValue()==0) { + # paused settimer(func me.standby(), 0.5); + return; } + me.printCode("In standby(%d)",me.status); + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4541,8 +4615,11 @@ var AIM = { # looping in starting mode #print("startup"); if (deltaSec.getValue()==0) { + # Paused settimer(func me.startup(), 0.5); + return; } + me.printCode("In startup()"); if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4550,7 +4627,11 @@ var AIM = { me.standby(); return; } - if (me.ready_standby_time != 0 and getprop("sim/time/elapsed-sec") > (me.ready_standby_time+me.ready_time)) { + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + + if (me.ready_standby_time != 0 and me.hasPowerEnough() and ((!me.powerOnRequired and me.curr_time > (me.ready_standby_time+me.ready_time)) or (me.powerOnRequired and (me.powerOn and me.curr_time > me.power_on_time + me.ready_time)))) { me.status = MISSILE_SEARCH; me.search(); return; @@ -4752,6 +4833,7 @@ var AIM = { if (deltaSec.getValue()==0) { settimer(func me.search(), 0.5); } + me.printCode("In search()"); if (me.deleted) { return; } elsif ( me.status == MISSILE_FLYING ) { @@ -4765,13 +4847,21 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif ( me.status == MISSILE_LOCK) { # Locked. me.printSearch("in search loop, but locked!"); me.return_to_search(); return; } - + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("searching"); @@ -5130,6 +5220,15 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + me.reset_seeker(); + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif (!me.Tgt.isValid()) { # Lost of lock due to target disapearing: # return to search mode. @@ -5143,6 +5242,7 @@ var AIM = { me.return_to_search(); return; } + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("lock (caged:%d slave:%d point:%s same:%d noCommon:%d)",me.caged,me.slave_to_radar,contactPoint!=nil,me.getContact() == me.Tgt,me.noCommonTarget); # Time interval since lock time or last track loop. #if (me.status == MISSILE_LOCK) { @@ -5158,13 +5258,16 @@ var AIM = { me.coolingSyst(); me.computeSeekerPos(); if (me.status != MISSILE_STANDBY ) {#TODO: should this also check for starting up? - me.in_view = me.check_t_in_fov(); - if (!me.in_view) { - me.printSearch("out of view"); - me.return_to_search(); - return; - } + # This code does not seem needed anymore: + #me.in_view = me.check_t_in_fov(); + # + #if (!me.in_view) { + # if (me.noCommonTarget and me.guidance == "radiation") me.Tgt = nil;#Hack, todo fix this + # me.printSearch("out of view"); + # me.return_to_search(); + # return; + #} if (!me.caged or me.slave_to_radar) { me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); @@ -5184,7 +5287,8 @@ var AIM = { # Notice: seeker_xxxx_target is used both for denoting where seeker should move towards and where the target is. In this case its the latter: me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); me.testSeeker(); - if (!me.inBeam or (me.guidance == "semi-radar" and !me.is_painted(me.Tgt))) { + me.tagt = me.Tgt;# Used in checkForLock + if (!me.inBeam or !me.checkForLock()) { me.printSearch("out of beam or no beam for fox 1"); me.status = MISSILE_SEARCH; me.Tgt = nil; @@ -5298,6 +5402,7 @@ var AIM = { if (me.seeMe != nil and me.seeMe) { return 1; } + return target.isSpikingMe(); } return 0; }, @@ -5319,15 +5424,6 @@ var AIM = { me.moveSeeker(); }, - 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 { diff --git a/MIM-104D/Nasal/vector.nas b/MIM-104D/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/MIM-104D/Nasal/vector.nas +++ b/MIM-104D/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/S-75/Nasal/ArmamentNotification.nas b/S-75/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/S-75/Nasal/ArmamentNotification.nas +++ b/S-75/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/S-75/Nasal/GeoBridgedTransmitter.nas b/S-75/Nasal/GeoBridgedTransmitter.nas index 54a7842..2e73fbb 100644 --- a/S-75/Nasal/GeoBridgedTransmitter.nas +++ b/S-75/Nasal/GeoBridgedTransmitter.nas @@ -27,18 +27,18 @@ #i.e. change to [notifications.ArmamentInFlightNotification.new(nil), notifications.ArmamentNotification.new(nil)]; var geoRoutedNotifications = [notifications.ArmamentInFlightNotification.new()]; var geoBridgedTransmitter = emesary.Transmitter.new("geoOutgoingBridge"); -var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("sa2mp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); +var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); # bridge should be tuned to be around 90% of the packet size full. geooutgoingBridge.TransmitFrequencySeconds = 0.75; -geooutgoingBridge.MPStringMaxLen = 225; +geooutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(geoRoutedNotifications, 18, emesary.GlobalTransmitter); #----- bridge hit (armament) notifications var hitRoutedNotifications = [notifications.ArmamentNotification.new(),notifications.StaticNotification.new()]; var hitBridgedTransmitter = emesary.Transmitter.new("armamentNotificationBridge"); -var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("sa2mp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); +var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); hitoutgoingBridge.TransmitFrequencySeconds = 1.5; hitoutgoingBridge.MPStringMaxLen = 120; emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, emesary.GlobalTransmitter); @@ -46,8 +46,38 @@ emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, eme #----- bridge object notifications var objectRoutedNotifications = [notifications.ObjectInFlightNotification.new()]; var objectBridgedTransmitter = emesary.Transmitter.new("objectNotificationBridge"); -var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("sa2mp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); +var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/S-75/Nasal/damage.nas b/S-75/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/S-75/Nasal/damage.nas +++ b/S-75/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/S-75/Nasal/datalink.nas b/S-75/Nasal/datalink.nas index 7b88482..0f01161 100644 --- a/S-75/Nasal/datalink.nas +++ b/S-75/Nasal/datalink.nas @@ -678,4 +678,4 @@ var decode_point = func(aircrafts_data, callsign, str) { return aircrafts_data; } -register_extension("point", "P", ContactPoint, encode_point, decode_point); \ No newline at end of file +register_extension("point", "P", ContactPoint, encode_point, decode_point); diff --git a/S-75/Nasal/guided-missiles.nas b/S-75/Nasal/guided-missiles.nas index 98f3b94..0c83c1d 100644 --- a/S-75/Nasal/guided-missiles.nas +++ b/S-75/Nasal/guided-missiles.nas @@ -153,19 +153,11 @@ var slugs_to_lbm = SLUGS2LBM;# since various aircraft use this from outside miss var first_in_air = 0;# first missile is in the air, other missiles should not write to MP. var first_in_air_max_sec = 30; -var versionString = getprop("sim/version/flightgear"); -var version = split(".", versionString); -var major = num(version[0]); -var minor = num(version[1]); -var pica = num(version[2]); -var pickingMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - pickingMethod = 1; -} -var offsetMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - offsetMethod = 1; -} +#var versionString = getprop("sim/version/flightgear"); +#var version = split(".", versionString); +#var major = num(version[0]); +#var minor = num(version[1]); +#var pica = num(version[2]); var wingedGuideFactor = 0.1; @@ -193,27 +185,28 @@ var contactPoint = nil; # 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() +# getElevation() - Pitch to target from launch vehicle +# get_bearing() - Bearing to target from launch vehicle +# get_Callsign() +# get_range() - NM # get_Coord() -# get_altitude() +# get_altitude() - FT # get_Pitch() # get_Speed() # get_heading() -# get_uBody() -# get_vBody() -# get_wBody() +# get_uBody() - Body velocities in ft/s. Forward component. +# get_vBody() - Right component +# get_wBody() - Down component # getLastGroundTrackBlep() - Used for sample guidance # getFlareNode() - Used for flares. # getChaffNode() - Used for chaff. # isPainted() - Tells if this target is still being radar tracked by the launch platform, only used in semi-radar guided missiles. # isLaserPainted() - Tells if this target is still being tracked by the launch platform, only used by laser guided ordnance. # isRadiating(coord) - Tell if anti-radiation missile is hit by radiation from target. coord is the weapon position. -# isCommandActive() +# isSpikingMe() - Tells if the target is STT locked on launch platform. +# isCommandActive() - If surface based launcher is still tracking (command guidance) or have a search blep (TVM) on target. # isVirtual() - Tells if the target is just a position, and should not be considered for damage. -# get_closure_rate() - closure rate in kt +# get_closure_rate() - closure rate in kt to launch platform var AIM = { lowestETA: nil, @@ -312,6 +305,8 @@ var AIM = { m.radarZ = getprop(m.nodeString~"FCS-z"); # In future I will add direction to it also, for now its center gimbal is along -x axis. m.expand_min = getprop(m.nodeString~"expand-min-fire-range"); # Bool. Default false. If min fire range should expand with closing rate. Mainly use this for A/A missiles. m.asc = getprop(m.nodeString~"attack-steering-cue-enabled");# Bool. ASC enabled. + m.powerOnRequired = getprop(m.nodeString~"requires-power-on"); # Bool. ASC enabled. + m.powerOnBatteryTime = getprop(m.nodeString~"power-on-battery-time"); # Seconds. # navigation, guiding and seekerhead m.max_seeker_dev = getprop(m.nodeString~"seeker-field-deg") / 2; # missiles own seekers total FOV diameter. m.guidance = getprop(m.nodeString~"guidance"); # heat/radar/semi-radar/tvm/laser/gps/gps-laser/vision/unguided/level/gyro-pitch/radiation/inertial/remote/remote-stable/command/sample @@ -412,6 +407,14 @@ var AIM = { m.eject_speed = 0; } + if (m.powerOnRequired == nil) { + m.powerOnRequired = 0; + } + + if (m.powerOnBatteryTime == nil) { + m.powerOnBatteryTime = 0;# infinite + } + if (m.rail_forward == 1) { m.rail_pitch_deg = 0; m.rail_head_deg = 0; @@ -720,6 +723,7 @@ var AIM = { m.contacts = [];# contacts that should be considered to lock onto. In slave it will only lock to the first. m.warm = 1;# normalized warm/cold m.ready_standby_time = 0;# time when started from standby + m.power_on_time = 0;# time when started from standby m.cooling = 0; m.slave_to_radar = m.seam_support?1:0; m.seeker_last_time = 0; @@ -729,6 +733,11 @@ var AIM = { m.cooling_last_time = 0; m.cool_total_time = 0; + m.powerOn = 0; + m.powerAvailable = m.powerOnBatteryTime; + m.powerOnPassedTime = 0; + m.powerOnLastCheck = 0; + # # Emesary damage system # @@ -1351,7 +1360,11 @@ var AIM = { # do NOT call this after launch # see also release(vect) if (me.status == MISSILE_FLYING) return; - me.contacts = vect; + me.contacts = vect; + }, + + clearTgt: func { + me.Tgt = nil; }, commandDir: func (heading_deg, pitch_deg) { @@ -1408,6 +1421,28 @@ var AIM = { return me.cooling; }, + togglePowerOn: func { + me.powerOn = !me.powerOn; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + setPowerOn: func (enable) { + me.powerOn = enable; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + isPowerOn: func { + return me.powerOn; + }, + + hasPowerEnough: func { + return !me.powerOnRequired or me.powerOnBatteryTime == 0 or me.powerAvailable > me.powerOnBatteryTime * 0.05; + }, + start: func { if (me.status == MISSILE_STANDBY) { me.status = MISSILE_STARTING; @@ -1421,9 +1456,17 @@ var AIM = { if (me.status != MISSILE_FLYING) { me.status = MISSILE_STANDBY; #printf("stop #%3d %s", me.ID, me.type); + if (me.guidance == "radiation") { + me.Tgt = nil; + me.contacts = []; + } } }, + getStatus: func { + return me.status; + }, + isCaged: func () { if (!me.seam_support) return me.status != MISSILE_FLYING; return me.caged; @@ -1484,6 +1527,9 @@ var AIM = { me.elapsed_last = systime(); me.status = MISSILE_FLYING; + me.powerOnLastCheck = 0;# switch to use me.life_time from elapsed sim time. + me.powerOn = 1;# If pilot releases while power off, we make sure it on now. + if (vect!= nil) { # sets a vector of contacts the weapons will try to lock onto @@ -1549,7 +1595,7 @@ var AIM = { me.railEnd = vector.Math.plus(me.railBegin, vector.Math.product(me.rail_dist_m, me.railvec)); } } - if (offsetMethod and (!me.rail or me.rail_forward)) { + if (!me.rail or me.rail_forward) { var pos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); init_coord = geo.Coord.new(); init_coord.set_xyz(pos.x, pos.y, pos.z); @@ -2094,6 +2140,13 @@ var AIM = { me.life_time += me.dt; + me.consumeBattery(me.life_time); + if (!me.hasPowerEnough()) { + # Smart weapon reduced to dumb weapon + me.free = 1; + me.printStats("Ran out of battery, stopped guiding"); + } + me.handleMidFlightFunc(); if (me.hasGuided and me.maddog) { @@ -2394,12 +2447,8 @@ var AIM = { # missile on rail, lets move it on the rail if (me.rail_forward) { var init_coord = nil; - if (offsetMethod) { - me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); - me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); - } else { - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); - } + me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); + me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); } else { me.coord = me.getGPS(-me.railPos[0], -me.railPos[1], me.railPos[2], OurPitch.getValue(), OurHdg.getValue()); } @@ -2723,8 +2772,13 @@ var AIM = { } else { me.remote_control_pitch = 0; } - if (me.settings["abort_midflight_function"] != nil) { + if (me.settings["seeker_fov"] != nil) { + me.max_seeker_dev = me.settings.seeker_fov; + me.printStats("Seeker FOV switched to %s",me.max_seeker_dev); + } + if (me.settings["abort_midflight_function"] == 1) { me.mfFunction = nil; + me.printStats("Midflight function finished"); } } }, @@ -2872,7 +2926,7 @@ var AIM = { } if (!me.simple_drag) { - if (me.vector_thrust and me.thrust_lbf>0) N=N*0.35; + if (me.vector_thrust and me.thrust_lbf>0) N=N*0.15; if (mach < 1.1) { me.Cdi = (me.Cd_base+me.Cd_delta*me.deploy)*N;# N = normal force in G } else { @@ -3337,7 +3391,7 @@ var AIM = { }, checkForLOS: func () { - if (pickingMethod and me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { + if (me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { me.xyz = {"x":me.coord.x(), "y":me.coord.y(), "z":me.coord.z()}; me.directionLOS = {"x":me.t_coord.x()-me.coord.x(), "y":me.t_coord.y()-me.coord.y(), "z":me.t_coord.z()-me.coord.z()}; @@ -3456,6 +3510,10 @@ var AIM = { me.printStats(me.type~": Passed minimum speed for guiding after %.1f seconds. Target %d%% inside view.", me.life_time, me.normFOV*100); } } + if (me.guidance == "gps" or me.guidance == "inertial" or me.guidance == "sample") { + # To make sure deviation_deg is updated in midflight function. + me.FOV_check(me.hdg, me.pitch, me.curr_deviation_h, me.curr_deviation_e, me.max_seeker_dev, me.myMath); + } if (me.chaffLock and (me.guidance == "command" or me.guidance == "semi-radar" or me.guidance == "tvm") and (me.life_time - me.chaffLockTime) > (me.gnd_launch?4:6)) { me.chaffLock = 0; me.printStats(me.type~": Chaff dissipated, regained track."); @@ -4285,7 +4343,7 @@ var AIM = { notifyInFlight: func (lat,lon,alt,rdar,semiRdr,typeID,typ,unique,thrustOn,callsign, heading, pitch, speed, is_deleted=0) { ## thrustON cannot be named 'thrust' as FG for some reason will then think its a function (probably fixed by the way call() now is used) - var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, 21+typeID); + var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, damage.DamageRecipient.typeID2emesaryID(typeID)); if (lat != nil) { msg.Position.set_latlon(lat,lon,alt); } else { @@ -4324,7 +4382,7 @@ var AIM = { }, notifyHit: func (RelativeAltitude, Distance, callsign, Bearing, reason, typeID, type, self) { - var msg = notifications.ArmamentNotification.new("mhit", 4, 21+typeID); + var msg = notifications.ArmamentNotification.new("mhit", 4, damage.DamageRecipient.typeID2emesaryID(typeID)); msg.RelativeAltitude = RelativeAltitude; msg.Bearing = Bearing; msg.Distance = Distance; @@ -4516,11 +4574,27 @@ var AIM = { } }, + consumeBattery: func (curr_time) { + if (me.powerOnRequired) { + me.powerOnPassedTime = curr_time-me.powerOnLastCheck; + me.powerAvailable += (me.powerOn?-1:1)*me.powerOnPassedTime; + me.powerAvailable = math.clamp(me.powerAvailable, 0, me.powerOnBatteryTime); + me.powerOnLastCheck = curr_time; + } + }, + standby: func { # looping in standby mode if (deltaSec.getValue()==0) { + # paused settimer(func me.standby(), 0.5); + return; } + me.printCode("In standby(%d)",me.status); + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4541,8 +4615,11 @@ var AIM = { # looping in starting mode #print("startup"); if (deltaSec.getValue()==0) { + # Paused settimer(func me.startup(), 0.5); + return; } + me.printCode("In startup()"); if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4550,7 +4627,11 @@ var AIM = { me.standby(); return; } - if (me.ready_standby_time != 0 and getprop("sim/time/elapsed-sec") > (me.ready_standby_time+me.ready_time)) { + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + + if (me.ready_standby_time != 0 and me.hasPowerEnough() and ((!me.powerOnRequired and me.curr_time > (me.ready_standby_time+me.ready_time)) or (me.powerOnRequired and (me.powerOn and me.curr_time > me.power_on_time + me.ready_time)))) { me.status = MISSILE_SEARCH; me.search(); return; @@ -4752,6 +4833,7 @@ var AIM = { if (deltaSec.getValue()==0) { settimer(func me.search(), 0.5); } + me.printCode("In search()"); if (me.deleted) { return; } elsif ( me.status == MISSILE_FLYING ) { @@ -4765,13 +4847,21 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif ( me.status == MISSILE_LOCK) { # Locked. me.printSearch("in search loop, but locked!"); me.return_to_search(); return; } - + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("searching"); @@ -5130,6 +5220,15 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + me.reset_seeker(); + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif (!me.Tgt.isValid()) { # Lost of lock due to target disapearing: # return to search mode. @@ -5143,6 +5242,7 @@ var AIM = { me.return_to_search(); return; } + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("lock (caged:%d slave:%d point:%s same:%d noCommon:%d)",me.caged,me.slave_to_radar,contactPoint!=nil,me.getContact() == me.Tgt,me.noCommonTarget); # Time interval since lock time or last track loop. #if (me.status == MISSILE_LOCK) { @@ -5158,13 +5258,16 @@ var AIM = { me.coolingSyst(); me.computeSeekerPos(); if (me.status != MISSILE_STANDBY ) {#TODO: should this also check for starting up? - me.in_view = me.check_t_in_fov(); - if (!me.in_view) { - me.printSearch("out of view"); - me.return_to_search(); - return; - } + # This code does not seem needed anymore: + #me.in_view = me.check_t_in_fov(); + # + #if (!me.in_view) { + # if (me.noCommonTarget and me.guidance == "radiation") me.Tgt = nil;#Hack, todo fix this + # me.printSearch("out of view"); + # me.return_to_search(); + # return; + #} if (!me.caged or me.slave_to_radar) { me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); @@ -5184,7 +5287,8 @@ var AIM = { # Notice: seeker_xxxx_target is used both for denoting where seeker should move towards and where the target is. In this case its the latter: me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); me.testSeeker(); - if (!me.inBeam or (me.guidance == "semi-radar" and !me.is_painted(me.Tgt))) { + me.tagt = me.Tgt;# Used in checkForLock + if (!me.inBeam or !me.checkForLock()) { me.printSearch("out of beam or no beam for fox 1"); me.status = MISSILE_SEARCH; me.Tgt = nil; @@ -5298,6 +5402,7 @@ var AIM = { if (me.seeMe != nil and me.seeMe) { return 1; } + return target.isSpikingMe(); } return 0; }, @@ -5319,15 +5424,6 @@ var AIM = { me.moveSeeker(); }, - 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 { diff --git a/S-75/Nasal/vector.nas b/S-75/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/S-75/Nasal/vector.nas +++ b/S-75/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/SA-6/Nasal/ArmamentNotification.nas b/SA-6/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/SA-6/Nasal/ArmamentNotification.nas +++ b/SA-6/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/SA-6/Nasal/GeoBridgedTransmitter.nas b/SA-6/Nasal/GeoBridgedTransmitter.nas index cd69a17..2e73fbb 100644 --- a/SA-6/Nasal/GeoBridgedTransmitter.nas +++ b/SA-6/Nasal/GeoBridgedTransmitter.nas @@ -27,7 +27,7 @@ #i.e. change to [notifications.ArmamentInFlightNotification.new(nil), notifications.ArmamentNotification.new(nil)]; var geoRoutedNotifications = [notifications.ArmamentInFlightNotification.new()]; var geoBridgedTransmitter = emesary.Transmitter.new("geoOutgoingBridge"); -var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); +var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter); # bridge should be tuned to be around 90% of the packet size full. geooutgoingBridge.TransmitFrequencySeconds = 0.75; @@ -38,7 +38,7 @@ emesary_mp_bridge.IncomingMPBridge.startMPBridge(geoRoutedNotifications, 18, eme #----- bridge hit (armament) notifications var hitRoutedNotifications = [notifications.ArmamentNotification.new(),notifications.StaticNotification.new()]; var hitBridgedTransmitter = emesary.Transmitter.new("armamentNotificationBridge"); -var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); +var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter); hitoutgoingBridge.TransmitFrequencySeconds = 1.5; hitoutgoingBridge.MPStringMaxLen = 120; emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, emesary.GlobalTransmitter); @@ -46,8 +46,38 @@ emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, eme #----- bridge object notifications var objectRoutedNotifications = [notifications.ObjectInFlightNotification.new()]; var objectBridgedTransmitter = emesary.Transmitter.new("objectNotificationBridge"); -var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); +var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("mp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter); objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; -emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); \ No newline at end of file +emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/SA-6/Nasal/damage.nas b/SA-6/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/SA-6/Nasal/damage.nas +++ b/SA-6/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/SA-6/Nasal/datalink.nas b/SA-6/Nasal/datalink.nas index 7b88482..0f01161 100644 --- a/SA-6/Nasal/datalink.nas +++ b/SA-6/Nasal/datalink.nas @@ -678,4 +678,4 @@ var decode_point = func(aircrafts_data, callsign, str) { return aircrafts_data; } -register_extension("point", "P", ContactPoint, encode_point, decode_point); \ No newline at end of file +register_extension("point", "P", ContactPoint, encode_point, decode_point); diff --git a/SA-6/Nasal/guided-missiles.nas b/SA-6/Nasal/guided-missiles.nas index 98f3b94..0c83c1d 100644 --- a/SA-6/Nasal/guided-missiles.nas +++ b/SA-6/Nasal/guided-missiles.nas @@ -153,19 +153,11 @@ var slugs_to_lbm = SLUGS2LBM;# since various aircraft use this from outside miss var first_in_air = 0;# first missile is in the air, other missiles should not write to MP. var first_in_air_max_sec = 30; -var versionString = getprop("sim/version/flightgear"); -var version = split(".", versionString); -var major = num(version[0]); -var minor = num(version[1]); -var pica = num(version[2]); -var pickingMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - pickingMethod = 1; -} -var offsetMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - offsetMethod = 1; -} +#var versionString = getprop("sim/version/flightgear"); +#var version = split(".", versionString); +#var major = num(version[0]); +#var minor = num(version[1]); +#var pica = num(version[2]); var wingedGuideFactor = 0.1; @@ -193,27 +185,28 @@ var contactPoint = nil; # 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() +# getElevation() - Pitch to target from launch vehicle +# get_bearing() - Bearing to target from launch vehicle +# get_Callsign() +# get_range() - NM # get_Coord() -# get_altitude() +# get_altitude() - FT # get_Pitch() # get_Speed() # get_heading() -# get_uBody() -# get_vBody() -# get_wBody() +# get_uBody() - Body velocities in ft/s. Forward component. +# get_vBody() - Right component +# get_wBody() - Down component # getLastGroundTrackBlep() - Used for sample guidance # getFlareNode() - Used for flares. # getChaffNode() - Used for chaff. # isPainted() - Tells if this target is still being radar tracked by the launch platform, only used in semi-radar guided missiles. # isLaserPainted() - Tells if this target is still being tracked by the launch platform, only used by laser guided ordnance. # isRadiating(coord) - Tell if anti-radiation missile is hit by radiation from target. coord is the weapon position. -# isCommandActive() +# isSpikingMe() - Tells if the target is STT locked on launch platform. +# isCommandActive() - If surface based launcher is still tracking (command guidance) or have a search blep (TVM) on target. # isVirtual() - Tells if the target is just a position, and should not be considered for damage. -# get_closure_rate() - closure rate in kt +# get_closure_rate() - closure rate in kt to launch platform var AIM = { lowestETA: nil, @@ -312,6 +305,8 @@ var AIM = { m.radarZ = getprop(m.nodeString~"FCS-z"); # In future I will add direction to it also, for now its center gimbal is along -x axis. m.expand_min = getprop(m.nodeString~"expand-min-fire-range"); # Bool. Default false. If min fire range should expand with closing rate. Mainly use this for A/A missiles. m.asc = getprop(m.nodeString~"attack-steering-cue-enabled");# Bool. ASC enabled. + m.powerOnRequired = getprop(m.nodeString~"requires-power-on"); # Bool. ASC enabled. + m.powerOnBatteryTime = getprop(m.nodeString~"power-on-battery-time"); # Seconds. # navigation, guiding and seekerhead m.max_seeker_dev = getprop(m.nodeString~"seeker-field-deg") / 2; # missiles own seekers total FOV diameter. m.guidance = getprop(m.nodeString~"guidance"); # heat/radar/semi-radar/tvm/laser/gps/gps-laser/vision/unguided/level/gyro-pitch/radiation/inertial/remote/remote-stable/command/sample @@ -412,6 +407,14 @@ var AIM = { m.eject_speed = 0; } + if (m.powerOnRequired == nil) { + m.powerOnRequired = 0; + } + + if (m.powerOnBatteryTime == nil) { + m.powerOnBatteryTime = 0;# infinite + } + if (m.rail_forward == 1) { m.rail_pitch_deg = 0; m.rail_head_deg = 0; @@ -720,6 +723,7 @@ var AIM = { m.contacts = [];# contacts that should be considered to lock onto. In slave it will only lock to the first. m.warm = 1;# normalized warm/cold m.ready_standby_time = 0;# time when started from standby + m.power_on_time = 0;# time when started from standby m.cooling = 0; m.slave_to_radar = m.seam_support?1:0; m.seeker_last_time = 0; @@ -729,6 +733,11 @@ var AIM = { m.cooling_last_time = 0; m.cool_total_time = 0; + m.powerOn = 0; + m.powerAvailable = m.powerOnBatteryTime; + m.powerOnPassedTime = 0; + m.powerOnLastCheck = 0; + # # Emesary damage system # @@ -1351,7 +1360,11 @@ var AIM = { # do NOT call this after launch # see also release(vect) if (me.status == MISSILE_FLYING) return; - me.contacts = vect; + me.contacts = vect; + }, + + clearTgt: func { + me.Tgt = nil; }, commandDir: func (heading_deg, pitch_deg) { @@ -1408,6 +1421,28 @@ var AIM = { return me.cooling; }, + togglePowerOn: func { + me.powerOn = !me.powerOn; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + setPowerOn: func (enable) { + me.powerOn = enable; + if (me.powerOn) { + me.power_on_time = getprop("sim/time/elapsed-sec"); + } + }, + + isPowerOn: func { + return me.powerOn; + }, + + hasPowerEnough: func { + return !me.powerOnRequired or me.powerOnBatteryTime == 0 or me.powerAvailable > me.powerOnBatteryTime * 0.05; + }, + start: func { if (me.status == MISSILE_STANDBY) { me.status = MISSILE_STARTING; @@ -1421,9 +1456,17 @@ var AIM = { if (me.status != MISSILE_FLYING) { me.status = MISSILE_STANDBY; #printf("stop #%3d %s", me.ID, me.type); + if (me.guidance == "radiation") { + me.Tgt = nil; + me.contacts = []; + } } }, + getStatus: func { + return me.status; + }, + isCaged: func () { if (!me.seam_support) return me.status != MISSILE_FLYING; return me.caged; @@ -1484,6 +1527,9 @@ var AIM = { me.elapsed_last = systime(); me.status = MISSILE_FLYING; + me.powerOnLastCheck = 0;# switch to use me.life_time from elapsed sim time. + me.powerOn = 1;# If pilot releases while power off, we make sure it on now. + if (vect!= nil) { # sets a vector of contacts the weapons will try to lock onto @@ -1549,7 +1595,7 @@ var AIM = { me.railEnd = vector.Math.plus(me.railBegin, vector.Math.product(me.rail_dist_m, me.railvec)); } } - if (offsetMethod and (!me.rail or me.rail_forward)) { + if (!me.rail or me.rail_forward) { var pos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); init_coord = geo.Coord.new(); init_coord.set_xyz(pos.x, pos.y, pos.z); @@ -2094,6 +2140,13 @@ var AIM = { me.life_time += me.dt; + me.consumeBattery(me.life_time); + if (!me.hasPowerEnough()) { + # Smart weapon reduced to dumb weapon + me.free = 1; + me.printStats("Ran out of battery, stopped guiding"); + } + me.handleMidFlightFunc(); if (me.hasGuided and me.maddog) { @@ -2394,12 +2447,8 @@ var AIM = { # missile on rail, lets move it on the rail if (me.rail_forward) { var init_coord = nil; - if (offsetMethod) { - me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); - me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); - } else { - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); - } + me.geodPos = aircraftToCart({x:-me.x, y:me.y, z: -me.z}); + me.coord.set_xyz(me.geodPos.x, me.geodPos.y, me.geodPos.z); } else { me.coord = me.getGPS(-me.railPos[0], -me.railPos[1], me.railPos[2], OurPitch.getValue(), OurHdg.getValue()); } @@ -2723,8 +2772,13 @@ var AIM = { } else { me.remote_control_pitch = 0; } - if (me.settings["abort_midflight_function"] != nil) { + if (me.settings["seeker_fov"] != nil) { + me.max_seeker_dev = me.settings.seeker_fov; + me.printStats("Seeker FOV switched to %s",me.max_seeker_dev); + } + if (me.settings["abort_midflight_function"] == 1) { me.mfFunction = nil; + me.printStats("Midflight function finished"); } } }, @@ -2872,7 +2926,7 @@ var AIM = { } if (!me.simple_drag) { - if (me.vector_thrust and me.thrust_lbf>0) N=N*0.35; + if (me.vector_thrust and me.thrust_lbf>0) N=N*0.15; if (mach < 1.1) { me.Cdi = (me.Cd_base+me.Cd_delta*me.deploy)*N;# N = normal force in G } else { @@ -3337,7 +3391,7 @@ var AIM = { }, checkForLOS: func () { - if (pickingMethod and me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { + if (me.guidance != "gps" and me.guidance != "gps-laser" and me.guidance != "unguided" and me.guidance != "inertial" and me.guidance != "sample") { me.xyz = {"x":me.coord.x(), "y":me.coord.y(), "z":me.coord.z()}; me.directionLOS = {"x":me.t_coord.x()-me.coord.x(), "y":me.t_coord.y()-me.coord.y(), "z":me.t_coord.z()-me.coord.z()}; @@ -3456,6 +3510,10 @@ var AIM = { me.printStats(me.type~": Passed minimum speed for guiding after %.1f seconds. Target %d%% inside view.", me.life_time, me.normFOV*100); } } + if (me.guidance == "gps" or me.guidance == "inertial" or me.guidance == "sample") { + # To make sure deviation_deg is updated in midflight function. + me.FOV_check(me.hdg, me.pitch, me.curr_deviation_h, me.curr_deviation_e, me.max_seeker_dev, me.myMath); + } if (me.chaffLock and (me.guidance == "command" or me.guidance == "semi-radar" or me.guidance == "tvm") and (me.life_time - me.chaffLockTime) > (me.gnd_launch?4:6)) { me.chaffLock = 0; me.printStats(me.type~": Chaff dissipated, regained track."); @@ -4285,7 +4343,7 @@ var AIM = { notifyInFlight: func (lat,lon,alt,rdar,semiRdr,typeID,typ,unique,thrustOn,callsign, heading, pitch, speed, is_deleted=0) { ## thrustON cannot be named 'thrust' as FG for some reason will then think its a function (probably fixed by the way call() now is used) - var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, 21+typeID); + var msg = notifications.ArmamentInFlightNotification.new("mfly", unique, is_deleted?damage.DESTROY:damage.MOVE, damage.DamageRecipient.typeID2emesaryID(typeID)); if (lat != nil) { msg.Position.set_latlon(lat,lon,alt); } else { @@ -4324,7 +4382,7 @@ var AIM = { }, notifyHit: func (RelativeAltitude, Distance, callsign, Bearing, reason, typeID, type, self) { - var msg = notifications.ArmamentNotification.new("mhit", 4, 21+typeID); + var msg = notifications.ArmamentNotification.new("mhit", 4, damage.DamageRecipient.typeID2emesaryID(typeID)); msg.RelativeAltitude = RelativeAltitude; msg.Bearing = Bearing; msg.Distance = Distance; @@ -4516,11 +4574,27 @@ var AIM = { } }, + consumeBattery: func (curr_time) { + if (me.powerOnRequired) { + me.powerOnPassedTime = curr_time-me.powerOnLastCheck; + me.powerAvailable += (me.powerOn?-1:1)*me.powerOnPassedTime; + me.powerAvailable = math.clamp(me.powerAvailable, 0, me.powerOnBatteryTime); + me.powerOnLastCheck = curr_time; + } + }, + standby: func { # looping in standby mode if (deltaSec.getValue()==0) { + # paused settimer(func me.standby(), 0.5); + return; } + me.printCode("In standby(%d)",me.status); + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4541,8 +4615,11 @@ var AIM = { # looping in starting mode #print("startup"); if (deltaSec.getValue()==0) { + # Paused settimer(func me.startup(), 0.5); + return; } + me.printCode("In startup()"); if(me.seam_support and me.uncage_auto) { me.caged = 1; } @@ -4550,7 +4627,11 @@ var AIM = { me.standby(); return; } - if (me.ready_standby_time != 0 and getprop("sim/time/elapsed-sec") > (me.ready_standby_time+me.ready_time)) { + + me.curr_time = getprop("sim/time/elapsed-sec"); + me.consumeBattery(me.curr_time); + + if (me.ready_standby_time != 0 and me.hasPowerEnough() and ((!me.powerOnRequired and me.curr_time > (me.ready_standby_time+me.ready_time)) or (me.powerOnRequired and (me.powerOn and me.curr_time > me.power_on_time + me.ready_time)))) { me.status = MISSILE_SEARCH; me.search(); return; @@ -4752,6 +4833,7 @@ var AIM = { if (deltaSec.getValue()==0) { settimer(func me.search(), 0.5); } + me.printCode("In search()"); if (me.deleted) { return; } elsif ( me.status == MISSILE_FLYING ) { @@ -4765,13 +4847,21 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif ( me.status == MISSILE_LOCK) { # Locked. me.printSearch("in search loop, but locked!"); me.return_to_search(); return; } - + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("searching"); @@ -5130,6 +5220,15 @@ var AIM = { #me.trackWeak = 1; me.standby(); return; + } elsif (me.powerOnRequired and (!me.powerOn or (me.powerOnBatteryTime > 0 and me.powerAvailable < me.powerOnBatteryTime * 0.05))) { + me.status = MISSILE_STARTING; + me.reset_seeker(); + # Stand by. + me.SwSoundVol.setDoubleValue(0); + me.SwSoundOnOff.setBoolValue(0); + #me.trackWeak = 1; + me.standby(); + return; } elsif (!me.Tgt.isValid()) { # Lost of lock due to target disapearing: # return to search mode. @@ -5143,6 +5242,7 @@ var AIM = { me.return_to_search(); return; } + me.consumeBattery(getprop("sim/time/elapsed-sec")); me.printSearch("lock (caged:%d slave:%d point:%s same:%d noCommon:%d)",me.caged,me.slave_to_radar,contactPoint!=nil,me.getContact() == me.Tgt,me.noCommonTarget); # Time interval since lock time or last track loop. #if (me.status == MISSILE_LOCK) { @@ -5158,13 +5258,16 @@ var AIM = { me.coolingSyst(); me.computeSeekerPos(); if (me.status != MISSILE_STANDBY ) {#TODO: should this also check for starting up? - me.in_view = me.check_t_in_fov(); - if (!me.in_view) { - me.printSearch("out of view"); - me.return_to_search(); - return; - } + # This code does not seem needed anymore: + #me.in_view = me.check_t_in_fov(); + # + #if (!me.in_view) { + # if (me.noCommonTarget and me.guidance == "radiation") me.Tgt = nil;#Hack, todo fix this + # me.printSearch("out of view"); + # me.return_to_search(); + # return; + #} if (!me.caged or me.slave_to_radar) { me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); @@ -5184,7 +5287,8 @@ var AIM = { # Notice: seeker_xxxx_target is used both for denoting where seeker should move towards and where the target is. In this case its the latter: me.convertGlobalToSeekerViewDirection(me.Tgt.get_bearing(), me.Tgt.getElevation(), OurHdg.getValue(), OurPitch.getValue(), OurRoll.getValue()); me.testSeeker(); - if (!me.inBeam or (me.guidance == "semi-radar" and !me.is_painted(me.Tgt))) { + me.tagt = me.Tgt;# Used in checkForLock + if (!me.inBeam or !me.checkForLock()) { me.printSearch("out of beam or no beam for fox 1"); me.status = MISSILE_SEARCH; me.Tgt = nil; @@ -5298,6 +5402,7 @@ var AIM = { if (me.seeMe != nil and me.seeMe) { return 1; } + return target.isSpikingMe(); } return 0; }, @@ -5319,15 +5424,6 @@ var AIM = { me.moveSeeker(); }, - 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 { diff --git a/SA-6/Nasal/vector.nas b/SA-6/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/SA-6/Nasal/vector.nas +++ b/SA-6/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/depot/Nasal/ArmamentNotification.nas b/depot/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/depot/Nasal/ArmamentNotification.nas +++ b/depot/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/depot/Nasal/GeoBridgedTransmitter.nas b/depot/Nasal/GeoBridgedTransmitter.nas index 973e4a4..2e73fbb 100644 --- a/depot/Nasal/GeoBridgedTransmitter.nas +++ b/depot/Nasal/GeoBridgedTransmitter.nas @@ -51,3 +51,33 @@ objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/depot/Nasal/damage.nas b/depot/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/depot/Nasal/damage.nas +++ b/depot/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/depot/Nasal/vector.nas b/depot/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/depot/Nasal/vector.nas +++ b/depot/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/gci-radar/Nasal/ArmamentNotification.nas b/gci-radar/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/gci-radar/Nasal/ArmamentNotification.nas +++ b/gci-radar/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/gci-radar/Nasal/GeoBridgedTransmitter.nas b/gci-radar/Nasal/GeoBridgedTransmitter.nas index 973e4a4..2e73fbb 100644 --- a/gci-radar/Nasal/GeoBridgedTransmitter.nas +++ b/gci-radar/Nasal/GeoBridgedTransmitter.nas @@ -51,3 +51,33 @@ objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/gci-radar/Nasal/damage.nas b/gci-radar/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/gci-radar/Nasal/damage.nas +++ b/gci-radar/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/gci-radar/Nasal/radar-logic.nas b/gci-radar/Nasal/radar-logic.nas index 0a43172..6b92c8b 100644 --- a/gci-radar/Nasal/radar-logic.nas +++ b/gci-radar/Nasal/radar-logic.nas @@ -1,427 +1,427 @@ -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 versionString = getprop("sim/version/flightgear"); -var version = split(".", versionString); -var major = num(version[0]); -var minor = num(version[1]); -var pica = num(version[2]); -var pickingMethod = 0; -if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { - pickingMethod = 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.roll = obj.oriProp.getNode("roll-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.ubody = obj.rdrProp.getNode("velocities/uBody-fps"); - obj.vbody = obj.rdrProp.getNode("velocities/vBody-fps"); - obj.wbody = obj.rdrProp.getNode("velocities/wBody-fps"); -#}); - obj.deviation = nil; - - obj.node = c; - obj.class = class; - - obj.polar = [0,0]; - obj.cartesian = [0,0]; - - return obj; - }, - - get_uBody: func { - var body = nil; - if (me.ubody != nil) { - body = me.ubody.getValue(); - } - if(body == nil) { - body = me.get_Speed()*KT2FPS; - } - return body; - }, - - get_vBody: func { - var body = nil; - if (me.ubody != nil) { - body = me.vbody.getValue(); - } - if(body == nil) { - body = 0; - } - return body; - }, - - get_wBody: func { - var body = nil; - if (me.ubody != nil) { - body = me.wbody.getValue(); - } - if(body == nil) { - body = 0; - } - return body; - }, - - isValid: func () { - var valid = me.valid.getValue(); - if (valid == nil) { - valid = 0; - } - if (me.callsign.getValue() != me.orig_callsign) { - valid = 0; - } - return valid; - }, - - isPainted: func () { - if (getprop("/carrier/sunk") == 1) { - return 0; - } - return isNotBehindTerrain(me.node)[0]; - }, - - isCommandActive: func { - if (getprop("/carrier/sunk") == 1) { - return 0; - } - return isNotBehindTerrain(me.node)[0]; - }, - - 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(); - self.set_alt(self.alt()+fire_control.radar_elevation_above_terrain_m); - 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("rotors/main/blade[3]/flap-deg"); - }, - - getChaffNode: func () { - return me.node.getNode("rotors/main/blade[3]/position-deg"); - }, - - 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; - }, - - isVirtual: func(){ - return 0; - }, - - get_Roll: func(){ - var n = me.roll.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 [0,0]; # invisible and not in front of terrain - } - var aircraftPos = geo.Coord.new().set_latlon(lat, lon, alt*FT2M); - - var myPos = geo.aircraft_position(); - - myPos.set_alt(myPos.alt()+fire_control.radar_elevation_above_terrain_m); - - var target_distance = myPos.direct_distance_to(aircraftPos); - - call(func { - if (target_distance*0.001 > 3.57*(math.sqrt(myPos.alt())+math.sqrt(alt*FT2M))) { - # behind earth curvature - #print("behind earth curvature"); - return [0,0]; # invisible and not in front of terrain - } - },nil,nil,var err =[]); - - var xyz = {"x":myPos.x(), "y":myPos.y(), "z":myPos.z()}; - var dir = {"x":aircraftPos.x()-myPos.x(), "y":aircraftPos.y()-myPos.y(), "z":aircraftPos.z()-myPos.z()}; - - # Check for terrain between own aircraft and other: - v = get_cart_ground_intersection(xyz, dir); - if (v == nil) { - return [1,0]; # visible and not in front of terrain - #printf("No terrain, planes has clear view of each other"); - } else { - var terrain = geo.Coord.new(); - terrain.set_latlon(v.lat, v.lon, v.elevation); - var maxDist = myPos.direct_distance_to(aircraftPos); - var terrainDist = myPos.direct_distance_to(terrain); - if (terrainDist < maxDist-1) { - #printf("terrain found between me and aircraft %.1f meter away.", terrainDist); - return [0,0]; # visible and not in front of terrain - } else { - #print("The planes has clear view of each other"); - return [1,1]; # visible and in front of terrain - } - } +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 versionString = getprop("sim/version/flightgear"); +var version = split(".", versionString); +var major = num(version[0]); +var minor = num(version[1]); +var pica = num(version[2]); +var pickingMethod = 0; +if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) { + pickingMethod = 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.roll = obj.oriProp.getNode("roll-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.ubody = obj.rdrProp.getNode("velocities/uBody-fps"); + obj.vbody = obj.rdrProp.getNode("velocities/vBody-fps"); + obj.wbody = obj.rdrProp.getNode("velocities/wBody-fps"); +#}); + obj.deviation = nil; + + obj.node = c; + obj.class = class; + + obj.polar = [0,0]; + obj.cartesian = [0,0]; + + return obj; + }, + + get_uBody: func { + var body = nil; + if (me.ubody != nil) { + body = me.ubody.getValue(); + } + if(body == nil) { + body = me.get_Speed()*KT2FPS; + } + return body; + }, + + get_vBody: func { + var body = nil; + if (me.ubody != nil) { + body = me.vbody.getValue(); + } + if(body == nil) { + body = 0; + } + return body; + }, + + get_wBody: func { + var body = nil; + if (me.ubody != nil) { + body = me.wbody.getValue(); + } + if(body == nil) { + body = 0; + } + return body; + }, + + isValid: func () { + var valid = me.valid.getValue(); + if (valid == nil) { + valid = 0; + } + if (me.callsign.getValue() != me.orig_callsign) { + valid = 0; + } + return valid; + }, + + isPainted: func () { + if (getprop("/carrier/sunk") == 1) { + return 0; + } + return isNotBehindTerrain(me.node)[0]; + }, + + isCommandActive: func { + if (getprop("/carrier/sunk") == 1) { + return 0; + } + return isNotBehindTerrain(me.node)[0]; + }, + + 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(); + self.set_alt(self.alt()+fire_control.radar_elevation_above_terrain_m); + 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("rotors/main/blade[3]/flap-deg"); + }, + + getChaffNode: func () { + return me.node.getNode("rotors/main/blade[3]/position-deg"); + }, + + 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; + }, + + isVirtual: func(){ + return 0; + }, + + get_Roll: func(){ + var n = me.roll.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 [0,0]; # invisible and not in front of terrain + } + var aircraftPos = geo.Coord.new().set_latlon(lat, lon, alt*FT2M); + + var myPos = geo.aircraft_position(); + + myPos.set_alt(myPos.alt()+fire_control.radar_elevation_above_terrain_m); + + var target_distance = myPos.direct_distance_to(aircraftPos); + + call(func { + if (target_distance*0.001 > 3.57*(math.sqrt(myPos.alt())+math.sqrt(alt*FT2M))) { + # behind earth curvature + #print("behind earth curvature"); + return [0,0]; # invisible and not in front of terrain + } + },nil,nil,var err =[]); + + var xyz = {"x":myPos.x(), "y":myPos.y(), "z":myPos.z()}; + var dir = {"x":aircraftPos.x()-myPos.x(), "y":aircraftPos.y()-myPos.y(), "z":aircraftPos.z()-myPos.z()}; + + # Check for terrain between own aircraft and other: + v = get_cart_ground_intersection(xyz, dir); + if (v == nil) { + return [1,0]; # visible and not in front of terrain + #printf("No terrain, planes has clear view of each other"); + } else { + var terrain = geo.Coord.new(); + terrain.set_latlon(v.lat, v.lon, v.elevation); + var maxDist = myPos.direct_distance_to(aircraftPos); + var terrainDist = myPos.direct_distance_to(terrain); + if (terrainDist < maxDist-1) { + #printf("terrain found between me and aircraft %.1f meter away.", terrainDist); + return [0,0]; # visible and not in front of terrain + } else { + #print("The planes has clear view of each other"); + return [1,1]; # visible and in front of terrain + } + } } \ No newline at end of file diff --git a/gci-radar/Nasal/vector.nas b/gci-radar/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/gci-radar/Nasal/vector.nas +++ b/gci-radar/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start(); diff --git a/groundtarget/Nasal/ArmamentNotification.nas b/groundtarget/Nasal/ArmamentNotification.nas index e0d2bb5..5bcb62a 100644 --- a/groundtarget/Nasal/ArmamentNotification.nas +++ b/groundtarget/Nasal/ArmamentNotification.nas @@ -87,6 +87,48 @@ var ArmamentInFlightNotification = new_class.Flags = 0; # 8 bits for whatever. new_class.UniqueIdentity = _unique; + new_class.isValid = func { + var valid = ""; + var limitMin = -124; + var limitMax = 123; + # 1 byte integer + if (int(me.Kind) > limitMax or int(me.Kind) < limitMin) { + valid ~= "|ArmamentInFlight.Kind is "~me.Kind; + } + # 1 byte integer + if (int(me.SecondaryKind) > limitMax or int(me.SecondaryKind) < limitMin) { + valid ~= "|ArmamentInFlight.SecondaryKind is "~me.SecondaryKind; + } + # 1 byte float + if (int((me.u_fps-3348)*0.03703) > limitMax or int((me.u_fps-3348)*0.03703) < limitMin) { + valid ~= "|ArmamentInFlight.u_fps is "~me.u_fps; + } + # 1 byte float + if (int(me.Pitch*1.38) > limitMax or int(me.Pitch*1.38) < limitMin) { + valid ~= "|ArmamentInFlight.Pitch is "~me.Pitch; + } + # 1 byte float + if (int(geo.normdeg180(me.Heading)/1.54) > limitMax or int(geo.normdeg180(me.Heading)/1.54) < limitMin) { + valid ~= "|ArmamentInFlight.Heading is "~me.Heading; + } + # 1 byte integer + if (int(me.Flags) > limitMax or int(me.Flags) < limitMin) { + valid ~= "|ArmamentInFlight.Flags is "~me.Flags; + } + # 1 byte integer + if (int(me.UniqueIdentity) > limitMax or int(me.UniqueIdentity) < limitMin) { + valid ~= "|ArmamentInFlight.UniqueIdentity is "~me.UniqueIdentity; + } + # 7 byte string + if (size(me.RemoteCallsign) > 7) { + valid ~= "|ArmamentInFlight.RemoteCallsign is "~me.RemoteCallsign; + } + if (valid != "") { + print("\nEMESARY ENCODING WILL FAIL:"); + print(" "~valid); + } + }; + new_class.GetBridgeMessageNotificationTypeKey = func { return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex; }; @@ -257,4 +299,4 @@ var ObjectInFlightNotification = }; return new_class; }, -}; \ No newline at end of file +}; diff --git a/groundtarget/Nasal/GeoBridgedTransmitter.nas b/groundtarget/Nasal/GeoBridgedTransmitter.nas index 973e4a4..2e73fbb 100644 --- a/groundtarget/Nasal/GeoBridgedTransmitter.nas +++ b/groundtarget/Nasal/GeoBridgedTransmitter.nas @@ -51,3 +51,33 @@ objectoutgoingBridge.TransmitFrequencySeconds = 0.2; objectoutgoingBridge.MessageLifeTime = 1; objectoutgoingBridge.MPStringMaxLen = 150; emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter); + +# +# debug all messages - this can be removed when testing isn't required. +var debugRecipient = emesary.Recipient.new("Debug"); +debugRecipient.Receive = func(notification) +{ + if (notification.NotificationType != "FrameNotification") { + print ("recv(0): type=",notification.NotificationType, " fromIncoming=",notification.FromIncomingBridge); + + if (notification.NotificationType == "ArmamentInFlightNotification") { + print("recv(1): ",notification.NotificationType, " ", notification.Ident); + debug.dump(notification); + + } else if (notification.NotificationType == "ArmamentNotification") { + if (notification.FromIncomingBridge) { + print("recv(2): ",notification.NotificationType, " ", notification.Ident, + " Kind=",notification.Kind, + " SecondaryKind=",notification.SecondaryKind, + " RelativeAltitude=",notification.RelativeAltitude, + " Distance=",notification.Distance, + " Bearing=",notification.Bearing, + " RemoteCallsign=",notification.RemoteCallsign); + debug.dump(notification); + } + } + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; # we're not processing it, just looking +} +# uncomment next line to activate debug recipient. +#emesary.GlobalTransmitter.Register(debugRecipient); \ No newline at end of file diff --git a/groundtarget/Nasal/damage.nas b/groundtarget/Nasal/damage.nas index 81e859c..2c7f0e3 100644 --- a/groundtarget/Nasal/damage.nas +++ b/groundtarget/Nasal/damage.nas @@ -27,7 +27,7 @@ var mlw_max=getprop("payload/d-config/mlw_max"); # var auto_flare_caller = getprop("payload/d-config/auto_flare_caller"); # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft ############################################################################################################################ - +srand(); var hp = hp_max; setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD @@ -60,6 +60,7 @@ var shells = { "DEFA 554": [14,0.060], # 30x113mm Mirage, 220g "20mm APDS": [15,0.030], # CIWS "LAU-10": [16,0.500], # 127mm, ~4-7kg warhead + # Max id is 41 }; # lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. @@ -165,6 +166,8 @@ var warheads = { "3M9": [96, 125.00,0,0],# 3M9M3 Missile used with 2K12/SA-6 "5V28V": [97, 478.00,0,0],# Missile used with S-200D/SA-5 "AIM-9X": [98, 20.80,0,0], + "R-23R": [99, 55.00,0,0],# mig23 fox 1 + # Max id is 180 }; var AIR_RADAR = "air"; @@ -182,9 +185,12 @@ var radar_signatures = { "m2000-5B": AIR_RADAR, "MiG-21bis": AIR_RADAR, "MiG-21MF-75": AIR_RADAR, + "Mig-23MLD": AIR_RADAR, "MiG-29": AIR_RADAR, "SU-27": AIR_RADAR, "EC-137R": AIR_RADAR, + "E-3R": AIR_RADAR, + "E-3": AIR_RADAR, "RC-137R": AIR_RADAR, "E-8R": AIR_RADAR, "EC-137D": AIR_RADAR, @@ -193,7 +199,7 @@ var radar_signatures = { "s-200": "gnd-05", "ZSU-23-4M": "gnd-23", "S-75": "gnd-02", - "buk-m2": "gnd-11", + "buk-m2": "gnd-17", "s-300": "gnd-20", "MIM104D": "gnd-p2", "missile_frigate": "gnd-nk", @@ -308,7 +314,7 @@ var DamageRecipient = var radarOn = bits.test(notification.Flags, 0); var thrustOn = bits.test(notification.Flags, 1); var CWIOn = bits.test(notification.Flags, 2); - var index = notification.SecondaryKind-21; + var index = DamageRecipient.emesaryID2typeID(notification.SecondaryKind); var typ = id2warhead[index]; if (notification.Kind == MOVE) { @@ -402,6 +408,7 @@ var DamageRecipient = setprop("payload/armament/MAW-active", 1);# resets every 1 seconds } elsif (CWIOn) { setprop("payload/armament/MAW-semiactive", 1);# resets every 1 seconds + if (notification.Callsign != nil) setprop("payload/armament/MAW-semiactive-callsign", notification.Callsign);# resets every 1 seconds } MAW_elapsed = elapsed; var appr = approached[notification.Callsign~notification.UniqueIdentity]; @@ -435,9 +442,9 @@ var DamageRecipient = # if (tacview_supported and tacview.starttime and (getprop("sim/multiplay/txhost") != "mpserver.opredflag.com" or m28_auto)) { var node = getCallsign(notification.RemoteCallsign); - if (node != nil and notification.SecondaryKind > 20) { + if (node != nil and (notification.SecondaryKind > 20 or notification.SecondaryKind < -40)) { # its a warhead - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var lbs = wh[1]; var hitCoord = geo.Coord.new(); hitCoord.set_latlon(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M+notification.RelativeAltitude); @@ -474,11 +481,11 @@ var DamageRecipient = damageLog.push(sprintf("%s hit you with %d %s.", notification.Callsign, hit_count, typ)); nearby_explosion(); } - } elsif (notification.SecondaryKind > 20) { + } elsif (notification.SecondaryKind > 20 or notification.SecondaryKind < -40) { # its a warhead if (m28_auto) mig28.engagedBy(notification.Callsign, 1); var dist = notification.Distance; - var wh = id2warhead[notification.SecondaryKind - 21]; + var wh = id2warhead[DamageRecipient.emesaryID2typeID(notification.SecondaryKind)]; var type = wh[4];#test code if (wh[3] == 1) { # cluster munition @@ -603,7 +610,29 @@ var DamageRecipient = return emesary.Transmitter.ReceiptStatus_NotProcessed; }; return new_class; - } + }, + + typeID2emesaryID: func (typeID) { + if (typeID <= 100) { + return typeID + 21; + } elsif (typeID <= 180) { + return (typeID - 100) * -1 - 40; + } else { + print("Missile TypeID too large value, max is 180"); + return 0; + } + }, + + emesaryID2typeID: func (emesaryID) { + if (emesaryID > 20) { + return emesaryID - 21; + } elsif (emesaryID < -40) { + return (emesaryID + 40) * -1 + 100; + } else { + print("Missile emesaryID not a warhead"); + return 0; + } + }, }; damage_recipient = DamageRecipient.new("DamageRecipient"); @@ -1107,6 +1136,46 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a } } } + if (rand() < probability and rand() < probability and getprop("sim/flight-model") == "yasim") { + setprop("consumables/fuel/tank[0]/level-norm", 0); + setprop("consumables/fuel/tank[1]/level-norm", 0); + setprop("consumables/fuel/tank[2]/level-norm", 0); + setprop("consumables/fuel/tank[3]/level-norm", 0); + setprop("consumables/fuel/tank[4]/level-norm", 0); + setprop("consumables/fuel/tank[5]/level-norm", 0); + setprop("consumables/fuel/tank[6]/level-norm", 0); + setprop("consumables/fuel/tank[7]/level-norm", 0); + setprop("consumables/fuel/tank[8]/level-norm", 0); + setprop("consumables/fuel/tank[9]/level-norm", 0); + setprop("consumables/fuel/tank[10]/level-norm", 0); + setprop("consumables/fuel/tank[11]/level-norm", 0); + # Somtimes these needs to be set too: + setprop("consumables/fuel/tank[0]/level-lbs", 0); + setprop("consumables/fuel/tank[1]/level-lbs", 0); + setprop("consumables/fuel/tank[2]/level-lbs", 0); + setprop("consumables/fuel/tank[3]/level-lbs", 0); + setprop("consumables/fuel/tank[4]/level-lbs", 0); + setprop("consumables/fuel/tank[5]/level-lbs", 0); + setprop("consumables/fuel/tank[6]/level-lbs", 0); + setprop("consumables/fuel/tank[7]/level-lbs", 0); + setprop("consumables/fuel/tank[8]/level-lbs", 0); + setprop("consumables/fuel/tank[9]/level-lbs", 0); + setprop("consumables/fuel/tank[10]/level-lbs", 0); + setprop("consumables/fuel/tank[11]/level-lbs", 0); + # these will make the fraction indicator in fuel dialog not work after relocation, but they are needed: + setprop("consumables/fuel/tank[0]/capacity-m3", 0); + setprop("consumables/fuel/tank[1]/capacity-m3", 0); + setprop("consumables/fuel/tank[2]/capacity-m3", 0); + setprop("consumables/fuel/tank[3]/capacity-m3", 0); + setprop("consumables/fuel/tank[4]/capacity-m3", 0); + setprop("consumables/fuel/tank[5]/capacity-m3", 0); + setprop("consumables/fuel/tank[6]/capacity-m3", 0); + setprop("consumables/fuel/tank[7]/capacity-m3", 0); + setprop("consumables/fuel/tank[8]/capacity-m3", 0); + setprop("consumables/fuel/tank[9]/capacity-m3", 0); + setprop("consumables/fuel/tank[10]/capacity-m3", 0); + setprop("consumables/fuel/tank[11]/capacity-m3", 0); + } return failed; } @@ -1136,6 +1205,42 @@ var repairYasim = func { } } +var setupYasimEngines = func { + if (getprop("engines/engine/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine")) { + var e0 = compat_failure_modes.fail_engine("engine"); + FailureMgr.add_failure_mode("engines/engine", "Engine 1", e0); + } + if (getprop("engines/engine[1]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[1]")) { + var e1 = compat_failure_modes.fail_engine("engine[1]"); + FailureMgr.add_failure_mode("engines/engine[1]", "Engine 2", e1); + } + if (getprop("engines/engine[2]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[2]")) { + var e2 = compat_failure_modes.fail_engine("engine[2]"); + FailureMgr.add_failure_mode("engines/engine[2]", "Engine 3", e2); + } + if (getprop("engines/engine[3]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[3]")) { + var e3 = compat_failure_modes.fail_engine("engine[3]"); + FailureMgr.add_failure_mode("engines/engine[3]", "Engine 4", e3); + } + if (getprop("engines/engine[4]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[4]")) { + var e4 = compat_failure_modes.fail_engine("engine[4]"); + FailureMgr.add_failure_mode("engines/engine[4]", "Engine 5", e4); + } + if (getprop("engines/engine[5]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[5]")) { + var e5 = compat_failure_modes.fail_engine("engine[5]"); + FailureMgr.add_failure_mode("engines/engine[5]", "Engine 6", e5); + } + if (getprop("engines/engine[6]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[6]")) { + var e6 = compat_failure_modes.fail_engine("engine[6]"); + FailureMgr.add_failure_mode("engines/engine[6]", "Engine 7", e6); + } + if (getprop("engines/engine[7]/n1") != nil and !contains(FailureMgr.get_failure_modes(), "engines/engine[7]")) { + var e7 = compat_failure_modes.fail_engine("engine[7]"); + FailureMgr.add_failure_mode("engines/engine[7]", "Engine 8", e7); + } +} +settimer(setupYasimEngines, 300); + setlistener("/sim/signals/reinit", repairYasim); hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; @@ -1268,6 +1373,7 @@ var processCallsigns = func () { if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) { setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning setprop("payload/armament/MAW-semiactive", 0); + setprop("payload/armament/MAW-semiactive-callsign", ""); } # spike handling: @@ -1438,11 +1544,25 @@ var writeDamageLog = func { io.close(file); } +var unitTest = func { + for (var i= 0; i<=180;i+=1) { + var em = DamageRecipient.typeID2emesaryID(i); + var b = DamageRecipient.emesaryID2typeID(em); + if (b != i) { + print("unit test failed for index "~i); + return; + } + } + print("unit test passed"); +} +#unitTest(); + setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/MAW-bearing"); #screen.property_display.add("payload/armament/MAW-active"); #screen.property_display.add("payload/armament/MAW-semiactive"); +#screen.property_display.add("payload/armament/MAW-semiactive-callsign"); #screen.property_display.add("payload/armament/MLW-bearing"); #screen.property_display.add("payload/armament/MLW-count"); #screen.property_display.add("payload/armament/MLW-launcher"); @@ -1452,7 +1572,7 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0); #screen.property_display.add("payload/armament/spike-gnd-02"); #screen.property_display.add("payload/armament/spike-gnd-05"); #screen.property_display.add("payload/armament/spike-gnd-06"); -#screen.property_display.add("payload/armament/spike-gnd-11"); +#screen.property_display.add("payload/armament/spike-gnd-17"); #screen.property_display.add("payload/armament/spike-gnd-23"); #screen.property_display.add("payload/armament/spike-gnd-p2"); #screen.property_display.add("payload/armament/spike-gnd-nk"); \ No newline at end of file diff --git a/groundtarget/Nasal/vector.nas b/groundtarget/Nasal/vector.nas index 621b0cb..ebc5347 100644 --- a/groundtarget/Nasal/vector.nas +++ b/groundtarget/Nasal/vector.nas @@ -2,7 +2,7 @@ var Math = { # # Authors: Nikolai V. Chr, Axel Paccalin. # - # Version 2.01 + # Version 2.02 # # When doing euler coords. to cartesian: +x = forw, +y = left, +z = up. # FG struct. coords: +x = back, +y = right, +z = up. @@ -287,13 +287,13 @@ var Math = { } if (coord1.alt() != coord2.alt()) { me.d12 = coord1.direct_distance_to(coord2); - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way. - me.d13 = coord1.alt()-me.coord3.alt(); if (me.d12 == 0) { # on top of each other, maybe rounding error.. return 0; } + me.coord3 = geo.Coord.new(coord1); + me.coord3.set_alt(coord1.alt()-me.d12*5);# this will increase the area of the triangle so that rounding errors dont get in the way. Changed to 5 May 2023, which gives more presision than 0.5 when c1 and c2 are very close. + me.d13 = coord1.alt()-me.coord3.alt(); me.d32 = me.coord3.direct_distance_to(coord2); if (math.abs(me.d13)+me.d32 < me.d12) { # rounding errors somewhere..one triangle side is longer than other 2 sides combined. @@ -479,4 +479,4 @@ var unitTest = { printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2)); }, }; -#unitTest.start(); \ No newline at end of file +#unitTest.start();