|
|
Line 1: |
Line 1: |
| Inspired by the crash system in the [[Mikoyan-Gurevich_MiG-15|Mig-15]], this system is meant to become generic and usable for all aircraft by adding just a few lines. For now you have to add a file with code though.
| | #REDIRECT [[Crash and stress damage system]] |
| | |
| == Features so far==
| |
| * Impact detection and tied into the new failure manager.
| |
| * Over-G detection for wing stress.
| |
| * Sounds.
| |
| | |
| == Planned features==
| |
| Feel free to suggest some in the forum topic linked to at the end of this article.
| |
| | |
| == How to install the current system on an aircraft ==
| |
| Copy this code into your aircrafts Nasal folder as crash-and-stress.nas file:
| |
| <syntaxhighlight lang="nasal">
| |
| #
| |
| # A Flightgear JSBSim crash and stress damage system.
| |
| #
| |
| # Inspired by the crash system in Mig15 by Slavutinsky Victor. And by Hvengel's formula for wingload stress.
| |
| #
| |
| # Authors: Slavutinsky Victor, Nikolai V. Chr. (Necolatis)
| |
| #
| |
| #
| |
| # Version 0.11
| |
| #
| |
| # License:
| |
| # GPL 2.0
| |
| | |
| | |
| var TRUE = 1;
| |
| var FALSE = 0;
| |
| | |
| var CrashAndStress = {
| |
| # pattern singleton
| |
| _instance: nil,
| |
| # Get the instance
| |
| new: func (gears, stressLimit = nil, wingsFailureModes = nil) {
| |
| | |
| var m = nil;
| |
| if(me._instance == nil) {
| |
| me._instance = {};
| |
| me._instance["parents"] = [CrashAndStress];
| |
| | |
| m = me._instance;
| |
| | |
| m.inService = FALSE;
| |
| m.repairing = FALSE;
| |
| | |
| m.exploded = FALSE;
| |
| | |
| m.wingsAttached = TRUE;
| |
| m.wingLoadLimitUpper = nil;
| |
| m.wingLoadLimitLower = nil;
| |
| m._looptimer = maketimer(0, m, m._loop);
| |
| | |
| m.input = {
| |
| # trembleOn: "damage/g-tremble-on",
| |
| # trembleMax: "damage/g-tremble-max",
| |
| replay: "sim/replay/replay-state",
| |
| lat: "position/latitude-deg",
| |
| lon: "position/longitude-deg",
| |
| alt: "position/altitude-ft",
| |
| altAgl: "position/altitude-agl-ft",
| |
| elev: "position/ground-elev-ft",
| |
| crackOn: "damage/sounds/crack-on",
| |
| creakOn: "damage/sounds/creaking-on",
| |
| crackVol: "damage/sounds/crack-volume",
| |
| creakVol: "damage/sounds/creaking-volume",
| |
| wCrashOn: "damage/sounds/water-crash-on",
| |
| crashOn: "damage/sounds/crash-on",
| |
| detachOn: "damage/sounds/detach-on",
| |
| explodeOn: "damage/sounds/explode-on",
| |
| };
| |
| foreach(var ident; keys(m.input)) {
| |
| m.input[ident] = props.globals.getNode(m.input[ident], 1);
| |
| }
| |
| | |
| m.fdm = nil;
| |
| | |
| if(getprop("sim/flight-model") == "jsb") {
| |
| m.fdm = jsbSimProp;
| |
| } elsif(getprop("sim/flight-model") == "yasim") {
| |
| m.fdm = yaSimProp;
| |
| } else {
| |
| return nil;
| |
| }
| |
| m.fdm.convert();
| |
|
| |
| m.wowStructure = [];
| |
| m.wowGear = [];
| |
| | |
| m.lastMessageTime = 0;
| |
| | |
| | |
| m._identifyGears(gears);
| |
| m.setStressLimit(stressLimit);
| |
| m.setWingsFailureModes(wingsFailureModes);
| |
| | |
| m._startImpactListeners();
| |
| } else {
| |
| m = me._instance;
| |
| }
| |
| | |
| return m;
| |
| },
| |
| # start the system
| |
| start: func () {
| |
| me.inService = TRUE;
| |
| },
| |
| # stop the system
| |
| stop: func () {
| |
| me.inService = FALSE;
| |
| },
| |
| # return TRUE if in progress
| |
| isStarted: func () {
| |
| return me.inService;
| |
| },
| |
| # repair the aircaft
| |
| repair: func () {
| |
| var failure_modes = FailureMgr._failmgr.failure_modes;
| |
| var mode_list = keys(failure_modes);
| |
| | |
| foreach(var failure_mode_id; mode_list) {
| |
| FailureMgr.set_failure_level(failure_mode_id, 0);
| |
| }
| |
| me.wingsAttached = TRUE;
| |
| me.exploded = FALSE;
| |
| me.lastMessageTime = 0;
| |
| me.repairing = TRUE;
| |
| var timer = maketimer(10, me, me._finishRepair);
| |
| timer.start();
| |
| },
| |
| # accepts a vector with failure mode IDs, they will fail when wings break off.
| |
| setWingsFailureModes: func (modes) {
| |
| if(modes == nil) {
| |
| modes = [];
| |
| }
| |
| | |
| ##
| |
| # Returns an actuator object that will set the serviceable property at
| |
| # the given node to zero when the level of failure is > 0.
| |
| # it will also fail additionally failure modes.
| |
| | |
| var set_unserviceable_cascading = func(path, casc_paths) {
| |
| | |
| var prop = path ~ "/serviceable";
| |
| | |
| if (props.globals.getNode(prop) == nil) {
| |
| props.globals.initNode(prop, TRUE, "BOOL");
| |
| } else {
| |
| props.globals.getNode(prop).setValue(TRUE);#in case this gets initialized empty from a recorder signal or MP alias.
| |
| }
| |
| | |
| return {
| |
| parents: [FailureMgr.FailureActuator],
| |
| mode_paths: casc_paths,
| |
| set_failure_level: func(level) {
| |
| setprop(prop, level > 0 ? 0 : 1);
| |
| foreach(var mode_path ; me.mode_paths) {
| |
| FailureMgr.set_failure_level(mode_path, level);
| |
| }
| |
| },
| |
| get_failure_level: func { getprop(prop) ? 0 : 1 }
| |
| }
| |
| }
| |
| | |
| var prop = me.fdm.wingsFailureID;
| |
| var actuator_wings = set_unserviceable_cascading(prop, modes);
| |
| FailureMgr.add_failure_mode(prop, "Main wings", actuator_wings);
| |
| },
| |
| # set the stresslimit for the main wings
| |
| setStressLimit: func (stressLimit = nil) {
| |
| if (stressLimit != nil) {
| |
| var wingloadMax = stressLimit['wingloadMaxLbs'];
| |
| var wingloadMin = stressLimit['wingloadMinLbs'];
| |
| var maxG = stressLimit['maxG'];
| |
| var minG = stressLimit['minG'];
| |
| var weight = stressLimit['weightLbs'];
| |
| if(wingloadMax != nil) {
| |
| me.wingLoadLimitUpper = wingloadMax;
| |
| } elsif (maxG != nil and weight != nil) {
| |
| me.wingLoadLimitUpper = maxG * weight;
| |
| }
| |
| | |
| if(wingloadMin != nil) {
| |
| me.wingLoadLimitLower = wingloadMin;
| |
| } elsif (minG != nil and weight != nil) {
| |
| me.wingLoadLimitLower = minG * weight;
| |
| } elsif (me.wingLoadLimitUpper != nil) {
| |
| me.wingLoadLimitLower = -me.wingLoadLimitUpper * 0.4;#estimate for when lower is not specified
| |
| }
| |
| me._looptimer.start();
| |
| } else {
| |
| me._looptimer.stop();
| |
| }
| |
| },
| |
| _identifyGears: func (gears) {
| |
| var contacts = props.globals.getNode("/gear").getChildren("gear");
| |
| | |
| foreach(var contact; contacts) {
| |
| var index = contact.getIndex();
| |
| var isGear = me._contains(gears, index);
| |
| var wow = contact.getChild("wow");
| |
| if (isGear == TRUE) {
| |
| append(me.wowGear, wow);
| |
| } else {
| |
| append(me.wowStructure, wow);
| |
| }
| |
| }
| |
| },
| |
| _finishRepair: func () {
| |
| me.repairing = FALSE;
| |
| },
| |
| _isStructureInContact: func () {
| |
| foreach(var structure; me.wowStructure) {
| |
| if (structure.getBoolValue() == TRUE) {
| |
| return TRUE;
| |
| }
| |
| }
| |
| return FALSE;
| |
| },
| |
| _isGearInContact: func () {
| |
| foreach(var gear; me.wowGear) {
| |
| if (gear.getBoolValue() == TRUE) {
| |
| return TRUE;
| |
| }
| |
| }
| |
| return FALSE;
| |
| },
| |
| _contains: func (vector, content) {
| |
| foreach(var vari; vector) {
| |
| if (vari == content) {
| |
| return TRUE;
| |
| }
| |
| }
| |
| return FALSE;
| |
| },
| |
| _startImpactListeners: func () {
| |
| ImpactStructureListener.crash = me;
| |
| foreach(var structure; me.wowStructure) {
| |
| setlistener(structure, func {call(ImpactStructureListener.run, nil, ImpactStructureListener, ImpactStructureListener)},0,0);
| |
| }
| |
| },
| |
| _isRunning: func () {
| |
| if (me.inService == FALSE or me.input.replay.getBoolValue() == TRUE or me.repairing == TRUE) {
| |
| return FALSE;
| |
| }
| |
| var time = me.fdm.input.simTime.getValue();
| |
| if (time != nil and time > 1) {
| |
| return TRUE;
| |
| }
| |
| return FALSE;
| |
| },
| |
| _calcGroundSpeed: func () {
| |
| var horzSpeed = me.fdm.input.vgFps.getValue();
| |
| var vertSpeed = me.fdm.input.downFps.getValue();
| |
| var realSpeed = math.sqrt((horzSpeed * horzSpeed) + (vertSpeed * vertSpeed));
| |
| realSpeed = me.fdm.fps2kt(realSpeed);
| |
| return realSpeed;
| |
| },
| |
| _impactDamage: func () {
| |
| var lat = me.input.lat.getValue();
| |
| var lon = me.input.lon.getValue();
| |
| var info = geodinfo(lat, lon);
| |
| var solid = info[1] == nil?TRUE:info[1].solid;
| |
| var speed = me._calcGroundSpeed();
| |
| | |
| if (me.exploded == FALSE) {
| |
| var failure_modes = FailureMgr._failmgr.failure_modes;
| |
| var mode_list = keys(failure_modes);
| |
| var probability = speed / 200.0;# 200kt will fail everything, 0kt will fail nothing.
| |
| | |
| # test for explosion
| |
| if(probability > 1.0 and me.fdm.input.fuel.getValue() > 2500) {
| |
| # 200kt+ and fuel in tanks will explode the aircraft on impact.
| |
| me._explodeBegin();
| |
| return;
| |
| }
| |
| | |
| foreach(var failure_mode_id; mode_list) {
| |
| if(rand() < probability) {
| |
| FailureMgr.set_failure_level(failure_mode_id, 1);
| |
| }
| |
| }
| |
| var str = "Aircraft hit "~info[1].names[size(info[1].names)-1]~".";
| |
| me._output(str);
| |
| } elsif (solid == TRUE) {
| |
| var pos= geo.Coord.new().set_latlon(lat, lon);
| |
| wildfire.ignite(pos, 1);
| |
| }
| |
| if(solid == TRUE) {
| |
| #print("solid");
| |
| me._impactSoundBegin(speed);
| |
| } else {
| |
| #print("water");
| |
| me._impactSoundWaterBegin(speed);
| |
| }
| |
| },
| |
| _impactSoundWaterBegin: func (speed) {
| |
| if (speed > 5) {#check if sound already running?
| |
| me.input.wCrashOn.setValue(1);
| |
| var timer = maketimer(3, me, me._impactSoundWaterEnd);
| |
| timer.start();
| |
| }
| |
| },
| |
| _impactSoundWaterEnd: func () {
| |
| me.input.wCrashOn.setValue(0);
| |
| },
| |
| _impactSoundBegin: func (speed) {
| |
| if (speed > 5) {
| |
| me.input.crashOn.setValue(1);
| |
| var timer = maketimer(3, me, me._impactSoundEnd);
| |
| timer.start();
| |
| }
| |
| },
| |
| _impactSoundEnd: func () {
| |
| me.input.crashOn.setValue(0);
| |
| },
| |
| _explodeBegin: func() {
| |
| me.input.explodeOn.setValue(1);
| |
| me.exploded = TRUE;
| |
| var failure_modes = FailureMgr._failmgr.failure_modes;
| |
| var mode_list = keys(failure_modes);
| |
| | |
| foreach(var failure_mode_id; mode_list) {
| |
| FailureMgr.set_failure_level(failure_mode_id, 1);
| |
| }
| |
| | |
| me._output("Aircraft exploded.", TRUE);
| |
| | |
| var timer = maketimer(3, me, me._explodeEnd);
| |
| timer.start();
| |
| },
| |
| _explodeEnd: func () {
| |
| me.input.explodeOn.setValue(0);
| |
| },
| |
| _stressDamage: func (str) {
| |
| me._output("Aircraft damaged: Wings broke off, due to "~str~" G forces.");
| |
| me.input.detachOn.setValue(1);
| |
|
| |
| FailureMgr.set_failure_level(me.fdm.wingsFailureID, 1);
| |
| | |
| me.wingsAttached = FALSE;
| |
| var timer = maketimer(3, me, me._stressDamageEnd);
| |
| timer.start();
| |
| },
| |
| _stressDamageEnd: func () {
| |
| me.input.detachOn.setValue(0);
| |
| },
| |
| _output: func (str, override = FALSE) {
| |
| var time = me.fdm.input.simTime.getValue();
| |
| if (override == TRUE or (time - me.lastMessageTime) > 3) {
| |
| me.lastMessageTime = time;
| |
| print(str);
| |
| screen.log.write(str, 0.7098, 0.5372, 0.0);# solarized yellow
| |
| }
| |
| },
| |
| _loop: func () {
| |
| me._testStress();
| |
| me._testWaterImpact();
| |
| },
| |
| _testWaterImpact: func () {
| |
| if(me.input.altAgl.getValue() < 0) {
| |
| var lat = me.input.lat.getValue();
| |
| var lon = me.input.lon.getValue();
| |
| var info = geodinfo(lat, lon);
| |
| var solid = info[1] == nil?TRUE:info[1].solid;
| |
| if(solid == FALSE) {
| |
| me._impactDamage();
| |
| }
| |
| }
| |
| },
| |
| _testStress: func () {
| |
| if (me._isRunning() == TRUE and me.wingsAttached == TRUE) {
| |
| var gForce = me.fdm.input.Nz.getValue() == nil?1:me.fdm.input.Nz.getValue();
| |
| var weight = me.fdm.input.weight.getValue();
| |
| var wingload = gForce * weight;
| |
| | |
| #print("wingload: "~wingload~" max: "~me.wingLoadLimitUpper);
| |
| var broken = FALSE;
| |
| | |
| if(wingload < 0) {
| |
| broken = me._testWingload(-wingload, -me.wingLoadLimitLower);
| |
| if(broken == TRUE) {
| |
| me._stressDamage("negative");
| |
| }
| |
| } else {
| |
| broken = me._testWingload(wingload, me.wingLoadLimitUpper);
| |
| if(broken == TRUE) {
| |
| me._stressDamage("positive");
| |
| }
| |
| }
| |
| } else {
| |
| me.input.crackOn.setValue(0);
| |
| me.input.creakOn.setValue(0);
| |
| #me.input.trembleOn.setValue(0);
| |
| }
| |
| },
| |
| _testWingload: func (wingload, wingLoadLimit) {
| |
| if (wingload > (wingLoadLimit * 0.5)) {
| |
| #me.input.trembleOn.setValue(1);
| |
| var tremble_max = math.sqrt((wingload - (wingLoadLimit * 0.5)) / (wingLoadLimit * 0.5));
| |
| #me.input.trembleMax.setValue(1);
| |
| | |
| if (wingload > (wingLoadLimit * 0.75)) {
| |
| | |
| #tremble_max = math.sqrt((wingload - (wingLoadLimit * 0.5)) / (wingLoadLimit * 0.5));
| |
| me.input.creakVol.setValue(tremble_max);
| |
| me.input.creakOn.setValue(1);
| |
| | |
| if (wingload > (wingLoadLimit * 0.90)) {
| |
| me.input.crackOn.setValue(1);
| |
| me.input.crackVol.setValue(tremble_max);
| |
| if (wingload > wingLoadLimit) {
| |
| me.input.crackVol.setValue(1);
| |
| me.input.creakVol.setValue(1);
| |
| #me.input.trembleMax.setValue(1);
| |
| return TRUE;
| |
| }
| |
| } else {
| |
| me.input.crackOn.setValue(0);
| |
| }
| |
| } else {
| |
| me.input.creakOn.setValue(0);
| |
| }
| |
| } else {
| |
| me.input.crackOn.setValue(0);
| |
| me.input.creakOn.setValue(0);
| |
| #me.input.trembleOn.setValue(0);
| |
| }
| |
| return FALSE;
| |
| },
| |
| };
| |
| | |
| | |
| var ImpactStructureListener = {
| |
| crash: nil,
| |
| run: func () {
| |
| if (crash._isRunning() == TRUE) {
| |
| var wow = crash._isStructureInContact();
| |
| if (wow == TRUE) {
| |
| crash._impactDamage();
| |
| }
| |
| }
| |
| },
| |
| };
| |
| | |
| | |
| # static class
| |
| var fdmProperties = {
| |
| input: {},
| |
| convert: func () {
| |
| foreach(var ident; keys(me.input)) {
| |
| me.input[ident] = props.globals.getNode(me.input[ident], 1);
| |
| }
| |
| },
| |
| fps2kt: func (fps) {
| |
| return fps * 0.5924838;
| |
| },
| |
| wingsFailureID: nil,
| |
| };
| |
| | |
| var jsbSimProp = {
| |
| parents: [fdmProperties],
| |
| input: {
| |
| weight: "fdm/jsbsim/inertia/weight-lbs",
| |
| fuel: "fdm/jsbsim/propulsion/total-fuel-lbs",
| |
| simTime: "fdm/jsbsim/simulation/sim-time-sec",
| |
| vgFps: "fdm/jsbsim/velocities/vg-fps",
| |
| downFps: "velocities/down-relground-fps",
| |
| Nz: "fdm/jsbsim/accelerations/Nz",
| |
| },
| |
| wingsFailureID: "fdm/jsbsim/structural/wings",
| |
| };
| |
| | |
| var yaSimProp = {
| |
| parents: [fdmProperties],
| |
| input: {
| |
| weight: "yasim/gross-weight-lbs",
| |
| fuel: "consumables/fuel/total-fuel-lbs",
| |
| simTime: "sim/time/elapsed-sec",
| |
| vgFps: "velocities/groundspeed-kt",
| |
| Nz: "accelerations/n-z-cg-fps_sec",
| |
| },
| |
| convert: func () {
| |
| call(fdmProperties.convert, [], me);
| |
| me.input.downFps = props.Node.new().setValue(0);
| |
| },
| |
| fps2kt: func (fps) {
| |
| return fps;
| |
| },
| |
| wingsFailureID: "structural/wings",
| |
| };
| |
| | |
| | |
| # TODO:
| |
| #
| |
| # Loss of inertia if impacting/sliding? Or should the jsb groundcontacts take care of that alone?
| |
| # If gears hit something at too high speed the gears should be damaged?
| |
| # Make property to control if system active, or method enough?
| |
| # Explosion depending on bumpiness and speed when sliding?
| |
| # Tie in with damage from Bombable?
| |
| # Use galvedro's UpdateLoop framework when it gets merged
| |
| | |
| | |
| # example uses:
| |
| #
| |
| # var crashCode = CrashAndStress.new([0,1,2];
| |
| #
| |
| # var crashCode = CrashAndStress.new([0,1,2], {"weightLbs":30000, "maxG": 12});
| |
| #
| |
| # var crashCode = CrashAndStress.new([0,1,2,3], {"weightLbs":20000, "maxG": 11, "minG": -5});
| |
| #
| |
| # var crashCode = CrashAndStress.new([0,1,2], {"wingloadMaxLbs": 90000, "wingloadMinLbs": -45000}, ["controls/flight/aileron", "controls/flight/elevator", "controls/flight/flaps"]);
| |
| #
| |
| # var crashCode = CrashAndStress.new([0,1,2], {"wingloadMaxLbs":90000}, ["controls/flight/aileron", "controls/flight/elevator", "controls/flight/flaps"]);
| |
| #
| |
| # Gears parameter must be defined.
| |
| # Stress parameter is optional. If minimum wing stress is not defined it will be set to -40% of max wingload stress if that is defined.
| |
| # The last optional parameter is a list of failure mode IDs that shall fail when wings detach. They must be defined in the FailureMgr.
| |
| #
| |
| #
| |
| # Remember to add sounds and to add the sound properties as custom signals to the replay recorder.
| |
| | |
| | |
| # use:
| |
| var crashCode = CrashAndStress.new([0,1,2], {"weightLbs":30000, "maxG": 12}, ["controls/gear1", "controls/gear2", "controls/flight/aileron", "controls/flight/elevator", "consumables/fuel/wing-tanks"]);
| |
| crashCode.start();
| |
| | |
| # test:
| |
| var repair = func {
| |
| crashCode.repair();
| |
| };
| |
| </syntaxhighlight>
| |
| | |
| Notice that you should edit the line underneath '''# use''' to fit your aircraft requirements.
| |
| | |
| In your -set.xml file under nasal tags add
| |
| | |
| <syntaxhighlight lang="xml">
| |
| <crash>
| |
| <file>Aircraft/[aircraft name here]/Nasal/crash-and-stress.nas</file>
| |
| </crash>
| |
| </syntaxhighlight>
| |
| | |
| Add this to your sounds file:
| |
| | |
| <syntaxhighlight lang="xml">
| |
| <aircraft-explode>
| |
| <name>aircraft-explode</name>
| |
| <path>Sounds/aircraft-explode.wav</path>
| |
| <mode>once</mode>
| |
| <condition>
| |
| <or>
| |
| <equals>
| |
| <property>damage/sounds/explode-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| <equals>
| |
| <property>damage/sounds/detach-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| </or>
| |
| </condition>
| |
| <volume>
| |
| <factor>1</factor>
| |
| </volume>
| |
| </aircraft-explode>
| |
| | |
| <aircraft-crash>
| |
| <name>aircraft-crash</name>
| |
| <path>Sounds/aircraft-crash.wav</path>
| |
| <mode>once</mode>
| |
| <condition>
| |
| <equals>
| |
| <property>damage/sounds/crash-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| </condition>
| |
| <volume>
| |
| <factor>1</factor>
| |
| </volume>
| |
| </aircraft-crash>
| |
| | |
| <aircraft-water-crash>
| |
| <name>aircraft-water-crash</name>
| |
| <path>Sounds/aircraft-water-crash.wav</path>
| |
| <mode>once</mode>
| |
| <condition>
| |
| <equals>
| |
| <property>damage/sounds/water-crash-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| </condition>
| |
| <volume>
| |
| <factor>1</factor>
| |
| </volume>
| |
| </aircraft-water-crash>
| |
| | |
| <aircraft-crack>
| |
| <name>aircraft-crack</name>
| |
| <path>Sounds/aircraft-crack.wav</path>
| |
| <mode>once</mode>
| |
| <condition>
| |
| <property>sim/current-view/internal</property>
| |
| <equals>
| |
| <property>damage/sounds/crack-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| </condition>
| |
| <volume>
| |
| <property>damage/sounds/crack-volume</property>
| |
| <factor>1</factor>
| |
| </volume>
| |
| </aircraft-crack>
| |
| | |
| <aircraft-creaking>
| |
| <name>aircraft-creaking</name>
| |
| <path>Sounds/aircraft-creaking.wav</path>
| |
| <mode>looped</mode>
| |
| <condition>
| |
| <property>sim/current-view/internal</property>
| |
| <equals>
| |
| <property>damage/sounds/creaking-on</property>
| |
| <value>1</value>
| |
| </equals>
| |
| </condition>
| |
| <volume>
| |
| <property>damage/sounds/creaking-volume</property>
| |
| <factor>1</factor>
| |
| </volume>
| |
| </aircraft-creaking>
| |
| </syntaxhighlight>
| |
| | |
| Add some sounds to the aircraft sound folder that corresponds to the file names used in the above. (you can use those in the Mig15 or Ja-37 folders)
| |
| | |
| Optionally add these signals to the replay recorder:
| |
| | |
| <syntaxhighlight lang="xml">
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">damage/sounds/explode-on</property>
| |
| </signal>
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">damage/sounds/crash-on</property>
| |
| </signal>
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">damage/sounds/water-crash-on</property>
| |
| </signal>
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">damage/sounds/crack-on</property>
| |
| </signal>
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">damage/sounds/creaking-on</property>
| |
| </signal>
| |
| <signal>
| |
| <type>float</type>
| |
| <property type="string">damage/sounds/crack-volume</property>
| |
| </signal>
| |
| <signal>
| |
| <type>float</type>
| |
| <property type="string">damage/sounds/creaking-volume</property>
| |
| </signal>
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">fdm/jsbsim/structural/wings/serviceable</property>
| |
| </signal>
| |
| </syntaxhighlight>
| |
| | |
| Notice that the last signal is jsbsim specific, the corresponding yasim signal is:
| |
| | |
| <syntaxhighlight lan="nasal">
| |
| <signal>
| |
| <type>bool</type>
| |
| <property type="string">structural/wings/serviceable</property>
| |
| </signal>
| |
| </syntaxhighlight>
| |
| | |
| That property is also what you would use to animate wings being detached and/or changes in the aerodynamics.
| |
| | |
| == Performance ==
| |
| As it is now you should feel no performance degradation at all from using it.
| |
| | |
| == People volunteering to work on this ==
| |
| * Bomber
| |
| * Necolatis
| |
| | |
| == Aircrafts that use this system ==
| |
| [[Saab JA-37 Viggen]] (version 2.83+)
| |
| | |
| == Related content ==
| |
| * [http://forum.flightgear.org/viewtopic.php?f=4&t=24901 Generic crash system for JSBSim aircraft] – Forum topic on the official FlightGear forum.
| |
| | |
| [[Category:Aircraft enhancement]]
| |