diff --git a/BUK-M2/Nasal/damage.nas b/BUK-M2/Nasal/damage.nas index c817407..1151b6b 100644 --- a/BUK-M2/Nasal/damage.nas +++ b/BUK-M2/Nasal/damage.nas @@ -1,29 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m = 3;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp = 80; -var hp_max = hp; +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -34,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -48,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -66,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -76,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -93,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1 == 1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -171,64 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - var prob = 1; - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - var failed = fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); - - #"Leto: Gun Splash On : SA-11" - #"Leto: KCA cannon shell hit: SA-11: 4 hits:..." if (size(last_vector) < 4) { # msg is either missing number of hits, or has no trailing dots from spam filter. print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); return; } - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - var failed = fail_systems(probability); - # damaged_sys = damaged_sys + failed; - } - # that someone is me! - #print("hitting me"); - - #printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -237,24 +332,132 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { - hp = hp - damage; - print("HP: " ~ hp ~ "/" ~ hp_max); - setprop("sam/damage", math.max(0,100*hp/hp_max)); - if ( hp < 0 ) { - setprop("/carrier/sunk/",1); - setprop("/sim/multiplay/generic/int[2]",1); - setprop("/sim/multiplay/generic/int[0]",1); - setprop("/sim/messages/copilot", "SAM dead."); - } +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } }; +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { + var no = 7; + while (no > 6 or hp_f[no] < 0) { + no = int(rand()*7); + if (hp_f[no] < 0) { + if (rand() > 0.9) { + armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); + return; + } + } + } + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { + setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); + armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { + setprop("/carrier/disabled",1); + armament.defeatSpamFilter("Captain our offensive capability is crippled!"); + } + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { + setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + armament.defeatSpamFilter("S.O.S. Heeelp"); + } else { + armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } + } + } +} + var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); settimer(func {stopIncomingSound(clock);},3); @@ -264,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -279,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); +setlistener("/sim/signals/reinit", re_init, 0, 0); diff --git a/Frigate/Nasal/damage.nas b/Frigate/Nasal/damage.nas index 2787b6f..94a2011 100644 --- a/Frigate/Nasal/damage.nas +++ b/Frigate/Nasal/damage.nas @@ -1,29 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m =20;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max =200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp = 200; -var hp_max = hp; +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -34,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -48,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -66,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -76,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -93,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1==1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -171,61 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - var prob = 1; - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - var failed = fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); if (size(last_vector) < 4) { - # msg is either missing number of hits, or has no trailing dots from spam filter. - print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); - return; - } - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + # msg is either missing number of hits, or has no trailing dots from spam filter. + print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); + return; + } + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - var failed = fail_systems(probability); - #damaged_sys = damaged_sys + failed; - } - # that someone is me! - #print("hitting me"); - - #printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -234,22 +332,132 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { - hp = hp - damage; - print("HP: " ~ hp ~ "/" ~ hp_max); - - if ( hp < 0 ) { - setprop("/carrier/sunk/",1); - setprop("/sim/multiplay/generic/int[0]",1); - } +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } }; +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { + var no = 7; + while (no > 6 or hp_f[no] < 0) { + no = int(rand()*7); + if (hp_f[no] < 0) { + if (rand() > 0.9) { + armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); + return; + } + } + } + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { + setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); + armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { + setprop("/carrier/disabled",1); + armament.defeatSpamFilter("Captain our offensive capability is crippled!"); + } + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { + setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + armament.defeatSpamFilter("S.O.S. Heeelp"); + } else { + armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } + } + } +} + var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); settimer(func {stopIncomingSound(clock);},3); @@ -259,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -274,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file +setlistener("/sim/signals/reinit", re_init, 0, 0); diff --git a/depot/Nasal/damage.nas b/depot/Nasal/damage.nas index 0642934..7560f8d 100644 --- a/depot/Nasal/damage.nas +++ b/depot/Nasal/damage.nas @@ -1,5 +1,5 @@ # -# Install: Include this code into an aircraft to make it damageable. (remember to add it to the -set file) +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) # # Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) # @@ -33,8 +33,8 @@ var hp = hp_max; var cannon_types = { # - # 0.20 means a direct hit will disable 20% of the failure modes in average. - # 0.20 also means a direct hit will do 20 hitpoints damage. + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. # " M70 rocket hit": 0.250, #135mm " S-5 rocket hit": 0.200, # 55mm @@ -64,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 3.00,# bomblet warhead. + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, diff --git a/fleet/Nasal/damage.nas b/fleet/Nasal/damage.nas index f9e8e4b..2fd0d6b 100644 --- a/fleet/Nasal/damage.nas +++ b/fleet/Nasal/damage.nas @@ -1,30 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m =20;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max = 200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 1; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp_max = 200; -var hp = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; - +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -35,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -49,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -67,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -77,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -94,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1 == 1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -172,61 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - var prob = 1; - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - var failed = fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); if (size(last_vector) < 4) { - # msg is either missing number of hits, or has no trailing dots from spam filter. - print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); - return; - } - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + # msg is either missing number of hits, or has no trailing dots from spam filter. + print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); + return; + } + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - var failed = fail_systems(probability); - #damaged_sys = damaged_sys + failed; - } - # that someone is me! - #print("hitting me"); - - #printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -235,44 +332,131 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } +}; + +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { var no = 7; - while (no > 6 or hp[no] < 0) { + while (no > 6 or hp_f[no] < 0) { no = int(rand()*7); - if (hp[no] < 0) { - if (rand() > 0.8) { + if (hp_f[no] < 0) { + if (rand() > 0.9) { armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); - hp[no] = hp[no] - damage; - print("HP["~no~"]: " ~ hp[no] ~ "/" ~ hp_max); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); return; } } } - hp[no] = hp[no] - damage; - print("HP["~no~"]: " ~ hp[no] ~ "/" ~ hp_max); - - if ( hp[no] < 0 ) { + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); - if (!getprop("/carrier/disabled") and hp[0]<0 and hp[1]<0 and hp[2]<0) { + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { setprop("/carrier/disabled",1); armament.defeatSpamFilter("Captain our offensive capability is crippled!"); } - if (hp[0]<0 and hp[1]<0 and hp[2]<0 and hp[3]<0 and hp[4]<0 and hp[5]<0 and hp[6]<0) { + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); armament.defeatSpamFilter("S.O.S. Heeelp"); } else { armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } } } - -}; +} var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); @@ -283,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -298,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file +setlistener("/sim/signals/reinit", re_init, 0, 0); diff --git a/gci-radar/Nasal/damage.nas b/gci-radar/Nasal/damage.nas index a4ea122..eb3714b 100644 --- a/gci-radar/Nasal/damage.nas +++ b/gci-radar/Nasal/damage.nas @@ -1,30 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m =10;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max = 80;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp = 80; -var hp_max = hp; - +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -35,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -49,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -67,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -77,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -85,6 +187,7 @@ var incoming_listener = func { var last_vector = split(":", last); var author = last_vector[0]; var callsign = getprop("sim/multiplay/callsign"); + callsign = size(callsign) < 8 ? callsign : left(callsign,7); if (size(last_vector) > 1 and author != callsign) { # not myself #print("not me"); @@ -93,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1 == 1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -171,56 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - var prob = 1; - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - var failed = fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + if (size(last_vector) < 4) { + # msg is either missing number of hits, or has no trailing dots from spam filter. + print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); + return; + } + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - var failed = fail_systems(probability); - #damaged_sys = damaged_sys + failed; - } - # that someone is me! - #print("hitting me"); - - #printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -229,22 +332,132 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { - hp = hp - damage; - print("HP: " ~ hp ~ "/" ~ hp_max); - - if ( hp < 0 ) { - setprop("/carrier/sunk/",1); - setprop("/sim/multiplay/generic/int[0]",1); - } +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } }; +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { + var no = 7; + while (no > 6 or hp_f[no] < 0) { + no = int(rand()*7); + if (hp_f[no] < 0) { + if (rand() > 0.9) { + armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); + return; + } + } + } + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { + setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); + armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { + setprop("/carrier/disabled",1); + armament.defeatSpamFilter("Captain our offensive capability is crippled!"); + } + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { + setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + armament.defeatSpamFilter("S.O.S. Heeelp"); + } else { + armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } + } + } +} + var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); settimer(func {stopIncomingSound(clock);},3); @@ -254,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -269,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file +setlistener("/sim/signals/reinit", re_init, 0, 0); diff --git a/gci-radar/gci-set.xml b/gci-radar/gci-set.xml index 5efe416..771ed49 100644 --- a/gci-radar/gci-set.xml +++ b/gci-radar/gci-set.xml @@ -102,6 +102,12 @@ false + + + + true + + Aircraft/gci-radar/Nasal/damage.nas diff --git a/groundtarget/Nasal/damage.nas b/groundtarget/Nasal/damage.nas index adb1e89..d91e582 100644 --- a/groundtarget/Nasal/damage.nas +++ b/groundtarget/Nasal/damage.nas @@ -1,29 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m = 2;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max = 5;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp = 5; -var hp_max = hp; +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -34,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -48,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -66,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -76,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -93,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1 == 1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -171,60 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - var prob = 1; - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); if (size(last_vector) < 4) { - # msg is either missing number of hits, or has no trailing dots from spam filter. - print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); - return; - } - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + # msg is either missing number of hits, or has no trailing dots from spam filter. + print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); + return; + } + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - fail_systems(probability); - } - # that someone is me! - #print("hitting me"); - - printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -233,22 +332,132 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { - hp = hp - damage; - print("HP: " ~ hp ~ "/" ~ hp_max); - - if ( hp < 0 ) { - setprop("/carrier/sunk/",1); - setprop("/sim/multiplay/generic/int[0]",1); - } +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } }; +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { + var no = 7; + while (no > 6 or hp_f[no] < 0) { + no = int(rand()*7); + if (hp_f[no] < 0) { + if (rand() > 0.9) { + armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); + return; + } + } + } + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { + setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); + armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { + setprop("/carrier/disabled",1); + armament.defeatSpamFilter("Captain our offensive capability is crippled!"); + } + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { + setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + armament.defeatSpamFilter("S.O.S. Heeelp"); + } else { + armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } + } + } +} + var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); settimer(func {stopIncomingSound(clock);},3); @@ -258,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -273,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file +setlistener("/sim/signals/reinit", re_init, 0, 0); diff --git a/groundtarget/truck-set.xml b/groundtarget/truck-set.xml index ab4de28..e6326c6 100644 --- a/groundtarget/truck-set.xml +++ b/groundtarget/truck-set.xml @@ -127,6 +127,12 @@ + + + + true + + diff --git a/missile-frigate/Nasal/damage.nas b/missile-frigate/Nasal/damage.nas index 7add889..124b433 100644 --- a/missile-frigate/Nasal/damage.nas +++ b/missile-frigate/Nasal/damage.nas @@ -1,29 +1,59 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } +# +# Install: Include this code into an aircraft to make it damagable. (remember to add it to the -set file) +# +# Authors: Nikolai V. Chr. and Pinto (with improvement by Onox) +# +# + + +############################ Config ###################################################################################### +var full_damage_dist_m =20;# Can vary from aircraft to aircraft depending on how many failure modes it has. + # Many modes (like Viggen) ought to have lower number like zero. + # Few modes (like F-14) ought to have larger number such as 3. + # For assets this should be average radius of the asset. +var use_hitpoints_instead_of_failure_modes_bool = 1;# mainly used by assets that don't have failure modes. +var hp_max =200;# given a direct hit, how much pounds of warhead is needed to kill. Only used if hitpoints is enabled. +var hitable_by_air_munitions = 0; # if anti-air can do damage +var hitable_by_cannon = 1; # if cannon can do damage +var hitable_by_ground_munitions = 1;# if anti-ground/marine can do damage +var is_fleet = 0; # Is really 7 ships, 3 of which has offensive missiles. +########################################################################################################################## + +## +## TODO: +## Include mechanics for hitting fleet +## Move all to emesary +## + var TRUE = 1; var FALSE = 0; -var hp = 200; -var hp_max = hp; +var hp = hp_max; var cannon_types = { - " M70 rocket hit": 0.25, #135mm - " M55 cannon shell hit": 0.10, # 30mm - " KCA cannon shell hit": 0.10, # 30mm - " Gun Splash On ": 0.10, # 30mm - " M61A1 shell hit": 0.05, # 20mm - " GAU-8/A hit": 0.10, # 30mm - " BK27 cannon hit": 0.07, # 27mm - " GSh-30 hit": 0.10, # 30mm - " GSh-23 hit": 0.065,# 23mm - " 7.62 hit": 0.005,# 7.62mm - " 50 BMG hit": 0.015,# 12.7mm - " S-5 rocket hit": 0.20, #55mm -}; - - - + # + # 0.20 means a direct hit will disable 20% of the failure modes on average. + # or, 0.20 also means a direct hit can do 20 hitpoints damage. + # + " M70 rocket hit": 0.250, #135mm + " S-5 rocket hit": 0.200, # 55mm + " M55 cannon shell hit": 0.100, # 30mm + " KCA cannon shell hit": 0.100, # 30mm + " Gun Splash On ": 0.100, # 30mm + " GSh-30 hit": 0.100, # 30mm + " GAU-8/A hit": 0.100, # 30mm + " BK27 cannon hit": 0.070, # 27mm + " GSh-23 hit": 0.065, # 23mm + " M61A1 shell hit": 0.050, # 20mm + " 50 BMG hit": 0.015, # 12.7mm (non-explosive) + " 7.62 hit": 0.005, # 7.62mm (non-explosive) +}; + +# lbs of warheads is explosive+fragmentation+fuse, so total warhead mass. + var warhead_lbs = { + # Anti-ground/marine warheads (sorted alphabetically) "AGM-65": 126.00, "AGM-84": 488.00, "AGM-88": 146.00, @@ -34,7 +64,7 @@ var warhead_lbs = { "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, "AS30L": 529.00, - "CBU-87": 128.00, + "CBU-87": 10.00,# bomblet warhead. Mix of armour piecing and HE. "Exocet": 364.00, "FAB-100": 92.59, "FAB-250": 202.85, @@ -48,12 +78,10 @@ var warhead_lbs = { "KAB-500": 564.38, "Kh-25MP": 197.53, "Kh-66": 244.71, - "KN-06": 315.00, "LAU-68": 10.00, - "M317": 145.00, "M71": 200.00, "M71R": 200.00, - "M90": 500.00, + "M90": 3.00,# bomblet warhead. "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, @@ -66,8 +94,7 @@ var warhead_lbs = { "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, "S-21": 245.00, - "S-24": 271.00, - "S530D": 66.00, + "S-24": 271.00, "SCALP": 992.00, "Sea Eagle": 505.00, "SeaEagle": 505.00, @@ -76,6 +103,82 @@ var warhead_lbs = { "ZB-500": 473.99, }; +var warhead_air_lbs = { + # Anti-air warheads (sorted alphabetically) + "aim-120": 44.00, + "AIM-120": 44.00, + "AIM-54": 135.00, + "aim-7": 88.00, + "AIM-7": 88.00, + "aim-9": 20.80, + "AIM-9": 20.80, + "AIM120": 44.00, + "AIM132": 22.05, + "AIM9": 20.80, + "KN-06": 315.00, + "M317": 145.00, + "Magic-2": 27.00, + "Matra MICA": 30.00, + "Matra R550 Magic 2": 27.00, + "MATRA-R530": 55.00, + "MatraMica": 30.00, + "MatraMicaIR": 30.00, + "MatraR550Magic2": 27.00, + "Meteor": 55.00, + "MICA-EM": 30.00, + "MICA-IR": 30.00, + "R-13M": 16.31, + "R-27R1": 85.98, + "R-27T1": 85.98, + "R-3R": 16.31, + "R-3S": 16.31, + "R-55": 20.06, + "R-60": 6.60, + "R-60M": 7.70, + "R-73E": 16.31, + "R-77": 49.60, + "R74": 16.00, + "RB-05A": 353.00, + "RB-24": 20.80, + "RB-24J": 20.80, + "RB-71": 88.00, + "RB-74": 20.80, + "RB-99": 44.00, + "S530D": 66.00, +}; + +var cluster = { + # cluster munition list + "M90": nil, + "CBU-87": nil, +}; + +var fireMsgs = { + + # F14 + " FOX3 at": nil, # radar + " FOX2 at": nil, # heat + " FOX1 at": nil, # semi-radar + + # Generic + " Fox 1 at": nil, # semi-radar + " Fox 2 at": nil, # heat + " Fox 3 at": nil, # radar + " Greyhound at": nil, # cruise missile + " Bombs away at": nil, # bombs + " Bruiser at": nil, # anti-ship + " Rifle at": nil, # TV guided + " Sniper at": nil, # anti-radiation + + # SAM, fleet and missile frigate + " Bird away at": nil, # G/A + + # F15 + " aim7 at": nil, + " aim9 at": nil, + " aim120 at": nil, +}; + var incoming_listener = func { var history = getprop("/sim/multiplay/chat-history"); var hist_vector = split("\n", history); @@ -93,67 +196,10 @@ var incoming_listener = func { # a m2000 is firing at us m2000 = TRUE; } - if (last_vector[1] == " FOX2 at" or last_vector[1] == " aim7 at" or last_vector[1] == " aim9 at" - or last_vector[1] == " aim120 at" or last_vector[1] == " RB-24J fired at" or last_vector[1] == " RB-74 fired at" - or last_vector[1] == " RB-71 fired at" or last_vector[1] == " RB-15F fired at" - or last_vector[1] == " RB-99 fired at" or m2000 == TRUE) { + if (contains(fireMsgs, last_vector[1]) or m2000 == TRUE) { # air2air being fired - if (size(last_vector) > 2 or m2000 == TRUE) { - #print("Missile launch detected at"~last_vector[2]~" from "~author); - if (m2000 == TRUE or last_vector[2] == " "~callsign) { - # its being fired at me - #print("Incoming!"); - var enemy = getCallsign(author); - if (enemy != nil) { - #print("enemy identified"); - var bearingNode = enemy.getNode("radar/bearing-deg"); - if (bearingNode != nil) { - #print("bearing to enemy found"); - var bearing = bearingNode.getValue(); - var heading = getprop("orientation/heading-deg"); - var clock = bearing - heading; - while(clock < 0) { - clock = clock + 360; - } - while(clock > 360) { - clock = clock - 360; - } - #print("incoming from "~clock); - if (clock >= 345 or clock < 15) { - playIncomingSound("12"); - } elsif (clock >= 15 and clock < 45) { - playIncomingSound("1"); - } elsif (clock >= 45 and clock < 75) { - playIncomingSound("2"); - } elsif (clock >= 75 and clock < 105) { - playIncomingSound("3"); - } elsif (clock >= 105 and clock < 135) { - playIncomingSound("4"); - } elsif (clock >= 135 and clock < 165) { - playIncomingSound("5"); - } elsif (clock >= 165 and clock < 195) { - playIncomingSound("6"); - } elsif (clock >= 195 and clock < 225) { - playIncomingSound("7"); - } elsif (clock >= 225 and clock < 255) { - playIncomingSound("8"); - } elsif (clock >= 255 and clock < 285) { - playIncomingSound("9"); - } elsif (clock >= 285 and clock < 315) { - playIncomingSound("10"); - } elsif (clock >= 315 and clock < 345) { - playIncomingSound("11"); - } else { - playIncomingSound(""); - } - return; - } - } - } - } - } elsif (1 == 1) { # mirage: getprop("/controls/armament/mp-messaging") - # latest version of failure manager and taking damage enabled - #print("damage enabled"); + warn(last_vector,m2000,callsign,author); + } elsif (getprop("payload/armament/msg")) { var last1 = split(" ", last_vector[1]); if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { #print("missile hitting someone"); @@ -171,61 +217,113 @@ var incoming_listener = func { if(distance != nil) { var dist = distance; - #check distance w/ if statement here + if (contains(cluster, type)) { + # cluster munition + var lbs = warhead_lbs[type]; + var maxDist = maxDamageDistFromWarhead(lbs); + var distance = math.max(0,rand()*2-full_damage_dist_m);#being 0 to 2 meters from a bomblet on average. + var diff = maxDist-distance; + diff = diff * diff; + var probability = diff / (maxDist*maxDist); + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; + } + var failed = fail_systems(probability, hp_max); + var percent = 100 * prob; + printf("Took %.1f%% damage from %s clusterbomb at %0.1f meters from bomblet. %s systems was hit", percent,type,distance,failed); + nearby_explosion(); + return; + } + + distance = math.max(distance-full_damage_dist_m, 0); - if (contains(warhead_lbs, type)) { - #maxDist = maxDamageDistFromWarhead(warhead_lbs[type]); + var maxDist = 0;# distance where the explosion dont hurt us anymore + var lbs = 0; + + if (hitable_by_ground_munitions and contains(warhead_lbs, type)) { + lbs = warhead_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs);#3*sqrt(lbs) + } elsif (hitable_by_air_munitions and contains(warhead_air_lbs, type)) { + lbs = warhead_air_lbs[type]; + maxDist = maxDamageDistFromWarhead(lbs); } else { return; } - - var prob = 1; - if (type == "M90" and distance < 300) { - var failed = fail_systems(warhead_lbs[type]/2); - return; - } elsif (distance < 150) { - if ( distance > 50 ) { - distance = distance - 50; - prob = 1 - (distance/100); - } + + var diff = maxDist-distance; + if (diff < 0) { + diff = 0; + } + diff = diff * diff; + + var probability = diff / (maxDist*maxDist); + + if (use_hitpoints_instead_of_failure_modes_bool) { + var hpDist = maxDamageDistFromWarhead(hp_max); + probability = (maxDist/hpDist)*probability; } - - var failed = fail_systems(warhead_lbs[type] * prob); - #ar percent = 100 * probability; - #printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + var failed = fail_systems(probability, hp_max); + var percent = 100 * probability; + printf("Took %.1f%% damage from %s missile at %0.1f meters. %s systems was hit", percent,type,dist,failed); + nearby_explosion(); + + #### + # I don't remember all the considerations that went into our original warhead damage model. + # But looking at the formula it looks like they all do 100% damage at 0 meter hit, + # and warhead size is only used to determine the decrease of damage with distance increase. + # It sorta gets the job done though, so I am hesitant to advocate that warheads above a certain + # size should give 100% damage for some distance, and that warheads smaller than certain size should + # not give 100% damage even on direct hit. + # Anyway, for hitpoint based assets, this is now the case. Maybe we should consider to also do something + # similar for failure mode based aircraft. ~Nikolai + #### + + ## example 1: ## + # 300 lbs warhead, 50 meters distance + # maxDist=52 + # diff = 52-50 = 2 + # diff^2 = 4 + # prob = 4/2700 = 0.15% + + ## example 2: ## + # 300 lbs warhead, 25 meters distance + # maxDist=52 + # diff = 52-25 = 27 + # diff^2 = 729 + # prob = 729/2700 = 27% } } - } elsif (cannon_types[last_vector[1]] != nil) { - # cannon hitting someone - print("cannon"); + } elsif (hitable_by_cannon and cannon_types[last_vector[1]] != nil) { if (size(last_vector) > 2 and last_vector[2] == " "~callsign) { - print("cannon hit us"); if (size(last_vector) < 4) { - # msg is either missing number of hits, or has no trailing dots from spam filter. - print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); - return; - } - var last3 = split(" ", last_vector[3]); - #print("last3[2]: " ~ last3[2]); - #print("last3[1]: " ~ last3[1]); - if(size(last3) > 2) { - if ( last3[2] == "hits" ) { - var hit_count = num(last3[1]); + # msg is either missing number of hits, or has no trailing dots from spam filter. + print('"'~last~'" is not a legal hit message, tell the shooter to upgrade his OPRF plane :)'); + return; + } + var last3 = split(" ", last_vector[3]); + if(size(last3) > 2 and size(last3[2]) > 2 and last3[2] == "hits" ) { + var probability = cannon_types[last_vector[1]]; + var hit_count = num(last3[1]); + if (hit_count != nil) { + var damaged_sys = 0; + for (var i = 1; i <= hit_count; i = i + 1) { + var failed = fail_systems(probability); + damaged_sys = damaged_sys + failed; + } + + printf("Took %.1f%% x %2d damage from cannon! %s systems was hit.", probability*100, hit_count, damaged_sys); + nearby_explosion(); } } else { - var hit_count = 4; + var probability = cannon_types[last_vector[1]]; + #print("probability: " ~ probability); + + var failed = fail_systems(probability * 3);# Old messages is assumed to be 3 hits + printf("Took %.1f%% x 3 damage from cannon! %s systems was hit.", probability*100, failed); + nearby_explosion(); } - var damaged_sys = 0; - var probability = cannon_types[last_vector[1]]; - for (var i = 1; i <= hit_count; i = i + 1) { - var failed = fail_systems(probability); - #damaged_sys = damaged_sys + failed; - } - # that someone is me! - #print("hitting me"); - - #printf("Took %.1f%% damage from cannon! %s systems was hit.", probability*hit_count*100, damaged_sys); } } } @@ -234,22 +332,132 @@ var incoming_listener = func { } var maxDamageDistFromWarhead = func (lbs) { - # very simple - var dist = 7*math.sqrt(lbs); - + # Calc at what distance the warhead will do zero damage every time. + var dist = 3*math.sqrt(lbs); return dist; } -var fail_systems = func (damage) { - hp = hp - damage; - print("HP: " ~ hp ~ "/" ~ hp_max); - - if ( hp < 0 ) { - setprop("/sim/multiplay/generic/int[2]",1); - setprop("/carrier/sunk/",1); - } +var fail_systems = func (probability, factor = 100) {#this factor needs tuning after all asset hitpoints have been evaluated. + if (fleet) { + return fail_fleet_systems(probability, factor); + } elsif (use_hitpoints_instead_of_failure_modes_bool) { + hp -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP: %d/%d", hp, hp_max); + setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp < 0 ) { + setprop("/carrier/sunk/",1);#we are dead + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/multiplay/generic/int[0]",1);#smoke on + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + } + return -1; + } else { + var failure_modes = FailureMgr._failmgr.failure_modes; + var mode_list = keys(failure_modes); + var failed = 0; + foreach(var failure_mode_id; mode_list) { + if (rand() < probability) { + FailureMgr.set_failure_level(failure_mode_id, 1); + failed += 1; + } + } + return failed; + } }; +hp_f = [hp_max,hp_max,hp_max,hp_max,hp_max,hp_max,hp_max]; + +var fail_fleet_systems = func (probability, factor) { + var no = 7; + while (no > 6 or hp_f[no] < 0) { + no = int(rand()*7); + if (hp_f[no] < 0) { + if (rand() > 0.9) { + armament.defeatSpamFilter("You shot one of our already sinking ships, you are just mean."); + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + print("HP["~no~"]: " ~ hp_f[no] ~ "/" ~ hp_max); + return; + } + } + } + hp_f[no] -= factor * probability*(0.75+rand()*0.25);# from 75 to 100% damage + printf("HP[%d]: %d/%d", no, hp_f[no], hp_max); + #setprop("sam/damage", math.max(0,100*hp/hp_max));#used in HUD + if ( hp_f[no] < 0 ) { + setprop("/sim/multiplay/generic/bool["~(no+40)~"]",1); + armament.defeatSpamFilter("So you sank one of our ships, we will get you for that!"); + if (!getprop("/carrier/disabled") and hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0) { + setprop("/carrier/disabled",1); + armament.defeatSpamFilter("Captain our offensive capability is crippled!"); + } + if (hp_f[0]<0 and hp_f[1]<0 and hp_f[2]<0 and hp_f[3]<0 and hp_f[4]<0 and hp_f[5]<0 and hp_f[6]<0) { + setprop("/carrier/sunk",1); + setprop("/sim/multiplay/generic/int[2]",1);#radar off + setprop("/sim/messages/copilot", getprop("sim/multiplay/callsign")~" dead."); + armament.defeatSpamFilter("S.O.S. Heeelp"); + } else { + armament.defeatSpamFilter("This is not over yet.."); + } + } + return -1; +}; + +var warn = func (last_vector,m2000,callsign,author) { + if (size(last_vector) > 2 or m2000 == TRUE) { + #print("Missile launch detected at"~last_vector[2]~" from "~author); + if (m2000 == TRUE or last_vector[2] == " "~callsign) { + # its being fired at me + #print("Incoming!"); + var enemy = getCallsign(author); + if (enemy != nil) { + #print("enemy identified"); + var bearingNode = enemy.getNode("radar/bearing-deg"); + if (bearingNode != nil) { + #print("bearing to enemy found"); + var bearing = bearingNode.getValue(); + var heading = getprop("orientation/heading-deg"); + var clock = bearing - heading; + while(clock < 0) { + clock = clock + 360; + } + while(clock > 360) { + clock = clock - 360; + } + #print("incoming from "~clock); + if (clock >= 345 or clock < 15) { + playIncomingSound("12"); + } elsif (clock >= 15 and clock < 45) { + playIncomingSound("1"); + } elsif (clock >= 45 and clock < 75) { + playIncomingSound("2"); + } elsif (clock >= 75 and clock < 105) { + playIncomingSound("3"); + } elsif (clock >= 105 and clock < 135) { + playIncomingSound("4"); + } elsif (clock >= 135 and clock < 165) { + playIncomingSound("5"); + } elsif (clock >= 165 and clock < 195) { + playIncomingSound("6"); + } elsif (clock >= 195 and clock < 225) { + playIncomingSound("7"); + } elsif (clock >= 225 and clock < 255) { + playIncomingSound("8"); + } elsif (clock >= 255 and clock < 285) { + playIncomingSound("9"); + } elsif (clock >= 285 and clock < 315) { + playIncomingSound("10"); + } elsif (clock >= 315 and clock < 345) { + playIncomingSound("11"); + } else { + playIncomingSound(""); + } + return; + } + } + } + } +} + var playIncomingSound = func (clock) { setprop("sound/incoming"~clock, 1); settimer(func {stopIncomingSound(clock);},3); @@ -259,6 +467,20 @@ var stopIncomingSound = func (clock) { setprop("sound/incoming"~clock, 0); } +var nearby_explosion = func { + setprop("damage/sounds/nearby-explode-on", 0); + settimer(nearby_explosion_a, 0); +} + +var nearby_explosion_a = func { + setprop("damage/sounds/nearby-explode-on", 1); + settimer(nearby_explosion_b, 0.5); +} + +var nearby_explosion_b = func { + setprop("damage/sounds/nearby-explode-on", 0); +} + var callsign_struct = {}; var getCallsign = func (callsign) { var node = callsign_struct[callsign]; @@ -274,20 +496,25 @@ var processCallsigns = func () { callsign_struct[callsign] = player; } } - settimer(processCallsigns, 1.5); + settimer(processCallsigns, 5); } processCallsigns(); -var logTime = func{ - #log time and date for outputing ucsv files for converting into KML files for google earth. - if (getprop("logging/log[0]/enabled") == TRUE and getprop("sim/time/utc/year") != nil) { - var date = getprop("sim/time/utc/year")~"/"~getprop("sim/time/utc/month")~"/"~getprop("sim/time/utc/day"); - var time = getprop("sim/time/utc/hour")~":"~getprop("sim/time/utc/minute")~":"~getprop("sim/time/utc/second"); +setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); - setprop("logging/date-log", date); - setprop("logging/time-log", time); +# prevent flooding the pilots screen with failure modes that fail when getting hit. +setprop("/sim/failure-manager/display-on-screen", FALSE); + +var re_init = func { + # repair the aircraft at relocation to another airport. + + 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); } } -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file +setlistener("/sim/signals/reinit", re_init, 0, 0);