Merge pull request #27 from SammySkycrafts/sa3

Add SA-3 and update versions.json
This commit is contained in:
Nikolai V. Chr
2023-12-05 00:55:11 +01:00
committed by GitHub
56 changed files with 78460 additions and 0 deletions

567
SA-3/Hud/hud.xml Normal file
View File

@@ -0,0 +1,567 @@
<?xml version="1.0"?>
<PropertyList>
<name>Vinson HUD</name>
<enable3d>false</enable3d>
<!-- <tape>
<name>Rel Brg</name>
<x>-100</x>
<y>190</y>
<width>200</width>
<height>28</height>
<input>
<property>/sim/current-view/heading-offset-rel-deg</property>
</input>
<display-span>25.0</display-span>
<option>top</option>
<major-divisions>5</major-divisions>
<minor-divisions>1</minor-divisions>
<tick-bottom>false</tick-bottom>
<tick-top>false</tick-top>
<tick-right>true</tick-right>
<tick-left>true</tick-left>
<cap-bottom>true</cap-bottom>
<cap-top>false</cap-top>
<cap-right>false</cap-right>
<cap-left>false</cap-left>
<marker-offset>0.0</marker-offset>
<enable-pointer>true</enable-pointer>
<pointer-type>fixed</pointer-type>
<tick-type>line</tick-type>
<tick-length>variable</tick-length>
</tape> -->
<ladder>
<name>Ladder</name>
<type>pitch</type>
<x>-60</x>
<y>-90</y>
<width>120</width>
<height>180</height>
<condition>
<equals>
<property>/sim/current-view/name</property>
<value>Approach View</value>
</equals>
</condition>
<roll-input>
<property>/orientation/roll-deg</property>
<factor>-1.0</factor>
</roll-input>
<pitch-input>
<property>/orientation/pitch-deg</property>
<factor>-1.0</factor>
</pitch-input>
<compression-factor>30</compression-factor>
<display-span>8</display-span>
<divisions>1.0</divisions>
<screen-hole>100</screen-hole>
<lbl-pos>0</lbl-pos>
<glide-slope>0.0</glide-slope>
<enable-fuselage-ref-line>false</enable-fuselage-ref-line>
<enable-target-spot>false</enable-target-spot>
<enable-target-markers>true</enable-target-markers>
<enable-velocity-vector>false</enable-velocity-vector>
<enable-drift-marker>false</enable-drift-marker>
<enable-alpha-bracket>false</enable-alpha-bracket>
<enable-energy-marker>false</enable-energy-marker>
<enable-climb-dive-marker>false</enable-climb-dive-marker>
<enable-glide-slope-marker>false</enable-glide-slope-marker>
<enable-energy-worm>false</enable-energy-worm>
<enable-waypoint-marker>false</enable-waypoint-marker>
<enable-zenith>false</enable-zenith>
<enable-nadir>false</enable-nadir>
<enable-hat>false</enable-hat>
</ladder>
<!--<label>
<name>Wind Angle Runway</name>
<x>-300</x>
<y>190</y>
<width>200</width>
<height>25</height>
<prefix> Wind Angle Runway: </prefix>
<postfix> deg</postfix>
<halign>left</halign>
<format>%03.0f</format>
<box>true</box>
<input>
<property>/orientation/relative-wind-deg</property>
</input>
</label>-->
<label>
<name>Height</name>
<x>-300</x>
<y>80</y>
<width>200</width>
<height>25</height>
<condition>
<or>
<equals>
<property>/sim/current-view/name</property>
<value>Landing Signal Officer View</value>
</equals>
<equals>
<property>/sim/current-view/name</property>
<value>Approach View</value>
</equals>
</or>
</condition>
<box>false</box>
<prefix>HEIGHT: </prefix>
<halign>left</halign>
<input>
<property>/sim/current-view/height</property>
</input>
<blinking>
<interval>0.25</interval>
<condition>
<and>
<equals>
<property>/sim/current-view/height</property>
<value>VERY LOW</value>
</equals>
<less-than>
<property>/sim/current-view/distance</property>
<value>1</value>
</less-than>
</and>
</condition>
</blinking>
</label>
<label>
<name>Gear</name>
<x>-300</x>
<y>60</y>
<width>200</width>
<height>25</height>
<condition>
<or>
<equals>
<property>/sim/current-view/name</property>
<value>Landing Signal Officer View</value>
</equals>
<equals>
<property>/sim/current-view/name</property>
<value>Approach View</value>
</equals>
</or>
</condition>
<box>false</box>
<prefix>GEAR: </prefix>
<halign>left</halign>
<input>
<property>/sim/current-view/gear</property>
</input>
<blinking>
<interval>0.25</interval>
<condition>
<and>
<less-than>
<property>/sim/current-view/gear</property>
<value>1</value>
</less-than>
<less-than>
<property>/sim/current-view/distance</property>
<value>1</value>
</less-than>
</and>
</condition>
</blinking>
</label>
<label>
<name>Flap</name>
<x>-300</x>
<y>40</y>
<width>200</width>
<height>25</height>
<condition>
<or>
<equals>
<property>/sim/current-view/name</property>
<value>Landing Signal Officer View</value>
</equals>
<equals>
<property>/sim/current-view/name</property>
<value>Approach View</value>
</equals>
</or>
</condition>
<box>false</box>
<prefix>FLAPS: </prefix>
<halign>left</halign>
<input>
<property>/sim/current-view/flap</property>
</input>
<blinking>
<interval>0.25</interval>
<condition>
<and>
<less-than>
<property>/sim/current-view/flap</property>
<value>1</value>
</less-than>
<less-than>
<property>/sim/current-view/distance</property>
<value>1</value>
</less-than>
</and>
</condition>
</blinking>
</label>
<label>
<name>Hook</name>
<x>-300</x>
<y>20</y>
<width>200</width>
<height>25</height>
<condition>
<or>
<equals>
<property>/sim/current-view/name</property>
<value>Landing Signal Officer View</value>
</equals>
<equals>
<property>/sim/current-view/name</property>
<value>Approach View</value>
</equals>
</or>
</condition>
<box>false</box>
<prefix>HOOK: </prefix>
<halign>left</halign>
<input>
<property>/sim/current-view/hook</property>
</input>
</label>
<!--<label>
<name>Magnetic Heading</name>
<x>-300</x>
<y>-225</y>
<width>200</width>
<height>25</height>
<prefix> Mag. Heading: </prefix>
<postfix> deg</postfix>
<halign>left</halign>
<format>%03.0f</format>
<box>true</box>
<input>
<property>/orientation/heading-magnetic-deg</property>
</input>
</label>
<label>
<name>Magnetic Heading Runway</name>
<x>-300</x>
<y>-200</y>
<width>200</width>
<height>25</height>
<prefix> Mag. Heading Runway: </prefix>
<postfix> deg</postfix>
<halign>left</halign>
<format>%03.0f</format>
<box>true</box>
<input>
<property>/orientation/heading-runway-magnetic-deg</property>
</input>
</label>
<label>
<name>Wind Speed Runway</name>
<x>100</x>
<y>190</y>
<width>200</width>
<height>25</height>
<prefix> Wind Speed Runway: </prefix>
<postfix> kts</postfix>
<halign>left</halign>
<format>%2.0f</format>
<box>true</box>
<input>
<property>/velocities/wind-speed-runway-kt</property>
</input>
</label>-->
<!--<label>
<name>Speed</name>
<x>100</x>
<y>-225</y>
<width>200</width>
<height>25</height>
<prefix> My speed: </prefix>
<postfix> kts</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>/velocities/groundspeed-kt</property>
</input>
</label>-->
<!--<label>
<name>Target Speed</name>
<x>100</x>
<y>-200</y>
<width>200</width>
<height>25</height>
<prefix> Target Speed: </prefix>
<postfix> kts</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>/carrier/fbw/target/groundspeed-kt</property>
</input>
</label>-->
<label>
<name>Time to impact</name>
<x>150</x>
<y>50</y>
<width>200</width>
<height>25</height>
<prefix>1st Time to impact: </prefix>
<postfix> sec</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>sam/impact0</property>
</input>
</label>
<label>
<name>Time to impact</name>
<x>150</x>
<y>0</y>
<width>200</width>
<height>25</height>
<prefix>2nd Time to impact: </prefix>
<postfix> sec</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>sam/impact1</property>
</input>
</label>
<label>
<name>Time to impact</name>
<x>150</x>
<y>-50</y>
<width>200</width>
<height>25</height>
<prefix>3rd Time to impact: </prefix>
<postfix> sec</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>sam/impact2</property>
</input>
</label>
<label>
<name>Time to impact</name>
<x>150</x>
<y>-100</y>
<width>200</width>
<height>25</height>
<prefix>4th Time to impact: </prefix>
<postfix> sec</postfix>
<halign>left</halign>
<format>%2.1f</format>
<box>true</box>
<input>
<property>sam/impact3</property>
</input>
</label>
<label>
<name>Hit chance</name>
<x>150</x>
<y>25</y>
<width>200</width>
<height>25</height>
<prefix>1st Hit chance: </prefix>
<postfix> %%</postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>sam/hit0</property>
</input>
</label>
<label>
<name>Hit chance</name>
<x>150</x>
<y>-25</y>
<width>200</width>
<height>25</height>
<prefix>2nd Hit chance: </prefix>
<postfix> %%</postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>sam/hit1</property>
</input>
</label>
<label>
<name>Hit chance</name>
<x>150</x>
<y>-75</y>
<width>200</width>
<height>25</height>
<prefix>3rd Hit chance: </prefix>
<postfix> %%</postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>sam/hit2</property>
</input>
</label>
<label>
<name>Hit chance</name>
<x>150</x>
<y>-125</y>
<width>200</width>
<height>25</height>
<prefix>4th Hit chance: </prefix>
<postfix> %%</postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>sam/hit3</property>
</input>
</label>
<label>
<name>ETA</name>
<x>150</x>
<y>-175</y>
<width>200</width>
<height>25</height>
<prefix>Time before operational: </prefix>
<postfix> sec</postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>sam/timeleft</property>
</input>
</label>
<label>
<name>stores</name>
<x>-300</x>
<y>-175</y>
<width>125</width>
<height>25</height>
<prefix> Missiles ready: </prefix>
<postfix></postfix>
<halign>left</halign>
<format>%d</format>
<box>true</box>
<input>
<property>/sam/missiles</property>
</input>
</label>
<label>
<name>damage</name>
<x>-150</x>
<y>-175</y>
<width>125</width>
<height>25</height>
<prefix> Integrity: </prefix>
<postfix> %%</postfix>
<halign>left</halign>
<format>%3.1f</format>
<box>true</box>
<input>
<property>/sam/damage</property>
</input>
</label>
<label>
<name>cs</name>
<x>0</x>
<y>-175</y>
<width>125</width>
<height>25</height>
<prefix> Callsign: </prefix>
<postfix></postfix>
<halign>left</halign>
<format>%s</format>
<box>true</box>
<input>
<property>/sim/multiplay/callsign</property>
</input>
</label>
<label>
<name>Target info</name>
<x>-300</x>
<y>-225</y>
<width>375</width>
<height>25</height>
<prefix> Next target: </prefix>
<postfix></postfix>
<halign>left</halign>
<format>%s</format>
<box>true</box>
<input>
<property>/sam/info</property>
</input>
</label>
<label>
<name>tower</name>
<x>100</x>
<y>-225</y>
<width>200</width>
<height>25</height>
<prefix> Placement near: </prefix>
<postfix></postfix>
<halign>left</halign>
<format>%s</format>
<box>true</box>
<input>
<property>/sim/tower/airport-id</property>
</input>
</label>
</PropertyList>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

View File

@@ -0,0 +1,74 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>bombhit-smoke</name>
<texture>smoke.png</texture>
<emissive>false</emissive>
<lighting>false</lighting>
<align>billboard</align>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>30</theta-min-deg>
<theta-max-deg>60</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg> 180</phi-max-deg>
<speed-mps>
<value>5</value>
<spread>5</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>-60</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>60</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>3</value>
<spread>5</spread>
</particles-per-sec>
</counter>
<particle>
<start>
<color>
<red><value>0.0</value></red>
<green><value>0.0</value></green>
<blue><value>0.0</value></blue>
<alpha><value>0.2</value></alpha>
</color>
<size>
<value>12.0</value>
</size>
</start>
<end>
<color>
<red><value>0.0</value></red>
<green><value>0.0</value></green>
<blue><value>0.0</value></blue>
<alpha><value>0.05</value></alpha>
</color>
<size>
<value>80.0</value>
</size>
</end>
<life-sec>
<value>120</value>
</life-sec>
<mass-kg>0.025</mass-kg>
<radius-m>0.40</radius-m>
</particle>
<program>
<fluid>air</fluid>
<gravity>true</gravity>
<wind>true</wind>
</program>
</particlesystem>
</PropertyList>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@@ -0,0 +1,103 @@
<?xml version="1.0"?>
<PropertyList>
<path>crater.ac</path>
<animation>
<type>scale</type>
<object-name>Sphere</object-name>
<x-offset>1.8</x-offset>
<y-offset>1.8</y-offset>
<z-offset>1.4</z-offset>
</animation>
<animation>
<object-name>Sphere</object-name>
<enable-hot type="bool">true</enable-hot>
</animation>
<particlesystem>
<name>explosion-smoke-longterm</name>
<texture>smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>0</theta-min-deg>
<theta-max-deg>20</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>30</value>
<spread>10</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>5</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>2</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.3 </value></alpha>
</color>
<size>
<value>6</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.05 </value></alpha>
</color>
<size>
<value>12</value>
</size>
</end>
<life-sec>
<value>90</value>
</life-sec>
<mass-kg> 4.0 </mass-kg>
<radius-m> 0.4 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0"?>
<PropertyList>
<path>crater.ac</path>
<animation>
<object-name>Sphere</object-name>
<enable-hot type="bool">true</enable-hot>
</animation>
<particlesystem>
<name>explosion-smoke-longterm</name>
<texture>smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>0</theta-min-deg>
<theta-max-deg>20</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>30</value>
<spread>10</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>5</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>2</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.3 </value></alpha>
</color>
<size>
<value>6</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.05 </value></alpha>
</color>
<size>
<value>10</value>
</size>
</end>
<life-sec>
<value>50</value>
</life-sec>
<mass-kg> 5.0 </mass-kg>
<radius-m> 0.4 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,87 @@
<PropertyList>
<path>aim-emesary.ac</path>
<particlesystem>
<name>emesary-heavy-smoke</name>
<texture>smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>9</value>
</size>
</start>
<end>
<color>
<red><value> 0.2 </value></red>
<green><value> 0.2 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>15</value>
</size>
</end>
<life-sec>
<value>50</value>
</life-sec>
<mass-kg> 0.5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,87 @@
<PropertyList>
<path>aim-emesary.ac</path>
<particlesystem>
<name>emesary-light-smoke</name>
<texture>smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 1.0 </value></green>
<blue><value> 0.9 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>1.5</value>
</size>
</start>
<end>
<color>
<red><value> 0.9 </value></red>
<green><value> 0.9 </value></green>
<blue><value> 0.9 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>3</value>
</size>
</end>
<life-sec>
<value>50</value>
</life-sec>
<mass-kg> 0.5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<PropertyList>
<path>parachutist.ac</path>
<offsets>
<x-m> 0.5</x-m>
<y-m> 0.000</y-m>
<z-m> 1.50</z-m>
</offsets>
</PropertyList>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,144 @@
<?xml version="1.0"?>
<PropertyList>
<path>Models/Effects/procedural_light.ac</path>
<animation>
<type>scale</type>
<object-name>procedural_light</object-name>
<x-offset>1000</x-offset>
<y-offset>1000</y-offset>
<z-offset>1000</z-offset>
</animation>
<effect>
<inherits-from>Effects/procedural-light</inherits-from>
<object-name>procedural_light</object-name>
<parameters>
<light_color_base_r type="float">0.84</light_color_base_r>
<light_color_base_g type="float">0.55</light_color_base_g>
<light_color_base_b type="float">0.42</light_color_base_b>
<light_color_center_r type="float">1.0</light_color_center_r>
<light_color_center_g type="float">0.9</light_color_center_g>
<light_color_center_b type="float">0.7</light_color_center_b>
<intensity_scale type="float">1.0</intensity_scale>
<is_directional type="bool">false</is_directional>
<is_strobe type="bool">false</is_strobe>
<outer_gain type="float">1</outer_gain>
</parameters>
</effect>
<animation>
<type>dist-scale</type>
<object-name>procedural_light</object-name>
<interpolation>
<entry><ind>0</ind> <dep>0.0075</dep></entry>
<entry><ind>3750</ind> <dep>0.750</dep></entry>
<entry><ind>10000</ind><dep>1.000</dep></entry>
</interpolation>
</animation>
<particlesystem>
<name>flare-smoke</name>
<texture>smoke.png</texture>
<emissive>false</emissive>
<lighting>false</lighting>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
<pitch-deg>90</pitch-deg>
<roll-deg>90</roll-deg>
</offsets>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-5</theta-min-deg>
<theta-max-deg>5</theta-max-deg>
<phi-min-deg>-5</phi-min-deg>
<phi-max-deg>5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>2</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>10</x-min-deg-sec>
<y-min-deg-sec>10</y-min-deg-sec>
<z-min-deg-sec>10</z-min-deg-sec>
<x-max-deg-sec>40</x-max-deg-sec>
<y-max-deg-sec>40</y-max-deg-sec>
<z-max-deg-sec>40</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>25</value>
<spread>5</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</red>
<green>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</green>
<blue>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</blue>
<alpha>
<value>1</value>
</alpha>
</color>
<size>
<value>0.6</value>
</size>
</start>
<end>
<color>
<red>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</red>
<green>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</green>
<blue>
<expression><product><value>0.9</value><property>/rendering/scene/diffuse/red</property></product></expression>
</blue>
<alpha>
<value>0.05</value>
</alpha>
</color>
<size>
<value>6.0</value>
</size>
</end>
<life-sec>
<value>2.75</value>
</life-sec>
<mass-kg>0.00001</mass-kg>
<radius-m>0.015</radius-m>
</particle>
<program>
<fluid>air</fluid>
<gravity type="bool">false</gravity>
<wind type="bool">true</wind>
</program>
</particlesystem>
</PropertyList>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<PropertyList>
<name>aim-120</name>
<path>V-601.ac</path>
<animation>
<type>select</type>
<object-name>missile</object-name>
<condition>
<property>payload/armament/V601/flags/msl-id-0</property>
</condition>
</animation>
<model>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/motor-smoke-0.xml</path>
<offsets>
<x-m>2.764</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<model>
<name>explosion</name>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/explosion-0.xml</path>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<animation>
<object-name>missile</object-name>
<enable-hot type="bool">false</enable-hot>
</animation>
</PropertyList>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<PropertyList>
<name>aim-120</name>
<path>V-601.ac</path>
<animation>
<type>select</type>
<object-name>missile</object-name>
<condition>
<property>payload/armament/V601/flags/msl-id-1</property>
</condition>
</animation>
<model>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/motor-smoke-1.xml</path>
<offsets>
<x-m>2.764</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<model>
<name>explosion</name>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/explosion-1.xml</path>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<animation>
<object-name>missile</object-name>
<enable-hot type="bool">false</enable-hot>
</animation>
</PropertyList>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<PropertyList>
<name>aim-120</name>
<path>V-601.ac</path>
<animation>
<type>select</type>
<object-name>missile</object-name>
<condition>
<property>payload/armament/V601/flags/msl-id-2</property>
</condition>
</animation>
<model>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/motor-smoke-2.xml</path>
<offsets>
<x-m>2.764</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<model>
<name>explosion</name>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/explosion-2.xml</path>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<animation>
<object-name>missile</object-name>
<enable-hot type="bool">false</enable-hot>
</animation>
</PropertyList>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<PropertyList>
<name>aim-120</name>
<path>V-601.ac</path>
<animation>
<type>select</type>
<object-name>missile</object-name>
<condition>
<property>payload/armament/V601/flags/msl-id-3</property>
</condition>
</animation>
<model>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/motor-smoke-3.xml</path>
<offsets>
<x-m>2.764</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<model>
<name>explosion</name>
<path>Aircraft/SA-3/Models/Armament/Weapons/V601/explosion-3.xml</path>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
</model>
<animation>
<object-name>missile</object-name>
<enable-hot type="bool">false</enable-hot>
</animation>
</PropertyList>

View File

@@ -0,0 +1 @@
<?xml version="1.0"?>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>explosion</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-id-0</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>80</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>30</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 1.0 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>2</value>
</size>
</start>
<end>
<color>
<red><value> 0.8 </value></red>
<green><value> 0.5 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>55</value>
</size>
</end>
<life-sec>
<value>0.5</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>explosion-smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-smoke-id-0</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>60</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>10</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.04 </value></alpha>
</color>
<size>
<value>60</value>
</size>
</end>
<life-sec>
<value>40</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>explosion</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-id-1</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>80</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>30</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 1.0 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>2</value>
</size>
</start>
<end>
<color>
<red><value> 0.8 </value></red>
<green><value> 0.5 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>55</value>
</size>
</end>
<life-sec>
<value>0.5</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>explosion-smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-smoke-id-1</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>60</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>10</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.04 </value></alpha>
</color>
<size>
<value>60</value>
</size>
</end>
<life-sec>
<value>40</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>explosion</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-id-2</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>80</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>30</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 1.0 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>2</value>
</size>
</start>
<end>
<color>
<red><value> 0.8 </value></red>
<green><value> 0.5 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>55</value>
</size>
</end>
<life-sec>
<value>0.5</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>explosion-smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-smoke-id-2</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>60</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>10</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.04 </value></alpha>
</color>
<size>
<value>60</value>
</size>
</end>
<life-sec>
<value>40</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>explosion</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-id-3</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>80</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>30</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 1.0 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>2</value>
</size>
</start>
<end>
<color>
<red><value> 0.8 </value></red>
<green><value> 0.5 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>55</value>
</size>
</end>
<life-sec>
<value>0.5</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>explosion-smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">false</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/explode-smoke-id-3</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>-180</theta-min-deg>
<theta-max-deg>180</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg>180</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>60</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>10</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.3 </value></red>
<green><value> 0.3 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>10</value>
</size>
</start>
<end>
<color>
<red><value> 0.4 </value></red>
<green><value> 0.4 </value></green>
<blue><value> 0.4 </value></blue>
<alpha><value> 0.04 </value></alpha>
</color>
<size>
<value>60</value>
</size>
</end>
<life-sec>
<value>40</value>
</life-sec>
<mass-kg> 5 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> true </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-0</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>7</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>5</value>
</size>
</end>
<life-sec>
<value>60</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>exhaust</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-0</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 0.7 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.7 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.9 </value></red>
<green><value> 0.6 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>1.5</value>
</size>
</end>
<life-sec>
<value>0.15</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> false </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-1</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>7</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>5</value>
</size>
</end>
<life-sec>
<value>60</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>exhaust</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-1</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 0.7 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.7 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.9 </value></red>
<green><value> 0.6 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>1.5</value>
</size>
</end>
<life-sec>
<value>0.15</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> false </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-2</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>7</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>5</value>
</size>
</end>
<life-sec>
<value>60</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>exhaust</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-2</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 0.7 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.7 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.9 </value></red>
<green><value> 0.6 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>1.5</value>
</size>
</end>
<life-sec>
<value>0.15</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> false </wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<PropertyList>
<particlesystem>
<name>smoke</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-3</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>7</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 1 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.25 </value></red>
<green><value> 0.25 </value></green>
<blue><value> 0.25 </value></blue>
<alpha><value> 0.5 </value></alpha>
</color>
<size>
<value>5</value>
</size>
</end>
<life-sec>
<value>60</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> true </wind>
</program>
</particlesystem>
<particlesystem>
<name>exhaust</name>
<texture>../smoke.png</texture>
<emissive type="bool">true</emissive>
<lighting type="bool">false</lighting>
<condition>
<property>payload/armament/V601/flags/smoke-id-3</property>
</condition>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<offsets>
<x-m>0</x-m>
<y-m>0</y-m>
<z-m>0</z-m>
</offsets>
<shooter>
<theta-min-deg>-1.5</theta-min-deg>
<theta-max-deg>1.5</theta-max-deg>
<phi-min-deg>-1.5</phi-min-deg>
<phi-max-deg>1.5</phi-max-deg>
<speed-mps>
<value>0</value>
<spread>0</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>0</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>0</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>100</value>
<spread>0</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red><value> 1.0 </value></red>
<green><value> 0.7 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.7 </value></alpha>
</color>
<size>
<value>1.0</value>
</size>
</start>
<end>
<color>
<red><value> 0.9 </value></red>
<green><value> 0.6 </value></green>
<blue><value> 0.2 </value></blue>
<alpha><value> 0.1 </value></alpha>
</color>
<size>
<value>1.5</value>
</size>
</end>
<life-sec>
<value>0.15</value>
</life-sec>
<mass-kg> 0.001 </mass-kg>
<radius-m> 0.5 </radius-m>
</particle>
<program>
<fluid> air </fluid>
<gravity type="bool"> false </gravity>
<wind typ="bool"> false </wind>
</program>
</particlesystem>
</PropertyList>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

19680
SA-3/Models/SA-3.ac Normal file

File diff suppressed because it is too large Load Diff

155
SA-3/Models/SA-3.xml Normal file
View File

@@ -0,0 +1,155 @@
<?xml version="1.0"?>
<PropertyList>
<path>SA-3.ac</path>
<animation>
<type>rotate</type>
<object-name>launcher</object-name>
<object-name>missiles</object-name>
<property>sim/multiplay/generic/float[5]</property>
<!-- Pitch of missiles + rails -->
<factor>1.0</factor>
<offset-deg>-33</offset-deg>
<axis>
<x1-m> 1.30999 </x1-m>
<y1-m> -0.949105 </y1-m>
<z1-m> 2.1287 </z1-m>
<x2-m> 1.30999 </x2-m>
<y2-m> 0.949105 </y2-m>
<z2-m> 2.1287 </z2-m>
</axis>
</animation>
<animation>
<type>rotate</type>
<object-name>launch_mech</object-name>
<object-name>launcher</object-name>
<object-name>missiles</object-name>
<!-- <object-name>missiles</object-name> -->
<property>sim/multiplay/generic/float[6]</property>
<factor>-1.0</factor>
<!-- <offset-deg>33</offset-deg> -->
<axis>
<x1-m> -0.00802 </x1-m>
<y1-m> 0.02807 </y1-m>
<z1-m> 1.07179 </z1-m>
<x2-m> -0.00802 </x2-m>
<y2-m> 0.02807 </y2-m>
<z2-m> 0.2 </z2-m>
</axis>
</animation>
<animation>
<type>select</type>
<object-name>missile1</object-name>
<condition>
<less-than>
<property>active</property>
<value>1</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>missile2</object-name>
<condition>
<less-than>
<property>active</property>
<value>2</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>missile3</object-name>
<condition>
<less-than>
<property>active</property>
<value>3</value>
</less-than>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>missile4</object-name>
<condition>
<less-than>
<property>active</property>
<value>4</value>
</less-than>
</condition>
</animation>
<model>
<path>Aircraft/SA-3/Models/smoke.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<model>
<path>Aircraft/SA-3/Models/fire.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<model>
<path>Aircraft/SA-3/Models/smoke.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<model>
<path>Aircraft/SA-3/Models/fire.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<model>
<path>Aircraft/SA-3/Models/smoke.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<model>
<path>Aircraft/SA-3/Models/fire.xml</path>
<offsets>
<x-m>-0.01136</x-m>
<y-m>0.02663</y-m>
<z-m>0.25</z-m>
<pitch-deg>0</pitch-deg>
</offsets>
</model>
<nasal>
<load><![CDATA[
]]>
</load>
</nasal>
</PropertyList>

BIN
SA-3/Models/colors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

BIN
SA-3/Models/fire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

87
SA-3/Models/fire.xml Normal file
View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" ?>
<PropertyList>
<particlesystem>
<texture>fire.png</texture>
<condition>
<equals>
<property>sim/multiplay/generic/int[0]</property>
<value>1</value>
</equals>
</condition>
<emissive>true</emissive>
<lighting>false</lighting>
<offsets>
<roll-deg>0</roll-deg>
</offsets>
<attach>world</attach>
<placer>
<type>point</type>
</placer>
<shooter>
<theta-min-deg>80</theta-min-deg>
<theta-max-deg>30</theta-max-deg>
<phi-min-deg>-180</phi-min-deg>
<phi-max-deg> 180</phi-max-deg>
<speed>
<value>20</value>
<spread>40</spread>
</speed>
</shooter>
<counter>
<particles-per-sec>
<value>40</value>
<spread>60</spread>
</particles-per-sec>
</counter>
<align>billboard</align>
<particle>
<start>
<color>
<red>
<value>0.6</value>
</red>
<green>
<value>0.6</value>
</green>
<blue>
<value>0.6</value>
</blue>
<alpha>
<value>0.4</value>
</alpha>
</color>
<size>
<value>1.2</value>
</size>
</start>
<end>
<color>
<red>
<value>0</value>
</red>
<green>
<value>0</value>
</green>
<blue>
<value>0</value>
</blue>
<alpha>
<value>0</value>
</alpha>
</color>
<size>
<value>4.0</value>
</size>
</end>
<life-sec>
<value>3</value>
</life-sec>
<mass-kg>1.3</mass-kg>
<radius-m>0.03</radius-m>
</particle>
</particlesystem>
</PropertyList>

BIN
SA-3/Models/smoke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

81
SA-3/Models/smoke.xml Normal file
View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" ?>
<PropertyList>
<particlesystem>
<texture>smoke.png</texture>
<condition>
<equals>
<property>sim/multiplay/generic/int[0]</property>
<value>1</value>
</equals>
</condition>
<emissive>false</emissive>
<lighting>false</lighting>
<align>billboard</align> <!-- billboard / fixed -->
<attach>world</attach> <!-- world / local-->
<placer>
<type>point</type>damage.fail_systems(10000);
</placer>
<shooter>
<theta-min-deg>65</theta-min-deg>
<theta-max-deg>35</theta-max-deg>
<phi-min-deg>-20</phi-min-deg>
<phi-max-deg> 20</phi-max-deg>
<speed-mps>
<value>500</value>
<spread>10</spread>
</speed-mps>
<rotation-speed>
<x-min-deg-sec>0</x-min-deg-sec>
<y-min-deg-sec>0</y-min-deg-sec>
<z-min-deg-sec>-180</z-min-deg-sec>
<x-max-deg-sec>0</x-max-deg-sec>
<y-max-deg-sec>0</y-max-deg-sec>
<z-max-deg-sec>360</z-max-deg-sec>
</rotation-speed>
</shooter>
<counter>
<particles-per-sec>
<value>50</value>
<spread>1</spread>
</particles-per-sec>
</counter>
<particle>
<start>
<color>
<red><value>0.0</value></red>
<green><value>0.0</value></green>
<blue><value>0.0</value></blue>
<alpha><value>0.08</value></alpha>
</color>
<size>
<value>5</value>
</size>
</start>
<end>
<color>
<red><value>0.0</value></red>
<green><value>0.0</value></green>
<blue><value>0.0</value></blue>
<alpha><value>0.0</value></alpha>
</color>
<size>
<value>50.0</value>
</size>
</end>
<life-sec>
<value>5</value>
</life-sec>
<mass-kg>0.0085</mass-kg>
<radius-m>0.50</radius-m>
</particle>
<program>
<fluid>air</fluid>
<gravity type="bool">true</gravity>
<wind type="bool">true</wind>
</program>
</particlesystem>
</PropertyList>

View File

@@ -0,0 +1,260 @@
#
#
# Use to transmit events that happen at a specific place; can be used to make
# models that are simulated locally (e.g. tankers) appear on other player's MP sessions.
var ArmamentNotification_Id = 19;
var ArmamentNotification =
{
# new:
# _ident - the identifier for the notification. not bridged.
# _name - name of the notification, bridged.
# _kind - created, moved, deleted, impact (see notifications.nas)
# _secondary_kind - This is the entity on which the activity is being performed. See below for predefined types.
##
new: func(_ident="none", _kind=0, _secondary_kind=0)
{
var new_class = emesary.Notification.new("ArmamentNotification", _ident, ArmamentNotification_Id);
new_class.Kind = _kind;
new_class.SecondaryKind = _secondary_kind;
new_class.RelativeAltitude = 0;
new_class.IsDistinct = 0;
new_class.Distance = 0;
new_class.Bearing = 0;
new_class.RemoteCallsign = ""; # associated remote callsign.
new_class.bridgeProperties = func
{
return
[
{
getValue:func{return emesary.TransferByte.encode(new_class.Kind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.SecondaryKind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(new_class.RelativeAltitude,2,1/10);},
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,1/10,pos);new_class.RelativeAltitude=dv.value;return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(new_class.Distance,2,1/10);},
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,1/10,pos);new_class.Distance=dv.value;return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(geo.normdeg180(new_class.Bearing),1,1.54);},
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,1,1.54,pos);new_class.Bearing=geo.normdeg(dv.value);return dv},
},
{
getValue:func{return emesary.TransferString.encode(new_class.RemoteCallsign);},
setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.RemoteCallsign=dv.value;return dv},
},
];
};
return new_class;
},
};
var ArmamentInFlightNotification_Id = 21;
var ArmamentInFlightNotification =
{
# new:
# _ident - the identifier for the notification. not bridged.
# _name - name of the notification, bridged.
# _kind - created, moved, deleted (see below). This is the activity that the notification represents, called kind to avoid confusion with notification type.
# _secondary_kind - This is the entity on which the activity is being performed. See below for predefined types.
#
# UniqueIdentity - an identity that is unique to the sending instance of FG. Can be combined with the callsign to create an MP unique ID.
##
new: func(_ident="none", _unique=0, _kind=0, _secondary_kind=0)
{
var new_class = emesary.Notification.new("ArmamentInFlightNotification", _ident, ArmamentInFlightNotification_Id);
new_class.Kind = _kind;
new_class.SecondaryKind = _secondary_kind;
new_class.Position = geo.aircraft_position();
new_class.UniqueIndex = 0;
new_class.Heading = 360;
new_class.Pitch = 90;
new_class.u_fps = 0;
new_class.IsDistinct = 0;
new_class.Callsign = nil; # populated automatically by the incoming bridge when routed
new_class.RemoteCallsign = ""; # associated remote callsign.
new_class.Flags = 0; # 8 bits for whatever.
new_class.UniqueIdentity = _unique;
new_class.GetBridgeMessageNotificationTypeKey = func {
return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex;
};
new_class.bridgeProperties = func
{
return
[
{
getValue:func{return emesary.TransferCoord.encode(new_class.Position);},
setValue:func(v,root,pos){var dv=emesary.TransferCoord.decode(v, pos);new_class.Position=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Kind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.SecondaryKind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv},
},
{
#0..6696 fps (3967kts), mach 6.1 (SL) - factor 0.03703
getValue:func{return emesary.TransferFixedDouble.encode(new_class.u_fps-3348,1,1/0.03703);},
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,1,1/0.03703,pos);new_class.u_fps=dv.value+3348;return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(geo.normdeg180(new_class.Heading),1,1.54);},#1.0/0.65
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,1,1.54,pos);new_class.Heading=geo.normdeg(dv.value);return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(new_class.Pitch, 1, 1/1.38);},
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,1, 1/1.38, pos);new_class.Pitch=dv.value;return dv},
},
{
getValue:func{return emesary.TransferString.encode(new_class.RemoteCallsign);},
setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.RemoteCallsign=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Flags);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Flags=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.UniqueIdentity);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.UniqueIdentity=dv.value;return dv},
},
];
};
return new_class;
},
};
var StaticNotification_Id = 25;
var StaticNotification =
{
# new:
# _ident - the identifier for the notification. not bridged.
# _kind - created, moved, deleted (see below). This is the activity that the notification represents, called kind to avoid confusion with notification type.
# _secondary_kind - This is the entity on which the activity is being performed. See below for predefined types.
#
# UniqueIdentity - an identity that is unique to the sending instance of FG. Can be combined with the callsign to create an MP unique ID.
##
new: func(_ident="stat", _unique=0, _kind=0, _secondary_kind=0)
{
var new_class = emesary.Notification.new("StaticNotification", _ident, StaticNotification_Id);
# _ident -> "stat"
new_class.UniqueIdentity = _unique; # random from 0 to 15000000 that identifies each static object
new_class.Kind = _kind; # 1=create, 2=move, 3=delete, 4=request_all
new_class.SecondaryKind = _secondary_kind; # 0 = small crater, 1 = big crater, 2 = smoke
new_class.IsDistinct = 0; # keep it 0
new_class.Callsign = nil; # populated automatically by the incoming bridge when routed
new_class.Position = geo.aircraft_position(); # position
new_class.Heading = 360; # heading
new_class.Flags1 = 0; # 7 bits for whatever.
new_class.Flags2 = 0; # 7 bits for whatever.
new_class.GetBridgeMessageNotificationTypeKey = func {
return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIdentity;
};
new_class.bridgeProperties = func
{
return
[
{
getValue:func{return emesary.TransferCoord.encode(new_class.Position);},
setValue:func(v,root,pos){var dv=emesary.TransferCoord.decode(v, pos);new_class.Position=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Kind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.SecondaryKind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferFixedDouble.encode(geo.normdeg180(new_class.Heading),1,1.54);},#1.0/0.65
setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,1,1.54,pos);new_class.Heading=geo.normdeg(dv.value);return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Flags1);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Flags1=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Flags2);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Flags2=dv.value;return dv},
},
{
getValue:func{return emesary.TransferInt.encode(new_class.UniqueIdentity,3);},
setValue:func(v,root,pos){var dv=emesary.TransferInt.decode(v,3,pos);new_class.UniqueIdentity=dv.value;return dv},
},
];
};
return new_class;
},
};
var ObjectInFlightNotification_Id = 22;
var ObjectInFlightNotification =
{
# new:
# _ident - the identifier for the notification. not bridged.
# _name - name of the notification, bridged.
# _kind - created, moved, deleted (see below). This is the activity that the notification represents, called kind to avoid confusion with notification type.
# _secondary_kind - This is the entity on which the activity is being performed. See below for predefined types.
#
# UniqueIdentity - an identity that is unique to the sending instance of FG. Can be combined with the callsign to create an MP unique ID.
##
new: func(_ident="none", _unique=0, _kind=0, _secondary_kind=0)
{
var new_class = emesary.Notification.new("ObjectInFlightNotification", _ident, ObjectInFlightNotification_Id);
new_class.Kind = _kind;
new_class.SecondaryKind = _secondary_kind;
new_class.Position = geo.aircraft_position();
new_class.UniqueIndex = 0;
new_class.IsDistinct = 1;
new_class.Callsign = nil; # populated automatically by the incoming bridge when routed
new_class.UniqueIdentity = _unique;
new_class.GetBridgeMessageNotificationTypeKey = func {
return new_class.NotificationType~"."~new_class.Ident~"."~new_class.UniqueIndex;
};
new_class.bridgeProperties = func
{
return
[
{
getValue:func{return emesary.TransferCoord.encode(new_class.Position);},
setValue:func(v,root,pos){var dv=emesary.TransferCoord.decode(v, pos);new_class.Position=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.Kind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.SecondaryKind);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv},
},
{
getValue:func{return emesary.TransferByte.encode(new_class.UniqueIdentity);},
setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.UniqueIdentity=dv.value;return dv},
},
];
};
return new_class;
},
};

View File

@@ -0,0 +1,53 @@
# Emesary bridged transmitter for armament notifications.
#
# Richard Harrison 2017
#
# NOTES:
# 1.The incoming bridges that is defined here will apply to all models that
# are loaded over MP; it is better to create the bridges here rather than in the model.xml
# So given that we don't want a bridge on all MP models only those that are on OPRF
# aircraft that want to receive notifications we will create the incoming bridge here
# and thus only an OPRF model will receive notifications from another OPRF model.
#
# 2. The Emesary MP bridge requires two sides; the outgoing and incoming.
# - The outgoing aircraft will forwards all received notifications via MP;
# and these will be received by a similarly equipped craft.
# - The receiving aircraft will receive all notifications from other MP craft via
# the globalTransmitter - which is bridged via property #18 /sim/multiplay/emesary/bridge[18]
#------------------------------------------------------------------------------------------
# Setup the bridge
# armament notification 24 bytes
# geoEventNotification - 34 bytes + the length of the RemoteCallsign and Name fields.
#NOTE: due to bug in Emesary MP Bridge (fixed in 2019.2 after 24/3/2020) we can only
# reliably send one message type per bridge - so for the maximum compatibility
# we will use two bridges.
# If at some point in the future we target 2019.2 as a min ver we can use a single
# bridge and setup the notification list to contain all of the armament hit/flying notifications
#i.e. change to [notifications.ArmamentInFlightNotification.new(nil), notifications.ArmamentNotification.new(nil)];
var geoRoutedNotifications = [notifications.ArmamentInFlightNotification.new()];
var geoBridgedTransmitter = emesary.Transmitter.new("geoOutgoingBridge");
var geooutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.geo",geoRoutedNotifications, 18, "", geoBridgedTransmitter);
# bridge should be tuned to be around 90% of the packet size full.
geooutgoingBridge.TransmitFrequencySeconds = 0.75;
geooutgoingBridge.MPStringMaxLen = 150;
emesary_mp_bridge.IncomingMPBridge.startMPBridge(geoRoutedNotifications, 18, emesary.GlobalTransmitter);
#----- bridge hit (armament) notifications
var hitRoutedNotifications = [notifications.ArmamentNotification.new(),notifications.StaticNotification.new()];
var hitBridgedTransmitter = emesary.Transmitter.new("armamentNotificationBridge");
var hitoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.hit",hitRoutedNotifications, 19, "", hitBridgedTransmitter);
hitoutgoingBridge.TransmitFrequencySeconds = 1.5;
hitoutgoingBridge.MPStringMaxLen = 120;
emesary_mp_bridge.IncomingMPBridge.startMPBridge(hitRoutedNotifications, 19, emesary.GlobalTransmitter);
#----- bridge object notifications
var objectRoutedNotifications = [notifications.ObjectInFlightNotification.new()];
var objectBridgedTransmitter = emesary.Transmitter.new("objectNotificationBridge");
var objectoutgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("patriotmp.object",objectRoutedNotifications, 17, "", objectBridgedTransmitter);
objectoutgoingBridge.TransmitFrequencySeconds = 0.2;
objectoutgoingBridge.MessageLifeTime = 1;
objectoutgoingBridge.MPStringMaxLen = 150;
emesary_mp_bridge.IncomingMPBridge.startMPBridge(objectRoutedNotifications, 17, emesary.GlobalTransmitter);

1460
SA-3/Nasal/damage.nas Normal file

File diff suppressed because it is too large Load Diff

681
SA-3/Nasal/datalink.nas Normal file
View File

@@ -0,0 +1,681 @@
#### Datalink
# Copyright 2020-2021 Colin Geniet.
# Licensed under the GNU General Public License 2.0 or any later version.
#### Usage
### Generalities
#
# The datalink protocol consists of a core protocol which implements a notion
# of datalink channel, and extensions which allow transmitting actual data.
### Core protocol usage:
#
# Define the following properties (must be defined at nasal loading time).
# * Mandatory
# /instrumentation/datalink/power_prop path to property indicating if datalink is on
# /instrumentation/datalink/channel_prop path to property containing datalink channel
# (the channel property can contain anything, and is transmitted/compared as a string).
# * Optional
# /instrumentation/datalink/receive_period = 1 receiving loop update rate
#
# Optional: Re-define the function
# datalink.can_transmit(callsign, mp_prop, mp_index)
#
# This function should return 'true' when the given aircraft is able to transmit over datalink to us.
# For instance, it can be used to check line of sight and maximum range.
# The default implementation always returns true (always able to transmit).
# Arguments are callsign, property node /ai/models/multiplayer[i], index of the former node.
#
#
# API:
# - get_data(callsign)
# Returns all datalink information about 'callsign' as an object, or nil if there is none.
# This object must not be modified.
# It contains the following methods:
# callsign(): The aircraft callsign (same as the argument of get_data()).
# index(): The aircraft index in /ai/models/multiplayer[i].
# on_link(): Returns a bool indicating whether 'callsign' is connected to this aircraft through datalink.
#
# Extensions can define other methods in this object.
#
# - get_connected_callsigns() / get_connected_indices()
# Returns a vector containing all callsigns, resp. indices
# in /ai/models/multiplayer[i], of aircrafts connected on datalink.
# Both vectors use the same order, i.e. get_connected_callsigns()[i]
# and get_connected_indices()[i] correspond to the same aircraft.
# Furthermore this order is stable (the relative order of two aircrafts
# does not change as long as neither disconnects from multiplayer).
#
# - get_all_callsigns()
# Returns a vector containing all callsigns of aircraft with any associated data.
# There is no guarantee on the order of callsigns.
#
# - send_data(data, timeout=nil)
# Send data on the datalink. 'data' is a hash of the form
# {
# <extension_name>: <extension_data>,
# ...
# }
# If 'timeout' is set, clear_data() will be called after this delay.
# Data sent with send_data() is deleted at the next call of send_data(), or by clear_data().
#
# - clear_data()
# Clear data transmitted by this aircraft.
#
# Important note:
# After a send_data(), and until the next send_data() or clear_data(),
# the datalink behaves as if you are continuously sending the same data.
# Thus, it is important to
# 1. either call send_data() regularly
# 2. or set the timeout argument of send_data()
### Extensions
### Aircraft contacts (extension name: "contacts")
#
# This extension allows to simulate an aircraft transmitting information about
# another aircraft (typically one tracked on radar). The position data is not
# actually transmitted (since everyone can access it from simulator internals).
#
## Receiving data
# This extension adds the following methods to the result of get_data("A"):
# tracked(): A bool indicating that some aircraft "B" connected on datalink
# is transmitting information about aircraft "A".
# iff(): One of IFF_UNKNOWN, IFF_HOSTILE, IFF_FRIENDLY, or nil if tracked() is false.
# Indicates the result of IFF interrogation of "A" by "B"
# IFF_UNKNOWN means that e.g. no IFF interrogation was performed.
# tracked_by(): The callsign of the transmitting aircraft ("A"), or nil if tracked() is false.
# tracked_by_index(): The index of the transmitting aircraft, or nil if tracked() is false.
# The index refers to property nodes /ai/models/multiplayer[i].
# is_known(): Equivalent to (on_link() or tracked()).
# Indicates if the position of this aircraft is supposed to be known
# (i.e. whether or not it should be displayed on a HSD or whatever).
# is_friendly(): Equivalent to (on_link() or iff() == IFF_FRIENDLY).
# is_hostile(): Equivalent to (!on_link() and iff() == IFF_HOSTILE).
#
## Sending data
# usage: send_data({ contacts: <contacts>, ...}, ...)
# where <contacts> is a vector of hashes of the form { callsign: <callsign>, [iff: <iff>,] }.
# <callsign> is the multiplayer callsign of the tracked aircraft.
# <iff> (optional) is one of IFF_UNKNOWN, IFF_HOSTILE, IFF_FRIENDLY
### Datalink identifier (extension name: "identifier")
#
# This extension allows each aircraft on datalink to transmit a personal
# identifier, e.g. the number of the aircraft in a flight.
#
## Receiving data
# This extension adds the method identifier() to the result of get_data(),
# which returns the identifier, or nil if there is none).
#
## Sending data
# Set the identifier with send_data({"identifier": <identifier>, ...});
# The identifier must be a string. It must not contain '!'.
### Coordinate transmission (extension name: "point")
#
# This extension allows each aircraft to broadcast a coordinate (geo.Coord object).
#
## Receiving data
# This extension adds the method point() to the result of get_data(),
# which results the transmitted geo.Coord object, or nil if there is none.
#
## Sending data
# Transmit a geo.Coord object <coord> with send_data({"point": <coord>, ...});
#### Protocol:
#
# Data is transmitted on MP generic string[7], with the following format:
# <channel>(!<data>)+
#
# <channel> is a hash of the datalink channel. See hash_channel() and check_channel_hash().
# Each <data> block corresponds to data sent by an extension.
# It starts with a prefix uniquely defining the extension.
# The rest of the block can contain any character (including non-ascii) except '!'.
#
# Remark: '!' as separator is specifically chosen to allow encoding with emesary.Transfer<type>.
#
# The current extension prefixes are the following:
# contacts: C
# identifier: I
# point: P
#### Extensions API
#
# Creating a new extension is done with
# register_extension(name, prefix, object, encode, decode)
# name the extension name, used as key in the 'data' argument of send_data().
# prefix the protocol prefix.
# class contact class parent.
# A class from which all contact objects will inherit.
# It must have an init() method, which is called whenever a contact is created.
#
# encode(data) extension encoding function.
# Must return the encoding of the extension data (i.e. <data> when calling
# send_data({name: <data>})) into a string, which may use any character except '!'.
# The extension prefix must not be part of the encoded string.
#
# decode(aircrafts_data, callsign, index, string) extension decoding function.
# 'aircrafts_data' is a hash from callsigns to contact objects (see below).
# 'callsign' is the callsign of the aircraft which transmitted this data.
# 'index' is the index of the aircraft which transmitted this data.
# 'string' is the data encoded by encode() and transmitted through datalink.
# Each contact in 'data' inherits from the core 'Contact' class, and the extension 'class'.
# decode() is expected to modify 'aircrafts_data', by possibly editing
# existing contacts and adding new ones. It should be careful when
# overwriting existing data in these contacts, including its own: decode()
# will be called several time on the same 'aircrafts_data' (once for each
# transmitting aircraft).
# The modified 'aircrafts_data' hash must be returned.
#
# decode() may use the following helper functions:
# add_if_missing(aircrafts_data, callsign):
# Create a new contact object for 'callsign' and add it to 'aircrafts_data',
# unless an entry for 'callsign' already exists. Returns the modified hash.
#### Version and changelog
# current: v1.1.0, minimum compatible: v1.0.0
#
## v1.1.0:
# Allow external transmission restrictions
# Make transmitting contact IFF optional
# Ensure personal identifier has no '!'
# '\n' is redundant for printf()
# Fix separator character in documentation
# Fix error when sending unknown extension
#
## v1.0.1:
# Add is_known(), is_friendly(), is_hostile() helpers to extension "contacts".
#
## v1.0.0: Initial version
# - Core protocol for datalink channel.
# - Extensions "contacts", "identifier", and "point".
### Parameters
#
# Remark: most parameters need to be the same on all aircrafts.
# Index of multiplayer string used to transmit datalink info.
# Must be the same for all aircrafts.
var mp_string = 7;
var mp_path = "sim/multiplay/generic/string["~mp_string~"]";
var channel_hash_period = 600;
var receive_period = getprop("/instrumentation/datalink/receive_period") or 1;
# Should be overwitten to add transmission restrictions.
var can_transmit = func(contact, mp_prop, mp_index) {
return 1;
}
### Properties
var input = {
power: getprop("/instrumentation/datalink/power_prop"),
channel: getprop("/instrumentation/datalink/channel_prop"),
ident: getprop("/instrumentation/datalink/identifier_prop"),
mp: mp_path,
models: "/ai/models",
callsign: "/sim/multiplay/callsign",
};
foreach (var name; keys(input)) {
if (input[name] != nil) {
input[name] = props.globals.getNode(input[name], 1);
}
}
#### Core protocol implementation
### Channel hash (based on iff.nas)
#
# Channel is hashed with current time (rounded to 10min) and own callsign.
var clean_callsign = func(callsign) {
return damage.processCallsign(callsign);
}
var my_callsign = func {
return clean_callsign(input.callsign.getValue());
}
# Time, rounded to 'channel_hash_period'. This is used to hash channel.
var get_time = func {
return int(math.floor(systime() / channel_hash_period) * channel_hash_period);
}
# Previous / next time (with channel_hash_period interval).
# This is used to give a bit of margin on the time check.
# (will work if system clocks are coordinated within 10min).
var get_prev_time = func { return get_time() - channel_hash_period; }
var get_next_time = func { return get_time() + channel_hash_period; }
var parse_hexadecimal = func(str) {
var res = 0;
for (var i=0; i<size(str); i+=1) {
res *= 10;
var c = str[i];
if (c >= 48 and c < 58) {
# digit
res += c - 48;
} elsif (c >= 65 and c < 71) {
# upper case letter
res += c - 55;
} elsif (c >= 97 and c < 103) {
# lower case letter
res += c - 87;
}
}
return res;
}
var _hash_channel = func(time, callsign, channel) {
# 5 hex digits (2^20) fit in 3 chars for emesary int encoding.
var hash = parse_hexadecimal(left(md5(time ~ callsign ~ channel), 5));
return emesary.TransferInt.encode(hash, 3);
}
# Hash channel (when sending).
var encode_channel = func(channel) {
return _hash_channel(get_time(), my_callsign(), channel);
}
# Check that the hash transmitted by aircraft 'callsign' is correct for 'channel'.
var check_channel = func(hash, callsign, channel) {
return hash == _hash_channel(get_time(), callsign, channel)
or hash == _hash_channel(get_prev_time(), callsign, channel)
or hash == _hash_channel(get_next_time(), callsign, channel);
}
### Contact object
var Contact = {
new: func(callsign) {
var c = {
# contact_parents is the list of all classes from which contacts inherit (for extensions).
parents: contact_parents,
_callsign: callsign,
};
# Initialize all inherited classes.
foreach (var class; contact_parents) {
call(class.init, [], c, nil, nil);
}
return c;
},
init: func { me._on_link = 0; },
callsign: func { return me._callsign; },
index: func { return callsign_to_index[me._callsign]; },
on_link: func { return me._on_link; },
set_on_link: func(b) { me._on_link = b; },
};
### Extensions
var extensions = {};
var extension_prefixes = {};
var max_prefix_length = 0;
var contact_parents = [Contact];
var register_extension = func(name, prefix, class, encode, decode) {
if (contains(extensions, name)) {
printf("Datalink: double registration of extension '%s'. Skipping.", name);
return -1;
}
if (contains(extension_prefixes, prefix)) {
printf("Datalink: double registration of extension prefix '%s'. Skipping.", name);
return -1;
}
extensions[name] = { prefix: prefix, encode: encode, decode: decode, };
extension_prefixes[prefix] = name;
max_prefix_length = math.max(max_prefix_length, size(prefix));
append(contact_parents, class);
return 0;
}
var data_separator = "!";
### Transmission
var clear_data = func {
send_data({});
}
var clear_timer = maketimer(1, clear_data);
clear_timer.singleShot = 1;
# Send data through datalink.
#
# timeout: if set, sent data will be cleared after this time (other aircrafts
# won't receive it anymore). Useful if 'send_data' is not called often.
var send_data = func(data, timeout=nil) {
if (!input.power.getBoolValue()) {
last_data = {};
input.mp.setValue("");
return;
}
# First encode channel
var str = encode_channel(input.channel.getValue());
# Then all extensions
last_data = data;
foreach(var ext; keys(data)) {
# Skip missing extensions with a warning
if (!contains(extensions, ext)) {
printf("Warning: unknown datalink extension %s in send_data().", ext);
continue;
}
str = str ~ data_separator ~ extensions[ext].prefix ~ extensions[ext].encode(data[ext]);
}
input.mp.setValue(str);
if (timeout != nil) {
clear_timer.restart(timeout);
}
}
# Used internally to update the channel/identifier while keeping the same data.
# Does not touch timeout.
var last_data = {};
var resend_data = func {
send_data(last_data);
}
# Very slow timer to ensure the channel hash is updated regularly.
# Only relevant if you never call send_data();
var hash_update_timer = maketimer(channel_hash_period/2, resend_data);
hash_update_timer.start();
### Receiving
# callsign to data hash
var aircrafts_data = {};
# List of callsigns / indices connected on datalink (index is for /ai/models/multiplayer[i]).
var connected_callsigns = [];
var connected_indices = [];
# Maintain callsign to multiplayer index hash
# (doesn't cost much since we already iterate over MP models).
var callsign_to_index = {};
var get_data = func(callsign) {
return aircrafts_data[callsign];
}
var get_connected_callsigns = func {
return connected_callsigns;
}
var get_connected_indices = func {
return connected_indices;
}
var get_all_callsigns = func {
return keys(aircrafts_data);
}
# Helper for modifying aircrafts_data.
var add_if_missing = func(aircrafts_data, callsign) {
if (!contains(aircrafts_data, callsign)) {
aircrafts_data[callsign] = Contact.new(callsign);
}
return aircrafts_data;
}
var receive_loop = func {
var my_channel = input.channel.getValue();
aircrafts_data = {};
connected_callsigns = [];
connected_indices = [];
var mp_models = input.models.getChildren("multiplayer");
foreach(var mp; mp_models) {
var idx = mp.getIndex();
if (!mp.getValue("valid")) continue;
var callsign = mp.getValue("callsign");
if (callsign == nil) continue;
callsign_to_index[callsign] = idx;
var data = mp.getValue(mp_path);
if (data == nil) continue;
# Split channel part and data part
var tokens = split(data_separator, data);
# Check channel
if (!check_channel(tokens[0], callsign, my_channel)) continue;
# We check this _after_ the channel. Checking the channel is quite cheap,
# and we don't know how slow this function is, it might have a get_cart_ground_intersection()
if (!can_transmit(callsign, mp, idx)) continue;
# Add to list of connected aircrafts.
append(connected_callsigns, callsign);
append(connected_indices, idx);
# Add to data
aircrafts_data = add_if_missing(aircrafts_data, callsign);
aircrafts_data[callsign].set_on_link(1);
# Parse extensions data
for (var i=1; i<size(tokens); i+=1) {
var extension = nil;
# Identify extension prefix. This is not very clever code, but
# realistically it doesn't matter since prefixes are very short.
var len = 1;
for (; len <= max_prefix_length; len += 1) {
if (len > size(tokens)) break;
var prefix = left(tokens[i], len);
if (contains(extension_prefixes, prefix)) {
extension = extension_prefixes[prefix];
break;
}
}
# Unknown extension, skip
if (extension == nil) continue;
# Remove prefix
var data = substr(tokens[i], len);
# Decode
aircrafts_data = extensions[extension].decode(aircrafts_data, callsign, data);
}
}
}
var receive_timer = maketimer(receive_period, receive_loop);
# Start / stop listener
setlistener(input.power, func (node) {
if (node.getBoolValue()) {
receive_timer.start();
resend_data(); # Sets channel/identifier
} else {
receive_timer.stop();
aircrafts_data = {};
clear_data();
}
}, 1, 0);
# Listener to resend data so as to update the channel.
setlistener(input.channel, resend_data);
#### Extensions
## Identifier
var ContactIdentifier = {
init: func {
me._identifier = nil;
},
set_identifier: func(ident) {
me._identifier = ident;
},
identifier: func {
return me._identifier;
},
};
var encode_identifier = func(ident) {
# Force string conversion
ident = ""~ident;
if (find("!", ident) >= 0) {
printf("Datalink: Identifier is not allowed to contain '!': %s.", ident);
return "";
} else {
return ident;
}
}
var decode_identifier = func(aircrafts_data, callsign, str) {
aircrafts_data = add_if_missing(aircrafts_data, callsign);
aircrafts_data[callsign].set_identifier(str);
return aircrafts_data;
}
register_extension("identifier", "I", ContactIdentifier, encode_identifier, decode_identifier);
## Contacts
# IFF status transmitted over datalink.
var IFF_UNKNOWN = 0; # Unknown status
var IFF_HOSTILE = 1; # Considered hostile (no response to IFF).
var IFF_FRIENDLY = 2; # Friendly, because positive IFF identification.
# This is also the priority order for IFF reports in case of conflicts:
# e.g. a contact will be reported as friendly if anyone on datalink reports it as friendly.
var ContactTracked = {
init: func {
me._tracked_by = nil;
me._iff = IFF_UNKNOWN;
},
set_tracked_by: func(callsign) {
me._tracked_by = callsign;
},
set_iff: func(iff) {
# Priority order on IFF values (friendly, then hostile, then no data).
me._iff = math.max(me._iff, iff);
},
tracked: func {
return me._tracked_by != nil;
},
tracked_by: func {
return me._tracked_by;
},
tracked_by_index: func {
return (me._tracked_by != nil) ? callsign_to_index[me._tracked_by] : nil;
},
iff: func {
return me._iff;
},
is_known: func {
return me.on_link() or me.tracked();
},
is_friendly: func {
return me.on_link() or me.iff() == IFF_FRIENDLY;
},
is_hostile: func {
return !me.on_link() and me.iff() == IFF_HOSTILE;
},
};
# Contact encoding: callsign + bits
# callsign: the callsign encoded with emesary.TransferString
# bits: bitfield |xxxxxxff| (left is most significant)
# f: IFF, x: unused
# encoded with emesary.TransferByte
# Additional values may be appended for extensions.
var encode_contact = func(contact) {
# Encode bitfield
var bits = contact["iff"] != nil ? contact.iff : IFF_UNKNOWN;
return emesary.TransferString.encode(clean_callsign(contact.callsign))
~ emesary.TransferByte.encode(bits);
}
var decode_contact = func(str) {
var res = {};
var dv = emesary.TransferString.decode(str, 0);
res.callsign = dv.value;
dv = emesary.TransferByte.decode(str, dv.pos);
var bits = dv.value;
res.iff = math.mod(bits, 4);
return res;
}
# Special character, won't be used by emesary encoding.
var contacts_separator = "#";
var encode_contacts = func(contacts) {
var str = "";
foreach (var contact; contacts) {
str = str~encode_contact(contact)~contacts_separator;
}
return str;
}
var decode_contacts = func(aircrafts_data, callsign, str) {
var contacts = split(contacts_separator, str);
foreach (var contact; contacts) {
if (contact == "") continue;
var res = decode_contact(contact);
aircrafts_data = add_if_missing(aircrafts_data, res.callsign);
aircrafts_data[res.callsign].set_iff(res.iff);
aircrafts_data[res.callsign].set_tracked_by(callsign);
}
return aircrafts_data;
}
register_extension("contacts", "C", ContactTracked, encode_contacts, decode_contacts);
## Coordinate
var ContactPoint = {
init: func {
me._point = nil;
},
set_point: func(point) {
me._point = point;
},
point: func {
return me._point;
},
};
var encode_point = func(coord) {
return emesary.TransferCoord.encode(coord);
}
var decode_point = func(aircrafts_data, callsign, str) {
var coord = emesary.TransferCoord.decode(str, 0).value;
aircrafts_data = add_if_missing(aircrafts_data, callsign);
aircrafts_data[callsign].set_point(coord);
return aircrafts_data;
}
register_extension("point", "P", ContactPoint, encode_point, decode_point);

78
SA-3/Nasal/fdm.nas Normal file
View File

@@ -0,0 +1,78 @@
# 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/>.
#io.include("Aircraft/ExpansionPack/Nasal/init.nas");
srand();# seed the random generator with systime
#with("updateloop");
# Number of iterations per second
var frequency = 60.0;
# Change in heading per second at full rudder deflection
var heading_ps = 0.5;
time_last = 0;
sim_speed = 1;
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 PositionUpdater = func () {
var position = geo.aircraft_position();
var time_now = getprop("/sim/time/elapsed-sec");
var dt = (time_now - time_last) * sim_speed;
if (dt == 0) {settimer( PositionUpdater, 1/frequency ); return;}
time_last = time_now;
var heading = getprop("/orientation/heading-deg");
var speed = getprop("/velocities/groundspeed-kt");
var rudder = getprop("/surface-positions/rudder-pos-norm");
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());
setprop("/position/altitude-ft", getprop("/position/ground-elev-ft"));
if ( getprop("/carrier/sunk") == 0 ) {
# Update heading
var course = heading + rudder * heading_ps * dt;
setprop("/orientation/heading-deg", course);
} else {
return;
}
#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"));
settimer( PositionUpdater, 1/frequency );
};
#PositionUpdater();

View File

@@ -0,0 +1,60 @@
################### SAM INFO ###################
var setupTime = 300;#minimum 'launcher_tilt_time' secs no matter what, due to anim and stuff.
var reload_time = 300;
var launcher_final_tilt_deg = 33;
var launcher_start_tilt_deg = 0;
var launcher_tilt_time = 15;
var sam_align_to_target = 0;
var launcher_align_to_target = 1;
var align_speed_dps = 20;
var radar_elevation_above_terrain_m = 6.9;#estimate - Low Blow is around 25 ft (CIA)
var radar_lowest_pitch = 0.4;# 0.5 degs = roughly 925 feet at 20 nm, 25 feet at half a nm. # 0.35 = roughly 925 feet at 20 nm, 25 feet at half a nm.
var radar_off_time_min = 4;# When turning off to lure enemies into engagement zone, and to not make the SAM too easy to find, minimum turn off this duration.
var radar_off_time_max = 8;# Bigger missile_max_distance give crew incentive to keep this a bit longer
var radar_on_time = 45;# Minimum the time it would take to scan whole sky
var radar_on_after_detect_time = 180;# Crew is alert after spotting an aircraft, how long should they stay that way?
var can_detect_anti_rad = 0.15;# 0.5 very good, 0 not at all.
#reaction tme for s-300p is 28 secs acording to http://www.astronautix.com/s/s-300p.html
# sounds a bit high for pmu, as its 4 secs for s-400
################### CIWS INFO ###################
var ciws_installed = 0;
var ciws_domain_nm = 1.50; #range where it can kill
var ciws_chance = 0.20; #chance to get a kill at 0nm distance
var ciws_burst_rounds = 60;#how many rounds in a burst
var ciws_shell = 15;#from lookup table in damage.nas
var ROUNDS_init = 30;
var ROUNDS = ROUNDS_init;#CIWS bursts remaining
################### MISSILE INFO - http://www.astronautix.com/k/kub.html ###################
var NUM_MISSILES = 3; # total carried minus 1
var missile_name = "V601";
var missile_brevity = damage.id2warhead[getprop("payload/armament/"~string.lc(missile_name)~"/type-id")][4];
var missile_max_distance = getprop("payload/armament/"~string.lc(missile_name)~"/max-fire-range-nm"); #max distance of target in nm when it will fire
var missile_min_distance = getprop("payload/armament/"~string.lc(missile_name)~"/min-fire-range-nm"); #minimum distance in nm when it will fire
var lockon_time = 12; #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 = 1;# max number of missiles in air against same target
var isInEngagementEnvelope = func (target_radial_airspeed, target_ground_distance, target_relative_altitude) {
return 1;
}
var midflight = func (struct) {
if (struct.guidance == "semi-radar") {
# This makes the SAM system keep lock on target when missile in-flight and no longer tracking the target.
# Usage is to make the RWR lock sound go off in targets cockpit.
thread.lock(mutexLock);
semi_active_track = struct.callsign;
thread.unlock(mutexLock);
}
return {};
};

646
SA-3/Nasal/fire-control.nas Normal file
View File

@@ -0,0 +1,646 @@
################### GLOBALS
var AIR = 0;
var MARINE = 1;
var SURFACE = 2;
var ORDNANCE = 3;
var radar_update_time = 2;
var launch_update_time = 0.3;
var reloading = 0;
var missile_delay_time = 0;#time when last track was initiated
var missile_release_time = 0;#time when last missile was fired
var ciws_delay_time = 0;#time when CIWS was last fired
var launch_in_progress = 0;# if a firing solution is currently being calculated
var reload_starting = 0;
var ACTIVE_MISSILE = 0;
var semi_active_track = nil;# with multiple missiles flying in semi-active-radar mode, this can change rather fast. Which means pilots wont get a steady tone, but disrupted tones. (tradeoff)
var mutexLock = thread.newlock();
var radarStatus = 0;# Current radar status.
var radarOnTimestamp = -500;# Last timestamp of when the radar was online
var radarOffTimestamp = -500;# Last timestamp of when the radar was offline
var radarOffDuration = radar_off_time_min;
var radarTrackTime = 0;# Last time stamp where any enemy aircraft has been spotted.
##########################################################################
######################## Common SAM code ###########################
##########################################################################
################### VARIOUS
setprop("sim/multiplay/generic/float[7]", 0);# tracking radar vert rotation
setprop("sim/multiplay/generic/float[8]", 0);# tracking radar horiz rotation
setprop("sim/multiplay/generic/float[5]", 0);# launcher vert rotation
setprop("sim/multiplay/generic/float[6]", 0);# launcher horiz rotation
var start_time = systime();
var opfor_switch = "enemies/opfor-switch";
setprop(opfor_switch,0);
var friend_switch = "enemies/friend-switch";
setprop(friend_switch,0);
################### IFF
var target = {
new: func (callsign) {
var m = { parents: [target] };
m.callsign = callsign;
m.in_air = 0;
m.tracking = 0;
m.missiles = [];
return m;
},
};
var base = props.globals.getNode("/");
base.getNode("instrumentation/transponder/id-code",1).setIntValue(-9999);
base.getNode("instrumentation/transponder/transmitted-id",1).setIntValue(-9999);# transponder off
###################################################################
var targetsV2 = nil;
var buildTargetList = func {
# rather crappy code, but is not ran so often, so dont really matter.
if (getprop(opfor_switch) == 0 and getprop(friend_switch) == 0) {
for(var i = 1; i<=12;i+=1) {
# add new callsign to list
var sign = getprop("enemies/e"~i);
var tgt = lookup(sign);
if (tgt == nil) {
targetsV2.append(target.new(sign));
print("added callsign: "~sign);
}
}
}
var removeList = [];
foreach(tgt ; targetsV2.vector) {
# schedule removed callsigns to be removed
if (getprop(opfor_switch) == 0 and getprop(friend_switch) == 0) {
if (tgt.callsign != getprop("enemies/e1") and tgt.callsign != getprop("enemies/e2") and tgt.callsign != getprop("enemies/e3") and tgt.callsign != getprop("enemies/e4") and tgt.callsign != getprop("enemies/e5") and tgt.callsign != getprop("enemies/e6") and tgt.callsign != getprop("enemies/e7") and tgt.callsign != getprop("enemies/e8") and tgt.callsign != getprop("enemies/e9") and tgt.callsign != getprop("enemies/e10") and tgt.callsign != getprop("enemies/e11") and tgt.callsign != getprop("enemies/e12")) {
append(removeList, tgt);
print("will remove callsign: "~tgt.callsign);
}
}
}
foreach(tgt ; removeList) {
# remove callsigns
call(func {targetsV2.remove(tgt);}, nil, var err = []);
}
};
var lookup = func (sign) {
foreach (tgt ; targetsV2.vector) {
if (tgt.callsign == sign) {
return tgt;
}
}
if ( getprop(opfor_switch) == 1 and (left(sign, 5) != "OPFOR" and left(sign,5) != "opfor")) {
print("creating new target in: " ~ sign);
tgt = target.new(sign);
targetsV2.append(tgt);
return tgt;
}
if ( getprop(friend_switch) == 1 and ( left(sign, 5) == "OPFOR" or left(sign,5) == "opfor" )) {
print("creating new target in: " ~ sign);
tgt = target.new(sign);
targetsV2.append(tgt);
return tgt;
}
return nil;
}
var PRIO_NORM = 0;
var PRIO_HIGH = 1;
var PRIO_LOW = 2;
var PRIO_FAST = 3;
var PRIO_SLOW = 4;
var PRIO_CLOSE = 5;
var PRIO_FAR = 6;
################### MAIN LOOP
var scan = func() {
setprop("sim/multiplay/visibility-range-nm", 250);
buildTargetList();
if ( getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) {
setprop("sim/multiplay/generic/string[6]", "");
datalink.clear_data();
return;
}
if (isSetup() ) {
printf("Seconds till activation: %.1f", setupTime - (systime() - start_time));
setprop("sam/timeleft", setupTime - (systime() - start_time));
setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE));
setprop("sam/bursts", ROUNDS);
setprop("sam/info", "Preparing launch system");
settimer(scan,radar_update_time);
if (launcher_tilt_time > 0) {
setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg*math.max(0,(systime() - start_time)-(setupTime-launcher_tilt_time))/launcher_tilt_time);#raise the tubes in last 15 secs of activation
} else {
setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg);
}
setprop("sim/multiplay/generic/float[6]", 0);
setprop("sim/multiplay/generic/float[7]", 0);
setprop("sim/multiplay/generic/float[8]", 0);
setprop("sim/multiplay/generic/string[6]", "");
setprop("/sim/multiplay/generic/int[2]", 1);
datalink.clear_data();
return;
}
setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg);
#### ITERATE THROUGH MP LIST ####
if (radarStatus == 1) {
var my_pos = geo.aircraft_position();
var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m;
my_pos.set_alt(radarAlt);
var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer");
var tvec = props.globals.getNode("/ai/models").getChildren("tanker");
var avec = props.globals.getNode("/ai/models").getChildren("aircraft");
if (mvec!=nil and tvec != nil) {
mvec = mvec ~ tvec;
}
if (mvec!=nil and avec != nil) {
mvec = mvec ~ avec;
}
var prio_vector = [];#0:node,1:trigger,2:prio value
foreach(var mp; mvec){
if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue;
var prio = fire_control(mp, my_pos);
append(prio_vector, prio);
if (prio[3] == 1) {
radarTrackTime = systime();#this target is within envelope, we turn on radar.
}
}
prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2]<b[2]) {return 1;} else {return 0;}});
foreach(var mp; prio_vector) {
#### DO WE HAVE FIRING SOLUTION... ####
# is plane in range, do we still have missiles, and is a missile already inbound, and has it been 4 seconds since the last missile launch?
var trigger = mp[1];
#print("dist to target = " ~ dist_to_target);
var lu = lookup(mp[0].getNode("callsign").getValue());
if ( launch_in_progress == 0 and trigger == 1 and lu != nil and lu.in_air < same_target_max_missiles and !lu.tracking and ACTIVE_MISSILE <= NUM_MISSILES and ( systime() - missile_delay_time > fire_minimum_interval ) ) { #
#### ... FOR THE MISSILE ####
#print("callsign " ~ cs ~ " found at " ~ dist_to_target);
missile_delay_time = systime();
mp[0].getNode("unique",1).setValue(rand());
armament.contact = radar_logic.Contact.new(mp[0], AIR);
launch_in_progress = 1;
missile_launch(mp[0], systime(), my_pos);
} elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) {
#### ... FOR THE CIWS ####
var contact = radar_logic.Contact.new(mp[0], AIR);
var cord = contact.get_Coord();
var dist_nm = my_pos.direct_distance_to(cord)*M2NM;
var probabilityOfBurstKill = 0.50;
if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) {
probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0);
}
var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx
var target_bearing = my_pos.course_to(cord);
setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted)
setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert
setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound
if (hits > 0) {
var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1));
msg.RelativeAltitude = 0;
msg.Bearing = 0;
msg.Distance = hits;
msg.RemoteCallsign = mp[0].getNode("callsign").getValue();
notifications.hitBridgedTransmitter.NotifyAll(msg);
damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue());
}
print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm");
ciws_delay_time = systime();
ROUNDS = ROUNDS - 1;
setprop("sam/bursts", ROUNDS);
if (ROUNDS == 0) {
print("Launcher out of shell ammo.");
settimer(autoreload, reload_time);
reloading = 1;
reload_starting = systime();
}
}
if (lu != nil and lu.tracking) {
radarTrackTime = systime();
}
}
}
if (launcher_align_to_target and ACTIVE_MISSILE > NUM_MISSILES and systime()-missile_release_time > 2) {
# rotate launcher back to forward:
var tgt_dir = 0;
var cur_dir = getprop("sim/multiplay/generic/float[6]");
var move_dir = geo.normdeg180(tgt_dir-cur_dir);
var max_dir = radar_update_time*align_speed_dps*0.25;
move_dir = math.clamp(move_dir, -max_dir, max_dir);
setprop("sim/multiplay/generic/float[6]", cur_dir+move_dir);
}
if (launch_in_progress == 0) {
if (reloading) {
setprop("sam/info", "Reloading");
setprop("sam/timeleft", reload_time - (systime() - reload_starting));
} else {
setprop("sam/info", "Search radar is "~(radarStatus==1?"ON":"OFF"));
setprop("sam/timeleft", 0);
}
clearSingleLock();
thread.lock(mutexLock);
semi_active_track = nil;
thread.unlock(mutexLock);
} else {
setprop("sam/timeleft", 0);
}
if (getprop(getprop("payload/armament/MP-alt-ft")) == 0.0) {
# reset 'Follow view' to look at launcher
setprop(getprop("payload/armament/MP-lat"),getprop("position/latitude-deg"));
setprop(getprop("payload/armament/MP-lon"),getprop("position/longitude-deg"));
setprop(getprop("payload/armament/MP-alt-ft"),getprop("position/altitude-ft"));
}
setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE));
settimer(scan,radar_update_time);
}
var isSetup = func {
return systime() - start_time < setupTime;
}
var acquisitionRadarLoop = func {
if (isSetup() or reloading or getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) return;
var reason = "";
var time = systime();
if (time - radarTrackTime < radar_on_after_detect_time) {
radarStatus = 1;
reason = sprintf("Crew alert for %d seconds", radar_on_after_detect_time - (time - radarTrackTime));
} elsif (radarStatus == 0 and time - radarOnTimestamp > radarOffDuration) {
radarStatus = 1;
reason = sprintf("Periodic check for %d seconds", radar_on_time);
} elsif (radarStatus == 1 and time - radarOffTimestamp > radar_on_time) {
radarStatus = 0;
radarOffDuration = rand()*(radar_off_time_max - radar_off_time_min)+radar_off_time_min;
reason = sprintf("Periodic pause for %d seconds", radarOffDuration);
}
if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") == "tvm" and radarStatus == 0) {
foreach(var tgt ; targetsV2.vector) {
if (tgt.in_air > 0) {
radarTrackTime = time;
radarStatus = 1;
break;
}
}
}
setprop("/sim/multiplay/generic/int[2]", radarStatus != 1);
if (radarStatus == 1) radarOnTimestamp = time;
else radarOffTimestamp = time;
#print("Search radar is "~(radarStatus==1?"ON":"OFF"));
if (reason != "") print(reason);
}
var radTimer = maketimer(0.5, acquisitionRadarLoop);
radTimer.start();
var clearSingleLock = func () {
thread.lock(mutexLock);
if (semi_active_track == nil) {
setprop("sim/multiplay/generic/string[6]", "");
datalink.clear_data();
} else {
setprop("sim/multiplay/generic/string[6]", left(md5(semi_active_track), 4));
datalink.send_data({"contacts":[{"callsign":semi_active_track,"iff":0}]});
}
thread.unlock(mutexLock);
}
################## FIRE CONTROL
################## ALL AI LOGIC RELATED TO FIGURING OUT IF A TARGET SHOULD BE SHOT AT SHOULD GO HERE.
var fire_control = func(mp, my_pos) {
# gather some data about the target
if ( mp.getNode("valid").getValue() == 0 ) { return [mp,0,0,0]; }
var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * FT2M));
var target_distance = my_pos.direct_distance_to(ufo_pos);
var target_altitude = mp.getNode("position/altitude-ft").getValue();
# is this plane a friend or foe?
var lu = lookup(mp.getNode("callsign").getValue());
if ( lu == nil ) { return [mp,0,0,0]; }
var target_airspeed = mp.getNode("velocities/true-airspeed-kt").getValue();
var target_ground_distance = my_pos.distance_to(ufo_pos);
var target_heading = mp.getNode("orientation/true-heading-deg").getValue();
var relative_bearing = math.abs(geo.normdeg180(ufo_pos.course_to(my_pos) - target_heading));
var target_relative_altitude = target_altitude - my_pos.alt()*M2FT;
var target_radial_airspeed = math.abs(math.abs(((relative_bearing / 90 ) - 1) * target_airspeed));#200kt against us = 200kt, 200kt at angle of us = 0kt, 200kt away from us = 200kt
var target_relative_pitch = vector.Math.getPitch(my_pos, ufo_pos);
# can the radar see it?
var data_link_match = 0;
# foreach(var cx; gci.data_receive_callsigns) {
# if (cx[0] == callsign) {
# data_link_match = true;
# #print('tgt confirmed via datalink');
# }
# }
var visible = radar_logic.isNotBehindTerrain(mp);
if (ciws_installed and ROUNDS > 0 and target_distance*M2NM < ciws_domain_nm) {
#CIWS
var radarSeeTarget = 1;
if ( data_link_match == 0 ) {
if ( visible[0] == 0 ) {radarSeeTarget = 0 }
if ( target_relative_pitch < radar_lowest_pitch ) { radarSeeTarget = 0 } #
if ( target_distance*M2NM > 1.0 and visible[1] and math.abs(target_radial_airspeed) < 20 ) {radarSeeTarget = 0 } # i.e. notching with terrain behind
}
if (radarSeeTarget and rand() > target_distance*M2NM / ciws_domain_nm) {
var score = 0;
var priority = getprop("priority");
if (priority==PRIO_NORM) {
score = rand()-0.5;
} elsif (priority==PRIO_HIGH) {
score = target_altitude;
} elsif (priority==PRIO_LOW) {
score = -target_altitude;
} elsif (priority==PRIO_FAST) {
score = target_radial_airspeed;
} elsif (priority==PRIO_SLOW) {
score = -target_radial_airspeed;
} elsif (priority==PRIO_CLOSE) {
score = -target_distance;
} elsif (priority==PRIO_FAR) {
score = target_distance;
}
return [mp,-1,score,1];
}
}
if ( data_link_match == 0 ) {
if (target_airspeed < 60) {return [mp,0,0,0];}
if ( visible[0] == 0 ) { return [mp,0,0,0]; }
if ( target_relative_pitch < radar_lowest_pitch ) { return [mp,0,0,0]; } #
if ( visible[1] and math.abs(target_radial_airspeed) < 20 ) { return [mp,0,0,0]; } # i.e. notching, landed aircraft
}
if ( target_distance * M2NM > missile_max_distance ) { return [mp,0,0,0]; }
if ( target_distance * M2NM < missile_min_distance ) { return [mp,0,0,0]; }
# is the plane within the engagement envelope?
# the numbers after target_radial_airspeed are ( offset_of_engagement_envelope / speed_to_apply_that_offset )
# larger offset means it wont fire until the plane is closer.
# for visualization: https://www.desmos.com/calculator/gw570fa9km
if (!isInEngagementEnvelope(target_radial_airspeed, target_ground_distance, target_relative_altitude) ) { return [mp,0,0,0]; }
var the_dice = rand();
if ( the_dice > 0.20 ) {
var score = 0;
var priority = getprop("priority");
if (priority==PRIO_NORM) {
score = rand()-0.5;
} elsif (priority==PRIO_HIGH) {
score = target_altitude;
} elsif (priority==PRIO_LOW) {
score = -target_altitude;
} elsif (priority==PRIO_FAST) {
score = target_radial_airspeed;
} elsif (priority==PRIO_SLOW) {
score = -target_radial_airspeed;
} elsif (priority==PRIO_CLOSE) {
score = -target_distance;
} elsif (priority==PRIO_FAR) {
score = target_distance;
}
return [mp,1,score,1];
} else {
return [mp,0,0,1];
}
}
################### MISSILE CONTROL
### missile reload
var reload = func(force = 1) {
#figure out how many to add
if (ACTIVE_MISSILE > NUM_MISSILES or force == 1) {
for ( var i = 0; i <= NUM_MISSILES; i = i + 1 ) {
if (armament.AIM.new(i,missile_name,missile_brevity, midflight) != -1) {
#if statement just in case reload was called before all missiles were fired. Cause avoid calling search() on same missile twice.
armament.AIM.active[i].radarZ = radar_elevation_above_terrain_m;
armament.AIM.active[i].start();
}
}
ACTIVE_MISSILE = 0;setprop("active", 0);
print("Launcher reloaded with "~(NUM_MISSILES+1)~" missiles.");
setprop("/sim/messages/copilot", "Launcher reloaded with "~(NUM_MISSILES+1)~" missiles.");
}
if (ROUNDS == 0 or force == 1) {
ROUNDS = ROUNDS_init;
print("Reloaded with "~(ROUNDS)~" bursts.");
}
setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE));
setprop("sam/bursts", ROUNDS);
reloading = 0;
}
var autoreload = func() {
if ( getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) {
return;
}
if (reloading) {
reloading = 0;
if (ACTIVE_MISSILE > NUM_MISSILES or ROUNDS == 0) {
reload(0);
print("Auto reloading completed.")
}
} else {
print("Auto reloading cancelled due to manual reload.")
}
}
### missile launch
var missile_launch = func(mp, launchtime, my_pos) {
if ( getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) {
var lu = lookup(mp.getNode("callsign").getValue());
if (lu != nil) {
lu.tracking = 0;
}
launch_in_progress = 0;
clearSingleLock();
return;
}
var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048));
var target_bearing = my_pos.course_to(ufo_pos);
var target_pitch = vector.Math.getPitch(my_pos, ufo_pos);
var info = mp.getNode("callsign").getValue();
var lu = lookup(mp.getNode("callsign").getValue());
if (getprop("sam/timeleft") != 0) {
launch_in_progress = 0;
if (lu != nil) {
lu.tracking = 0;
}
return;
} elsif ( armament.AIM.active[ACTIVE_MISSILE].status == 1 and systime() - launchtime > lockon_time and radar_logic.isNotBehindTerrain(mp)[0] == 1 ) {
var theMissile = armament.AIM.active[ACTIVE_MISSILE];
var brevity = theMissile.brevity;
#armament.defeatSpamFilter(brevity ~ " at: " ~ mp.getNode("callsign").getValue());
damage.damageLog.push(brevity ~ " at: " ~ mp.getNode("callsign").getValue());
if (launcher_align_to_target) {
setprop("sim/multiplay/generic/float[6]", target_bearing-getprop("orientation/heading-deg"));
theMissile.rail_head_deg = getprop("sim/multiplay/generic/float[6]");
}
setprop("sim/multiplay/generic/float[7]", target_pitch);
setprop("sim/multiplay/generic/float[8]", target_bearing-getprop("orientation/heading-deg"));
if (sam_align_to_target) {
setprop("/orientation/heading-deg",target_bearing);
}
theMissile.release();
missile_release_time = systime();
ACTIVE_MISSILE = ACTIVE_MISSILE + 1;setprop("active", ACTIVE_MISSILE);
print("Fired "~missile_name~" #" ~ ACTIVE_MISSILE ~ " at: " ~ mp.getNode("callsign").getValue());
setprop("/sim/messages/copilot", "Launch at "~mp.getNode("callsign").getValue());
if (ACTIVE_MISSILE > NUM_MISSILES) {
setprop("/sim/messages/copilot", "Launcher out of ammo");
print("Launcher out of missile ammo.");
settimer(autoreload, reload_time);
reloading = 1;
reload_starting = systime();
}
if (lu != nil) {
append(lu.missiles, theMissile);
lu.in_air += 1;
lu.tracking = 0;
}
launch_in_progress = 0;
info = info~" (launched)";
setprop("sam/info", info);
setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE));
clearSingleLock();
return;
} elsif ((systime() - launchtime) > (lockon_time*1.5) or radar_logic.isNotBehindTerrain(mp)[0] == 0) {
# launch cancelled so it dont forever goes in this loop and dont allow for other firings.
if (lu != nil) {
lu.tracking = 0;
}
setprop("/sim/messages/copilot", "SAM Canceled launch at "~mp.getNode("callsign").getValue());
launch_in_progress = 0;
info = info~" (cancelled)";
setprop("sam/info", info);
setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE));
clearSingleLock();
return;
} else {
info = sprintf("%s (tracking %d nm at %d ft)", info, armament.contact.get_range(), armament.contact.get_altitude());
if (systime()-missile_release_time > 1.5) {
var tgt_dir = target_bearing-getprop("orientation/heading-deg");
var max_dir = launch_update_time*align_speed_dps;
if (launcher_align_to_target) {
# now smoothly rotate launcher:
var cur_dir = getprop("sim/multiplay/generic/float[6]");
var move_dir = geo.normdeg180(tgt_dir-cur_dir);
move_dir = math.clamp(move_dir, -max_dir, max_dir);
setprop("sim/multiplay/generic/float[6]", cur_dir+move_dir);
}
var cur_dir2 = getprop("sim/multiplay/generic/float[8]");
var move_dir2 = geo.normdeg180(tgt_dir-cur_dir2);
move_dir2 = math.clamp(move_dir2, -max_dir, max_dir);
setprop("sim/multiplay/generic/float[8]", cur_dir2+move_dir2);
if (sam_align_to_target) {
var tgt_dir = target_bearing;
var cur_dir = getprop("orientation/heading-deg");
var move_dir = geo.normdeg180(tgt_dir-cur_dir);
var max_dir = launch_update_time*align_speed_dps;
move_dir = math.clamp(move_dir, -max_dir, max_dir);
setprop("orientation/heading-deg", cur_dir+move_dir);
}
}
setprop("sam/info", info);
if (mp.getNode("callsign") != nil and mp.getNode("callsign").getValue() != nil and mp.getNode("callsign").getValue() != "") {
if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") != "tvm") setprop("sim/multiplay/generic/string[6]", left(md5(mp.getNode("callsign").getValue()), 4));
datalink.send_data({"contacts":[{"callsign":mp.getNode("callsign").getValue(),"iff":0}]});
} else {
clearSingleLock();
}
if (lu != nil) {
lu.tracking = 1;
radarTrackTime = systime();
}
}
settimer( func { missile_launch(mp, launchtime, my_pos); },launch_update_time);
}
### missile set launched to false for target
var missile_listener = func {
foreach(var tgt ; targetsV2.vector) {
var missilesNew = [];
foreach(var missile ; tgt.missiles) {
if (missile != nil) {
if (missile.deleted) {
tgt.in_air -= 1;
} else {
append(missilesNew, missile);
}
}
}
tgt.missiles = missilesNew;
}
settimer(missile_listener, 2.5);
}
var extrapolate = func (x, x1, x2, y1, y2) {
return y1 + ((x - x1) / (x2 - x1)) * (y2 - y1);
}
################### MISC
var main_init_listener = setlistener("sim/signals/fdm-initialized", func {
print("fdm-initialized");
targetsV2 = std.Vector.new();# OO vector (to make removing item easier)
reload();
scan();
aircraft.data.save(0.5);# save the target list every 30 seconds
removelistener(main_init_listener);
setlistener(opfor_switch, func {
targetsV2 = std.Vector.new();# OO vector (to make removing item easier)
}, 0, 0);
setlistener(friend_switch, func {
targetsV2 = std.Vector.new();# OO vector (to make removing item easier)
}, 0, 0);
missile_listener();
setprop(getprop("payload/armament/MP-lat"),getprop("position/latitude-deg"));
setprop(getprop("payload/armament/MP-lon"),getprop("position/longitude-deg"));
setprop(getprop("payload/armament/MP-alt-ft"),getprop("position/altitude-ft"));
view.setViewByIndex(200);#follow view
}, 0, 0);
setlistener("sim/signals/reinit", func {
print("reinit");
start_time = systime();
});

File diff suppressed because it is too large Load Diff

427
SA-3/Nasal/radar-logic.nas Normal file
View File

@@ -0,0 +1,427 @@
var clamp = func(v, min, max) { v < min ? min : v > max ? max : v }
var encode3bits = func(first, second, third) {
var integer = first;
integer = integer + 2 * second;
integer = integer + 4 * third;
return integer;
}
var versionString = getprop("sim/version/flightgear");
var version = split(".", versionString);
var major = num(version[0]);
var minor = num(version[1]);
var pica = num(version[2]);
var pickingMethod = 0;
if ((major == 2017 and minor == 2 and pica >= 1) or (major == 2017 and minor > 2) or major > 2017) {
pickingMethod = 1;
}
var AIR = 0;
var MARINE = 1;
var SURFACE = 2;
var ORDNANCE = 3;
var Contact = {
# For now only used in guided missiles, to make it compatible with Mirage 2000-5.
new: func(c, class) {
var obj = { parents : [Contact]};
#debug.benchmark("radar process1", func {
obj.rdrProp = c.getNode("radar");
obj.oriProp = c.getNode("orientation");
obj.velProp = c.getNode("velocities");
obj.posProp = c.getNode("position");
obj.heading = obj.oriProp.getNode("true-heading-deg");
#});
#debug.benchmark("radar process2", func {
obj.alt = obj.posProp.getNode("altitude-ft");
obj.lat = obj.posProp.getNode("latitude-deg");
obj.lon = obj.posProp.getNode("longitude-deg");
#});
#debug.benchmark("radar process3", func {
#As it is a geo.Coord object, we have to update lat/lon/alt ->and alt is in meters
obj.coord = geo.Coord.new();
obj.coord.set_latlon(obj.lat.getValue(), obj.lon.getValue(), obj.alt.getValue() * FT2M);
#});
#debug.benchmark("radar process4", func {
obj.pitch = obj.oriProp.getNode("pitch-deg");
obj.roll = obj.oriProp.getNode("roll-deg");
obj.speed = obj.velProp.getNode("true-airspeed-kt");
obj.vSpeed = obj.velProp.getNode("vertical-speed-fps");
obj.callsign = c.getNode("callsign", 1);
obj.shorter = c.getNode("model-shorter");
obj.orig_callsign = obj.callsign.getValue();
obj.name = c.getNode("name");
obj.sign = c.getNode("sign",1);
obj.valid = c.getNode("valid");
obj.painted = c.getNode("painted");
obj.unique = c.getNode("unique");
obj.validTree = 0;
#});
#debug.benchmark("radar process5", func {
#obj.transponderID = c.getNode("instrumentation/transponder/transmitted-id");
#});
#debug.benchmark("radar process6", func {
obj.acType = c.getNode("sim/model/ac-type");
obj.type = c.getName();
obj.index = c.getIndex();
obj.string = "ai/models/" ~ obj.type ~ "[" ~ obj.index ~ "]";
obj.shortString = obj.type ~ "[" ~ obj.index ~ "]";
#});
#debug.benchmark("radar process7", func {
obj.range = obj.rdrProp.getNode("range-nm");
obj.bearing = obj.rdrProp.getNode("bearing-deg");
obj.elevation = obj.rdrProp.getNode("elevation-deg");
obj.ubody = obj.rdrProp.getNode("velocities/uBody-fps");
obj.vbody = obj.rdrProp.getNode("velocities/vBody-fps");
obj.wbody = obj.rdrProp.getNode("velocities/wBody-fps");
#});
obj.deviation = nil;
obj.node = c;
obj.class = class;
obj.polar = [0,0];
obj.cartesian = [0,0];
return obj;
},
get_uBody: func {
var body = nil;
if (me.ubody != nil) {
body = me.ubody.getValue();
}
if(body == nil) {
body = me.get_Speed()*KT2FPS;
}
return body;
},
get_vBody: func {
var body = nil;
if (me.ubody != nil) {
body = me.vbody.getValue();
}
if(body == nil) {
body = 0;
}
return body;
},
get_wBody: func {
var body = nil;
if (me.ubody != nil) {
body = me.wbody.getValue();
}
if(body == nil) {
body = 0;
}
return body;
},
isValid: func () {
var valid = me.valid.getValue();
if (valid == nil) {
valid = 0;
}
if (me.callsign.getValue() != me.orig_callsign) {
valid = 0;
}
return valid;
},
isPainted: func () {
if (getprop("/carrier/sunk") == 1) {
return 0;
}
return isNotBehindTerrain(me.node)[0];
},
isCommandActive: func {
if (getprop("/carrier/sunk") == 1) {
return 0;
}
return isNotBehindTerrain(me.node)[0];
},
getUnique: func () {
if (me.unique == nil) {
me.unique = me.node.getNode("unique");
}
if (me.unique == nil) {
return nil;
}
var u = me.unique.getValue();
return u;
},
getElevation: func() {
var e = 0;
e = me.elevation.getValue();
if(e == nil or e == 0) {
# AI/MP has no radar properties
var self = geo.aircraft_position();
self.set_alt(self.alt()+fire_control.radar_elevation_above_terrain_m);
me.get_Coord();
var angleInv = clamp(self.distance_to(me.coord)/self.direct_distance_to(me.coord), -1, 1);
e = (self.alt()>me.coord.alt()?-1:1)*math.acos(angleInv)*R2D;
}
return e;
},
getNode: func () {
return me.node;
},
getFlareNode: func () {
return me.node.getNode("rotors/main/blade[3]/flap-deg");
},
getChaffNode: func () {
return me.node.getNode("rotors/main/blade[3]/position-deg");
},
setPolar: func(dist, angle) {
me.polar = [dist,angle];
},
setCartesian: func(x, y) {
me.cartesian = [x,y];
},
remove: func(){
if(me.validTree != 0){
me.validTree.setValue(0);
}
},
get_Coord: func(){
me.coord.set_latlon(me.lat.getValue(), me.lon.getValue(), me.alt.getValue() * FT2M);
var TgTCoord = geo.Coord.new(me.coord);
return TgTCoord;
},
get_Callsign: func(){
var n = me.callsign.getValue();
if(n != "" and n != nil) {
return n;
}
if (me.name == nil) {
me.name = me.getNode().getNode("name");
}
if (me.name == nil) {
n = "";
} else {
n = me.name.getValue();
}
if(n != "" and n != nil) {
return n;
}
n = me.sign.getValue();
if(n != "" and n != nil) {
return n;
}
return "UFO";
},
get_model: func(){
var n = "";
if (me.shorter == nil) {
me.shorter = me.node.getNode("model-shorter");
}
if (me.shorter != nil) {
n = me.shorter.getValue();
}
if(n != "" and n != nil) {
return n;
}
n = me.sign.getValue();
if(n != "" and n != nil) {
return n;
}
if (me.name == nil) {
me.name = me.getNode().getNode("name");
}
if (me.name == nil) {
n = "";
} else {
n = me.name.getValue();
}
if(n != "" and n != nil) {
return n;
}
return me.get_Callsign();
},
get_Speed: func(){
# return true airspeed
var n = me.speed.getValue();
return n;
},
get_Longitude: func(){
var n = me.lon.getValue();
return n;
},
get_Latitude: func(){
var n = me.lat.getValue();
return n;
},
get_Pitch: func(){
var n = me.pitch.getValue();
return n;
},
isVirtual: func(){
return 0;
},
get_Roll: func(){
var n = me.roll.getValue();
return n;
},
get_heading : func(){
var n = me.heading.getValue();
if(n == nil)
{
n = 0;
}
return n;
},
get_bearing: func(){
var n = 0;
n = me.bearing.getValue();
if(n == nil or n == 0) {
# AI/MP has no radar properties
n = me.get_bearing_from_Coord(geo.aircraft_position());
}
return n;
},
get_bearing_from_Coord: func(MyAircraftCoord){
me.get_Coord();
var myBearing = 0;
if(me.coord.is_defined()) {
myBearing = MyAircraftCoord.course_to(me.coord);
}
return myBearing;
},
get_reciprocal_bearing: func(){
return geo.normdeg(me.get_bearing() + 180);
},
get_deviation: func(true_heading_ref, coord){
me.deviation = - deviation_normdeg(true_heading_ref, me.get_bearing_from_Coord(coord));
return me.deviation;
},
get_altitude: func(){
#Return Alt in feet
return me.alt.getValue();
},
get_Elevation_from_Coord: func(MyAircraftCoord) {
me.get_Coord();
var value = (me.coord.alt() - MyAircraftCoord.alt()) / me.coord.direct_distance_to(MyAircraftCoord);
if (math.abs(value) > 1) {
# warning this else will fail if logged in as observer and see aircraft on other side of globe
return 0;
}
var myPitch = math.asin(value) * R2D;
return myPitch;
},
get_total_elevation_from_Coord: func(own_pitch, MyAircraftCoord){
var myTotalElevation = - deviation_normdeg(own_pitch, me.get_Elevation_from_Coord(MyAircraftCoord));
return myTotalElevation;
},
get_total_elevation: func(own_pitch) {
me.deviation = - deviation_normdeg(own_pitch, me.getElevation());
return me.deviation;
},
get_range: func() {
var r = 0;
if(me.range == nil or me.range.getValue() == nil or me.range.getValue() == 0) {
# AI/MP has no radar properties
me.get_Coord();
r = me.coord.direct_distance_to(geo.aircraft_position()) * M2NM;
} else {
r = me.range.getValue();
}
return r;
},
get_range_from_Coord: func(MyAircraftCoord) {
var myCoord = me.get_Coord();
var myDistance = 0;
if(myCoord.is_defined()) {
myDistance = MyAircraftCoord.direct_distance_to(myCoord) * M2NM;
}
return myDistance;
},
get_type: func () {
return me.class;
},
get_cartesian: func() {
return me.cartesian;
},
get_polar: func() {
return me.polar;
},
};
var isNotBehindTerrain = func( mp ) {
var pos = mp.getNode("position");
var alt = pos.getNode("altitude-ft").getValue();
var lat = pos.getNode("latitude-deg").getValue();
var lon = pos.getNode("longitude-deg").getValue();
if(alt == nil or lat == nil or lon == nil) {
return [0,0]; # invisible and not in front of terrain
}
var aircraftPos = geo.Coord.new().set_latlon(lat, lon, alt*FT2M);
var myPos = geo.aircraft_position();
myPos.set_alt(myPos.alt()+fire_control.radar_elevation_above_terrain_m);
var target_distance = myPos.direct_distance_to(aircraftPos);
call(func {
if (target_distance*0.001 > 3.57*(math.sqrt(myPos.alt())+math.sqrt(alt*FT2M))) {
# behind earth curvature
#print("behind earth curvature");
return [0,0]; # invisible and not in front of terrain
}
},nil,nil,var err =[]);
var xyz = {"x":myPos.x(), "y":myPos.y(), "z":myPos.z()};
var dir = {"x":aircraftPos.x()-myPos.x(), "y":aircraftPos.y()-myPos.y(), "z":aircraftPos.z()-myPos.z()};
# Check for terrain between own aircraft and other:
v = get_cart_ground_intersection(xyz, dir);
if (v == nil) {
return [1,0]; # visible and not in front of terrain
#printf("No terrain, planes has clear view of each other");
} else {
var terrain = geo.Coord.new();
terrain.set_latlon(v.lat, v.lon, v.elevation);
var maxDist = myPos.direct_distance_to(aircraftPos);
var terrainDist = myPos.direct_distance_to(terrain);
if (terrainDist < maxDist-1) {
#printf("terrain found between me and aircraft %.1f meter away.", terrainDist);
return [0,0]; # visible and not in front of terrain
} else {
#print("The planes has clear view of each other");
return [1,1]; # visible and in front of terrain
}
}
}

482
SA-3/Nasal/vector.nas Normal file
View File

@@ -0,0 +1,482 @@
var Math = {
#
# Authors: Nikolai V. Chr, Axel Paccalin.
#
# Version 2.01
#
# When doing euler coords. to cartesian: +x = forw, +y = left, +z = up.
# FG struct. coords: +x = back, +y = right, +z = up.
#
# If euler to cartesian (with inverted heading) then:
# cartesian vector will be x: north, y: west, z: skyward
#
# 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) {
return [-x, -y, z];
},
convertAngles: func (heading,pitch,roll) {
return [-heading, pitch, roll];
},
# returns direction in geo coordinate system
vectorToGeoVector: func (a, coord) {
me.handp = me.cartesianToEuler(a);
me.end_dist_m = 100;# not too low for floating point precision. Not too high to get into earth curvature stuff.
me.tgt_coord = geo.Coord.new(coord);
if (me.handp[0] != nil) {
me.tgt_coord.apply_course_distance(me.handp[0],me.end_dist_m);
me.upamount = me.end_dist_m * math.tan(me.handp[1]*D2R);
} elsif (me.handp[1] == 90) {
me.upamount = me.end_dist_m;
} else {
me.upamount = -me.end_dist_m;
}
me.tgt_coord.set_alt(coord.alt()+me.upamount);
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.rollPitchYawVector(roll_deg, pitch_deg, yaw_deg, me.bodyVelocity);
},
# angle between 2 vectors. Returns 0-180 degrees.
angleBetweenVectors: func (a,b) {
a = me.normalize(a);
b = me.normalize(b);
me.value = me.clamp((me.dotProduct(a,b)/me.magnitudeVector(a))/me.magnitudeVector(b),-1,1);#just to be safe in case some floating point error makes it out of bounds
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;
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
dotProduct: func (a,b) {
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
},
# 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);
me.rotation = me.multiplyMatrices(me.rollM, me.multiplyMatrices(me.pitchM, me.yawM));
return me.multiplyMatrixWithVector(me.rotation, vector);
},
# 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.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],
matrix[3]*vector[0]+matrix[4]*vector[1]+matrix[5]*vector[2],
matrix[6]*vector[0]+matrix[7]*vector[1]+matrix[8]*vector[2]];
},
# multiply 2 3x3 matrices
multiplyMatrices: func (a,b) {
return [a[0]*b[0]+a[1]*b[3]+a[2]*b[6], a[0]*b[1]+a[1]*b[4]+a[2]*b[7], a[0]*b[2]+a[1]*b[5]+a[2]*b[8],
a[3]*b[0]+a[4]*b[3]+a[5]*b[6], a[3]*b[1]+a[4]*b[4]+a[5]*b[7], a[3]*b[2]+a[4]*b[5]+a[5]*b[8],
a[6]*b[0]+a[7]*b[3]+a[8]*b[6], a[6]*b[1]+a[7]*b[4]+a[8]*b[7], a[6]*b[2]+a[7]*b[5]+a[8]*b[8]];
},
# matrix for rolling
rollMatrix: func (roll) {
roll = roll * D2R;
return [1,0,0,
0,math.cos(roll),-math.sin(roll),
0,math.sin(roll), math.cos(roll)];
},
# matrix for pitching
pitchMatrix: func (pitch) {
pitch = -pitch * D2R;
return [math.cos(pitch),0,math.sin(pitch),
0,1,0,
-math.sin(pitch),0,math.cos(pitch)];
},
# matrix for yawing
yawMatrix: func (yaw) {
yaw = yaw * D2R;
return [math.cos(yaw),-math.sin(yaw),0,
math.sin(yaw),math.cos(yaw),0,
0,0,1];
},
# vector to heading/pitch
cartesianToEuler: 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;
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.pitch = vector[2]>=0?90:-90;
me.hdg = nil;
}
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;
me.pitch = pitch_deg * D2R;
me.roll = roll_deg * D2R;
me.x = -math.cos(me.yaw)*math.sin(me.pitch)*math.cos(me.roll) + math.sin(me.yaw)*math.sin(me.roll);
me.y = -math.sin(me.yaw)*math.sin(me.pitch)*math.cos(me.roll) - math.cos(me.yaw)*math.sin(me.roll);
me.z = math.cos(me.pitch)*math.cos(me.roll);#roll changed from sin to cos, since the rotation matrix is wrong
return [me.x,me.y,me.z];
},
# gives an vector that points forward from fuselage
eulerToCartesian3X: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
me.pitch = pitch_deg * D2R;
me.roll = roll_deg * D2R;
me.x = math.cos(me.yaw)*math.cos(me.pitch);
me.y = math.sin(me.yaw)*math.cos(me.pitch);
me.z = math.sin(me.pitch);
return [me.x,me.y,me.z];
},
# gives an vector that points left from fuselage
eulerToCartesian3Y: func (yaw_deg, pitch_deg, roll_deg) {
me.yaw = yaw_deg * D2R;
me.pitch = pitch_deg * D2R;
me.roll = roll_deg * D2R;
me.x = -math.cos(me.yaw)*math.sin(me.pitch)*math.sin(me.roll) - math.sin(me.yaw)*math.cos(me.roll);
me.y = -math.sin(me.yaw)*math.sin(me.pitch)*math.sin(me.roll) + math.cos(me.yaw)*math.cos(me.roll);
me.z = math.cos(me.pitch)*math.sin(me.roll);
return [me.x,me.y,me.z];
},
# same as eulerToCartesian3X, except it needs no roll
eulerToCartesian2: func (yaw_deg, pitch_deg) {
me.yaw = yaw_deg * D2R;
me.pitch = pitch_deg * D2R;
me.x = math.cos(me.pitch) * math.cos(me.yaw);
me.y = math.cos(me.pitch) * math.sin(me.yaw);
me.z = math.sin(me.pitch);
return [me.x,me.y,me.z];
},
#pitch from coord1 to coord2 in degrees (takes curvature of earth into effect.)
getPitch: func (coord1, coord2) {
if (coord1.lat() == coord2.lat() and coord1.lon() == coord2.lon()) {
if (coord2.alt() > coord1.alt()) {
return 90;
} elsif (coord2.alt() < coord1.alt()) {
return -90;
} else {
return 0;
}
}
if (coord1.alt() != coord2.alt()) {
me.d12 = coord1.direct_distance_to(coord2);
me.coord3 = geo.Coord.new(coord1);
me.coord3.set_alt(coord1.alt()-me.d12*0.5);# this will increase the area of the triangle so that rounding errors dont get in the way.
me.d13 = coord1.alt()-me.coord3.alt();
if (me.d12 == 0) {
# on top of each other, maybe rounding error..
return 0;
}
me.d32 = me.coord3.direct_distance_to(coord2);
if (math.abs(me.d13)+me.d32 < me.d12) {
# rounding errors somewhere..one triangle side is longer than other 2 sides combined.
return 0;
}
# standard formula for a triangle where all 3 side lengths are known:
me.len = (math.pow(me.d12, 2)+math.pow(me.d13,2)-math.pow(me.d32, 2))/(2 * me.d12 * math.abs(me.d13));
if (me.len < -1 or me.len > 1) {
# something went wrong, maybe rounding error..
return 0;
}
me.angle = R2D * math.acos(me.len);
me.pitch = -1* (90 - me.angle);
#printf("d12 %.4f d32 %.4f d13 %.4f len %.4f pitch %.4f angle %.4f", me.d12, me.d32, me.d13, me.len, me.pitch, me.angle);
return me.pitch;
} else {
# same altitude
me.nc = geo.Coord.new();
me.nc.set_xyz(0,0,0); # center of earth
me.radiusEarth = coord1.direct_distance_to(me.nc);# current distance to earth center
me.d12 = coord1.direct_distance_to(coord2);
# standard formula for a triangle where all 3 side lengths are known:
me.len = (math.pow(me.d12, 2)+math.pow(me.radiusEarth,2)-math.pow(me.radiusEarth, 2))/(2 * me.d12 * me.radiusEarth);
if (me.len < -1 or me.len > 1) {
# something went wrong, maybe rounding error..
return 0;
}
me.angle = R2D * math.acos(me.len);
me.pitch = -1* (90 - me.angle);
return me.pitch;
}
},
# 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){
# author: Paccalin
return [-v[0], -v[1], -v[2]];
},
# vector a - vector b
minus: func (a, b) {
return [a[0]-b[0], a[1]-b[1], a[2]-b[2]];
},
# vector a + vector b
plus: func (a, b) {
return [a[0]+b[0], a[1]+b[1], a[2]+b[2]];
},
# float * vector
product: func (scalar, vector) {
return [scalar*vector[0], scalar*vector[1], scalar*vector[2]]
},
# print vector to console
format: func (v) {
return sprintf("(%.2f, %.2f, %.2f)",v[0],v[1],v[2]);
},
# make vector length 1.0
normalize: func (v) {
me.mag = me.magnitudeVector(v);
return [v[0]/me.mag, v[1]/me.mag, v[2]/me.mag];
},
crossProduct: func (a,b) {
return [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]];
},
distance_from_point_to_line: func (coordP, coordL1, coordL2) {
var P = [coordP.x(), coordP.y(), coordP.z()];
var L1 = [coordL1.x(), coordL1.y(), coordL1.z()];
var L2 = [coordL2.x(), coordL2.y(), coordL2.z()];
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){
# author: Paccalin
me.op_refMag = me.magnitudeVector(ref);
if(me.op_refMag == 0)
die("Orthogonal projection on a null vector referential");
return me.dotProduct(vec, ref) / me.op_refMag;
},
# Time at which two particles will be at shortest distance !!can throw an exception if the relative speed is null!!
particleShortestDistTime: func (orig1, speed1, orig2, speed2) {
# author: Paccalin
# Compute the origin of the second particle in a referential positionally centered on the first particle.
me.psdt_tgtOrig = me.minus(orig2, orig1);
# Compute the speed of the second particle in a referential inertially based on the first particle.
me.psdt_tgtSpeed = me.minus(speed2, speed1);
# Project the origin of the particle1 referential onto the line supported by the particle2 trajectory in 1 unit of time.
# And divide the result by the magnitude of the speed to have it normalized relative to the time.
return me.orthogonalProjection(me.opposite(me.psdt_tgtOrig), me.psdt_tgtSpeed) / me.magnitudeVector(me.psdt_tgtSpeed);
},
# rotation matrices
#
#
#| 1 0 0 |
#| 0 cos(roll) -sin(roll) |
#| 0 sin(roll) cos(roll) |
#
#| cos(-pitch) 0 sin(-pitch) |
#| 0 1 0 |
#| -sin(-pitch) 0 cos(-pitch) |
#
#| cos(yaw) -sin(yaw) 0 |
#| sin(yaw) cos(yaw) 0 |
#| 0 0 1 |
#
# combined matrix from yaw, pitch, roll:
#
#| cos(yaw)cos(pitch) -cos(yaw)sin(pitch)sin(roll)-sin(yaw)cos(roll) -cos(yaw)sin(pitch)cos(roll)+sin(yaw)sin(roll)|
#| sin(yaw)cos(pitch) -sin(yaw)sin(pitch)sin(roll)+cos(yaw)cos(roll) -sin(yaw)sin(pitch)cos(roll)-cos(yaw)sin(roll)|
#| sin(pitch) cos(pitch)sin(roll) cos(pitch)cos(roll)|
#
#
};
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();

BIN
SA-3/Pictures/SA3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

426
SA-3/SA-3-set.xml Normal file
View File

@@ -0,0 +1,426 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<sim>
<author>pinto, Leto</author>
<description>SA-3</description>
<long-description>SA-3 (S-125) is a Soviet low to surface to air system designed to supplement .</long-description>
<status>alpha</status>
<aircraft-version>0.1</aircraft-version>
<!--Model Source:https://sketchfab.com/3d-models/sa3-8d893bb0e58e4ace9874710b97223b48-->
<flight-model>null</flight-model>
<aero>KPN-D</aero>
<aircraft-operator>KPN</aircraft-operator>
<model>
<path>Aircraft/SA-3/Models/SA-3.xml</path>
<!-- Uncloak weather balloons visiting Earth -->
<always-show-in-MP type="bool">true</always-show-in-MP>
</model>
<sound>
<path>Sounds/sound.xml</path>
</sound>
<presets>
<onground>true</onground>
</presets>
<current-view>
<deck-park type="bool">true</deck-park>
<z-offset-dec-step type="double">0.0</z-offset-dec-step>
<z-offset-inc-step type="double">0.0</z-offset-inc-step>
<can-change-z-offset type="bool">false</can-change-z-offset>
<z-offset-min-m type="float">100.0</z-offset-min-m>
<z-offset-max-m type="float">2000.0</z-offset-max-m>
</current-view>
<view n="101">
<name>Balcon</name>
<type>lookfrom</type>
<internal>false</internal>
<config>
<from-model type="bool">true</from-model>
<pitch-offset-deg>-33.0</pitch-offset-deg>
<heading-offset-deg>42</heading-offset-deg>
<x-offset-m>50</x-offset-m>
<y-offset-m>50</y-offset-m>
<z-offset-m>50</z-offset-m>
<default-field-of-view-deg>70</default-field-of-view-deg>
<limits>
<enabled type="bool">false</enabled>
</limits>
</config>
</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>
<chase-distance-m type="double" archive="y">-350.0</chase-distance-m>
<hud>
<path n="1">Aircraft/SA-3/Hud/hud.xml</path>
<visibility n="1">true</visibility>
<font>
<size type="float">11.0</size>
</font>
</hud>
<tower>
<auto-position type="bool">true</auto-position>
</tower>
<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>
<menubar>
<default>
<menu n="100">
<label>SA-3</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"));
var h = geo.elevation(getprop("sim/input/click/latitude-deg"),getprop("sim/input/click/longitude-deg"));
if (h!=nil)
setprop("position/altitude-ft",h*M2FT+0.010);
fire_control.start_time = systime();
</script>
</binding>
</item>
<item>
<label>Print log to console</label>
<binding>
<command>nasal</command>
<script>
setprop("payload/armament/msg",0);
damage.printDamageLog();
setprop("payload/armament/msg",1);
</script>
</binding>
</item>
<item>
<label>Event Log</label>
<binding>
<command>dialog-show</command>
<dialog-name>flightlog</dialog-name>
</binding>
</item>
</menu>
</default>
</menubar>
<aircraft-data>
<!-- Properties that get saved to persist locally between FG sessions -->
<path>enemies/e1</path>
<path>enemies/e2</path>
<path>enemies/e3</path>
<path>enemies/e4</path>
<path>enemies/e5</path>
<path>enemies/e6</path>
<path>enemies/e7</path>
<path>enemies/e8</path>
<path>enemies/e9</path>
<path>enemies/e10</path>
<path>enemies/e11</path>
<path>enemies/e12</path>
</aircraft-data>
</sim>
<controls>
<armament>
<station-select type="int">0</station-select>
<trigger type="bool">false</trigger>
<station n="1">
<offsets>
<x-m type="double"> 0.0</x-m>
<y-m type="double"> -1</y-m>
<z-m type="double"> 3.75</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="2">
<offsets>
<x-m type="double"> 0.0</x-m>
<y-m type="double"> 0</y-m>
<z-m type="double"> 3.75</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="3">
<offsets>
<x-m type="double"> 0.0</x-m>
<y-m type="double"> 1</y-m>
<z-m type="double"> 3.75</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
<station n="4">
<offsets>
<x-m type="double"> 0.0</x-m>
<y-m type="double"> 2</y-m>
<z-m type="double"> 3.75</z-m>
</offsets>
<trigger type="bool">false</trigger>
</station>
</armament>
</controls>
<payload>
<weight n="0">
<name>Main Store</name>
<weight-lb alias="/fdm/jsbsim/inertia/pointmass-weight-lbs[1]"/>
<selected>none</selected>
<opt>
<name>M317</name>
<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-3/Models/Armament/Weapons/</models>
<modelsUseCase type="bool">true</modelsUseCase>
<modelsUpperCase type="bool">false</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>
<MLW-bearing type="double">0</MLW-bearing>
<MLW-count type="int">0</MLW-count>
<MLW-launcher type="string"></MLW-launcher>
<MAW-bearing type="double">0</MAW-bearing>
<MAW-active type="bool">false</MAW-active>
<spike type="bool">false</spike>
<v601>
<type-id type="int">100</type-id>
<short-name type="string">V601</short-name>
<long-name type="string">5V27 (V601) Surface to Air Missile</long-name>
<max-fire-range-nm type="int">18</max-fire-range-nm>
<cross-section-sqft type="double">1.24</cross-section-sqft><!-- Authentic. 0.375m diameter -->
<weight-launch-lbs>2098</weight-launch-lbs><!-- SamSim docs -->
<vol-search type="double">0.00</vol-search>
<vol-track type="double">0.15</vol-track>
<vol-track-weak type="double">0.1</vol-track-weak>
<guidance type="string">command</guidance>
<navigation type="string">LOS</navigation>
<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>
<max-g type="int">22</max-g> <!-- More maneuverable than SA-2 -->
<thrust-lbf-stage-1 type="double">29500</thrust-lbf-stage-1>
<thrust-lbf-stage-2 type="double">4400</thrust-lbf-stage-2> <!--CIA Document-->
<stage-1-duration-sec type="double">6</stage-1-duration-sec>
<stage-2-duration-sec type="double">13</stage-2-duration-sec>
<weight-warhead-lbs>160</weight-warhead-lbs> <!-- https://cat-uxo.com/explosive-hazards/missiles/sa-3-goa-s-125-neva-missile -->
<weight-fuel-lbm>1000</weight-fuel-lbm> <!-- Estimate. Someone change this if deemed appropriate -->
<drag-coeff type="double">0.10</drag-coeff> <!-- estimate to fit in with parameters -->
<arming-time-sec type="double">1</arming-time-sec>
<min-speed-for-guiding-mach type="double">0.4</min-speed-for-guiding-mach>
<self-destruct-time-sec type="double">35</self-destruct-time-sec>
<chaff-resistance type="double">0.88</chaff-resistance>
<self-destruct-at-lock-lost type="bool">false</self-destruct-at-lock-lost>
<seeker-angular-speed-dps type="double">3000</seeker-angular-speed-dps>
<loft-altitude type="int">0</loft-altitude>
<min-fire-range-nm type="double">1.8</min-fire-range-nm> <!-- 3.5KM min range -->
<max-report-distance type="double">40</max-report-distance> <!-- weaponsystems.net claims 12.5m radius for the frag.-->
<rail type="bool">true</rail>
<rail-length-m type="double">3.96</rail-length-m> <!-- From CIA Document-->
<rail-point-forward type="bool">false</rail-point-forward>
<rail-pitch-deg type="double">33</rail-pitch-deg>
<rail-heading-deg type="double">0</rail-heading-deg>
<proportionality-constant type="double">3</proportionality-constant>
<class type="string">A</class>
<fire-msg>Bird away</fire-msg>
<telemetry type="bool">true</telemetry>
<FCS-at-origin type="bool">false</FCS-at-origin>
<FCS-x type="double">0</FCS-x>
<FCS-y type="double">0</FCS-y>
<FCS-z type="double">0</FCS-z>
</v601>
</armament>
</payload>
<rotors>
<main>
<blade n="0">
<flap-deg type="double">0</flap-deg>
</blade>
<blade n="1">
<flap-deg type="double">0</flap-deg>
</blade>
<blade n="2">
<flap-deg type="double">0</flap-deg>
<position-deg type="double">0</position-deg>
</blade>
<blade n="3">
<flap-deg type="double">0</flap-deg>
</blade>
</main>
</rotors>
<sam>
<impact0 type="double">-1</impact0>
<impact1 type="double">-1</impact1>
<impact2 type="double">-1</impact2>
<impact3 type="double">-1</impact3>
<hit0 type="double">-1</hit0>
<hit1 type="double">-1</hit1>
<hit2 type="double">-1</hit2>
<hit3 type="double">-1</hit3>
<missiles type="double">4</missiles>
<timeleft type="double">600</timeleft>
<damage type="double">100</damage>
</sam>
<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>
<instrumentation>
<datalink>
<data type="int">0</data>
<power type="bool">true</power>
<channel type="int">1212</channel>
<power_prop type="string">/instrumentation/datalink/power</power_prop>
<channel_prop type="string">instrumentation/datalink/channel</channel_prop>
<receive_period type="double">1</receive_period>
</datalink>
</instrumentation>
<nasal>
<notifications>
<file>Aircraft/SA-3/Nasal/ArmamentNotification.nas</file>
<file>Aircraft/SA-3/Nasal/GeoBridgedTransmitter.nas</file>
</notifications>
<carrier>
<file>Aircraft/SA-3/Nasal/fdm.nas</file>
</carrier>
<damage>
<file>Aircraft/SA-3/Nasal/damage.nas</file>
</damage>
<radar_logic>
<file>Aircraft/SA-3/Nasal/radar-logic.nas</file>
</radar_logic>
<vector>
<file>Aircraft/SA-3/Nasal/vector.nas</file><!-- needed by the guided-missiles -->
</vector>
<armament>
<file>Aircraft/SA-3/Nasal/guided-missiles.nas</file>
</armament>
<datalink>
<file>Aircraft/SA-3/Nasal/datalink.nas</file>
</datalink>
<fire_control>
<file>Aircraft/SA-3/Nasal/fire-control-custom.nas</file>
<file>Aircraft/SA-3/Nasal/fire-control.nas</file>
</fire_control>
</nasal>
</PropertyList>

BIN
SA-3/Sounds/launch.wav Normal file

Binary file not shown.

25
SA-3/Sounds/sound.xml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<fx>
<launch>
<name>missile</name>
<path>Aircraft/SA-3/Sounds/launch.wav</path>
<mode>once</mode>
<condition>
<property>payload/armament/V601/sound-fire-on-off</property>
</condition>
<reference-dist>50</reference-dist>
<max-dist>15000</max-dist>
<position>
<x> 0.01 </x>
<y> 0.01 </y>
<z> 0.01 </z>
</position>
<volume>
<factor>1</factor>
<max>1</max>
</volume>
</launch>
</fx>
</PropertyList>

View File

@@ -0,0 +1,348 @@
<?xml version="1.0"?>
<PropertyList>
<name>enemies</name>
<modal>false</modal>
<layout>vbox</layout>
<draggable>true</draggable>
<resizable>true</resizable>
<width>250</width>
<height>550</height>
<group>
<layout>hbox</layout>
<empty><stretch>1</stretch></empty>
<text>
<label>Targets</label>
</text>
<empty><stretch>1</stretch></empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<default>1</default>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
<binding>
<command>dialog-apply</command>
</binding>
</button>
</group>
<text>
<label>Callsigns:</label>
<height>25</height>
</text>
<input>
<width>250</width>
<height>25</height>
<label>1</label>
<property>enemies/e1</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>2</label>
<property>enemies/e2</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>3</label>
<property>enemies/e3</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>4</label>
<property>enemies/e4</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>5</label>
<property>enemies/e5</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>6</label>
<property>enemies/e6</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>7</label>
<property>enemies/e7</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>8</label>
<property>enemies/e8</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>9</label>
<property>enemies/e9</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>10</label>
<property>enemies/e10</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>11</label>
<property>enemies/e11</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<input>
<width>250</width>
<height>25</height>
<label>12</label>
<property>enemies/e12</property>
<live>true</live>
<halign>left</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>1</red>
<green>0.5</green>
<blue>0.5</blue>
</color>
<enable>
<not>
<property>enemies/opfor-switch</property>
</not>
<not>
<property>enemies/friend-switch</property>
</not>
</enable>
</input>
<checkbox>
<width>12</width>
<height>12</height>
<property>/enemies/opfor-switch</property>
<label>Target all non-OPFOR</label>
<binding>
<command>dialog-apply</command>
</binding>
</checkbox>
<checkbox>
<width>12</width>
<height>12</height>
<property>/enemies/friend-switch</property>
<label>Target all OPFOR</label>
<binding>
<command>dialog-apply</command>
</binding>
</checkbox>
</PropertyList>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0"?>
<PropertyList>
<name>flightlog</name>
<modal>false</modal>
<layout>vbox</layout>
<draggable>true</draggable>
<resizable>true</resizable>
<group>
<layout>hbox</layout>
<empty><stretch>1</stretch></empty>
<text>
<label>Event log</label>
</text>
<empty><stretch>1</stretch></empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<default>1</default>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
<binding>
<command>dialog-apply</command>
</binding>
</button>
</group>
<nasal>
<open>
<![CDATA[
setprop("sim/model/mig21/event-log", "Click refresh to see the event log..");
]]>
</open>
<close>
<![CDATA[
setprop("sim/model/mig21/event-log", "");
]]>
</close>
</nasal>
<button>
<!--<x>0</x>
<y>0</y>-->
<legend>Refresh</legend>
<binding>
<command>nasal</command>
<script><![CDATA[
var str = "";
if (1) {
var buffer = damage.damageLog.get_buffer();
foreach(entry; buffer) {
str = str~""~entry.time~" "~entry.message~"\n";
}
} else {
str = "The aircraft must be still to read the event log.";
}
setprop("sim/model/mig21/event-log", str);
]]></script>
</binding>
</button>
<text>
<label>Log:</label>
</text>
<textbox>
<!-- position -->
<!--<x>100</x>
<y>100</y>-->
<!-- dimensions -->
<pref-width>500</pref-width>
<pref-height>500</pref-height>
<property>/sim/model/mig21/event-log</property>
<live>true</live>
<slider>15</slider> <!--width for slider -->
<wrap>false</wrap> <!-- don't wrap text; default: true -->
<top-line>0</top-line> <!-- line to show at top, -ve numbers show last line -->
<editable>false</editable> <!-- if the puLargeInput is supposed to be editable -->
</textbox>
</PropertyList>

166
SA-3/gui/dialogs/prio.xml Normal file
View File

@@ -0,0 +1,166 @@
<?xml version="1.0"?>
<PropertyList>
<name>priority</name>
<modal>false</modal>
<layout>vbox</layout>
<draggable>true</draggable>
<resizable>false</resizable>
<width>150</width>
<height>250</height>
<group>
<empty><stretch>1</stretch></empty>
<layout>hbox</layout>
<text>
<label>Priority</label>
</text>
<empty><stretch>1</stretch></empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<default>1</default>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
<binding>
<command>dialog-apply</command>
</binding>
</button>
</group>
<group>
<layout>table</layout>
<text>
<label>Random</label>
<halign>right</halign>
<row>0</row>
<col>0</col>
</text>
<radio>
<row>0</row>
<col>1</col>
<property>priority0</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",0);setprop("priority0",1);setprop("priority6",0);setprop("priority1",0);setprop("priority2",0);setprop("priority3",0);setprop("priority4",0);setprop("priority5",0);</script>
</binding>
</radio>
<text>
<label>Highest</label>
<halign>right</halign>
<row>1</row>
<col>0</col>
</text>
<radio>
<row>1</row>
<col>1</col>
<property>priority1</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",1);setprop("priority1",1);setprop("priority0",0);setprop("priority6",0);setprop("priority2",0);setprop("priority3",0);setprop("priority4",0);setprop("priority5",0);</script>
</binding>
</radio>
<text>
<label>Lowest</label>
<halign>right</halign>
<row>2</row>
<col>0</col>
</text>
<radio>
<row>2</row>
<col>1</col>
<property>priority2</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",2);setprop("priority2",1);setprop("priority0",0);setprop("priority1",0);setprop("priority6",0);setprop("priority3",0);setprop("priority4",0);setprop("priority5",0);</script>
</binding>
</radio>
<text>
<label>Fastest</label>
<halign>right</halign>
<row>3</row>
<col>0</col>
</text>
<radio>
<row>3</row>
<col>1</col>
<property>priority3</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",3);setprop("priority3",1);setprop("priority0",0);setprop("priority1",0);setprop("priority2",0);setprop("priority6",0);setprop("priority4",0);setprop("priority5",0);</script>
</binding>
</radio>
<text>
<label>Slowest</label>
<halign>right</halign>
<row>4</row>
<col>0</col>
</text>
<radio>
<row>4</row>
<col>1</col>
<property>priority4</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",4);setprop("priority4",1);setprop("priority0",0);setprop("priority1",0);setprop("priority2",0);setprop("priority3",0);setprop("priority6",0);setprop("priority5",0);</script>
</binding>
</radio>
<text>
<label>Closest</label>
<halign>right</halign>
<row>5</row>
<col>0</col>
</text>
<radio>
<row>5</row>
<col>1</col>
<property>priority5</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",5);setprop("priority5",1);setprop("priority0",0);setprop("priority1",0);setprop("priority2",0);setprop("priority3",0);setprop("priority4",0);setprop("priority6",0);</script>
</binding>
</radio>
<text>
<label>Farthest</label>
<halign>right</halign>
<row>6</row>
<col>0</col>
</text>
<radio>
<row>6</row>
<col>1</col>
<property>priority6</property>
<live>true</live>
<binding>
<command>nasal</command>
<script>setprop("priority",6);setprop("priority6",1);setprop("priority0",0);setprop("priority1",0);setprop("priority2",0);setprop("priority3",0);setprop("priority4",0);setprop("priority5",0);</script>
</binding>
</radio>
<input>
<row>7</row>
<col>0</col>
<width>75</width>
<height>25</height>
<label>DLNK</label>
<property>instrumentation/datalink/channel</property>
<live>true</live>
<halign>right</halign>
<binding>
<command>dialog-apply</command>
</binding>
<color>
<red>0.75</red>
<green>0.75</green>
<blue>1.0</blue>
</color>
</input>
</group>
</PropertyList>

BIN
SA-3/thumbnail.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -44,6 +44,10 @@
"version": "2.0.3",
"path": "S-75/Nasal/vector.nas"
},
{
"version": null,
"path": "SA-3/Nasal/vector.nas"
},
{
"version": "2.0.3",
"path": "SA-6/Nasal/vector.nas"
@@ -66,6 +70,10 @@
"version": "1.1.2",
"path": "S-75/Nasal/guided-missiles.nas"
},
{
"version": null,
"path": "SA-3/Nasal/guided-missiles.nas"
},
{
"version": "1.1.2",
"path": "SA-6/Nasal/guided-missiles.nas"
@@ -116,6 +124,10 @@
"version": "1.4.3",
"path": "S-75/Nasal/damage.nas"
},
{
"version": null,
"path": "SA-3/Nasal/damage.nas"
},
{
"version": "1.4.3",
"path": "SA-6/Nasal/damage.nas"
@@ -154,6 +166,10 @@
"version": "1.1.0",
"path": "S-75/Nasal/datalink.nas"
},
{
"version": null,
"path": "SA-3/Nasal/datalink.nas"
},
{
"version": "1.1.0",
"path": "SA-6/Nasal/datalink.nas"
@@ -208,6 +224,10 @@
"version": "1.1.0",
"path": "S-75/Nasal/ArmamentNotification.nas"
},
{
"version": null,
"path": "SA-3/Nasal/ArmamentNotification.nas"
},
{
"version": "1.1.0",
"path": "SA-6/Nasal/ArmamentNotification.nas"
@@ -262,6 +282,10 @@
"version": "1.0.1",
"path": "S-75/Nasal/GeoBridgedTransmitter.nas"
},
{
"version": null,
"path": "SA-3/Nasal/GeoBridgedTransmitter.nas"
},
{
"version": "1.0.1",
"path": "SA-6/Nasal/GeoBridgedTransmitter.nas"
@@ -284,6 +308,10 @@
"version": "1.0.0",
"path": "S-75/Nasal/fire-control.nas"
},
{
"version": null,
"path": "SA-3/Nasal/fire-control.nas"
},
{
"version": "1.0.0",
"path": "SA-6/Nasal/fire-control.nas"
@@ -302,6 +330,10 @@
"version": "1.1.0",
"path": "S-75/Nasal/radar-logic.nas"
},
{
"version": null,
"path": "SA-3/Nasal/radar-logic.nas"
},
{
"version": "1.1.0",
"path": "SA-6/Nasal/radar-logic.nas"