Update to latest vector, missile-code and damage.

This commit is contained in:
Nikolai V. Chr
2022-11-25 01:36:41 +01:00
parent e26e1f2f9a
commit e26ecd5c0f
55 changed files with 6237 additions and 2841 deletions
+72 -106
View File
@@ -1,64 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 onox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
<environment>
<in-to-wind type="bool">false</in-to-wind>
</environment>
<carrier>
<fdm>
<settings>
<!-- The maximum bank angle during full rudder deflection -->
<bank-limit type="double">15.0</bank-limit>
<!-- Frequency and overshoot of the oscillator used to
simulate heeling (roll).
-->
<heel-frequency type="double">0.5</heel-frequency>
<heel-overshoot type="double">4.0</heel-overshoot>
<!-- Frequency and overshoot of the oscillator used to
simulate trimming (pitch).
-->
<trim-frequency type="double">0.32</trim-frequency>
<trim-overshoot type="double">0.45</trim-overshoot>
<!-- The speed of the waves plus speed of the ship in knots
which generates the maximum overshoot of the trim
oscillation.
-->
<max-trim-oscillation-wave-kt type="double">30.0</max-trim-oscillation-wave-kt>
<!-- The speed of the waves in knots which generates the
maximum overshoot of the heel oscillation.
-->
<max-heel-oscillation-wave-kt type="double">20.0</max-heel-oscillation-wave-kt>
<movement type="bool">true</movement>
</settings>
</fdm>
</carrier>
<sim>
<author>pinto</author>
<author>pinto, Nikolai V. Chr.</author>
<description>BUK-M2</description>
<long-description>BUK-M2 is a modern Russian self-propelled surface to air missile system.</long-description>
<status>alpha</status>
@@ -114,6 +59,36 @@
<z-offset-max-m type="float">2000.0</z-offset-max-m>
</current-view>
<view n="200">
<name>Follow</name>
<type>lookat</type>
<internal>false</internal>
<config>
<!--<from-model type="bool">true</from-model>
<from-model-idx type="int">0</from-model-idx>-->
<eye-fixed type="bool">true</eye-fixed>
<target-lat-deg-path>rotors/main/blade[0]/flap-deg</target-lat-deg-path>
<target-lon-deg-path>rotors/main/blade[1]/flap-deg</target-lon-deg-path>
<target-alt-ft-path>rotors/main/blade[2]/position-deg</target-alt-ft-path>
<eye-lat-deg-path>/position/latitude-deg</eye-lat-deg-path>
<eye-lon-deg-path>/position/longitude-deg</eye-lon-deg-path>
<eye-alt-ft-path>/position/altitude-ft</eye-alt-ft-path>
<!--<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg-path>/orientation/pitch-deg</eye-pitch-deg-path>
<eye-roll-deg-path>/orientation/roll-deg</eye-roll-deg-path>-->
<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg>0</eye-pitch-deg>
<eye-roll-deg>0</eye-roll-deg>
<x-offset-m>50</x-offset-m>
<y-offset-m>50</y-offset-m>
<z-offset-m>25</z-offset-m>
<default-field-of-view-deg>70</default-field-of-view-deg>
<limits>
<enabled type="bool">false</enabled>
</limits>
</config>
</view>
<hud>
<path n="1">Aircraft/BUK-M2/Hud/hud.xml</path>
<visibility n="1">true</visibility>
@@ -126,24 +101,13 @@
<auto-position type="bool">true</auto-position>
</tower>
<systems>
<autopilot>
<path>Systems/fbw.xml</path>
</autopilot>
<autopilot>
<path>Systems/fdm.xml</path>
</autopilot>
<property-rule n="100">
<path>Systems/views.xml</path>
</property-rule>
</systems>
<multiplay>
<visibility-range-nm type="int">500</visibility-range-nm>
<generic>
<int n="0" type="int">0</int>
<int n="2" type="int">0</int>
<string n="6" type="string"></string><!-- string 6 used by radar lock RWR warning -->
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
@@ -152,31 +116,7 @@
<menu n="100">
<label>BUK-M2</label>
<enabled type="bool">true</enabled>
<item>
<label>Reload</label>
<binding>
<command>nasal</command>
<script>
fire_control.reload();
</script>
</binding>
</item>
<item>
<label>Targets</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>enemies</dialog-name>
</binding>
</item>
<item>
<label>Priority</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>priority</dialog-name>
</binding>
</item>
<item>
<label>Event Log</label>
<binding>
@@ -254,9 +194,33 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">2.25</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/BUK-M2/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
<modelsUpperCase type="bool">true</modelsUpperCase>
<MP-lat type="string">rotors/main/blade[0]/flap-deg</MP-lat>
<MP-lon type="string">rotors/main/blade[1]/flap-deg</MP-lon>
<MP-alt type="string">rotors/main/blade[2]/flap-deg</MP-alt>
<MP-alt-ft type="string">rotors/main/blade[2]/position-deg</MP-alt-ft>
<hit-interpolation type="bool">true</hit-interpolation>
<gnd-launch type="bool">true</gnd-launch>
<pylon-offset type="int">1</pylon-offset>
<pylon-stations>controls/armament</pylon-stations>
<station-name type="string">station</station-name>
<msg type="bool">true</msg>
<damage type="bool">true</damage>
<enable-craters type="bool">true</enable-craters>
@@ -266,6 +230,7 @@
<MAW-bearing type="double">0</MAW-bearing>
<MAW-active type="bool">false</MAW-active>
<spike type="bool">false</spike>
<!-- speed: Mach 4 length: 5.5m -->
</armament>
</payload>
@@ -279,6 +244,7 @@
</blade>
<blade n="2">
<flap-deg type="double">0</flap-deg>
<position-deg type="double">0</position-deg>
</blade>
</main>
</rotors>
@@ -298,18 +264,18 @@
</sam>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<priority type="int">0</priority>
+1 -83
View File
@@ -210,89 +210,7 @@
<nasal>
<load><![CDATA[
var self = cmdarg();
var skip = 0;
var lat = self.getNode("rotors/main/blade[0]/flap-deg");
var lon = self.getNode("rotors/main/blade[1]/flap-deg");
var alt = self.getNode("rotors/main/blade[2]/flap-deg");
if (alt == nil or alt.getValue() == nil) {
skip = 1;
}
var objs = {};
var loop = func () {
if(alt.getValue() != 0) {
var objModel = objs["first"];
if (objModel == nil) {
# create model
#print("creating missile");
var n = props.globals.getNode("models", 1);
var i = 0;
for (i = 0; 1==1; i += 1) {
if (n.getChild("model", i, 0) == nil) {
break;
}
}
objModel = n.getChild("model", i, 1);
objModel.getNode("elevation",1).setDoubleValue(0);
objModel.getNode("latitude",1).setDoubleValue(0);
objModel.getNode("longitude",1).setDoubleValue(0);
objModel.getNode("elevation-ft-prop",1).setValue(objModel.getPath()~"/elevation");
objModel.getNode("latitude-deg-prop",1).setValue(objModel.getPath()~"/latitude");
objModel.getNode("longitude-deg-prop",1).setValue(objModel.getPath()~"/longitude");
objModel.getNode("heading-deg",1).setDoubleValue(0);
objModel.getNode("pitch-deg",1).setDoubleValue(0);
objModel.getNode("roll-deg",1).setDoubleValue(0);
objModel.getNode("path",1).setValue("Aircraft/JA37/Models/Armament/Weapons/MP_missile/mp_missile.xml");
var loadNode = objModel.getNode("load", 1);
loadNode.setBoolValue(1);
objs["first"] = objModel;
loadNode.remove();
}
}
var exist = 0;
if(alt.getValue() != 0) {
exist = 1;
var objModel = objs["first"];
if (objModel == nil) {
print("error: did not find mp missile.");
return;
}# else {
# print("found a missile!");
#}
objModel.getNode("latitude").setDoubleValue(lat.getValue());
objModel.getNode("longitude").setDoubleValue(lon.getValue());
objModel.getNode("elevation").setDoubleValue(alt.getValue()*M2FT);
}
if (exist == 0) {
# remove model
var objModel = objs["first"];
if (objModel != nil) {
objModel.remove();
delete(objs, "first");
}
}
if (self.getNode("valid") == 0 or self.getNode("valid") == nil) {
return;
}
settimer(loop, 0.05);
}
if (skip == 0) {
loop();
}
]]>
]]>
</load>
</nasal>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 2.5;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+17 -6
View File
@@ -58,7 +58,7 @@
</carrier>
<sim include="Sim/views.xml">
<author>pinto and onox</author>
<author>pinto, Nikolai V. Chr., onox</author>
<description>Frigate</description>
<status>alpha</status>
<aircraft-version>0.1</aircraft-version>
@@ -123,6 +123,7 @@
<multiplay>
<generic>
<int n="0" type="int">0</int>
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
@@ -227,7 +228,20 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">20</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">200</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/Frigate/Models/emesary/</models>
<msg type="bool">true</msg>
@@ -239,6 +253,7 @@
<MAW-bearing type="double">0</MAW-bearing>
<MAW-active type="bool">false</MAW-active>
<spike type="bool">false</spike>
</armament>
</payload>
@@ -259,8 +274,4 @@
</gci>
</nasal>
<input>
<mice include="Sim/mice.xml"/>
</input>
</PropertyList>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 20;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
-17
View File
@@ -1,22 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 onox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
</PropertyList>
+30 -17
View File
@@ -257,7 +257,20 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">5</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/MIM-104D/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
@@ -295,17 +308,17 @@
<all-aspect type="bool">true</all-aspect>
<FCS-field-deg type="int">360</FCS-field-deg>
<seeker-field-deg type="int">400</seeker-field-deg><!-- since launched vertical, it cannot be too small -->
<seeker-field-deg type="int">400</seeker-field-deg>
<max-g type="int">25</max-g>
<thrust-lbf-stage-1 type="double">15000</thrust-lbf-stage-1>
<thrust-lbf-stage-2 type="double">0</thrust-lbf-stage-2> <!-- max 2000 m/s speed, 93 NM range -->
<thrust-lbf-stage-2 type="double">0</thrust-lbf-stage-2> <!-- -->
<stage-1-duration-sec type="double">12</stage-1-duration-sec>
<stage-gap-duration-sec type="double">0</stage-gap-duration-sec>
<stage-2-duration-sec type="double">0</stage-2-duration-sec>
<weight-warhead-lbs>185</weight-warhead-lbs>
<weight-fuel-lbm>1000</weight-fuel-lbm> <!-- -->
<drag-coeff type="double">0.10</drag-coeff> <!-- max 2000 m/s speed, 93 NM range -->
<drag-coeff type="double">0.10</drag-coeff> <!-- -->
<arming-time-sec type="double">2</arming-time-sec>
<min-speed-for-guiding-mach type="double">0.45</min-speed-for-guiding-mach>
@@ -365,19 +378,19 @@
<damage type="double">100</damage>
</sam>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<priority type="int">0</priority>
+213 -80
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 5;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1171,21 +1295,22 @@ var code_ct = func () {
if (getprop("payload/armament/msg")) {
setprop("sim/rendering/redout/enabled", TRUE);
#call(func{fgcommand('dialog-close', multiplayer.dialog.dialog.prop())},nil,var err= []);# props.Node.new({"dialog-name": "location-in-air"}));
#call(func{multiplayer.dialog.del();},nil,var err= []);
call(func{multiplayer.dialog.del();},nil,var err= []);
if (!getprop("gear/gear[0]/wow")) {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+2 -1
View File
@@ -52,7 +52,8 @@ var PositionUpdater = func () {
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
setprop("/position/altitude-ft", getprop("/position/ground-elev-ft"));
var elev = geo.elevation(position.lat(),position.lon());
if (elev != nil) setprop("/position/altitude-ft", elev*M2FT);
if ( getprop("/carrier/sunk") == 0 ) {
File diff suppressed because it is too large Load Diff
+90 -8
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.97
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -39,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -85,21 +87,24 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
@@ -119,6 +124,12 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
@@ -187,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -285,13 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -398,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+1 -1
View File
@@ -8,7 +8,7 @@
<type>spin</type>
<object-name>Cube</object-name>
<property>/sim/multiplay/online</property>
<factor>5.0</factor>
<factor>20.0</factor>
<axis>
<x1-m> 3.95612 </x1-m>
<y1-m> 0 </y1-m>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<PropertyList>
<path>FlapLid_mast.ac</path>
<animation>
<type>rotate</type>
<object-name>FlapLid.001</object-name>
<property>sim/multiplay/generic/float[8]</property>
<factor>-1.0</factor>
<offset-deg>180</offset-deg>
<axis>
<x1-m> 3.95612 </x1-m>
<y1-m> -0.000003 </y1-m>
<z1-m> 19.88614 </z1-m>
<x2-m> 3.95612 </x2-m>
<y2-m> -0.000003 </y2-m>
<z2-m> 20.88614 </z2-m>
</axis>
</animation>
<nasal>
<load><![CDATA[
]]>
</load>
</nasal>
</PropertyList>
+1 -1
View File
@@ -119,7 +119,7 @@
</animation-->
<model>
<path>FlapLid_mast.ac</path>
<path>FlapLid_mast.xml</path>
<name>flaplid</name>
<offsets>
<x-m> 0</x-m>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 3;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+71 -2
View File
@@ -18,7 +18,7 @@
#with("updateloop");
# Number of iterations per second
var frequency = 60.0;
var frequency = 10.0;
# Change in heading per second at full rudder deflection
var heading_ps = 0.5;
@@ -33,6 +33,75 @@ setprop("/carrier/roll-offset",0);
setprop("/carrier/sunk",0);
var PositionUpdater = func () {
var position = geo.aircraft_position();
var elev0 = geo.elevation(position.lat(),position.lon());
if (elev0 == nil) elev0 = 0;
var geodPos = aircraftToCart({x:0, y:50, z: 0});
var co = geo.Coord.new();
co.set_xyz(geodPos.x, geodPos.y, geodPos.z);
var elev = geo.elevation(co.lat(),co.lon());
if (elev != nil) {
setprop("sim/multiplay/generic/float[10]", elev-elev0);
setprop("controls/armament/station[1]/offsets/z-m", elev-elev0+3.8);
setprop("controls/armament/station[2]/offsets/z-m", elev-elev0+3.8);
setprop("controls/armament/station[3]/offsets/z-m", elev-elev0+3.8);
setprop("controls/armament/station[4]/offsets/z-m", elev-elev0+3.8);
} else {
setprop("sim/multiplay/generic/float[10]", 0);
setprop("controls/armament/station[1]/offsets/z-m", 3.8);
setprop("controls/armament/station[2]/offsets/z-m", 3.8);
setprop("controls/armament/station[3]/offsets/z-m", 3.8);
setprop("controls/armament/station[4]/offsets/z-m", 3.8);
}
geodPos = aircraftToCart({x:40, y:-40, z: 0});
co = geo.Coord.new();
co.set_xyz(geodPos.x, geodPos.y, geodPos.z);
elev = geo.elevation(co.lat(),co.lon());
if (elev != nil) {
setprop("sim/multiplay/generic/float[11]", elev-elev0);
} else {
setprop("sim/multiplay/generic/float[11]", 0);
}
# not used:
geodPos = aircraftToCart({x:0, y:-40, z: 0});
co = geo.Coord.new();
co.set_xyz(geodPos.x, geodPos.y, geodPos.z);
elev = geo.elevation(co.lat(),co.lon());
if (elev != nil) {
setprop("sim/multiplay/generic/float[12]", elev-elev0);
} else {
setprop("sim/multiplay/generic/float[12]", 0);
}
geodPos = aircraftToCart({x:-40, y:-40, z: 0});
co = geo.Coord.new();
co.set_xyz(geodPos.x, geodPos.y, geodPos.z);
elev = geo.elevation(co.lat(),co.lon());
if (elev != nil) {
setprop("sim/multiplay/generic/float[13]", elev-elev0);
} else {
setprop("sim/multiplay/generic/float[13]", 0);
}
# not used:
geodPos = aircraftToCart({x:40, y:40, z: 0});
co = geo.Coord.new();
co.set_xyz(geodPos.x, geodPos.y, geodPos.z);
elev = geo.elevation(co.lat(),co.lon());
if (elev != nil) {
setprop("sim/multiplay/generic/float[14]", elev-elev0);
} else {
setprop("sim/multiplay/generic/float[14]", 0);
}
settimer( PositionUpdater, 1/frequency );
return;
var position = geo.aircraft_position();
@@ -75,4 +144,4 @@ var PositionUpdater = func () {
};
#PositionUpdater();
PositionUpdater();
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+45 -143
View File
@@ -1,64 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 onox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
<environment>
<in-to-wind type="bool">false</in-to-wind>
</environment>
<carrier>
<fdm>
<settings>
<!-- The maximum bank angle during full rudder deflection -->
<bank-limit type="double">15.0</bank-limit>
<!-- Frequency and overshoot of the oscillator used to
simulate heeling (roll).
-->
<heel-frequency type="double">0.5</heel-frequency>
<heel-overshoot type="double">4.0</heel-overshoot>
<!-- Frequency and overshoot of the oscillator used to
simulate trimming (pitch).
-->
<trim-frequency type="double">0.32</trim-frequency>
<trim-overshoot type="double">0.45</trim-overshoot>
<!-- The speed of the waves plus speed of the ship in knots
which generates the maximum overshoot of the trim
oscillation.
-->
<max-trim-oscillation-wave-kt type="double">30.0</max-trim-oscillation-wave-kt>
<!-- The speed of the waves in knots which generates the
maximum overshoot of the heel oscillation.
-->
<max-heel-oscillation-wave-kt type="double">20.0</max-heel-oscillation-wave-kt>
<movement type="bool">true</movement>
</settings>
</fdm>
</carrier>
<sim>
<author>pinto</author>
<author>pinto, Nikolai V. Chr., onox</author>
<description>S-300</description>
<long-description>S-300 (SA-10) is a Russian self-propelled surface to air missile system.</long-description>
<status>alpha</status>
@@ -94,11 +39,6 @@
<splash type="bool">true</splash>
<path>gui/1.png</path>
</preview>
<preview>
<type>exterior</type>
<splash type="bool">true</splash>
<path>gui/2.png</path>
</preview>
</previews>
<current-view>
@@ -129,7 +69,7 @@
</limits>
</config>
</view>
<view n="102">
<view n="200">
<name>Follow</name>
<type>lookat</type>
<internal>false</internal>
@@ -173,24 +113,13 @@
<auto-position type="bool">true</auto-position>
</tower>
<systems>
<autopilot>
<path>Systems/fbw.xml</path>
</autopilot>
<autopilot>
<path>Systems/fdm.xml</path>
</autopilot>
<property-rule n="100">
<path>Systems/views.xml</path>
</property-rule>
</systems>
<multiplay>
<visibility-range-nm type="int">500</visibility-range-nm>
<generic>
<int n="0" type="int">0</int>
<int n="2" type="int">0</int>
<string n="6" type="string"></string><!-- string 6 used by radar lock RWR warning -->
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
@@ -199,51 +128,7 @@
<menu n="100">
<label>S-300</label>
<enabled type="bool">true</enabled>
<item>
<label>Reload</label>
<binding>
<command>nasal</command>
<script>
fire_control.reload();
</script>
</binding>
</item>
<item>
<label>Targets</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>enemies</dialog-name>
</binding>
</item>
<item>
<label>Priority</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>priority</dialog-name>
</binding>
</item>
<item>
<label>Relocate to cursor last click</label>
<binding>
<command>nasal</command>
<script>
setprop("position/latitude-deg",getprop("sim/input/click/latitude-deg"));
setprop("position/longitude-deg",getprop("sim/input/click/longitude-deg"));
setprop("position/altitude-ft",getprop("sim/input/click/elevation-ft"));
</script>
</binding>
</item>
<item>
<label>Print log to console</label>
<binding>
<command>nasal</command>
<script>
damage.printDamageLog();
</script>
</binding>
</item>
<item>
<label>Event Log</label>
<binding>
@@ -274,39 +159,33 @@
<controls>
<armament>
<station-select type="int">0</station-select>
<trigger type="bool">false</trigger>
<station n="1">
<offsets>
<x-m type="double"> -5.63782</x-m>
<y-m type="double"> -1.1407</y-m>
<y-m type="double"> 48.8593</y-m>
<z-m type="double"> 3.8</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="2">
<offsets>
<x-m type="double"> -6.53782</x-m>
<y-m type="double"> -0.540702</y-m>
<y-m type="double"> 49.4593</y-m>
<z-m type="double"> 3.8</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="3">
<offsets>
<x-m type="double"> -5.63782</x-m>
<y-m type="double"> 1.1407</y-m>
<y-m type="double"> 51.1407</y-m>
<z-m type="double"> 3.8</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="4">
<offsets>
<x-m type="double"> -6.53782</x-m>
<y-m type="double"> 0.540702</y-m>
<y-m type="double"> 50.540702</y-m>
<z-m type="double"> 3.8</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
</armament>
</controls>
@@ -321,9 +200,32 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">3</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/S-300/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
<modelsUpperCase type="bool">true</modelsUpperCase>
<MP-lat type="string">rotors/main/blade[0]/flap-deg</MP-lat>
<MP-lon type="string">rotors/main/blade[1]/flap-deg</MP-lon>
<MP-alt type="string">rotors/main/blade[2]/flap-deg</MP-alt>
<MP-alt-ft type="string">rotors/main/blade[2]/position-deg</MP-alt-ft>
<gnd-launch type="bool">true</gnd-launch>
<pylon-offset type="int">1</pylon-offset>
<pylon-stations>controls/armament</pylon-stations>
<station-name type="string">station</station-name>
<msg type="bool">true</msg>
<damage type="bool">true</damage>
<enable-craters type="bool">true</enable-craters>
@@ -368,19 +270,19 @@
<damage type="double">100</damage>
</sam>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<priority type="int">0</priority>
+212 -79
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 2.25;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
File diff suppressed because it is too large Load Diff
+90 -8
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.97
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -39,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -85,21 +87,24 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
@@ -119,6 +124,12 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
@@ -187,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -285,13 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -398,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+14 -1
View File
@@ -240,7 +240,20 @@
</controls>
<payload>
<d-config>
<full_damage_dist_m type="double">2.25</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/S-75/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
+228 -84
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 4;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -162,9 +164,42 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
@@ -225,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -257,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -268,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -290,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -298,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -311,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -320,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -346,10 +382,9 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = getprop("sim/multiplay/callsign");
callsign = size(callsign) < 8 ? callsign : left(callsign,7);
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
if (!radarOn) return emesary.Transmitter.ReceiptStatus_OK;# this should be little more complex later
var heading = getprop("orientation/heading-deg");
@@ -380,8 +415,28 @@ var DamageRecipient =
# debug.dump(notification);
#
#
var callsign = getprop("sim/multiplay/callsign");
callsign = size(callsign) < 8 ? callsign : left(callsign,7);
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
if (m28_auto) mig28.engagedBy(notification.Callsign);
@@ -426,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -439,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -458,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -469,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -609,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -617,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -627,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -646,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -670,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -713,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -739,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -813,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -821,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -841,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -849,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -860,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -867,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -884,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -894,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -906,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -920,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1008,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1042,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1056,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1081,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1140,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1153,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1174,21 +1295,22 @@ var code_ct = func () {
if (getprop("payload/armament/msg")) {
setprop("sim/rendering/redout/enabled", TRUE);
#call(func{fgcommand('dialog-close', multiplayer.dialog.dialog.prop())},nil,var err= []);# props.Node.new({"dialog-name": "location-in-air"}));
#call(func{multiplayer.dialog.del();},nil,var err= []);
call(func{multiplayer.dialog.del();},nil,var err= []);
if (!getprop("gear/gear[0]/wow")) {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1206,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1244,6 +1367,20 @@ var printDamageLog = func {
print();
}
var processCallsign = func (callsign) {
# Convert the callsign to one that emesary can work with.
var l = size(callsign);
callsign = l < 8?callsign:left(callsign, 7);
var newCallsign = "";
for(var ii = 0; ii < l; ii += 1) {
var ev = emesary.TransferString.getalphanumericchar(substr(callsign,ii,1));
if (ev != nil) {
newCallsign ~= ev;
}
}
return newCallsign;
}
#TODO testing:
var writeDamageLog = func {
@@ -1271,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+1 -1
View File
@@ -53,7 +53,7 @@ var missile_name = "m3m9";
var missile_brevity = "3M9";
var missile_max_distance = 18; #max distance of target in nm when it will fire
var missile_min_distance = 0.5; #minimum distance in nm when it will fire
var lockon_time = 28; #time in seconds it takes to lock on and get a firing solution on a target
var lockon_time = 24; #time in seconds it takes to lock on and get a firing solution on a target
var fire_minimum_interval = 7;# time since last track was initiated till a new can be initiated
var same_target_max_missiles = 2;# max number of missiles in air against same target
File diff suppressed because it is too large Load Diff
+91 -9
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.97
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -39,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -85,21 +87,24 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
@@ -119,6 +124,12 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
@@ -187,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -285,13 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -397,4 +450,33 @@ var Math = {
#
#
};
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+28 -15
View File
@@ -238,7 +238,20 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">4</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/SA-6/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
@@ -263,7 +276,7 @@
<m3m9>
<type-id type="int">96</type-id>
<short-name type="string">3M9</short-name>
<long-name type="string">Anti Aircraft Guided Missile</long-name>
<long-name type="string">3M9M3 Surface to Air Missile</long-name>
<max-fire-range-nm type="int">18</max-fire-range-nm>
<cross-section-sqft type="double">1.42</cross-section-sqft>
<weight-launch-lbs>1333</weight-launch-lbs><!-- https://missilery.info/missile/kub -->
@@ -343,19 +356,19 @@
<damage type="double">100</damage>
</sam>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<priority type="int">0</priority>
+238 -99
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 2;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 5;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -37,24 +39,30 @@ var shells = {
# 0.20 means a direct hit will disable 20% of the failure modes on average.
# or, 0.20 also means a direct hit can do 20 hitpoints damage.
#
"M70 rocket": [0,0.250], #135mm
"S-5 rocket": [1,0.200], # 55mm
"M55 shell": [2,0.100], # 30mm
"KCA shell": [3,0.100], # 30mm
"GSh-30": [4,0.100], # 30mm mig29/su27
"GAU-8/A": [5,0.100], # 30mm
"Mk3Z": [6,0.100], # 30mm Jaguar
"BK27": [7,0.070], # 27mm
"GSh-23": [8,0.065], # 23mm
"M61A1 shell": [9,0.050], # 20mm F14, F15, F16
"50 BMG": [10,0.015], # 12.7mm (non-explosive)
# Damage roughly proportional to projectile weight.
# If weight isn't listed here, it was estimated from dimensions (proportional to diameter^2 * length).
# Approximate formulae for cannons:
# damage ~ weight / 3.6 (in g)
# or damage ~ diameter^2 * length / 1.6e6 (in mm)
#
"M70 rocket": [0,0.500], # 135mm, ~5kg warhead
"S-5 rocket": [1,0.200], # 55mm, ~1-2kg warhead
"M55 shell": [2,0.060], # 30x113mm, 220g
"KCA shell": [3,0.100], # 30x173mm, 360g
"GSh-30": [4,0.095], # 30x165mm mig29/su27
"GAU-8/A": [5,0.100], # 30x173mm, 360g
"Mk3Z": [6,0.060], # 30x113mm Jaguar, 220g
"BK27": [7,0.070], # 27x145mm, 270g
"GSh-23": [8,0.040], # 23x115mm,
"M61A1 shell": [9,0.030], # 20x102mm F14, F15, F16, 100g
"50 BMG": [10,0.015], # 12.7mm (non-explosive)
"7.62": [11,0.005], # 7.62mm (non-explosive)
"Hydra-70": [12,0.250], # F-16/A-6 LAU-68 and LAU-61
"SNEB": [13,0.250], # Jaguar
"DEFA 554": [14,0.100], # 30mm Mirage
"20mm APDS": [15,0.050], # CIWS
"LAU-10": [16,0.225], # 127mm
};
"Hydra-70": [12,0.500], # 70mm, F-16/A-6 LAU-68 and LAU-61, ~4-6kg warhead
"SNEB": [13,0.500], # 68mm, Jaguar
"DEFA 554": [14,0.060], # 30x113mm Mirage, 220g
"20mm APDS": [15,0.030], # CIWS
"LAU-10": [16,0.500], # 127mm, ~4-7kg warhead
};
# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass.
@@ -82,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -123,9 +131,9 @@ var warheads = {
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -156,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -218,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -250,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -261,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -283,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -291,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -304,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -313,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -339,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -372,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -417,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -430,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -449,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -460,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -600,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -608,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -618,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -637,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -661,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -704,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -730,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -804,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -812,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -832,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -840,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -851,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -858,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -875,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -885,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -897,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -911,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -999,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1033,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1047,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1072,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1131,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1144,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1170,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1197,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1276,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+60 -44
View File
@@ -23,29 +23,31 @@ print("loading fdm");
var frequency = 60.0;
# Change in heading per second at full rudder deflection
var heading_ps = 0.5;
var heading_ps = 20;
time_last = 0;
sim_speed = 1;
var time_last = 0;
var sim_speed = 1;
var distance = 0;
var speed = 15;
setprop("/carrier/pitch-deg",0);
setprop("/carrier/pitch-offset",0);
setprop("/carrier/roll-deg",0);
setprop("/carrier/roll-offset",0);
setprop("/carrier/sunk",0);
var arrived = 0;
setprop("/orientation/pitch-deg", 0);
#var arrived = 0;
var PositionUpdater = func () {
settimer( PositionUpdater, 1/frequency );
var position = geo.aircraft_position();
if ( arrived == 0 and getprop("/carrier/sunk") == 0 and getprop("/autopilot/route-manager/wp-last/dist") != nil and getprop("/autopilot/route-manager/wp-last/dist") < 1 ) {
setprop("/sim/multiplay/chat",getprop("sim/multiplay/callsign") ~ " has arrived safely!");
print("arrived");
arrived = 1;
#if ( arrived == 0 and getprop("/carrier/sunk") == 0 and getprop("/autopilot/route-manager/wp-last/dist") != nil and getprop("/autopilot/route-manager/wp-last/dist") < 1 ) {
#setprop("/sim/multiplay/chat",getprop("sim/multiplay/callsign") ~ " has arrived safely!");
# print("arrived");
# arrived = 1;
#return;
}
#}
var time_now = getprop("/sim/time/elapsed-sec");
var dt = (time_now - time_last) * sim_speed;
@@ -53,47 +55,61 @@ var PositionUpdater = func () {
time_last = time_now;
#var heading = getprop("/orientation/heading-deg");
#var speed = getprop("/velocities/groundspeed-kt");
var rudder = getprop("/surface-positions/rudder-pos-norm");
if ( getprop("/carrier/sunk") == 0 and getprop("/autopilot/route-manager/active") == 1 ) {
#for event
var speed = 25;
var cur_waypoint = getprop("/autopilot/route-manager/current-wp");
var cur_wp_lon = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/longitude-deg");
var cur_wp_lat = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/latitude-deg");
var rm_destination = geo.Coord.new().set_latlon(cur_wp_lat,cur_wp_lon);
var heading = position.course_to(rm_destination);
var distance = speed * globals.KT2MPS * dt;
position.apply_course_distance(heading, distance);
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
var g_alt = getprop("/position/ground-elev-ft")+1;
if (cur_waypoint > -1) {
var cur_wp_lon = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/longitude-deg");
var cur_wp_lat = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/latitude-deg");
var rm_destination = geo.Coord.new().set_latlon(cur_wp_lat,cur_wp_lon);
var heading = getprop("/orientation/heading-deg");
var course = position.course_to(rm_destination);
distance = speed * KT2MPS * dt;
var turn_max = heading_ps * dt;
var turn = math.clamp(geo.normdeg180(course-heading),-turn_max,turn_max);
heading += turn;
heading = geo.normdeg(heading);
position.apply_course_distance(heading, distance);
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
setprop("/orientation/heading-deg", heading);
setprop("velocities/groundspeed-kt",speed);
} else {
setprop("velocities/groundspeed-kt",0);
}
var g_alt = geo.elevation(position.lat(),position.lon());#getprop("/position/ground-elev-ft");
if (g_alt == nil) g_alt = 0;
g_alt *= M2FT;
var alt_diff = g_alt - getprop("/position/altitude-ft");
var pitch = math.atan2(alt_diff, distance*M2FT)*R2D;
setprop("/orientation/pitch-deg", pitch);
setprop("/position/altitude-ft",g_alt);
# Update heading
var course = heading + rudder * heading_ps * dt;
setprop("/orientation/heading-deg", course);
setprop("velocities/groundspeed-kt",speed);
} else {
setprop("velocities/groundspeed-kt",0);
}
#set pitch
setprop("/orientation/pitch-deg",getprop("/carrier/pitch-deg") + getprop("/carrier/pitch-offset"));
#set roll
setprop("/orientation/roll-deg",getprop("/carrier/roll-deg") + getprop("/carrier/roll-offset"));
setprop("/orientation/roll-deg", 0);
};
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+66 -77
View File
@@ -1,65 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
systems adapted from truck by onox
model by Jüttner Domokos aka Rudolf
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
<environment>
<in-to-wind type="bool">false</in-to-wind>
</environment>
<carrier>
<fdm>
<settings>
<!-- The maximum bank angle during full rudder deflection -->
<bank-limit type="double">15.0</bank-limit>
<!-- Frequency and overshoot of the oscillator used to
simulate heeling (roll).
-->
<heel-frequency type="double">0.5</heel-frequency>
<heel-overshoot type="double">4.0</heel-overshoot>
<!-- Frequency and overshoot of the oscillator used to
simulate trimming (pitch).
-->
<trim-frequency type="double">0.32</trim-frequency>
<trim-overshoot type="double">0.45</trim-overshoot>
<!-- The speed of the waves plus speed of the ship in knots
which generates the maximum overshoot of the trim
oscillation.
-->
<max-trim-oscillation-wave-kt type="double">30.0</max-trim-oscillation-wave-kt>
<!-- The speed of the waves in knots which generates the
maximum overshoot of the heel oscillation.
-->
<max-heel-oscillation-wave-kt type="double">20.0</max-heel-oscillation-wave-kt>
<movement type="bool">true</movement>
</settings>
</fdm>
</carrier>
<sim> <!-- include="Sim/views.xml"> -->
<author>pinto, onox, Rudolf</author>
<sim>
<author>Rudolf, pinto, Nikolai V. Chr., onox</author>
<description>ZSU-23-4M "Shilka" AAA defence system</description>
<status>alpha</status>
<aircraft-version>0.1</aircraft-version>
@@ -104,37 +55,34 @@
</font>
</hud>
<menubar include="Dialogs/menu.xml"/>
<!--menubar include="Dialogs/menu.xml"/-->
<multiplay>
<generic>
<int n="0" type="int">0</int>
<int n="1" type="int" alias="/controls/armament/trigger"/>
<float n="0" type="double">0</float> <!-- Turret azimuth -->
<float n="1" type="double">0</float> <!-- gun elevation -->
<float n="2" type="double">0</float> <!-- radar azimuth -->
<float n="3" type="double">0</float> <!-- radar elevation -->
<int n="0" type="int">0</int>
<int n="1" type="int" alias="/sim/multiplay/generic/bool[0]"/>
<float n="0" type="double">0</float> <!-- Turret azimuth -->
<float n="1" type="double">0</float> <!-- gun elevation -->
<float n="2" type="double">0</float> <!-- radar azimuth -->
<float n="3" type="double">0</float> <!-- radar elevation -->
<bool n="0" type="bool">false</bool>
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
<systems>
<autopilot>
<path>Systems/fbw.xml</path>
</autopilot>
<autopilot>
<path>Systems/fdm.xml</path>
</autopilot>
<property-rule>
<path>Systems/radar-pos.xml</path>\
</property-rule>
<property-rule>
<path>Systems/radar-pos.xml</path>\
</property-rule>
</systems>
<menubar>
<default>
<menu n="100">
<label>Truck</label>
<label>ZSU-23</label>
<enabled type="bool">true</enabled>
<item>
<label>Event Log</label>
<binding>
@@ -242,9 +190,45 @@
<trigger type="bool">0</trigger>
</armament>
</controls>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<priority type="int">0</priority>
<priority0 type="int">1</priority0>
<payload>
<d-config>
<full_damage_dist_m type="double">2</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">5</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<MP-lat type="string">rotors/main/blade[0]/flap-deg</MP-lat>
<MP-lon type="string">rotors/main/blade[1]/flap-deg</MP-lon>
<MP-alt type="string">rotors/main/blade[2]/flap-deg</MP-alt>
<MP-alt-ft type="string">rotors/main/blade[2]/position-deg</MP-alt-ft>
<models type="string">Aircraft/ZSU-23/Models/emesary/</models>
<msg type="bool">true</msg>
<enable-craters type="bool">true</enable-craters>
@@ -265,14 +249,19 @@
<carrier>
<file>Aircraft/ZSU-23/Nasal/fdm.nas</file>
</carrier>
<damage>
<file>Aircraft/ZSU-23/Nasal/damage.nas</file>
</damage>
<vector>
<file>Aircraft/ZSU-23/Nasal/vector.nas</file>
</vector>
<radar_logic>
<file>Aircraft/ZSU-23/Nasal/radar-logic.nas</file>
</radar_logic>
<damage>
<file>Aircraft/ZSU-23/Nasal/damage.nas</file>
</damage>
<fire_control>
<file>Aircraft/ZSU-23/Nasal/fire-control.nas</file>
</fire_control>
</nasal>
<input>
<mice include="Sim/mice.xml"/>
</input>
</PropertyList>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 10;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 900;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+20 -20
View File
@@ -42,26 +42,26 @@ var PositionUpdater = func () {
var type = getprop("sim/multiplay/generic/int[17]");
if (type != last_type) {
if (damage.hp_max == damage.hp) {
if (type == 0) {damage.hp_max=900;damage.hp=900;}
if (type == 1) {damage.hp_max=500;damage.hp=500;}
if (type == 2) {damage.hp_max=200;damage.hp=200;}
if (type == 3) {damage.hp_max=300;damage.hp=300;}
if (type == 4) {damage.hp_max=900;damage.hp=900;}
if (type == 5) {damage.hp_max=1500;damage.hp=1500;}
if (type == 6) {damage.hp_max=450;damage.hp=450;}
if (type == 7) {damage.hp_max=0;damage.hp=0;}
if (type == 8) {damage.hp_max=300;damage.hp=300;}
if (type == 9) {damage.hp_max=300;damage.hp=300;}
if (type == 10) {damage.hp_max=500;damage.hp=500;}
if (type == 11) {damage.hp_max=200;damage.hp=200;}
if (type == 12) {damage.hp_max=0;damage.hp=0;}
if (type == 14) {damage.hp_max=600;damage.hp=600;}
if (type == 15) {damage.hp_max=1400;damage.hp=1400;}
if (type == 16) {damage.hp_max=100;damage.hp=100;}
if (type == 17) {damage.hp_max=300;damage.hp=300;}
if (type == 18) {damage.hp_max=50;damage.hp=50;}
if (type == 19) {damage.hp_max=2500;damage.hp=2500;}
if (type == 20) {damage.hp_max=300;damage.hp=300;}
if (type == 0) {damage.hp_max=900;damage.hp=damage.hp_max;}
if (type == 1) {damage.hp_max=500;damage.hp=damage.hp_max;}
if (type == 2) {damage.hp_max=200;damage.hp=damage.hp_max;}
if (type == 3) {damage.hp_max=300;damage.hp=damage.hp_max;}
if (type == 4) {damage.hp_max=900;damage.hp=damage.hp_max;}
if (type == 5) {damage.hp_max=1500;damage.hp=damage.hp_max;}
if (type == 6) {damage.hp_max=450;damage.hp=damage.hp_max;}
if (type == 7) {damage.hp_max=0;damage.hp=damage.hp_max;}
if (type == 8) {damage.hp_max=300;damage.hp=damage.hp_max;}
if (type == 9) {damage.hp_max=300;damage.hp=damage.hp_max;}
if (type == 10) {damage.hp_max=500;damage.hp=damage.hp_max;}
if (type == 11) {damage.hp_max=200;damage.hp=damage.hp_max;}
if (type == 12) {damage.hp_max=0;damage.hp=damage.hp_max;}
if (type == 14) {damage.hp_max=600;damage.hp=damage.hp_max;}
if (type == 15) {damage.hp_max=1400;damage.hp=damage.hp_max;}
if (type == 16) {damage.hp_max=100;damage.hp=damage.hp_max;}
if (type == 17) {damage.hp_max=300;damage.hp=damage.hp_max;}
if (type == 18) {damage.hp_max=50;damage.hp=damage.hp_max;}
if (type == 19) {damage.hp_max=2500;damage.hp=damage.hp_max;}
if (type == 20) {damage.hp_max=300;damage.hp=damage.hp_max;}
last_type = type;
} else {
print("Can only switch type when not damaged!!");
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
-17
View File
@@ -1,22 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 onox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
</PropertyList>
+16 -1
View File
@@ -3,7 +3,7 @@
<PropertyList>
<sim>
<author>pinto</author>
<author>pinto, Nikolai V. Chr., FGUK</author>
<description>depot</description>
<status>alpha</status>
<aircraft-version>0.2</aircraft-version>
@@ -50,6 +50,7 @@
<int n="0" type="int">0</int>
<int n="2" type="int">1</int>
<int n="17" type="int">0</int>
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
@@ -67,6 +68,20 @@
</sim>
<payload>
<d-config>
<full_damage_dist_m type="double">10</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">900</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/depot/Models/emesary/</models>
<msg type="bool">true</msg>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 20;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 1; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+1 -1
View File
@@ -77,7 +77,7 @@ var PositionUpdater = func () {
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
var g_alt = getprop("/position/ground-elev-ft")+1;
var g_alt = getprop("/position/ground-elev-ft");
setprop("/position/altitude-ft",g_alt);
# Update heading
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+74 -36
View File
@@ -60,7 +60,7 @@
<disabled type="bool">false</disabled>
</carrier>
<sim include="Sim/views.xml">
<sim>
<author>pinto, onox, Nikolai V. Chr., Alexis Bory, Detlef Faber, T. Henkel</author>
<description>Fleet</description>
<long-description>
@@ -143,6 +143,7 @@
<bool n="44" type="bool">false</bool>
<bool n="45" type="bool">false</bool>
<bool n="46" type="bool">false</bool>
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
<menubar>
@@ -150,23 +151,7 @@
<menu n="100">
<label>Missile-frigate</label>
<enabled type="bool">true</enabled>
<item>
<label>Reload</label>
<binding>
<command>nasal</command>
<script>
fire_control.reload();
</script>
</binding>
</item>
<item>
<label>Targets</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>enemies</dialog-name>
</binding>
</item>
<item>
<label>Event Log</label>
<binding>
@@ -194,6 +179,36 @@
<path>enemies/e12</path>
</aircraft-data>
<view n="200">
<name>Follow</name>
<type>lookat</type>
<internal>false</internal>
<config>
<!--<from-model type="bool">true</from-model>
<from-model-idx type="int">0</from-model-idx>-->
<eye-fixed type="bool">true</eye-fixed>
<target-lat-deg-path>rotors/main/blade[0]/flap-deg</target-lat-deg-path>
<target-lon-deg-path>rotors/main/blade[1]/flap-deg</target-lon-deg-path>
<target-alt-ft-path>rotors/main/blade[2]/position-deg</target-alt-ft-path>
<eye-lat-deg-path>/position/latitude-deg</eye-lat-deg-path>
<eye-lon-deg-path>/position/longitude-deg</eye-lon-deg-path>
<eye-alt-ft-path>/position/altitude-ft</eye-alt-ft-path>
<!--<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg-path>/orientation/pitch-deg</eye-pitch-deg-path>
<eye-roll-deg-path>/orientation/roll-deg</eye-roll-deg-path>-->
<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg>0</eye-pitch-deg>
<eye-roll-deg>0</eye-roll-deg>
<x-offset-m>50</x-offset-m>
<y-offset-m>50</y-offset-m>
<z-offset-m>25</z-offset-m>
<default-field-of-view-deg>120</default-field-of-view-deg>
<limits>
<enabled type="bool">false</enabled>
</limits>
</config>
</view>
</sim>
<!-- need to adjust this (probably) -->
@@ -344,9 +359,33 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">20</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">200</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">true</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/fleet/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
<modelsUpperCase type="bool">true</modelsUpperCase>
<MP-lat type="string">rotors/main/blade[0]/flap-deg</MP-lat>
<MP-lon type="string">rotors/main/blade[1]/flap-deg</MP-lon>
<MP-alt type="string">rotors/main/blade[2]/flap-deg</MP-alt>
<MP-alt-ft type="string">rotors/main/blade[2]/position-deg</MP-alt-ft>
<hit-interpolation type="bool">true</hit-interpolation>
<gnd-launch type="bool">true</gnd-launch>
<pylon-offset type="int">1</pylon-offset>
<pylon-stations>controls/armament</pylon-stations>
<station-name type="string">station</station-name>
<msg type="bool">true</msg>
<damage type="bool">true</damage>
<enable-craters type="bool">true</enable-craters>
@@ -373,19 +412,22 @@
</main>
</rotors>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<priority type="int">0</priority>
<priority0 type="int">1</priority0>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<nasal>
@@ -413,8 +455,4 @@
</fire_control>
</nasal>
<input>
<mice include="Sim/mice.xml"/>
</input>
</PropertyList>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 10;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+1
View File
@@ -26,6 +26,7 @@ var prop_watch = {
"JA37Di-Viggen": [20,21,22],
"AJ37-Viggen": [20,21,22],
"AJS37-Viggen": [20,21,22],
"Jaguar-GR1": [20,21,22],
};
var radar_stations = [
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
-16
View File
@@ -1,21 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 onox
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<PropertyList>
+15 -5
View File
@@ -1,11 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<environment>
<in-to-wind type="bool">false</in-to-wind>
</environment>
<sim>
<author>pinto</author>
<description>gci</description>
@@ -111,6 +106,7 @@
<generic>
<int n="0" type="int">0</int>
<int n="2" type="int">0</int>
<string n="7" type="string"></string><!-- datalink -->
</generic>
<!--<visibility-range-nm type="int">500</visibility-range-nm>-->
</multiplay>
@@ -121,6 +117,20 @@
</carrier>
<payload>
<d-config>
<full_damage_dist_m type="double">10</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">80</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/gci-radar/Models/emesary/</models>
<msg type="bool">true</msg>
+238 -99
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 2;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 5;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -37,24 +39,30 @@ var shells = {
# 0.20 means a direct hit will disable 20% of the failure modes on average.
# or, 0.20 also means a direct hit can do 20 hitpoints damage.
#
"M70 rocket": [0,0.250], #135mm
"S-5 rocket": [1,0.200], # 55mm
"M55 shell": [2,0.100], # 30mm
"KCA shell": [3,0.100], # 30mm
"GSh-30": [4,0.100], # 30mm mig29/su27
"GAU-8/A": [5,0.100], # 30mm
"Mk3Z": [6,0.100], # 30mm Jaguar
"BK27": [7,0.070], # 27mm
"GSh-23": [8,0.065], # 23mm
"M61A1 shell": [9,0.050], # 20mm F14, F15, F16
"50 BMG": [10,0.015], # 12.7mm (non-explosive)
# Damage roughly proportional to projectile weight.
# If weight isn't listed here, it was estimated from dimensions (proportional to diameter^2 * length).
# Approximate formulae for cannons:
# damage ~ weight / 3.6 (in g)
# or damage ~ diameter^2 * length / 1.6e6 (in mm)
#
"M70 rocket": [0,0.500], # 135mm, ~5kg warhead
"S-5 rocket": [1,0.200], # 55mm, ~1-2kg warhead
"M55 shell": [2,0.060], # 30x113mm, 220g
"KCA shell": [3,0.100], # 30x173mm, 360g
"GSh-30": [4,0.095], # 30x165mm mig29/su27
"GAU-8/A": [5,0.100], # 30x173mm, 360g
"Mk3Z": [6,0.060], # 30x113mm Jaguar, 220g
"BK27": [7,0.070], # 27x145mm, 270g
"GSh-23": [8,0.040], # 23x115mm,
"M61A1 shell": [9,0.030], # 20x102mm F14, F15, F16, 100g
"50 BMG": [10,0.015], # 12.7mm (non-explosive)
"7.62": [11,0.005], # 7.62mm (non-explosive)
"Hydra-70": [12,0.250], # F-16/A-6 LAU-68 and LAU-61
"SNEB": [13,0.250], # Jaguar
"DEFA 554": [14,0.100], # 30mm Mirage
"20mm APDS": [15,0.050], # CIWS
"LAU-10": [16,0.225], # 127mm
};
"Hydra-70": [12,0.500], # 70mm, F-16/A-6 LAU-68 and LAU-61, ~4-6kg warhead
"SNEB": [13,0.500], # 68mm, Jaguar
"DEFA 554": [14,0.060], # 30x113mm Mirage, 220g
"20mm APDS": [15,0.030], # CIWS
"LAU-10": [16,0.500], # 127mm, ~4-7kg warhead
};
# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass.
@@ -82,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -123,9 +131,9 @@ var warheads = {
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -156,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -218,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -250,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -261,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -283,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -291,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -304,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -313,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -339,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -372,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -417,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -430,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -449,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -460,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -600,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -608,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -618,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -637,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -661,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -704,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -730,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -804,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -812,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -832,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -840,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -851,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -858,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -875,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -885,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -897,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -911,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -999,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1033,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1047,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1072,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1131,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1144,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1170,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1197,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1276,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+66 -49
View File
@@ -23,20 +23,22 @@ print("loading fdm");
var frequency = 60.0;
# Change in heading per second at full rudder deflection
var heading_ps = 0.5;
var heading_ps = 20;
time_last = 0;
sim_speed = 1;
var time_last = 0;
var sim_speed = 1;
var distance = 0;
var speed = 25;
setprop("/carrier/sunk",0);
setprop("/orientation/pitch-deg", 0);
var arrived = 0;
var last_type = 0;
setprop("/carrier/pitch-deg",0);
setprop("/carrier/pitch-offset",0);
setprop("/carrier/roll-deg",0);
setprop("/carrier/roll-offset",0);
setprop("/carrier/sunk",0);
var arrived = 0;
var PositionUpdater = func () {
settimer( PositionUpdater, 1/frequency );
@@ -47,7 +49,7 @@ var PositionUpdater = func () {
setprop("/sim/multiplay/chat",getprop("sim/multiplay/callsign") ~ " has arrived safely!");
print("arrived");
arrived = 1;
#return;
return;
}
var time_now = getprop("/sim/time/elapsed-sec");
@@ -56,58 +58,70 @@ var PositionUpdater = func () {
time_last = time_now;
#var heading = getprop("/orientation/heading-deg");
#var speed = getprop("/velocities/groundspeed-kt");
var rudder = getprop("/surface-positions/rudder-pos-norm");
if ( getprop("/carrier/sunk") == 0 and getprop("/autopilot/route-manager/active") == 1 ) {
#for event
var cur_waypoint = getprop("/autopilot/route-manager/current-wp");
var cur_wp_lon = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/longitude-deg");
var cur_wp_lat = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/latitude-deg");
var rm_destination = geo.Coord.new().set_latlon(cur_wp_lat,cur_wp_lon);
var heading = position.course_to(rm_destination);
var distance = speed * globals.KT2MPS * dt;
position.apply_course_distance(heading, distance);
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
var g_alt = getprop("/position/ground-elev-ft")+1;
if (cur_waypoint > -1) {
var cur_wp_lon = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/longitude-deg");
var cur_wp_lat = getprop("/autopilot/route-manager/route/wp[" ~ cur_waypoint ~ "]/latitude-deg");
var rm_destination = geo.Coord.new().set_latlon(cur_wp_lat,cur_wp_lon);
var heading = getprop("/orientation/heading-deg");
var course = position.course_to(rm_destination);
distance = speed * KT2MPS * dt;
var turn_max = heading_ps * dt;
var turn = math.clamp(geo.normdeg180(course-heading),-turn_max,turn_max);
heading += turn;
heading = geo.normdeg(heading);
position.apply_course_distance(heading, distance);
# Set new position
setprop("/position/latitude-deg", position.lat());
setprop("/position/longitude-deg", position.lon());
setprop("/orientation/heading-deg", heading);
setprop("velocities/groundspeed-kt",speed);
} else {
setprop("velocities/groundspeed-kt",0);
}
var g_alt = geo.elevation(position.lat(),position.lon());#getprop("/position/ground-elev-ft");
if (g_alt == nil) g_alt = 0;
g_alt *= M2FT;
var alt_diff = g_alt - getprop("/position/altitude-ft");
var pitch = math.atan2(alt_diff, distance*M2FT)*R2D;
setprop("/orientation/pitch-deg", pitch);
setprop("/position/altitude-ft",g_alt);
# Update heading
var course = heading + rudder * heading_ps * dt;
setprop("/orientation/heading-deg", course);
setprop("velocities/groundspeed-kt",speed);
} else {
setprop("velocities/groundspeed-kt",0);
}
#set pitch
setprop("/orientation/pitch-deg",getprop("/carrier/pitch-deg") + getprop("/carrier/pitch-offset"));
#set roll
setprop("/orientation/roll-deg",getprop("/carrier/roll-deg") + getprop("/carrier/roll-offset"));
setprop("/orientation/roll-deg", 0);
var type = getprop("sim/multiplay/generic/int[17]");
if (type != last_type) {
if (damage.hp_max == damage.hp) {
if (type == 0) {damage.hp_max=5;damage.hp=5;speed=25;}#humvee
if (type == 1) {damage.hp_max=100;damage.hp=100;speed=15;}#tank
if (type == 2) {damage.hp_max=75;damage.hp=75;speed=10;}#rocket launcher
if (type == 3) {damage.hp_max=85;damage.hp=85;speed=20;}#small tank
if (type == 4) {damage.hp_max=80;damage.hp=80;speed=20;}#APC (mine proected)
if (type == 5) {damage.hp_max=75;damage.hp=75;speed=20;}#APC
if (type == 0) {damage.hp_max=5;damage.hp=damage.hp_max;speed=25;}#humvee
if (type == 1) {damage.hp_max=100;damage.hp=damage.hp_max;speed=15;}#tank
if (type == 2) {damage.hp_max=75;damage.hp=damage.hp_max;speed=10;}#rocket launcher
if (type == 3) {damage.hp_max=85;damage.hp=damage.hp_max;speed=20;}#small tank
if (type == 4) {damage.hp_max=80;damage.hp=damage.hp_max;speed=20;}#APC (mine proected)
if (type == 5) {damage.hp_max=75;damage.hp=damage.hp_max;speed=20;}#APC
last_type = type;
} else {
print("Can only switch type when not damaged!!");
@@ -117,4 +131,7 @@ var PositionUpdater = func () {
};
PositionUpdater();
PositionUpdater();
+119 -8
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.94
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -39,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -57,6 +59,22 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
me.mag = 1;
@@ -69,21 +87,24 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
@@ -95,6 +116,20 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
@@ -163,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -261,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -369,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+15
View File
@@ -46,6 +46,7 @@
<generic>
<int n="0" type="int">0</int>
<int n="17" type="int">0</int>
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
@@ -75,6 +76,20 @@
</sim>
<payload>
<d-config>
<full_damage_dist_m type="double">2</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">5</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/groundtarget/Models/emesary/</models>
<msg type="bool">true</msg>
+20 -20
View File
@@ -2,8 +2,8 @@
<PropertyList>
<description>USS SanAntonio</description>
<author>Naval Postgraduate School SAVAGE project, Vivian Meazza, Heiko Schulz</author>
<status>early-production</status>
<author>Naval Postgraduate School SAVAGE project, Vivian Meazza, Heiko Schulz</author>
<status>early-production</status>
<path>LPD17.ac</path>
<nasal>
<load><![CDATA[
@@ -92,15 +92,15 @@
<property>surface-positions/elevators-pos-norm</property>
<factor>-90.0</factor>
<center>
<x-m>94.936</x-m>
<y-m>0.00</y-m>
<z-m>3.968</z-m>
</center>
<axis>
<x>0</x>
<y>1</y>
<z>0</z>
</axis>
<x-m>94.936</x-m>
<y-m>0.00</y-m>
<z-m>3.968</z-m>
</center>
<axis>
<x>0</x>
<y>1</y>
<z>0</z>
</axis>
</animation>
<animation>
@@ -109,15 +109,15 @@
<property>surface-positions/elevators-pos-norm</property>
<factor>90.0</factor>
<center>
<x-m>93.243</x-m>
<y-m>0.00</y-m>
<z-m>-6.464</z-m>
</center>
<axis>
<x>0</x>
<y>1</y>
<z>0</z>
</axis>
<x-m>93.243</x-m>
<y-m>0.00</y-m>
<z-m>-6.464</z-m>
</center>
<axis>
<x>0</x>
<y>1</y>
<z>0</z>
</axis>
</animation>
<model>
+220 -87
View File
@@ -1,28 +1,30 @@
#
# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file)
# for damage to be recognised, the property /payload/armament/msg must be 1
# if /payload/armament/spectator is 1 and damage off, missile trails, craters, flares,
# and missile warnings will be received, but not actual damage.
#
# Authors: Nikolai V. Chr., Pinto and Richard (with improvement by Onox)
# Authors: Nikolai V. Chr., Pinto, Colin Geniet and Richard (with improvement by Onox)
#
#
############################ Config ########################################################################################
var full_damage_dist_m = 20;# Can vary from aircraft to aircraft depending on how many failure modes it has.
var full_damage_dist_m = getprop("payload/d-config/full_damage_dist_m");# Can vary from aircraft to aircraft depending on how many failure modes it has.
# Many modes (like Viggen) ought to have lower number like zero.
# Few modes (like F-14) ought to have larger number such as 3.
# For assets this should be average radius of the asset.
var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes.
var hp_max = 200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = 0; # if anti-air can do damage
var hitable_by_cannon = 1; # if cannon can do damage
var use_hitpoints_instead_of_failure_modes_bool = getprop("payload/d-config/use_hitpoints_instead_of_failure_modes_bool");# bool. mainly used by assets that don't have failure modes.
var hp_max = getprop("payload/d-config/hp_max");# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled.
var hitable_by_air_munitions = getprop("payload/d-config/hitable_by_air_munitions"); # if anti-air can do damage
var hitable_by_cannon = getprop("payload/d-config/hitable_by_cannon"); # if cannon can do damage
#var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage
var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=0; # for aircraft that do not yet have proper RWR
var tacview_supported=0; # For aircraft with tacview support
var m28_auto=0; # only used by automats
var mlw_max=2.25; #
var auto_flare_caller = 0; # If damage.nas should detect flare releases, or if function is called from somewhere in aircraft
var is_fleet = getprop("payload/d-config/is_fleet"); # Is really 7 ships, 3 of which has offensive missiles.
var rwr_to_screen=getprop("payload/d-config/rwr_to_screen"); # for aircraft that do not yet have proper RWR
var rwr_audio_extended=getprop("payload/d-config/rwr_audio_extended"); # for aircraft that want seperate audio properties for different radar spikes.
var tacview_supported=getprop("payload/d-config/tacview_supported"); # For aircraft with tacview support
var m28_auto=getprop("payload/d-config/m28_auto"); # only used by automats
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
############################################################################################################################
var TRUE = 1;
@@ -66,7 +68,7 @@ var shells = {
var warheads = {
# [id,lbs,anti surface,cluster,(name)]
"AGM-65": [ 0, 126.00,1,0],
"AGM-65B": [ 0, 126.00,1,0],
"AGM-84": [ 1, 488.00,1,0],
"AGM-88": [ 2, 146.00,1,0],
"MK-82SE": [ 3, 192.00,1,0],# snake eye
@@ -75,12 +77,12 @@ var warheads = {
"AGM-158": [ 6, 1000.00,1,0],
"ALARM": [ 7, 450.00,1,0],
"AM 39 Exocet": [ 8, 364.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],
"AS 37 Martel": [ 9, 330.00,1,0],# Also : AJ 168 Martel
"AS30L": [10, 529.00,1,0],
"BL755": [11, 100.00,1,1],# 800lb bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-87": [12, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"CBU-105": [13, 100.00,1,1],# bomblet warhead. Mix of armour piecing and HE. 100 due to need to be able to kill buk-m2.
"AJ 168 Martel": [14, 330.00,1,0],
"AS 37 Armat": [14, 330.00,1,0],
"FAB-100": [15, 92.59,1,0],
"FAB-250": [16, 202.85,1,0],
"FAB-500": [17, 564.38,1,0],
@@ -88,7 +90,7 @@ var warheads = {
"GBU-24": [19, 945.00,1,0],
"GBU-31": [20, 945.00,1,0],
"GBU-54": [21, 190.00,1,0],
"GBU-10": [22, 2000.00,1,0],
"GBU-10": [22, 945.00,1,0],
"GBU-16": [23, 450.00,1,0],
"HVAR": [24, 7.50,1,0],#P51
"KAB-500": [25, 564.38,1,0],
@@ -124,14 +126,14 @@ var warheads = {
"AIM-7F": [55, 88.00,0,0],
"AGM-62": [56, 2000.00,1,0],
"AIM-9L": [57, 20.80,0,0],
"d-7": [58, 44.00,0,0],#deprecated
"AGM-65D": [58, 126.00,1,0],
"AIM-132": [59, 22.05,0,0],
"d-8": [60, 20.80,0,0],#deprecated
"Apache AP": [60, 110.23,0,1],# Real mass of bomblet. (x 10). Anti runway.
"KN-06": [61, 315.00,0,0],
"9M317": [62, 145.00,0,0],
"d9": [63, 27.00,0,0],#deprecated
"GEM": [63, 185.00,0,0],#MIM-104D
"R.550 Magic": [64, 26.45,0,0],# also called majic
"d-0": [65, 30.00,0,0],#deprecated
"5Ya23": [65, 414.00,0,0],#Volga-M
"R.550 Magic 2": [66, 27.00,0,0],
"R.530": [67, 55.00,0,0],
"MK-82AIR": [68, 192.00,1,0],
@@ -162,12 +164,46 @@ var warheads = {
"pilot": [93, 0.00,1,0],# ejected pilot
"BETAB-500ShP": [94, 1160.00,1,0],
"Flare": [95, 0.00,0,0],
"3M9": [96, 125.00,0,0],# 3M9 Missile used with 2K12/SA-6
};
var AIR_RADAR = "air";
var radar_signatures = {
"unknown-model": AIR_RADAR,
"f-14b": AIR_RADAR,
"F-14D": AIR_RADAR,
"F-15C": AIR_RADAR,
"F-15D": AIR_RADAR,
"F-16": AIR_RADAR,
"AJS37-Viggen": AIR_RADAR,
"JA37Di-Viggen": AIR_RADAR,
"m2000-5": AIR_RADAR,
"m2000-5B": AIR_RADAR,
"MiG-21bis": AIR_RADAR,
"MiG-21MF-75": AIR_RADAR,
"MiG-29": AIR_RADAR,
"SU-27": AIR_RADAR,
"EC-137R": AIR_RADAR,
"RC-137R": AIR_RADAR,
"E-8R": AIR_RADAR,
"EC-137D": AIR_RADAR,
"Mig-28": AIR_RADAR,
"SA-6": AIR_RADAR,#Air radar tone chosen so that there is at least some lock tone until asset-specific is created
"ZSU-23-4M": "gnd-23",
"S-75": "gnd-02",
"buk-m2": "gnd-11",
"s-300": "gnd-20",
"MIM104D": "gnd-p2",
"missile_frigate": "gnd-nk",
"fleet": "gnd-nk",
};
var id2warhead = [];
var launched = {};# callsign: elapsed-sec
var approached = {};# callsign: uniqueID
var heavy_smoke = [61,62,63,65,92];
var heavy_smoke = [61,62,63,65,92,96];
var k = keys(warheads);
@@ -224,12 +260,12 @@ var DamageRecipient =
}
#
#
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# This will be where movement and damage notifications are received.
# This can replace MP chat for damage notifications
# and allow missile visibility globally (i.e. all suitable equipped models) have the possibility
# to receive notifications from all other suitably equipped models.
if (notification.NotificationType == "ArmamentInFlightNotification" or notification.NotificationType == "ObjectInFlightNotification") {
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# print("recv(d1): ",notification.NotificationType, " ", notification.Ident,
# " UniqueIdentity=",notification.UniqueIdentity,
# " Kind=",notification.Kind,
# " SecondaryKind=",notification.SecondaryKind,
@@ -256,10 +292,10 @@ var DamageRecipient =
notification.Flags = 0;
notification.RemoteCallsign = "";
}
if(getprop("payload/armament/msg") == 0 and notification.RemoteCallsign != notification.Callsign) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1 and notification.RemoteCallsign != notification.Callsign) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
var elapsed = getprop("sim/time/elapsed-sec");
var ownPos = geo.aircraft_position();
var bearing = ownPos.course_to(notification.Position);
@@ -267,11 +303,11 @@ var DamageRecipient =
var thrustOn = bits.test(notification.Flags, 1);
var index = notification.SecondaryKind-21;
var typ = id2warhead[index];
if (notification.Kind == MOVE) {
if (thrustOn or index == 93 or index == 95) {
# visualize missile smoke trail
var smoke = 1;
if (index == 93) {
smoke = 0;
@@ -289,7 +325,7 @@ var DamageRecipient =
}
}
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime(), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,smoke];
} else {
# the +1.5 is the update time that missiles send notifications out in
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
@@ -297,8 +333,8 @@ var DamageRecipient =
} elsif (notification.Kind == DESTROY) {
dynamics["noti_"~notification.Callsign~"_"~notification.UniqueIdentity] = [systime()-(time_before_delete-1.6), notification.Position.lat(), notification.Position.lon(), notification.Position.alt(), notification.u_fps, notification.Heading, notification.Pitch,-1]
}
if (tacview_supported and getprop("sim/multiplay/txhost") == "mpserver.opredflag.com") {
if (tacview_supported and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
if (tacview.starttime) {
var tacID = left(md5(notification.Callsign~notification.UniqueIdentity),6);
if (notification.Kind == DESTROY) {
@@ -310,6 +346,7 @@ var DamageRecipient =
var typp = typ[4]=="pilot"?"Parachutist":typ[4];
var extra = typp=="Parachutist"?"|0|0|0":"";
var extra2 = typ[2]==0?",Type=Weapon+Missile":",Type=Weapon+Bomb";
extra2 = typ[4]=="Flare"?",Type=Flare":extra2;
extra2 = typp=="Parachutist"?"":extra2;
var color = radarOn?",Color=Red":",Color=Yellow";
thread.lock(tacview.mutexWrite);
@@ -319,15 +356,15 @@ var DamageRecipient =
}
}
}
if (notification.Kind == DESTROY) {
return emesary.Transmitter.ReceiptStatus_OK;
}
if (index == 95 or index == 93) {
return emesary.Transmitter.ReceiptStatus_OK;
}
# Missile launch warning:
if (thrustOn) {
var launch = launched[notification.Callsign~notification.UniqueIdentity];
@@ -345,7 +382,7 @@ var DamageRecipient =
}
}
}
# Missile approach warning:
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign != callsign) return emesary.Transmitter.ReceiptStatus_OK;
@@ -378,6 +415,27 @@ var DamageRecipient =
# debug.dump(notification);
#
#
if (tacview_supported and tacview.starttime and getprop("sim/multiplay/txhost") != "mpserver.opredflag.com") {
var node = getCallsign(notification.RemoteCallsign);
if (node != nil and notification.SecondaryKind > 20) {
# its a warhead
var wh = id2warhead[notification.SecondaryKind - 21];
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);
if (notification.Distance > math.abs(notification.RelativeAltitude)) {#just a sanity check
hitCoord = hitCoord.apply_course_distance(notification.Bearing, math.sqrt(notification.Distance*notification.Distance-notification.RelativeAltitude*notification.RelativeAltitude));
}
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(hitCoord.lat(),hitCoord.lon(),hitCoord.alt(), lbs*0.5);
thread.unlock(tacview.mutexWrite);
} elsif (node != nil and notification.SecondaryKind < 0) {
# its a cannon or rocket
thread.lock(tacview.mutexWrite);
tacview.writeExplosion(node.getNode("position/latitude-deg").getValue(), node.getNode("position/longitude-deg").getValue(), node.getNode("position/altitude-ft").getValue()*FT2M, 5);
thread.unlock(tacview.mutexWrite);
}
}
var callsign = processCallsign(getprop("sim/multiplay/callsign"));
if (notification.RemoteCallsign == callsign and getprop("payload/armament/msg") == 1) {
#damage enabled and were getting hit
@@ -423,10 +481,10 @@ var DamageRecipient =
}
var distance = math.max(dist-full_damage_dist_m, 0);
var maxDist = 0;# distance where the explosion dont hurt us anymore
var lbs = 0;
if (wh[2] == 1) {
lbs = wh[1];
maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs)
@@ -436,15 +494,15 @@ var DamageRecipient =
} else {
return;
}
var diff = maxDist-distance;
if (diff < 0) {
diff = 0;
}
diff = diff * diff;
var probability = diff / (maxDist*maxDist);
if (use_hitpoints_instead_of_failure_modes_bool) {
var hpDist = maxDamageDistFromWarhead(hp_max);
probability = (maxDist/hpDist)*probability;
@@ -455,7 +513,7 @@ var DamageRecipient =
printf("Took %.1f%% damage from %s at %0.1f meters. %s systems was hit", percent,type,dist,failed);
damageLog.push(sprintf("%s hit you with %s, %.1f meters distance.", notification.Callsign, type, dist));
nearby_explosion();
####
# I don't remember all the considerations that went into our original warhead damage model.
# But looking at the formula it looks like they all do 100% damage at 0 meter hit,
@@ -466,27 +524,27 @@ var DamageRecipient =
# Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something
# similar for failure mode based aircraft. ~Nikolai
####
## example 1: ##
# 300 lbs warhead, 50 meters distance
# maxDist=52
# diff = 52-50 = 2
# diff^2 = 4
# prob = 4/2700 = 0.15%
## example 2: ##
# 300 lbs warhead, 25 meters distance
# maxDist=52
# diff = 52-25 = 27
# diff^2 = 729
# prob = 729/2700 = 27%
}
}
}
# }
return emesary.Transmitter.ReceiptStatus_OK;
}
if (notification.NotificationType == "StaticNotification") {
if(getprop("payload/armament/msg") == 0) {
if(getprop("payload/armament/msg") == 0 and getprop("payload/armament/spectator") != 1) {
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
if (notification.Kind == CREATE and getprop("payload/armament/enable-craters") == 1 and statics["obj_"~notification.UniqueIdentity] == nil) {
@@ -606,7 +664,7 @@ var ModelManager = {
}
}
m.model = n.getChild("model", i, 1);
n = props.globals.getNode("sim/emesary-models", 1);
for (i = 0; 1==1; i += 1) {
if (n.getChild("dynamic", i, 0) == nil) {
@@ -614,9 +672,9 @@ var ModelManager = {
}
}
m.ai = n.getChild("dynamic", i, 1);
m.model.getNode("path", 1).setValue(path);
# Create the AI position and orientation properties.
m.lat = m.ai.getNode("position/latitude-deg", 1);
m.lon = m.ai.getNode("position/longitude-deg", 1);
@@ -624,14 +682,14 @@ var ModelManager = {
m.heading= m.ai.getNode("orientation/true-heading-deg", 1);
m.pitch = m.ai.getNode("orientation/pitch-deg", 1);
m.roll = m.ai.getNode("orientation/roll-deg", 1);
m.lat.setDoubleValue(lat);
m.lon.setDoubleValue(lon);
m.alt_ft.setDoubleValue(alt_ft);
m.heading.setDoubleValue(heading);
m.pitch.setDoubleValue(para?0:pitch);
m.roll.setDoubleValue(0);
m.vLat = lat;
m.vLon = lon;
m.vAlt_ft = alt_ft;
@@ -643,22 +701,22 @@ var ModelManager = {
m.pAlt_ft = m.vAlt_ft;
m.pHeading = m.vHeading;
m.pPitch = m.vPitch;
m.model.getNode("latitude-deg-prop", 1).setValue(m.lat.getPath());
m.model.getNode("longitude-deg-prop", 1).setValue(m.lon.getPath());
m.model.getNode("elevation-ft-prop", 1).setValue(m.alt_ft.getPath());
m.model.getNode("heading-deg-prop", 1).setValue(m.heading.getPath());
m.model.getNode("pitch-deg-prop", 1).setValue(m.pitch.getPath());
m.model.getNode("roll-deg-prop", 1).setValue(m.roll.getPath());
m.coord = geo.Coord.new();
m.uBody_fps = 0;
m.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
m.past = m.last;
m.frametime = 0;
m.delayTime = 0;
m.delayTime = 0;
return m;
},
moveRealtime: func (uBody_fps, dt, factor) {
@@ -667,17 +725,17 @@ var ModelManager = {
me.uBody_fps = uBody_fps;
me.alt_dist = me.slant_ft*math.sin(me.vPitch*D2R);
me.horiz_dist = me.slant_ft*math.cos(me.vPitch*D2R);
me.coord.set_latlon(me.vLat, me.vLon, (me.vAlt_ft+me.alt_dist) * FT2M);
me.coord = me.coord.apply_course_distance(me.vHeading, me.horiz_dist*FT2M);
me.latlon = me.coord.latlon();
me.vLat = me.latlon[0];
me.vLon = me.latlon[1];
me.vAlt_ft = me.latlon[2]*M2FT;
me.lat.setDoubleValue(me.vLat);
me.lon.setDoubleValue(me.vLon);
me.alt_ft.setDoubleValue(me.vAlt_ft);
@@ -710,20 +768,20 @@ var ModelManager = {
translateDelayed: func (lat,lon,alt_ft,heading,pitch, para) {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
me.pLat = me.vLat;
me.pLon = me.vLon;
me.pAlt_ft = me.vAlt_ft;
me.pHeading = me.vHeading;
me.pPitch = me.vPitch;
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
me.vHeading = heading;
me.vPitch = pitch;
#me.vRoll = 0;
me.past = me.last;
me.last = [geo.Coord.new().set_latlon(lat,lon,alt_ft*FT2M).xyz(),systime()];
me.delayTime = 0;
@@ -736,7 +794,7 @@ var ModelManager = {
me.heading.setDoubleValue(heading);
me.pitch.setDoubleValue(para?0:pitch);
#me.roll.setDoubleValue(0);
me.vLat = lat;
me.vLon = lon;
me.vAlt_ft = alt_ft;
@@ -810,7 +868,7 @@ var flare_sorter = func(a, b) {
if(a[0] < b[0]){
return -1; # A should before b in the returned vector
}elsif(a[0] == b[0]){
return 0; # A is equivalent to b
return 0; # A is equivalent to b
}else{
return 1; # A should after b in the returned vector
}
@@ -818,9 +876,6 @@ var flare_sorter = func(a, b) {
var animate_flare = func {
# Send out notifications about own flare positions every 0.4s
if (!getprop("payload/armament/msg")) {
return;
}
var stime = systime();
# old flares
var old_flares = [];
@@ -838,6 +893,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
continue;
}
if (flares_sent < flares_max_process_per_loop) {
@@ -846,7 +902,7 @@ var animate_flare = func {
flare = [stime, flare[1], flare[2], flare[3], (flare[4]<flare_terminal_speed)?(flare[4]+flare_dt*9.83*0.5):(flare[4]-flare_dt*3), math.max(0,flare[5]-flare_dt*20), flare[6]];
flare[2].apply_course_distance(flare[3], flare_dt*flare[5]);
flare[2].set_alt(flare[2].alt()-flare_dt*flare[4]);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
msg.Position = flare[2];
@@ -857,6 +913,7 @@ var animate_flare = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
flares_sent += 1;
}
append(old_flares, flare);
@@ -864,7 +921,7 @@ var animate_flare = func {
flare_list = old_flares;
if(auto_flare_caller) {
auto_flare_released();
}
}
}
var flaretimer = maketimer(flare_update_time, animate_flare);
flaretimer.start();
@@ -881,9 +938,6 @@ var auto_flare_released = func {
}
var flare_released = func {
if (!getprop("payload/armament/msg")) {
return;
}
# We released a flare. If you call this method manually, then make sure 'auto_flare_caller' is false.
var stime = systime();
var flare =[stime, stime,
@@ -891,7 +945,7 @@ var flare_released = func {
getprop("orientation/heading-deg"),
FT2M*getprop("velocities/speed-down-fps"),
FT2M*math.sqrt(getprop("velocities/speed-north-fps")*getprop("velocities/speed-north-fps")+getprop("velocities/speed-east-fps")*getprop("velocities/speed-east-fps")),
int(rand()*200)-100];
int(rand()*240)-120];
append(flare_list, flare);
var msg = notifications.ObjectInFlightNotification.new("ffly", flare[6], MOVE, 21+95);
msg.Flags = 0;
@@ -903,6 +957,30 @@ var flare_released = func {
msg.Heading = 0;
msg.u_fps = 0;
notifications.objectBridgedTransmitter.NotifyAll(msg);
recordOwnFlare(msg);
}
var recordOwnFlare = func (msg) {
if (tacview_supported) {
if (tacview.starttime) {
var tacID = left(md5("ownShip"~msg.UniqueIndex),6);
if (msg.Kind == DESTROY) {
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",Visible=0\n-"~tacID~"\n");
thread.unlock(tacview.mutexWrite);
} else {
var typp = "Flare";
var extra = "";
var extra2 = ",Type=Flare";
var color = ",Color=Yellow";
thread.lock(tacview.mutexWrite);
tacview.write("#" ~ (systime() - tacview.starttime)~"\n");
tacview.write(tacID~",T="~msg.Position.lon()~"|"~msg.Position.lat()~"|"~msg.Position.alt()~extra~",Name="~typp~color~extra2~"\n");
thread.unlock(tacview.mutexWrite);
}
}
}
}
#==================================================================
@@ -917,11 +995,15 @@ setlistener("payload/armament/msg", func {
check_for_Request();
},0,0);
setlistener("payload/armament/spectator", func {
check_for_Request();
},0,0);
var last_check = -65;
var check_for_Request = func {
# This sends out a notification to ask other aircraft for all craters
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and getprop("payload/armament/msg") and systime()-last_check > 60) {
if (getprop("payload/armament/enable-craters") == 1 and getprop("sim/multiplay/online") and (getprop("payload/armament/spectator") or getprop("payload/armament/msg")) and systime()-last_check > 60) {
last_check = systime();
var msg = notifications.StaticNotification.new("stat", int(rand()*15000000), REQUEST_ALL, 0);
msg.IsDistinct = 0;
@@ -1005,7 +1087,7 @@ var fail_systems = func (probability, factor = 100) {#this factor needs tuning a
}
}
}
return failed;
}
};
@@ -1039,7 +1121,7 @@ setlistener("/sim/signals/reinit", repairYasim);
hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max];
var fail_fleet_systems = func (probability, factor) {
var sinking_ships = (hp_f[0]<0) + (hp_f[1]<0) + (hp_f[2]<0) + (hp_f[3]<0) + (hp_f[4]<0) + (hp_f[5]<0) + (hp_f[6]<0);
var hit_sinking = 0;
if (sinking_ships == 0) {
@@ -1053,9 +1135,9 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean.");
return;
}
var no = 0;
for (no=0; no < 7; no+=1) {
if (hp_f[no] > 0) {
break;
@@ -1078,7 +1160,7 @@ var fail_fleet_systems = func (probability, factor) {
armament.defeatSpamFilter("S.O.S. Heeelp");
} else {
armament.defeatSpamFilter("This is not over yet..");
}
}
}
return -1;
};
@@ -1137,12 +1219,19 @@ var getCallsign = func (callsign) {
var MAW_elapsed = 0;
var radarSpikes = {};
foreach (key ; keys(radar_signatures)) {
radarSpikes[radar_signatures[key]] = 0;
}
var processCallsigns = func () {
callsign_struct = {};
var players = props.globals.getNode("ai/models").getChildren();
var myCallsign = getprop("sim/multiplay/callsign");
myCallsign = size(myCallsign) < 8 ? myCallsign : left(myCallsign,7);
var painted = 0;
var paint_list = [];
foreach (var player; players) {
if(player.getChild("valid") != nil and player.getChild("valid").getValue() == TRUE and player.getChild("callsign") != nil and player.getChild("callsign").getValue() != "" and player.getChild("callsign").getValue() != nil) {
var callsign = player.getChild("callsign").getValue();
@@ -1150,14 +1239,49 @@ var processCallsigns = func () {
var str6 = player.getNode("sim/multiplay/generic/string[6]");
if (str6 != nil and str6.getValue() != nil and str6.getValue() != "" and size(""~str6.getValue())==4 and left(md5(myCallsign),4) == str6.getValue()) {
painted = 1;
if (rwr_audio_extended) {
append(paint_list, getModel(player.getNode("sim/model/path")));
}
}
}
}
setprop("payload/armament/spike", painted);
if (getprop("sim/time/elapsed-sec")-MAW_elapsed > 1.1) {
setprop("payload/armament/MAW-active", 0);# resets every 1.1 seconds without warning
}
# spike handling:
setprop("payload/armament/spike", painted);
if (!rwr_audio_extended) return;
var roundSpike = rand();
foreach (var radarModel ; paint_list) {
var ref = radar_signatures[radarModel];
if (ref != nil) {
radarSpikes[ref] = roundSpike;
}
}
foreach(key ; keys(radarSpikes)) {
if (radarSpikes[key] == roundSpike) {
setprop("payload/armament/spike-"~key, 1);
} else {
setprop("payload/armament/spike-"~key, 0);
}
}
}
var remove_suffix = func(str, suffix) {
var len = size(suffix);
if (substr(str, -len) == suffix) return substr(str, 0, size(str) - len);
else return str;
};
var getModel = func (node) {
if (node == nil) return "unknown-model";
var value = node.getValue();
if (value == nil or value == "") return "";
var model = split(".", split("/", value)[-1])[0];
model = remove_suffix(model, "-model");
model = remove_suffix(model, "-anim");
return model;
}
processCallsignsTimer = maketimer(1.5, processCallsigns);
processCallsignsTimer.simulatedTime = 1;
processCallsignsTimer.start();
@@ -1176,16 +1300,17 @@ var code_ct = func () {
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "WeightAndFuel"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "system-failures"}))},nil,var err2 = []);
call(func{fgcommand('dialog-close', props.Node.new({"dialog-name": "instrument-failures"}))},nil,var err2 = []);
}
}
setprop("sim/freeze/fuel",0);
setprop("/sim/speed-up", 1);
setprop("/gui/map/draw-traffic", 0);
setprop("/sim/marker-pins/traffic", 0);
setprop("/sim/gui/dialogs/map-canvas/draw-TFC", 0);
#fgcommand("timeofday", props.Node.new({"timeofday": "real"}));
#setprop("/sim/rendering/als-filters/use-filtering", 1);
call(func{var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();
interfaceController.stop();},nil,var err2=[]);
}
interfaceController.stop();},nil,var err2=[]);
}
}
code_ctTimer = maketimer(1, code_ct);
code_ctTimer.simulatedTime = 1;
@@ -1203,13 +1328,14 @@ code_ctTimer.start();
var re_init = func (node) {
# repair the aircraft
if (node.getValue() == 0) return;
var failure_modes = FailureMgr._failmgr.failure_modes;
var mode_list = keys(failure_modes);
foreach(var failure_mode_id; mode_list) {
FailureMgr.set_failure_level(failure_mode_id, 0);
}
stopLaunch();
damageLog.push("Aircraft was repaired due to re-init.");
}
@@ -1282,4 +1408,11 @@ setlistener("sim/signals/exit", writeDamageLog, 0, 0);
#screen.property_display.add("payload/armament/MLW-bearing");
#screen.property_display.add("payload/armament/MLW-count");
#screen.property_display.add("payload/armament/MLW-launcher");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike");
#screen.property_display.add("payload/armament/spike-air");
#screen.property_display.add("payload/armament/spike-gnd-20");
#screen.property_display.add("payload/armament/spike-gnd-02");
#screen.property_display.add("payload/armament/spike-gnd-11");
#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");
+162 -18
View File
@@ -2,7 +2,7 @@ var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 1.93
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
@@ -13,6 +13,8 @@ var Math = {
# When doing euler angles (from pilots point of view): yaw = yaw left, pitch = rotate up, roll = roll right.
# FG rotations: heading = yaw right, pitch = rotate up, roll = roll right.
#
# Cartesian is right-handed coord system.
#
clamp: func(v, min, max) { v < min ? min : v > max ? max : v },
convertCoords: func (x,y,z) {
@@ -37,14 +39,16 @@ var Math = {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z()};
me.geoVeccy = me.product(me.magnitudeVector(a), me.normalize([me.tgt_coord.x()-coord.x(),me.tgt_coord.y()-coord.y(),me.tgt_coord.z()-coord.z()]));
return {"x":me.tgt_coord.x()-coord.x(), "y":me.tgt_coord.y()-coord.y(), "z":me.tgt_coord.z()-coord.z(), "vector": me.geoVeccy};
},
# When observing another MP aircraft the groundspeed velocity info is in body frame, this method will convert it to cartesian vector.
#
# Warning: If you input body velocities in fps the output will be in fps also. Likewise for mps.
getCartesianVelocity: func (yaw_deg, pitch_deg, roll_deg, uBody_fps, vBody_fps, wBody_fps) {
me.bodyVelocity = [uBody_fps, -vBody_fps, -wBody_fps];
return me.yawPitchRollVector(yaw_deg, pitch_deg, roll_deg, me.bodyVelocity);
return me.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
@@ -55,9 +59,27 @@ var Math = {
return R2D * math.acos(me.value);
},
# Rodrigues' rotation formula. Use unitVectorAxis as axis, and rotate around it.
rotateVectorAroundVector: func (a, unitVectorAxis, angle) {
return me.plus(
me.plus(
me.product(math.cos(angle*D2R),a),
me.product(math.sin(angle*D2R), me.crossProduct(unitVectorAxis,a))
),
me.product((1-math.cos(angle*D2R))*me.dotProduct(unitVectorAxis,a), unitVectorAxis)
);
},
#Rotate a certain amound of degrees towards 'towardsMe'.
rotateVectorTowardsVector: func (a, towardsMe, angle) {
return me.rotateVectorAroundVector(a, me.normalize(me.crossProduct(a, towardsMe)), angle);
},
# length of vector
magnitudeVector: func (a) {
return math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2));
me.mag = 1;
call(func {me.mag = math.sqrt(math.pow(a[0],2)+math.pow(a[1],2)+math.pow(a[2],2))},nil,nil,var err =[]);#strange bug in Nasal can sometimes mess up sqrt
return me.mag;
},
# dot product of 2 vectors
@@ -65,8 +87,20 @@ var Math = {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# rotate a vector. Order: roll, pitch, yaw
# rotate a vector. Order: roll, pitch, yaw (same as aircraft)
#
# The coordinate system is fixed during all the rolls.
#
rollPitchYawVector: func (roll, pitch, yaw, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (reverse of aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
@@ -74,15 +108,34 @@ var Math = {
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw, pitch, roll (like an aircraft)
yawPitchRollVector: func (yaw, pitch, roll, vector) {
me.rollM = me.rollMatrix(roll);
# rotate a vector. Order: yaw, pitch
yawPitchVector: func (yaw, pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.multiplyMatrices(me.pitchM, me.rollM));
me.rotation = me.multiplyMatrices(me.pitchM, me.yawM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: pitch, yaw
pitchYawVector: func (pitch, yaw, vector) {
me.pitchM = me.pitchMatrix(pitch);
me.yawM = me.yawMatrix(yaw);
me.rotation = me.multiplyMatrices(me.yawM, me.pitchM);
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# rotate a vector. Order: yaw
yawVector: func (yaw, vector) {
me.yawM = me.yawMatrix(yaw);
return me.multiplyMatrixWithVector(me.yawM, vector);
},
# rotate a vector. Order: pitch
pitchVector: func (pitch, vector) {
me.pitchM = me.pitchMatrix(pitch);
return me.multiplyMatrixWithVector(me.pitchM, vector);
},
# multiply 3x3 matrix with vector
multiplyMatrixWithVector: func (matrix, vector) {
return [matrix[0]*vector[0]+matrix[1]*vector[1]+matrix[2]*vector[2],
@@ -107,10 +160,10 @@ var Math = {
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = pitch * D2R;
return [math.cos(pitch),0,-math.sin(pitch),
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
math.sin(pitch),0,math.cos(pitch)];
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
@@ -126,7 +179,8 @@ var Math = {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
me.hdg = math.asin(-vector[1]/me.horz)*R2D;
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
@@ -144,6 +198,39 @@ var Math = {
return [me.hdg, me.pitch];
},
# vector to pitch
cartesianToEulerPitch: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.pitch = math.atan2(vector[2],me.horz)*R2D;
} else {
me.pitch = vector[2]>=0?90:-90;
}
return me.pitch;
},
# vector to heading
cartesianToEulerHeading: func (vector) {
me.horz = math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);
if (me.horz != 0) {
me.div = math.clamp(-vector[1]/me.horz, -1, 1);
me.hdg = math.asin(me.div)*R2D;
if (vector[0] < 0) {
# south
if (me.hdg >= 0) {
me.hdg = 180-me.hdg;
} else {
me.hdg = -180-me.hdg;
}
}
me.hdg = geo.normdeg(me.hdg);
} else {
me.hdg = 0;
}
return me.hdg;
},
# gives an vector that points up from fuselage
eulerToCartesian3Z: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
@@ -242,8 +329,22 @@ var Math = {
# supply a normal to the plane, and a vector. The vector will be projected onto the plane, and that projection is returned as a vector.
projVectorOnPlane: func (planeNormal, vector) {
if (me.magnitudeVector(planeNormal) == 0) return [0,0,0];#safety
return me.minus(vector, me.product(me.dotProduct(vector,planeNormal)/math.pow(me.magnitudeVector(planeNormal),2), planeNormal));
},
# Project a onto ontoMe.
projVectorOnVector: func (a, ontoMe) {
if (me.magnitudeVector(ontoMe) == 0) return [0,0,0];#safety
return me.product(me.dotProduct(a,ontoMe)/me.dotProduct(ontoMe,ontoMe), ontoMe);
},
# Project a onto ontoMe and measure how long along ontoMe it goes, opposite will give negative number.
scalarProjVectorOnVector: func (a, ontoMe) {
me.ontoMeMag = me.magnitudeVector(ontoMe);
if (me.ontoMeMag == 0) return 0;
return me.dotProduct(a,ontoMe)/me.ontoMeMag;
},
# unary - vector
opposite: func (v){
@@ -268,7 +369,7 @@ var Math = {
# print vector to console
format: func (v) {
return sprintf("(%.1f, %.1f, %.1f)",v[0],v[1],v[2]);
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
@@ -288,6 +389,20 @@ var Math = {
return me.magnitudeVector(me.crossProduct(me.minus(L2,L1), me.minus(L1,P)))/me.magnitudeVector(me.minus(L2,L1));
},
interpolateVector: func (start, end, fraction) {
me.xx = (start[0]*(1-fraction) +end[0]*fraction);
me.yy = (start[1]*(1-fraction) +end[1]*fraction);
me.zz = (start[2]*(1-fraction) +end[2]*fraction);
return [me.xx, me.yy, me.zz];
},
# move distance 'along' from start towards end. Total dist from start to end is dist.
alongVector: func (start, end, dist, along) {
me.fraction = along/dist;
return me.interpolateVector(start, end, me.fraction);
},
# Orthogonal projection of a vector `vec` onto another `ref` !!can throw an exception if the referential vector is null!!.
orthogonalProjection: func(vec, ref){
@@ -319,9 +434,9 @@ var Math = {
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(pitch) 0 -sin(pitch) |
#| 0 1 0 |
#| sin(pitch) 0 cos(pitch) |
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
@@ -336,3 +451,32 @@ var Math = {
#
};
var unitTest = {
start: func {
# 1: Simple test of rotation matrices and getting the input back in another way.
var localDir = Math.pitchYawVector(-40, -75, [1,0,0]);
me.eulerDir = Math.cartesianToEuler(localDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 75 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 2: Revert part of test #1
var forwardDir = Math.yawPitchVector(75, 0, localDir);
me.eulerDir = Math.cartesianToEuler(forwardDir);
me.eulerDir[0] = me.eulerDir[0]==nil?0:geo.normdeg(me.eulerDir[0]);
printf("Looking out of aircraft window at heading 0 and 40 degs down: %.4f heading %.4f pitch.",me.eulerDir[0],me.eulerDir[1]);
# 3: Tougher test, try it different places on earth since earth is not sphere. At KXTA runway it gives 0.03% error in pitch.
var someDir = Math.eulerToCartesian3X(-75, -40, 20);
var myCoord = geo.aircraft_position();
me.thousandVectorGeo = Math.vectorToGeoVector(someDir, myCoord).vector;# does not return magnitude with meaning, only its direction matters.
me.thousandVectorGeo = Math.normalize(me.thousandVectorGeo);
me.thousandVectorGeo = Math.product(100000, me.thousandVectorGeo);
me.lookAt = geo.Coord.new().set_xyz(myCoord.x()+me.thousandVectorGeo[0], myCoord.y()+me.thousandVectorGeo[1], myCoord.z()+me.thousandVectorGeo[2]);
printf("Looking out of aircraft window 100000m away heading 75 and 40 degs down: %.4f heading %.4f pitch %.4f meter.", myCoord.course_to(me.lookAt), Math.getPitch(myCoord, me.lookAt), myCoord.direct_distance_to(me.lookAt));
# 4:
var aircraft = Math.eulerToCartesian3X(-35, 72, 21);
var aircraft2 = Math.rollPitchYawVector(21, 72, -35, [1,0,0]);
printf("These two should be the same %s = %s",Math.format(aircraft),Math.format(aircraft2));
},
};
#unitTest.start();
+79 -36
View File
@@ -59,7 +59,7 @@
</carrier>
<sim include="Sim/views.xml">
<author>pinto and onox</author>
<author>pinto, Nikolai V. Chr., onox</author>
<description>Missile Frigate</description>
<status>alpha</status>
<aircraft-version>0.1</aircraft-version>
@@ -97,6 +97,36 @@
<chase-distance-m type="double" archive="y">-500.0</chase-distance-m>
<view n="200">
<name>Follow</name>
<type>lookat</type>
<internal>false</internal>
<config>
<!--<from-model type="bool">true</from-model>
<from-model-idx type="int">0</from-model-idx>-->
<eye-fixed type="bool">true</eye-fixed>
<target-lat-deg-path>rotors/main/blade[0]/flap-deg</target-lat-deg-path>
<target-lon-deg-path>rotors/main/blade[1]/flap-deg</target-lon-deg-path>
<target-alt-ft-path>rotors/main/blade[2]/position-deg</target-alt-ft-path>
<eye-lat-deg-path>/position/latitude-deg</eye-lat-deg-path>
<eye-lon-deg-path>/position/longitude-deg</eye-lon-deg-path>
<eye-alt-ft-path>/position/altitude-ft</eye-alt-ft-path>
<!--<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg-path>/orientation/pitch-deg</eye-pitch-deg-path>
<eye-roll-deg-path>/orientation/roll-deg</eye-roll-deg-path>-->
<eye-heading-deg-path>/orientation/heading-deg</eye-heading-deg-path>
<eye-pitch-deg>0</eye-pitch-deg>
<eye-roll-deg>0</eye-roll-deg>
<x-offset-m>50</x-offset-m>
<y-offset-m>50</y-offset-m>
<z-offset-m>25</z-offset-m>
<default-field-of-view-deg>70</default-field-of-view-deg>
<limits>
<enabled type="bool">false</enabled>
</limits>
</config>
</view>
<hud>
<path n="1">Aircraft/missile-frigate/Hud/hud.xml</path>
<visibility n="1">true</visibility>
@@ -116,15 +146,16 @@
<path>Aircraft/missile-frigate/autopilot.xml</path>
</autopilot>
<property-rule n="100">
<!--property-rule n="100">
<path>Systems/views.xml</path>
</property-rule>
</property-rule-->
</systems>
<multiplay>
<generic>
<int n="0" alias="/carrier/sunk"/>
<int n="2" type="int">0</int>
<string n="6" type="string"></string><!-- string 6 used by radar lock RWR warning -->
<string n="7" type="string"></string><!-- datalink -->
</generic>
</multiplay>
<menubar>
@@ -132,23 +163,7 @@
<menu n="100">
<label>Missile-frigate</label>
<enabled type="bool">true</enabled>
<item>
<label>Reload</label>
<binding>
<command>nasal</command>
<script>
fire_control.reload();
</script>
</binding>
</item>
<item>
<label>Targets</label>
<name>enemies</name>
<binding>
<command>dialog-show</command>
<dialog-name>enemies</dialog-name>
</binding>
</item>
<item>
<label>Event Log</label>
<binding>
@@ -262,9 +277,33 @@
<lbs>50</lbs>
</opt>
</weight>
<d-config>
<full_damage_dist_m type="double">20</full_damage_dist_m>
<use_hitpoints_instead_of_failure_modes_bool type="bool">true</use_hitpoints_instead_of_failure_modes_bool>
<hp_max type="double">200</hp_max>
<hitable_by_air_munitions type="bool">false</hitable_by_air_munitions>
<hitable_by_cannon type="bool">true</hitable_by_cannon>
<is_fleet type="bool">false</is_fleet>
<rwr_to_screen type="bool">false</rwr_to_screen>
<rwr_audio_extended type="bool">false</rwr_audio_extended>
<tacview_supported type="bool">false</tacview_supported>
<m28_auto type="bool">false</m28_auto>
<mlw_max type="double">2.25</mlw_max>
<auto_flare_caller type="bool">false</auto_flare_caller>
</d-config>
<armament>
<models type="string">Aircraft/missile-frigate/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
<modelsUpperCase type="bool">true</modelsUpperCase>
<MP-lat type="string">rotors/main/blade[0]/flap-deg</MP-lat>
<MP-lon type="string">rotors/main/blade[1]/flap-deg</MP-lon>
<MP-alt type="string">rotors/main/blade[2]/flap-deg</MP-alt>
<MP-alt-ft type="string">rotors/main/blade[2]/position-deg</MP-alt-ft>
<hit-interpolation type="bool">true</hit-interpolation>
<gnd-launch type="bool">true</gnd-launch>
<pylon-offset type="int">1</pylon-offset>
<pylon-stations>controls/armament</pylon-stations>
<station-name type="string">station</station-name>
<msg type="bool">true</msg>
<damage type="bool">true</damage>
<enable-craters type="bool">true</enable-craters>
@@ -287,23 +326,27 @@
</blade>
<blade n="2">
<flap-deg type="double">0</flap-deg>
<position-deg type="double">0</position-deg>
</blade>
</main>
</rotors>
<enemies>
<e1 userarchive="y" type="string">Leto</e1>
<e2 userarchive="y" type="string">swamp</e2>
<e3 userarchive="y" type="string">pinto</e3>
<e4 userarchive="y" type="string">fb237</e4>
<e5 userarchive="y" type="string">Wilco-1</e5>
<e6 userarchive="y" type="string">Kokos</e6>
<e7 userarchive="y" type="string">J-Mav16</e7>
<e8 userarchive="y" type="string">S</e8>
<e9 userarchive="y" type="string">Raider1</e9>
<e10 userarchive="y" type="string">Slobb</e10>
<e11 userarchive="y" type="string">5H1N0B1</e11>
<e12 userarchive="y" type="string">SNOWY1</e12>
<priority type="int">0</priority>
<priority0 type="int">1</priority0>
<enemies>
<e1 userarchive="y" type="string">target1</e1>
<e2 userarchive="y" type="string">target2</e2>
<e3 userarchive="y" type="string">target3</e3>
<e4 userarchive="y" type="string">target4</e4>
<e5 userarchive="y" type="string">target5</e5>
<e6 userarchive="y" type="string">target6</e6>
<e7 userarchive="y" type="string">target7</e7>
<e8 userarchive="y" type="string">target8</e8>
<e9 userarchive="y" type="string">target9</e9>
<e10 userarchive="y" type="string">targetA</e10>
<e11 userarchive="y" type="string">targetB</e11>
<e12 userarchive="y" type="string">targetC</e12>
</enemies>
<sam>
@@ -353,8 +396,8 @@
</fire_control>
</nasal>
<input>
<!--input>
<mice include="Sim/mice.xml"/>
</input>
</input-->
</PropertyList>