@jtff/miztemplate-lib 3.1.6 → 3.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- env.info('*** MOOSE GITHUB Commit Hash ID: 2023-11-14T11:57:58+01:00-1b8c9367a3b7c7c46eac1c19433a16d32d43b746 ***')
1
+ env.info('*** MOOSE GITHUB Commit Hash ID: 2023-12-10T14:37:41+01:00-c089e56060974539e58a346665f7536a2898c4cf ***')
2
2
  env.info('*** MOOSE STATIC INCLUDE START *** ')
3
3
  ENUMS={}
4
4
  env.setErrorMessageBoxEnabled(false)
@@ -416,6 +416,12 @@ Reaper="MQ-9",
416
416
  Predator="MQ-1A",
417
417
  }
418
418
  }
419
+ ENUMS.Link16Power={
420
+ none=0,
421
+ low=1,
422
+ medium=2,
423
+ high=3,
424
+ }
419
425
  ENUMS.Storage={
420
426
  weapons={
421
427
  missiles={},
@@ -1277,21 +1283,33 @@ end
1277
1283
  end
1278
1284
  end
1279
1285
  function UTILS.PrintTableToLog(table,indent)
1286
+ local text="\n"
1280
1287
  if not table then
1281
- BASE:E("No table passed!")
1282
- return
1288
+ env.warning("No table passed!")
1289
+ return nil
1283
1290
  end
1284
1291
  if not indent then indent=0 end
1285
1292
  for k,v in pairs(table)do
1293
+ if string.find(k," ")then k='"'..k..'"'end
1286
1294
  if type(v)=="table"then
1287
- BASE:I(string.rep(" ",indent)..tostring(k).." = {")
1288
- UTILS.PrintTableToLog(v,indent+1)
1289
- BASE:I(string.rep(" ",indent).."}")
1295
+ env.info(string.rep(" ",indent)..tostring(k).." = {")
1296
+ text=text..string.rep(" ",indent)..tostring(k).." = {\n"
1297
+ text=text..tostring(UTILS.PrintTableToLog(v,indent+1)).."\n"
1298
+ env.info(string.rep(" ",indent).."},")
1299
+ text=text..string.rep(" ",indent).."},\n"
1300
+ else
1301
+ local value
1302
+ if tostring(v)=="true"or tostring(v)=="false"or tonumber(v)~=nil then
1303
+ value=v
1290
1304
  else
1291
- BASE:I(string.rep(" ",indent)..tostring(k).." = "..tostring(v))
1305
+ value='"'..tostring(v)..'"'
1292
1306
  end
1307
+ env.info(string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n")
1308
+ text=text..string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n"
1293
1309
  end
1294
1310
  end
1311
+ return text
1312
+ end
1295
1313
  function UTILS.TableShow(tbl,loc,indent,tableshow_tbls)
1296
1314
  tableshow_tbls=tableshow_tbls or{}
1297
1315
  loc=loc or""
@@ -1817,9 +1835,15 @@ end
1817
1835
  function UTILS.VecSubstract(a,b)
1818
1836
  return{x=a.x-b.x,y=a.y-b.y,z=a.z-b.z}
1819
1837
  end
1838
+ function UTILS.VecSubtract(a,b)
1839
+ return UTILS.VecSubstract(a,b)
1840
+ end
1820
1841
  function UTILS.Vec2Substract(a,b)
1821
1842
  return{x=a.x-b.x,y=a.y-b.y}
1822
1843
  end
1844
+ function UTILS.Vec2Subtract(a,b)
1845
+ return UTILS.Vec2Substract(a,b)
1846
+ end
1823
1847
  function UTILS.VecAdd(a,b)
1824
1848
  return{x=a.x+b.x,y=a.y+b.y,z=a.z+b.z}
1825
1849
  end
@@ -2479,7 +2503,7 @@ filename=path.."\\"..filename
2479
2503
  end
2480
2504
  local exists=UTILS.CheckFileExists(Path,Filename)
2481
2505
  if not exists then
2482
- BASE:E(string.format("ERROR: File %s does not exist!",filename))
2506
+ BASE:I(string.format("ERROR: File %s does not exist!",filename))
2483
2507
  return false
2484
2508
  end
2485
2509
  local file=assert(io.open(filename,"rb"))
@@ -2991,6 +3015,300 @@ point_four:LineToAll(point_three,coalition,color,alpha,lineType)
2991
3015
  circle_center_fix_four:CircleToAll(UTILS.NMToMeters(turn_radius),coalition,color,alpha,nil,0,lineType)
2992
3016
  circle_center_two_three:CircleToAll(UTILS.NMToMeters(turn_radius),coalition,color,alpha,nil,0,lineType)
2993
3017
  end
3018
+ function UTILS.TimeNow()
3019
+ return UTILS.SecondsToClock(timer.getAbsTime(),false,false)
3020
+ end
3021
+ function UTILS.TimeDifferenceInSeconds(start_time,end_time)
3022
+ return UTILS.ClockToSeconds(end_time)-UTILS.ClockToSeconds(start_time)
3023
+ end
3024
+ function UTILS.TimeLaterThan(time_string)
3025
+ if timer.getAbsTime()>UTILS.ClockToSeconds(time_string)then
3026
+ return true
3027
+ end
3028
+ return false
3029
+ end
3030
+ function UTILS.TimeBefore(time_string)
3031
+ if timer.getAbsTime()<UTILS.ClockToSeconds(time_string)then
3032
+ return true
3033
+ end
3034
+ return false
3035
+ end
3036
+ function UTILS.CombineTimeStrings(time_string_01,time_string_02)
3037
+ local hours1,minutes1,seconds1=time_string_01:match("(%d+):(%d+):(%d+)")
3038
+ local hours2,minutes2,seconds2=time_string_02:match("(%d+):(%d+):(%d+)")
3039
+ local total_seconds=tonumber(seconds1)+tonumber(seconds2)+tonumber(minutes1)*60+tonumber(minutes2)*60+tonumber(hours1)*3600+tonumber(hours2)*3600
3040
+ total_seconds=total_seconds%(24*3600)
3041
+ if total_seconds<0 then
3042
+ total_seconds=total_seconds+24*3600
3043
+ end
3044
+ local hours=math.floor(total_seconds/3600)
3045
+ total_seconds=total_seconds-hours*3600
3046
+ local minutes=math.floor(total_seconds/60)
3047
+ local seconds=total_seconds%60
3048
+ return string.format("%02d:%02d:%02d",hours,minutes,seconds)
3049
+ end
3050
+ function UTILS.SubtractTimeStrings(time_string_01,time_string_02)
3051
+ local hours1,minutes1,seconds1=time_string_01:match("(%d+):(%d+):(%d+)")
3052
+ local hours2,minutes2,seconds2=time_string_02:match("(%d+):(%d+):(%d+)")
3053
+ local total_seconds=tonumber(seconds1)-tonumber(seconds2)+tonumber(minutes1)*60-tonumber(minutes2)*60+tonumber(hours1)*3600-tonumber(hours2)*3600
3054
+ total_seconds=total_seconds%(24*3600)
3055
+ if total_seconds<0 then
3056
+ total_seconds=total_seconds+24*3600
3057
+ end
3058
+ local hours=math.floor(total_seconds/3600)
3059
+ total_seconds=total_seconds-hours*3600
3060
+ local minutes=math.floor(total_seconds/60)
3061
+ local seconds=total_seconds%60
3062
+ return string.format("%02d:%02d:%02d",hours,minutes,seconds)
3063
+ end
3064
+ function UTILS.TimeBetween(start_time,end_time)
3065
+ return UTILS.TimeLaterThan(start_time)and UTILS.TimeBefore(end_time)
3066
+ end
3067
+ function UTILS.PercentageChance(chance)
3068
+ chance=chance or math.random(0,100)
3069
+ chance=UTILS.Clamp(chance,0,100)
3070
+ local percentage=math.random(0,100)
3071
+ if percentage<chance then
3072
+ return true
3073
+ end
3074
+ return false
3075
+ end
3076
+ function UTILS.Clamp(value,min,max)
3077
+ if value<min then value=min end
3078
+ if value>max then value=max end
3079
+ return value
3080
+ end
3081
+ function UTILS.ClampAngle(value)
3082
+ if value>360 then return value-360 end
3083
+ if value<0 then return value+360 end
3084
+ return value
3085
+ end
3086
+ function UTILS.RemapValue(value,old_min,old_max,new_min,new_max)
3087
+ new_min=new_min or 0
3088
+ new_max=new_max or 100
3089
+ local old_range=old_max-old_min
3090
+ local new_range=new_max-new_min
3091
+ local percentage=(value-old_min)/old_range
3092
+ return(new_range*percentage)+new_min
3093
+ end
3094
+ function UTILS.RandomPointInTriangle(pt1,pt2,pt3)
3095
+ local pt={math.random(),math.random()}
3096
+ table.sort(pt)
3097
+ local s=pt[1]
3098
+ local t=pt[2]-pt[1]
3099
+ local u=1-pt[2]
3100
+ return{x=s*pt1.x+t*pt2.x+u*pt3.x,
3101
+ y=s*pt1.y+t*pt2.y+u*pt3.y}
3102
+ end
3103
+ function UTILS.AngleBetween(angle,min,max)
3104
+ angle=(360+(angle%360))%360
3105
+ min=(360+min%360)%360
3106
+ max=(360+max%360)%360
3107
+ if min<max then return min<=angle and angle<=max end
3108
+ return min<=angle or angle<=max
3109
+ end
3110
+ function UTILS.WriteJSON(data,file_path)
3111
+ package.path=package.path..";.\\Scripts\\?.lua"
3112
+ local JSON=require("json")
3113
+ local pretty_json_text=JSON:encode_pretty(data)
3114
+ local write_file=io.open(file_path,"w")
3115
+ write_file:write(pretty_json_text)
3116
+ write_file:close()
3117
+ end
3118
+ function UTILS.ReadJSON(file_path)
3119
+ package.path=package.path..";.\\Scripts\\?.lua"
3120
+ local JSON=require("json")
3121
+ local read_file=io.open(file_path,"r")
3122
+ local contents=read_file:read("*a")
3123
+ io.close(read_file)
3124
+ return JSON:decode(contents)
3125
+ end
3126
+ function UTILS.GetZoneProperties(zone_name)
3127
+ local return_table={}
3128
+ for _,zone in pairs(env.mission.triggers.zones)do
3129
+ if zone["name"]==zone_name then
3130
+ if table.length(zone["properties"])>0 then
3131
+ for _,property in pairs(zone["properties"])do
3132
+ return_table[property["key"]]=property["value"]
3133
+ end
3134
+ return return_table
3135
+ else
3136
+ BASE:I(string.format("%s doesn't have any properties",zone_name))
3137
+ return{}
3138
+ end
3139
+ end
3140
+ end
3141
+ end
3142
+ function UTILS.RotatePointAroundPivot(point,pivot,angle)
3143
+ local radians=math.rad(angle)
3144
+ local x=point.x-pivot.x
3145
+ local y=point.y-pivot.y
3146
+ local rotated_x=x*math.cos(radians)-y*math.sin(radians)
3147
+ local rotatex_y=x*math.sin(radians)+y*math.cos(radians)
3148
+ local original_x=rotated_x+pivot.x
3149
+ local original_y=rotatex_y+pivot.y
3150
+ return{x=original_x,y=original_y}
3151
+ end
3152
+ function UTILS.UniqueName(base)
3153
+ base=base or""
3154
+ local ran=tostring(math.random(0,1000000))
3155
+ if base==""then
3156
+ return ran
3157
+ end
3158
+ return base.."_"..ran
3159
+ end
3160
+ function string.startswith(str,value)
3161
+ return string.sub(str,1,string.len(value))==value
3162
+ end
3163
+ function string.endswith(str,value)
3164
+ return value==""or str:sub(-#value)==value
3165
+ end
3166
+ function string.split(input,separator)
3167
+ local parts={}
3168
+ for part in input:gmatch("[^"..separator.."]+")do
3169
+ table.insert(parts,part)
3170
+ end
3171
+ return parts
3172
+ end
3173
+ function string.contains(str,value)
3174
+ return string.match(str,value)
3175
+ end
3176
+ function table.contains(tbl,element)
3177
+ if element==nil or tbl==nil then return false end
3178
+ local index=1
3179
+ while tbl[index]do
3180
+ if tbl[index]==element then
3181
+ return true
3182
+ end
3183
+ index=index+1
3184
+ end
3185
+ return false
3186
+ end
3187
+ function table.contains_key(tbl,key)
3188
+ if tbl[key]~=nil then return true else return false end
3189
+ end
3190
+ function table.insert_unique(tbl,element)
3191
+ if element==nil or tbl==nil then return end
3192
+ if not table.contains(tbl,element)then
3193
+ table.insert(tbl,element)
3194
+ end
3195
+ end
3196
+ function table.remove_by_value(tbl,element)
3197
+ local indices_to_remove={}
3198
+ local index=1
3199
+ for _,value in pairs(tbl)do
3200
+ if value==element then
3201
+ table.insert(indices_to_remove,index)
3202
+ end
3203
+ index=index+1
3204
+ end
3205
+ for _,idx in pairs(indices_to_remove)do
3206
+ table.remove(tbl,idx)
3207
+ end
3208
+ end
3209
+ function table.remove_key(table,key)
3210
+ local element=table[key]
3211
+ table[key]=nil
3212
+ return element
3213
+ end
3214
+ function table.index_of(table,element)
3215
+ for i,v in ipairs(table)do
3216
+ if v==element then
3217
+ return i
3218
+ end
3219
+ end
3220
+ return nil
3221
+ end
3222
+ function table.length(T)
3223
+ local count=0
3224
+ for _ in pairs(T)do count=count+1 end
3225
+ return count
3226
+ end
3227
+ function table.slice(tbl,first,last)
3228
+ local sliced={}
3229
+ local start=first or 1
3230
+ local stop=last or table.length(tbl)
3231
+ local count=1
3232
+ for key,value in pairs(tbl)do
3233
+ if count>=start and count<=stop then
3234
+ sliced[key]=value
3235
+ end
3236
+ count=count+1
3237
+ end
3238
+ return sliced
3239
+ end
3240
+ function table.count_value(tbl,value)
3241
+ local count=0
3242
+ for _,item in pairs(tbl)do
3243
+ if item==value then count=count+1 end
3244
+ end
3245
+ return count
3246
+ end
3247
+ function table.combine(t1,t2)
3248
+ if t1==nil and t2==nil then
3249
+ BASE:E("Both tables were empty!")
3250
+ end
3251
+ if t1==nil then return t2 end
3252
+ if t2==nil then return t1 end
3253
+ for i=1,#t2 do
3254
+ t1[#t1+1]=t2[i]
3255
+ end
3256
+ return t1
3257
+ end
3258
+ function table.merge(t1,t2)
3259
+ for k,v in pairs(t2)do
3260
+ if(type(v)=="table")and(type(t1[k]or false)=="table")then
3261
+ table.merge(t1[k],t2[k])
3262
+ else
3263
+ t1[k]=v
3264
+ end
3265
+ end
3266
+ return t1
3267
+ end
3268
+ function table.add(tbl,item)
3269
+ tbl[#tbl+1]=item
3270
+ end
3271
+ function table.shuffle(tbl)
3272
+ local new_table={}
3273
+ for _,value in ipairs(tbl)do
3274
+ local pos=math.random(1,#new_table+1)
3275
+ table.insert(new_table,pos,value)
3276
+ end
3277
+ return new_table
3278
+ end
3279
+ function table.find_key_value_pair(tbl,key,value)
3280
+ for k,v in pairs(tbl)do
3281
+ if type(v)=="table"then
3282
+ local result=table.find_key_value_pair(v,key,value)
3283
+ if result~=nil then
3284
+ return result
3285
+ end
3286
+ elseif k==key and v==value then
3287
+ return tbl
3288
+ end
3289
+ end
3290
+ return nil
3291
+ end
3292
+ function UTILS.DecimalToOctal(Number)
3293
+ if Number<8 then return Number end
3294
+ local number=tonumber(Number)
3295
+ local octal=""
3296
+ local n=1
3297
+ while number>7 do
3298
+ local number1=number%8
3299
+ octal=string.format("%d",number1)..octal
3300
+ local number2=math.abs(number/8)
3301
+ if number2<8 then
3302
+ octal=string.format("%d",number2)..octal
3303
+ end
3304
+ number=number2
3305
+ n=n+1
3306
+ end
3307
+ return tonumber(octal)
3308
+ end
3309
+ function UTILS.OctalToDecimal(Number)
3310
+ return tonumber(Number,8)
3311
+ end
2994
3312
  PROFILER={
2995
3313
  ClassName="PROFILER",
2996
3314
  Counters={},
@@ -5888,6 +6206,14 @@ UnitDeleteTask=world.event.S_EVENT_UNIT_DELETE_TASK or-1,
5888
6206
  SimulationStart=world.event.S_EVENT_SIMULATION_START or-1,
5889
6207
  WeaponRearm=world.event.S_EVENT_WEAPON_REARM or-1,
5890
6208
  WeaponDrop=world.event.S_EVENT_WEAPON_DROP or-1,
6209
+ UnitTaskTimeout=world.event.S_EVENT_UNIT_TASK_TIMEOUT or-1,
6210
+ UnitTaskStage=world.event.S_EVENT_UNIT_TASK_STAGE or-1,
6211
+ MacSubtaskScore=world.event.S_EVENT_MAC_SUBTASK_SCORE or-1,
6212
+ MacExtraScore=world.event.S_EVENT_MAC_EXTRA_SCORE or-1,
6213
+ MissionRestart=world.event.S_EVENT_MISSION_RESTART or-1,
6214
+ MissionWinner=world.event.S_EVENT_MISSION_WINNER or-1,
6215
+ PostponedTakeoff=world.event.S_EVENT_POSTPONED_TAKEOFF or-1,
6216
+ PostponedLand=world.event.S_EVENT_POSTPONED_LAND or-1,
5891
6217
  }
5892
6218
  local _EVENTMETA={
5893
6219
  [world.event.S_EVENT_SHOT]={
@@ -6201,6 +6527,54 @@ Side="I",
6201
6527
  Event="OnEventWeaponDrop",
6202
6528
  Text="S_EVENT_WEAPON_DROP"
6203
6529
  },
6530
+ [EVENTS.UnitTaskTimeout]={
6531
+ Order=1,
6532
+ Side="I",
6533
+ Event="OnEventUnitTaskTimeout",
6534
+ Text="S_EVENT_UNIT_TASK_TIMEOUT "
6535
+ },
6536
+ [EVENTS.UnitTaskStage]={
6537
+ Order=1,
6538
+ Side="I",
6539
+ Event="OnEventUnitTaskStage",
6540
+ Text="S_EVENT_UNIT_TASK_STAGE "
6541
+ },
6542
+ [EVENTS.MacSubtaskScore]={
6543
+ Order=1,
6544
+ Side="I",
6545
+ Event="OnEventMacSubtaskScore",
6546
+ Text="S_EVENT_MAC_SUBTASK_SCORE"
6547
+ },
6548
+ [EVENTS.MacExtraScore]={
6549
+ Order=1,
6550
+ Side="I",
6551
+ Event="OnEventMacExtraScore",
6552
+ Text="S_EVENT_MAC_EXTRA_SCOREP"
6553
+ },
6554
+ [EVENTS.MissionRestart]={
6555
+ Order=1,
6556
+ Side="I",
6557
+ Event="OnEventMissionRestart",
6558
+ Text="S_EVENT_MISSION_RESTART"
6559
+ },
6560
+ [EVENTS.MissionWinner]={
6561
+ Order=1,
6562
+ Side="I",
6563
+ Event="OnEventMissionWinner",
6564
+ Text="S_EVENT_MISSION_WINNER"
6565
+ },
6566
+ [EVENTS.PostponedTakeoff]={
6567
+ Order=1,
6568
+ Side="I",
6569
+ Event="OnEventPostponedTakeoff",
6570
+ Text="S_EVENT_POSTPONED_TAKEOFF"
6571
+ },
6572
+ [EVENTS.PostponedLand]={
6573
+ Order=1,
6574
+ Side="I",
6575
+ Event="OnEventPostponedLand",
6576
+ Text="S_EVENT_POSTPONED_LAND"
6577
+ },
6204
6578
  }
6205
6579
  function EVENT:New()
6206
6580
  local self=BASE:Inherit(self,BASE:New())
@@ -6524,11 +6898,13 @@ elseif Event.TgtObjectCategory==Object.Category.STATIC then
6524
6898
  Event.TgtDCSUnit=Event.target
6525
6899
  if Event.target:isExist()and Event.id~=33 then
6526
6900
  Event.TgtDCSUnitName=Event.TgtDCSUnit:getName()
6901
+ if Event.TgtDCSUnitName and Event.TgtDCSUnitName~=""then
6527
6902
  Event.TgtUnitName=Event.TgtDCSUnitName
6528
6903
  Event.TgtUnit=STATIC:FindByName(Event.TgtDCSUnitName,false)
6529
6904
  Event.TgtCoalition=Event.TgtDCSUnit:getCoalition()
6530
6905
  Event.TgtCategory=Event.TgtDCSUnit:getDesc().category
6531
6906
  Event.TgtTypeName=Event.TgtDCSUnit:getTypeName()
6907
+ end
6532
6908
  else
6533
6909
  Event.TgtDCSUnitName=string.format("No target object for Event ID %s",tostring(Event.id))
6534
6910
  Event.TgtUnitName=Event.TgtDCSUnitName
@@ -6560,7 +6936,7 @@ if Event.weapon then
6560
6936
  Event.Weapon=Event.weapon
6561
6937
  Event.WeaponName=Event.Weapon:getTypeName()
6562
6938
  Event.WeaponUNIT=CLIENT:Find(Event.Weapon,'',true)
6563
- Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon:getPlayerName()
6939
+ Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
6564
6940
  Event.WeaponCoalition=Event.WeaponUNIT and Event.Weapon:getCoalition()
6565
6941
  Event.WeaponCategory=Event.WeaponUNIT and Event.Weapon:getDesc().category
6566
6942
  Event.WeaponTypeName=Event.WeaponUNIT and Event.Weapon:getTypeName()
@@ -6568,7 +6944,7 @@ end
6568
6944
  if Event.place then
6569
6945
  if Event.id==EVENTS.LandingAfterEjection then
6570
6946
  else
6571
- if Event.place:isExist()and Event.place:getCategory()~=Object.Category.SCENERY then
6947
+ if Event.place:isExist()and Object.getCategory(Event.place)~=Object.Category.SCENERY then
6572
6948
  Event.Place=AIRBASE:Find(Event.place)
6573
6949
  Event.PlaceName=Event.Place:GetName()
6574
6950
  end
@@ -8108,7 +8484,13 @@ if Delay and Delay>0 then
8108
8484
  self:ScheduleOnce(Delay,ZONE_BASE.UndrawZone,self)
8109
8485
  else
8110
8486
  if self.DrawID then
8487
+ if type(self.DrawID)~="table"then
8111
8488
  UTILS.RemoveMark(self.DrawID)
8489
+ else
8490
+ for _,mark_id in pairs(self.DrawID)do
8491
+ UTILS.RemoveMark(mark_id)
8492
+ end
8493
+ end
8112
8494
  end
8113
8495
  end
8114
8496
  return self
@@ -8368,7 +8750,7 @@ radius=ZoneRadius,
8368
8750
  }
8369
8751
  local function EvaluateZone(ZoneObject)
8370
8752
  if ZoneObject then
8371
- local ObjectCategory=ZoneObject:getCategory()
8753
+ local ObjectCategory=Object.getCategory(ZoneObject)
8372
8754
  if(ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()and ZoneObject:isActive())or(ObjectCategory==Object.Category.STATIC and ZoneObject:isExist())then
8373
8755
  local CoalitionDCSUnit=ZoneObject:getCoalition()
8374
8756
  local Include=false
@@ -8823,8 +9205,68 @@ local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2())
8823
9205
  self:T3({PointVec2})
8824
9206
  return PointVec2
8825
9207
  end
9208
+ _ZONE_TRIANGLE={
9209
+ ClassName="ZONE_TRIANGLE",
9210
+ Points={},
9211
+ Coords={},
9212
+ CenterVec2={x=0,y=0},
9213
+ SurfaceArea=0,
9214
+ DrawIDs={}
9215
+ }
9216
+ function _ZONE_TRIANGLE:New(p1,p2,p3)
9217
+ local self=BASE:Inherit(self,ZONE_BASE:New())
9218
+ self.Points={p1,p2,p3}
9219
+ local center_x=(p1.x+p2.x+p3.x)/3
9220
+ local center_y=(p1.y+p2.y+p3.y)/3
9221
+ self.CenterVec2={x=center_x,y=center_y}
9222
+ for _,pt in pairs({p1,p2,p3})do
9223
+ table.add(self.Coords,COORDINATE:NewFromVec2(pt))
9224
+ end
9225
+ self.SurfaceArea=math.abs((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y))*0.5
9226
+ return self
9227
+ end
9228
+ function _ZONE_TRIANGLE:ContainsPoint(pt,points)
9229
+ points=points or self.Points
9230
+ local function sign(p1,p2,p3)
9231
+ return(p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y)
9232
+ end
9233
+ local d1=sign(pt,self.Points[1],self.Points[2])
9234
+ local d2=sign(pt,self.Points[2],self.Points[3])
9235
+ local d3=sign(pt,self.Points[3],self.Points[1])
9236
+ local has_neg=(d1<0)or(d2<0)or(d3<0)
9237
+ local has_pos=(d1>0)or(d2>0)or(d3>0)
9238
+ return not(has_neg and has_pos)
9239
+ end
9240
+ function _ZONE_TRIANGLE:GetRandomVec2(points)
9241
+ points=points or self.Points
9242
+ local pt={math.random(),math.random()}
9243
+ table.sort(pt)
9244
+ local s=pt[1]
9245
+ local t=pt[2]-pt[1]
9246
+ local u=1-pt[2]
9247
+ return{x=s*points[1].x+t*points[2].x+u*points[3].x,
9248
+ y=s*points[1].y+t*points[2].y+u*points[3].y}
9249
+ end
9250
+ function _ZONE_TRIANGLE:Draw(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
9251
+ Coalition=Coalition or-1
9252
+ Color=Color or{1,0,0}
9253
+ Alpha=Alpha or 1
9254
+ FillColor=FillColor or Color
9255
+ if not FillColor then UTILS.DeepCopy(Color)end
9256
+ FillAlpha=FillAlpha or Alpha
9257
+ if not FillAlpha then FillAlpha=1 end
9258
+ for i=1,#self.Coords do
9259
+ local c1=self.Coords[i]
9260
+ local c2=self.Coords[i%#self.Coords+1]
9261
+ table.add(self.DrawIDs,c1:LineToAll(c2,Coalition,Color,Alpha,LineType,ReadOnly))
9262
+ end
9263
+ return self.DrawIDs
9264
+ end
8826
9265
  ZONE_POLYGON_BASE={
8827
9266
  ClassName="ZONE_POLYGON_BASE",
9267
+ _Triangles={},
9268
+ SurfaceArea=0,
9269
+ DrawID={}
8828
9270
  }
8829
9271
  function ZONE_POLYGON_BASE:New(ZoneName,PointsArray)
8830
9272
  local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName))
@@ -8836,9 +9278,84 @@ self._.Polygon[i]={}
8836
9278
  self._.Polygon[i].x=PointsArray[i].x
8837
9279
  self._.Polygon[i].y=PointsArray[i].y
8838
9280
  end
9281
+ self._Triangles=self:_Triangulate()
9282
+ self.SurfaceArea=self:_CalculateSurfaceArea()
8839
9283
  end
8840
9284
  return self
8841
9285
  end
9286
+ function ZONE_POLYGON_BASE:_Triangulate()
9287
+ local points=self._.Polygon
9288
+ local triangles={}
9289
+ local function get_orientation(shape_points)
9290
+ local sum=0
9291
+ for i=1,#shape_points do
9292
+ local j=i%#shape_points+1
9293
+ sum=sum+(shape_points[j].x-shape_points[i].x)*(shape_points[j].y+shape_points[i].y)
9294
+ end
9295
+ return sum>=0 and"clockwise"or"counter-clockwise"
9296
+ end
9297
+ local function ensure_clockwise(shape_points)
9298
+ local orientation=get_orientation(shape_points)
9299
+ if orientation=="counter-clockwise"then
9300
+ local reversed={}
9301
+ for i=#shape_points,1,-1 do
9302
+ table.insert(reversed,shape_points[i])
9303
+ end
9304
+ return reversed
9305
+ end
9306
+ return shape_points
9307
+ end
9308
+ local function is_clockwise(p1,p2,p3)
9309
+ local cross_product=(p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x)
9310
+ return cross_product<0
9311
+ end
9312
+ local function divide_recursively(shape_points)
9313
+ if#shape_points==3 then
9314
+ table.insert(triangles,_ZONE_TRIANGLE:New(shape_points[1],shape_points[2],shape_points[3]))
9315
+ elseif#shape_points>3 then
9316
+ for i,p1 in ipairs(shape_points)do
9317
+ local p2=shape_points[(i%#shape_points)+1]
9318
+ local p3=shape_points[(i+1)%#shape_points+1]
9319
+ local triangle=_ZONE_TRIANGLE:New(p1,p2,p3)
9320
+ local is_ear=true
9321
+ if not is_clockwise(p1,p2,p3)then
9322
+ is_ear=false
9323
+ else
9324
+ for _,point in ipairs(shape_points)do
9325
+ if point~=p1 and point~=p2 and point~=p3 and triangle:ContainsPoint(point)then
9326
+ is_ear=false
9327
+ break
9328
+ end
9329
+ end
9330
+ end
9331
+ if is_ear then
9332
+ local is_valid_triangle=true
9333
+ for _,point in ipairs(points)do
9334
+ if point~=p1 and point~=p2 and point~=p3 and triangle:ContainsPoint(point)then
9335
+ is_valid_triangle=false
9336
+ break
9337
+ end
9338
+ end
9339
+ if is_valid_triangle then
9340
+ table.insert(triangles,triangle)
9341
+ local remaining_points={}
9342
+ for j,point in ipairs(shape_points)do
9343
+ if point~=p2 then
9344
+ table.insert(remaining_points,point)
9345
+ end
9346
+ end
9347
+ divide_recursively(remaining_points)
9348
+ break
9349
+ end
9350
+ else
9351
+ end
9352
+ end
9353
+ end
9354
+ end
9355
+ points=ensure_clockwise(points)
9356
+ divide_recursively(points)
9357
+ return triangles
9358
+ end
8842
9359
  function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
8843
9360
  self._.Polygon={}
8844
9361
  for i=1,#Vec2Array do
@@ -8846,6 +9363,8 @@ self._.Polygon[i]={}
8846
9363
  self._.Polygon[i].x=Vec2Array[i].x
8847
9364
  self._.Polygon[i].y=Vec2Array[i].y
8848
9365
  end
9366
+ self._Triangles=self:_Triangulate()
9367
+ self.SurfaceArea=self:_CalculateSurfaceArea()
8849
9368
  return self
8850
9369
  end
8851
9370
  function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
@@ -8855,8 +9374,17 @@ self._.Polygon[i]={}
8855
9374
  self._.Polygon[i].x=Vec3Array[i].x
8856
9375
  self._.Polygon[i].y=Vec3Array[i].z
8857
9376
  end
9377
+ self._Triangles=self:_Triangulate()
9378
+ self.SurfaceArea=self:_CalculateSurfaceArea()
8858
9379
  return self
8859
9380
  end
9381
+ function ZONE_POLYGON_BASE:_CalculateSurfaceArea()
9382
+ local area=0
9383
+ for _,triangle in pairs(self._Triangles)do
9384
+ area=area+triangle.SurfaceArea
9385
+ end
9386
+ return area
9387
+ end
8860
9388
  function ZONE_POLYGON_BASE:GetVec2()
8861
9389
  self:F(self.ZoneName)
8862
9390
  local Bounds=self:GetBoundingSquare()
@@ -8939,32 +9467,42 @@ i=i+1
8939
9467
  end
8940
9468
  return self
8941
9469
  end
8942
- function ZONE_POLYGON_BASE:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
9470
+ function ZONE_POLYGON_BASE:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,IncludeTriangles)
8943
9471
  if self._.Polygon and#self._.Polygon>=3 then
8944
- local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
8945
9472
  Coalition=Coalition or self:GetDrawCoalition()
8946
9473
  self:SetDrawCoalition(Coalition)
8947
9474
  Color=Color or self:GetColorRGB()
8948
9475
  Alpha=Alpha or 1
8949
9476
  self:SetColor(Color,Alpha)
8950
9477
  FillColor=FillColor or self:GetFillColorRGB()
8951
- if not FillColor then UTILS.DeepCopy(Color)end
9478
+ if not FillColor then
9479
+ UTILS.DeepCopy(Color)
9480
+ end
8952
9481
  FillAlpha=FillAlpha or self:GetFillColorAlpha()
8953
- if not FillAlpha then FillAlpha=0.15 end
9482
+ if not FillAlpha then
9483
+ FillAlpha=0.15
9484
+ end
8954
9485
  self:SetFillColor(FillColor,FillAlpha)
8955
- if#self._.Polygon==4 then
8956
- local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
8957
- local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
8958
- local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
8959
- self.DrawID=coordinate:QuadToAll(Coord2,Coord3,Coord4,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
9486
+ IncludeTriangles=IncludeTriangles or false
9487
+ if IncludeTriangles then
9488
+ for _,triangle in pairs(self._Triangles)do
9489
+ local draw_ids=triangle:Draw()
9490
+ table.combine(self.DrawID,draw_ids)
9491
+ end
8960
9492
  else
8961
- local Coordinates=self:GetVerticiesCoordinates()
8962
- table.remove(Coordinates,1)
8963
- self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
9493
+ local coords=self:GetVerticiesCoordinates()
9494
+ for i=1,#coords do
9495
+ local c1=coords[i]
9496
+ local c2=coords[i%#coords+1]
9497
+ table.add(self.DrawID,c1:LineToAll(c2,Coalition,Color,Alpha,LineType,ReadOnly))
9498
+ end
8964
9499
  end
8965
9500
  end
8966
9501
  return self
8967
9502
  end
9503
+ function ZONE_POLYGON_BASE:GetSurfaceArea()
9504
+ return self.SurfaceArea
9505
+ end
8968
9506
  function ZONE_POLYGON_BASE:GetRadius()
8969
9507
  local center=self:GetVec2()
8970
9508
  local radius=0
@@ -9073,17 +9611,18 @@ local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
9073
9611
  return InZone
9074
9612
  end
9075
9613
  function ZONE_POLYGON_BASE:GetRandomVec2()
9076
- local BS=self:GetBoundingSquare()
9077
- local Nmax=1000;local n=0
9078
- while n<Nmax do
9079
- local Vec2={x=math.random(BS.x1,BS.x2),y=math.random(BS.y1,BS.y2)}
9080
- if self:IsVec2InZone(Vec2)then
9081
- return Vec2
9614
+ local weights={}
9615
+ for _,triangle in pairs(self._Triangles)do
9616
+ weights[triangle]=triangle.SurfaceArea/self.SurfaceArea
9617
+ end
9618
+ local random_weight=math.random()
9619
+ local accumulated_weight=0
9620
+ for triangle,weight in pairs(weights)do
9621
+ accumulated_weight=accumulated_weight+weight
9622
+ if accumulated_weight>=random_weight then
9623
+ return triangle:GetRandomVec2()
9082
9624
  end
9083
- n=n+1
9084
9625
  end
9085
- self:E("Could not find a random point in the polygon zone!")
9086
- return nil
9087
9626
  end
9088
9627
  function ZONE_POLYGON_BASE:GetRandomPointVec2()
9089
9628
  self:F2()
@@ -9162,6 +9701,7 @@ i=i+1
9162
9701
  end
9163
9702
  return self
9164
9703
  end
9704
+ do
9165
9705
  ZONE_POLYGON={
9166
9706
  ClassName="ZONE_POLYGON",
9167
9707
  }
@@ -9186,6 +9726,36 @@ self:F({GroupName,ZoneGroup,self._.Polygon})
9186
9726
  _EVENTDISPATCHER:CreateEventNewZone(self)
9187
9727
  return self
9188
9728
  end
9729
+ function ZONE_POLYGON:NewFromDrawing(DrawingName)
9730
+ local points={}
9731
+ for _,layer in pairs(env.mission.drawings.layers)do
9732
+ for _,object in pairs(layer["objects"])do
9733
+ if object["name"]==DrawingName then
9734
+ if(object["primitiveType"]=="Line"and object["closed"]==true)or(object["polygonMode"]=="free")then
9735
+ for _,point in UTILS.spairs(object["points"])do
9736
+ local p={x=object["mapX"]+point["x"],
9737
+ y=object["mapY"]+point["y"]}
9738
+ table.add(points,p)
9739
+ end
9740
+ elseif object["polygonMode"]=="rect"then
9741
+ local angle=object["angle"]
9742
+ local half_width=object["width"]/2
9743
+ local half_height=object["height"]/2
9744
+ local center={x=object["mapX"],y=object["mapY"]}
9745
+ local p1=UTILS.RotatePointAroundPivot({x=center.x-half_height,y=center.y+half_width},center,angle)
9746
+ local p2=UTILS.RotatePointAroundPivot({x=center.x+half_height,y=center.y+half_width},center,angle)
9747
+ local p3=UTILS.RotatePointAroundPivot({x=center.x+half_height,y=center.y-half_width},center,angle)
9748
+ local p4=UTILS.RotatePointAroundPivot({x=center.x-half_height,y=center.y-half_width},center,angle)
9749
+ points={p1,p2,p3,p4}
9750
+ else
9751
+ end
9752
+ end
9753
+ end
9754
+ end
9755
+ local self=BASE:Inherit(self,ZONE_POLYGON_BASE:New(DrawingName,points))
9756
+ _EVENTDISPATCHER:CreateEventNewZone(self)
9757
+ return self
9758
+ end
9189
9759
  function ZONE_POLYGON:FindByName(ZoneName)
9190
9760
  local ZoneFound=_DATABASE:FindZone(ZoneName)
9191
9761
  return ZoneFound
@@ -9212,7 +9782,7 @@ radius=ZoneRadius,
9212
9782
  }
9213
9783
  local function EvaluateZone(ZoneObject)
9214
9784
  if ZoneObject then
9215
- local ObjectCategory=ZoneObject:getCategory()
9785
+ local ObjectCategory=Object.getCategory(ZoneObject)
9216
9786
  if(ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()and ZoneObject:isActive())or(ObjectCategory==Object.Category.STATIC and ZoneObject:isExist())then
9217
9787
  local CoalitionDCSUnit=ZoneObject:getCoalition()
9218
9788
  local Include=false
@@ -9372,6 +9942,7 @@ end
9372
9942
  function ZONE_POLYGON:IsNoneInZone()
9373
9943
  return self:CountScannedCoalitions()==0
9374
9944
  end
9945
+ end
9375
9946
  do
9376
9947
  ZONE_ELASTIC={
9377
9948
  ClassName="ZONE_ELASTIC",
@@ -9465,6 +10036,124 @@ table.remove(h,#h)
9465
10036
  return h
9466
10037
  end
9467
10038
  end
10039
+ ZONE_OVAL={
10040
+ ClassName="OVAL",
10041
+ ZoneName="",
10042
+ MajorAxis=nil,
10043
+ MinorAxis=nil,
10044
+ Angle=0,
10045
+ DrawPoly=nil
10046
+ }
10047
+ function ZONE_OVAL:New(name,vec2,major_axis,minor_axis,angle)
10048
+ self=BASE:Inherit(self,ZONE_BASE:New())
10049
+ self.ZoneName=name
10050
+ self.CenterVec2=vec2
10051
+ self.MajorAxis=major_axis
10052
+ self.MinorAxis=minor_axis
10053
+ self.Angle=angle or 0
10054
+ _DATABASE:AddZone(name,self)
10055
+ return self
10056
+ end
10057
+ function ZONE_OVAL:NewFromDrawing(DrawingName)
10058
+ self=BASE:Inherit(self,ZONE_BASE:New(DrawingName))
10059
+ for _,layer in pairs(env.mission.drawings.layers)do
10060
+ for _,object in pairs(layer["objects"])do
10061
+ if string.find(object["name"],DrawingName,1,true)then
10062
+ if object["polygonMode"]=="oval"then
10063
+ self.CenterVec2={x=object["mapX"],y=object["mapY"]}
10064
+ self.MajorAxis=object["r1"]
10065
+ self.MinorAxis=object["r2"]
10066
+ self.Angle=object["angle"]
10067
+ end
10068
+ end
10069
+ end
10070
+ end
10071
+ _DATABASE:AddZone(DrawingName,self)
10072
+ return self
10073
+ end
10074
+ function ZONE_OVAL:GetMajorAxis()
10075
+ return self.MajorAxis
10076
+ end
10077
+ function ZONE_OVAL:GetMinorAxis()
10078
+ return self.MinorAxis
10079
+ end
10080
+ function ZONE_OVAL:GetAngle()
10081
+ return self.Angle
10082
+ end
10083
+ function ZONE_OVAL:GetVec2()
10084
+ return self.CenterVec2
10085
+ end
10086
+ function ZONE_OVAL:IsVec2InZone(vec2)
10087
+ local cos,sin=math.cos,math.sin
10088
+ local dx=vec2.x-self.CenterVec2.x
10089
+ local dy=vec2.y-self.CenterVec2.y
10090
+ local rx=dx*cos(self.Angle)+dy*sin(self.Angle)
10091
+ local ry=-dx*sin(self.Angle)+dy*cos(self.Angle)
10092
+ return rx*rx/(self.MajorAxis*self.MajorAxis)+ry*ry/(self.MinorAxis*self.MinorAxis)<=1
10093
+ end
10094
+ function ZONE_OVAL:GetBoundingSquare()
10095
+ local min_x=self.CenterVec2.x-self.MajorAxis
10096
+ local min_y=self.CenterVec2.y-self.MinorAxis
10097
+ local max_x=self.CenterVec2.x+self.MajorAxis
10098
+ local max_y=self.CenterVec2.y+self.MinorAxis
10099
+ return{
10100
+ {x=min_x,y=min_x},{x=max_x,y=min_y},{x=max_x,y=max_y},{x=min_x,y=max_y}
10101
+ }
10102
+ end
10103
+ function ZONE_OVAL:PointsOnEdge(num_points)
10104
+ num_points=num_points or 40
10105
+ local points={}
10106
+ local dtheta=2*math.pi/num_points
10107
+ for i=0,num_points-1 do
10108
+ local theta=i*dtheta
10109
+ local x=self.CenterVec2.x+self.MajorAxis*math.cos(theta)*math.cos(self.Angle)-self.MinorAxis*math.sin(theta)*math.sin(self.Angle)
10110
+ local y=self.CenterVec2.y+self.MajorAxis*math.cos(theta)*math.sin(self.Angle)+self.MinorAxis*math.sin(theta)*math.cos(self.Angle)
10111
+ table.insert(points,{x=x,y=y})
10112
+ end
10113
+ return points
10114
+ end
10115
+ function ZONE_OVAL:GetRandomVec2()
10116
+ local theta=math.rad(self.Angle)
10117
+ local random_point=math.sqrt(math.random())
10118
+ local phi=math.random()*2*math.pi
10119
+ local x_c=random_point*math.cos(phi)
10120
+ local y_c=random_point*math.sin(phi)
10121
+ local x_e=x_c*self.MajorAxis
10122
+ local y_e=y_c*self.MinorAxis
10123
+ local rx=(x_e*math.cos(theta)-y_e*math.sin(theta))+self.CenterVec2.x
10124
+ local ry=(x_e*math.sin(theta)+y_e*math.cos(theta))+self.CenterVec2.y
10125
+ return{x=rx,y=ry}
10126
+ end
10127
+ function ZONE_OVAL:GetRandomPointVec2()
10128
+ return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
10129
+ end
10130
+ function ZONE_OVAL:GetRandomPointVec3()
10131
+ return POINT_VEC2:NewFromVec3(self:GetRandomVec2())
10132
+ end
10133
+ function ZONE_OVAL:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType)
10134
+ Coalition=Coalition or self:GetDrawCoalition()
10135
+ self:SetDrawCoalition(Coalition)
10136
+ Color=Color or self:GetColorRGB()
10137
+ Alpha=Alpha or 1
10138
+ self:SetColor(Color,Alpha)
10139
+ FillColor=FillColor or self:GetFillColorRGB()
10140
+ if not FillColor then
10141
+ UTILS.DeepCopy(Color)
10142
+ end
10143
+ FillAlpha=FillAlpha or self:GetFillColorAlpha()
10144
+ if not FillAlpha then
10145
+ FillAlpha=0.15
10146
+ end
10147
+ LineType=LineType or 1
10148
+ self:SetFillColor(FillColor,FillAlpha)
10149
+ self.DrawPoly=ZONE_POLYGON:NewFromPointsArray(self.ZoneName,self:PointsOnEdge(80))
10150
+ self.DrawPoly:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType)
10151
+ end
10152
+ function ZONE_OVAL:UndrawZone()
10153
+ if self.DrawPoly then
10154
+ self.DrawPoly:UndrawZone()
10155
+ end
10156
+ end
9468
10157
  do
9469
10158
  ZONE_AIRBASE={
9470
10159
  ClassName="ZONE_AIRBASE",
@@ -10776,7 +11465,10 @@ self:T3({LastObject})
10776
11465
  return LastObject
10777
11466
  end
10778
11467
  function SET_BASE:GetRandom()
10779
- local tablemax=table.maxn(self.Index)
11468
+ local tablemax=0
11469
+ for _,_ind in pairs(self.Index)do
11470
+ tablemax=tablemax+1
11471
+ end
10780
11472
  local RandomItem=self.Set[self.Index[math.random(1,tablemax)]]
10781
11473
  self:T3({RandomItem})
10782
11474
  return RandomItem
@@ -11691,6 +12383,16 @@ end
11691
12383
  end
11692
12384
  return CountU
11693
12385
  end
12386
+ function SET_UNIT:GetAliveSet()
12387
+ local AliveSet=SET_UNIT:New()
12388
+ for GroupName,GroupObject in pairs(self.Set)do
12389
+ local GroupObject=GroupObject
12390
+ if GroupObject and GroupObject:IsAlive()then
12391
+ AliveSet:Add(GroupName,GroupObject)
12392
+ end
12393
+ end
12394
+ return AliveSet.Set or{},AliveSet
12395
+ end
11694
12396
  function SET_UNIT:_ContinousZoneFilter()
11695
12397
  local Database=_DATABASE.UNITS
11696
12398
  for ObjectName,Object in pairs(Database)do
@@ -11915,40 +12617,41 @@ self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText})
11915
12617
  return MaxThreatLevelA2G,MaxThreatText
11916
12618
  end
11917
12619
  function SET_UNIT:GetCoordinate()
11918
- local Coordinate=self:GetRandom():GetCoordinate()
11919
- local x1=Coordinate.x
11920
- local x2=Coordinate.x
11921
- local y1=Coordinate.y
11922
- local y2=Coordinate.y
11923
- local z1=Coordinate.z
11924
- local z2=Coordinate.z
11925
- local MaxVelocity=0
11926
- local AvgHeading=nil
11927
- local MovingCount=0
11928
- for UnitName,UnitData in pairs(self:GetSet())do
11929
- local Unit=UnitData
11930
- local Coordinate=Unit:GetCoordinate()
11931
- x1=(Coordinate.x<x1)and Coordinate.x or x1
11932
- x2=(Coordinate.x>x2)and Coordinate.x or x2
11933
- y1=(Coordinate.y<y1)and Coordinate.y or y1
11934
- y2=(Coordinate.y>y2)and Coordinate.y or y2
11935
- z1=(Coordinate.y<z1)and Coordinate.z or z1
11936
- z2=(Coordinate.y>z2)and Coordinate.z or z2
11937
- local Velocity=Coordinate:GetVelocity()
11938
- if Velocity~=0 then
11939
- MaxVelocity=(MaxVelocity<Velocity)and Velocity or MaxVelocity
11940
- local Heading=Coordinate:GetHeading()
11941
- AvgHeading=AvgHeading and(AvgHeading+Heading)or Heading
11942
- MovingCount=MovingCount+1
12620
+ local function GetSetVec3(units)
12621
+ local x=0
12622
+ local y=0
12623
+ local z=0
12624
+ local n=0
12625
+ for _,unit in pairs(units)do
12626
+ local vec3=nil
12627
+ if unit and unit:IsAlive()then
12628
+ vec3=unit:GetVec3()
11943
12629
  end
12630
+ if vec3 then
12631
+ x=x+vec3.x
12632
+ y=y+vec3.y
12633
+ z=z+vec3.z
12634
+ n=n+1
12635
+ end
12636
+ end
12637
+ if n>0 then
12638
+ local Vec3={x=x/n,y=y/n,z=z/n}
12639
+ return Vec3
12640
+ end
12641
+ return nil
12642
+ end
12643
+ local Coordinate=nil
12644
+ local Vec3=GetSetVec3(self.Set)
12645
+ if Vec3 then
12646
+ Coordinate=COORDINATE:NewFromVec3(Vec3)
12647
+ end
12648
+ if Coordinate then
12649
+ local heading=self:GetHeading()or 0
12650
+ local velocity=self:GetVelocity()or 0
12651
+ Coordinate:SetHeading(heading)
12652
+ Coordinate:SetVelocity(velocity)
12653
+ self:I(UTILS.PrintTableToLog(Coordinate))
11944
12654
  end
11945
- AvgHeading=AvgHeading and(AvgHeading/MovingCount)
11946
- Coordinate.x=(x2-x1)/2+x1
11947
- Coordinate.y=(y2-y1)/2+y1
11948
- Coordinate.z=(z2-z1)/2+z1
11949
- Coordinate:SetHeading(AvgHeading)
11950
- Coordinate:SetVelocity(MaxVelocity)
11951
- self:F({Coordinate=Coordinate})
11952
12655
  return Coordinate
11953
12656
  end
11954
12657
  function SET_UNIT:GetVelocity()
@@ -12805,6 +13508,31 @@ self:_FilterStart()
12805
13508
  end
12806
13509
  return self
12807
13510
  end
13511
+ function SET_CLIENT:_EventPlayerEnterUnit(Event)
13512
+ self:I("_EventPlayerEnterUnit")
13513
+ if Event.IniDCSUnit then
13514
+ if Event.IniObjectCategory==1 and Event.IniGroup and Event.IniGroup:IsGround()then
13515
+ local ObjectName,Object=self:AddInDatabase(Event)
13516
+ self:I(ObjectName,UTILS.PrintTableToLog(Object))
13517
+ if Object and self:IsIncludeObject(Object)then
13518
+ self:Add(ObjectName,Object)
13519
+ end
13520
+ end
13521
+ end
13522
+ return self
13523
+ end
13524
+ function SET_CLIENT:_EventPlayerLeaveUnit(Event)
13525
+ self:I("_EventPlayerLeaveUnit")
13526
+ if Event.IniDCSUnit then
13527
+ if Event.IniObjectCategory==1 and Event.IniGroup and Event.IniGroup:IsGround()then
13528
+ local ObjectName,Object=self:FindInDatabase(Event)
13529
+ if ObjectName then
13530
+ self:Remove(ObjectName)
13531
+ end
13532
+ end
13533
+ end
13534
+ return self
13535
+ end
12808
13536
  function SET_CLIENT:AddInDatabase(Event)
12809
13537
  self:F3({Event})
12810
13538
  return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName]
@@ -14886,7 +15614,7 @@ local gotunits=false
14886
15614
  local gotscenery=false
14887
15615
  local function EvaluateZone(ZoneObject)
14888
15616
  if ZoneObject then
14889
- local ObjectCategory=ZoneObject:getCategory()
15617
+ local ObjectCategory=Object.getCategory(ZoneObject)
14890
15618
  if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()then
14891
15619
  table.insert(Units,UNIT:Find(ZoneObject))
14892
15620
  gotunits=true
@@ -16113,6 +16841,9 @@ end
16113
16841
  end
16114
16842
  return BRAANATO
16115
16843
  end
16844
+ function COORDINATE.GetBullseyeCoordinate(Coalition)
16845
+ return COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
16846
+ end
16116
16847
  function COORDINATE:ToStringBULLS(Coalition,Settings,MagVar)
16117
16848
  local BullsCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
16118
16849
  local DirectionVec3=BullsCoordinate:GetDirectionVec3(self)
@@ -16692,7 +17423,7 @@ end
16692
17423
  if CoalitionSide then
16693
17424
  if self.MessageDuration~=0 then
16694
17425
  self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration)
16695
- trigger.action.outTextForCoalition(CoalitionSide,self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen)
17426
+ trigger.action.outTextForCoalition(CoalitionSide,self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$",""),self.MessageDuration,self.ClearScreen)
16696
17427
  end
16697
17428
  end
16698
17429
  self.CoalitionSide=CoalitionSide
@@ -16736,29 +17467,40 @@ return self
16736
17467
  end
16737
17468
  _MESSAGESRS={}
16738
17469
  function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate)
16739
- _MESSAGESRS.MSRS=MSRS:New(PathToSRS,Frequency,Modulation,Volume)
16740
- _MESSAGESRS.MSRS:SetCoalition(Coalition)
17470
+ _MESSAGESRS.MSRS=MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM,Volume)
17471
+ _MESSAGESRS.frequency=Frequency
17472
+ _MESSAGESRS.modulation=Modulation or radio.modulation.AM
17473
+ _MESSAGESRS.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL)
17474
+ _MESSAGESRS.coalition=Coalition or coalition.side.NEUTRAL
17475
+ _MESSAGESRS.coordinate=Coordinate
16741
17476
  _MESSAGESRS.MSRS:SetCoordinate(Coordinate)
16742
17477
  _MESSAGESRS.MSRS:SetCulture(Culture)
16743
- _MESSAGESRS.Culture=Culture
17478
+ _MESSAGESRS.Culture=Culture or"en-GB"
16744
17479
  _MESSAGESRS.MSRS:SetGender(Gender)
16745
- _MESSAGESRS.Gender=Gender
17480
+ _MESSAGESRS.Gender=Gender or"female"
16746
17481
  _MESSAGESRS.MSRS:SetGoogle(PathToCredentials)
17482
+ _MESSAGESRS.google=PathToCredentials
16747
17483
  _MESSAGESRS.MSRS:SetLabel(Label or"MESSAGE")
16748
- _MESSAGESRS.MSRS:SetPort(Port)
16749
- _MESSAGESRS.MSRS:SetVoice(Voice)
16750
- _MESSAGESRS.Voice=Voice
17484
+ _MESSAGESRS.label=Label or"MESSAGE"
17485
+ _MESSAGESRS.MSRS:SetPort(Port or 5002)
17486
+ _MESSAGESRS.port=Port or 5002
17487
+ _MESSAGESRS.volume=Volume or 1
17488
+ _MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
17489
+ if Voice then _MESSAGESRS.MSRS:SetVoice(Voice)end
17490
+ _MESSAGESRS.voice=Voice
16751
17491
  _MESSAGESRS.SRSQ=MSRSQUEUE:New(Label or"MESSAGE")
16752
- env.info(_MESSAGESRS.MSRS.provider,false)
16753
17492
  end
16754
17493
  function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate)
17494
+ local tgender=gender or _MESSAGESRS.Gender
16755
17495
  if _MESSAGESRS.SRSQ then
16756
- _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice)
17496
+ if voice then
17497
+ _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.voice)
17498
+ end
16757
17499
  if coordinate then
16758
17500
  _MESSAGESRS.MSRS:SetCoordinate(coordinate)
16759
17501
  end
16760
17502
  local category=string.gsub(self.MessageCategory,":","")
16761
- _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,category,coordinate)
17503
+ _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate)
16762
17504
  end
16763
17505
  return self
16764
17506
  end
@@ -17328,6 +18070,7 @@ self.SpawnInitModexPrefix=nil
17328
18070
  self.SpawnInitModexPostfix=nil
17329
18071
  self.SpawnInitAirbase=nil
17330
18072
  self.TweakedTemplate=false
18073
+ self.SpawnRandomCallsign=false
17331
18074
  self.SpawnGroups={}
17332
18075
  else
17333
18076
  error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'")
@@ -17617,6 +18360,18 @@ self:_RandomizeZones(SpawnGroupID)
17617
18360
  end
17618
18361
  return self
17619
18362
  end
18363
+ function SPAWN:InitRandomizeCallsign()
18364
+ self.SpawnRandomCallsign=true
18365
+ return self
18366
+ end
18367
+ function SPAWN:InitCallSign(ID,Name,Minor,Major)
18368
+ self.SpawnInitCallSign=true
18369
+ self.SpawnInitCallSignID=ID or 1
18370
+ self.SpawnInitCallSignMinor=Minor or 1
18371
+ self.SpawnInitCallSignMajor=Major or 1
18372
+ self.SpawnInitCallSignName=string.lower(Name)or"enfield"
18373
+ return self
18374
+ end
17620
18375
  function SPAWN:InitPositionCoordinate(Coordinate)
17621
18376
  self:T({self.SpawnTemplatePrefix,Coordinate:GetVec2()})
17622
18377
  self:InitPositionVec2(Coordinate:GetVec2())
@@ -18738,19 +19493,133 @@ SpawnTemplate.units[UnitID].name=string.format('%s#%03d-%02d',UnitPrefix,SpawnIn
18738
19493
  SpawnTemplate.units[UnitID].unitId=nil
18739
19494
  end
18740
19495
  end
19496
+ if self.SpawnRandomCallsign and SpawnTemplate.units[1].callsign then
19497
+ if type(SpawnTemplate.units[1].callsign)~="number"then
19498
+ local min=1
19499
+ local max=8
19500
+ local ctable=CALLSIGN.Aircraft
19501
+ if string.find(SpawnTemplate.units[1].type,"A-10",1,true)then
19502
+ max=12
19503
+ end
19504
+ if string.find(SpawnTemplate.units[1].type,"18",1,true)then
19505
+ min=9
19506
+ max=20
19507
+ ctable=CALLSIGN.F18
19508
+ end
19509
+ if string.find(SpawnTemplate.units[1].type,"16",1,true)then
19510
+ min=9
19511
+ max=20
19512
+ ctable=CALLSIGN.F16
19513
+ end
19514
+ if SpawnTemplate.units[1].type=="F-15E"then
19515
+ min=9
19516
+ max=18
19517
+ ctable=CALLSIGN.F15E
19518
+ end
19519
+ local callsignnr=math.random(min,max)
19520
+ local callsignname="Enfield"
19521
+ for name,value in pairs(ctable)do
19522
+ if value==callsignnr then
19523
+ callsignname=name
19524
+ end
19525
+ end
19526
+ for UnitID=1,#SpawnTemplate.units do
19527
+ SpawnTemplate.units[UnitID].callsign[1]=callsignnr
19528
+ SpawnTemplate.units[UnitID].callsign[2]=UnitID
19529
+ SpawnTemplate.units[UnitID].callsign[3]="1"
19530
+ SpawnTemplate.units[UnitID].callsign["name"]=tostring(callsignname)..tostring(UnitID).."1"
19531
+ end
19532
+ else
19533
+ for UnitID=1,#SpawnTemplate.units do
19534
+ SpawnTemplate.units[UnitID].callsign=math.random(1,999)
19535
+ end
19536
+ end
19537
+ end
19538
+ if self.SpawnInitCallSign then
19539
+ for UnitID=1,#SpawnTemplate.units do
19540
+ local Callsign=SpawnTemplate.units[UnitID].callsign
19541
+ if Callsign and type(Callsign)~="number"then
19542
+ SpawnTemplate.units[UnitID].callsign[1]=self.SpawnInitCallSignID
19543
+ SpawnTemplate.units[UnitID].callsign[2]=self.SpawnInitCallSignMinor
19544
+ SpawnTemplate.units[UnitID].callsign[3]=self.SpawnInitCallSignMajor
19545
+ SpawnTemplate.units[UnitID].callsign["name"]=string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor)
19546
+ end
19547
+ end
19548
+ end
18741
19549
  for UnitID=1,#SpawnTemplate.units do
18742
19550
  local Callsign=SpawnTemplate.units[UnitID].callsign
18743
19551
  if Callsign then
18744
- if type(Callsign)~="number"then
19552
+ if type(Callsign)~="number"and not self.SpawnInitCallSign then
18745
19553
  Callsign[2]=((SpawnIndex-1)%10)+1
18746
19554
  local CallsignName=SpawnTemplate.units[UnitID].callsign["name"]
18747
19555
  CallsignName=string.match(CallsignName,"^(%a+)")
18748
19556
  local CallsignLen=CallsignName:len()
19557
+ SpawnTemplate.units[UnitID].callsign[2]=UnitID
18749
19558
  SpawnTemplate.units[UnitID].callsign["name"]=CallsignName:sub(1,CallsignLen)..SpawnTemplate.units[UnitID].callsign[2]..SpawnTemplate.units[UnitID].callsign[3]
18750
- else
19559
+ elseif type(Callsign)=="number"then
18751
19560
  SpawnTemplate.units[UnitID].callsign=Callsign+SpawnIndex
18752
19561
  end
18753
19562
  end
19563
+ local AddProps=SpawnTemplate.units[UnitID].AddPropAircraft
19564
+ if AddProps then
19565
+ if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then
19566
+ if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16)~=nil then
19567
+ local octal=SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16
19568
+ local decimal=UTILS.OctalToDecimal(octal)+UnitID-1
19569
+ SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16=string.format("%05d",UTILS.DecimalToOctal(decimal))
19570
+ else
19571
+ local STN=math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088))
19572
+ STN=STN+UnitID-1
19573
+ local OSTN=UTILS.DecimalToOctal(STN)
19574
+ SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16=string.format("%05d",OSTN)
19575
+ end
19576
+ end
19577
+ if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then
19578
+ if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN)~=nil then
19579
+ local octal=SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN
19580
+ local decimal=UTILS.OctalToDecimal(octal)+UnitID-1
19581
+ SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN=string.format("%04d",UTILS.DecimalToOctal(decimal))
19582
+ else
19583
+ local STN=math.floor(UTILS.RandomGaussian(504/2,nil,100,504))
19584
+ STN=STN+UnitID-1
19585
+ local OSTN=UTILS.DecimalToOctal(STN)
19586
+ SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN=string.format("%04d",OSTN)
19587
+ end
19588
+ end
19589
+ if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type(Callsign)~="number"then
19590
+ SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber=SpawnTemplate.units[UnitID].callsign[2]..SpawnTemplate.units[UnitID].callsign[3]
19591
+ end
19592
+ if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type(Callsign)~="number"then
19593
+ local CallsignName=SpawnTemplate.units[UnitID].callsign["name"]
19594
+ CallsignName=string.match(CallsignName,"^(%a+)")
19595
+ local label="NY"
19596
+ if not string.find(CallsignName," ")then
19597
+ label=string.upper(string.match(CallsignName,"^%a")..string.match(CallsignName,"%a$"))
19598
+ end
19599
+ SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel=label
19600
+ end
19601
+ if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then
19602
+ SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead=UnitID==1 and true or false
19603
+ end
19604
+ if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then
19605
+ SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead=UnitID==1 and true or false
19606
+ end
19607
+ end
19608
+ end
19609
+ for UnitID=1,#SpawnTemplate.units do
19610
+ if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.network then
19611
+ local team={}
19612
+ local isF16=string.find(SpawnTemplate.units[UnitID].type,"F-16",1,true)and true or false
19613
+ for ID=1,#SpawnTemplate.units do
19614
+ local member={}
19615
+ member.missionUnitId=ID
19616
+ if isF16 then
19617
+ member.TDOA=true
19618
+ end
19619
+ table.insert(team,member)
19620
+ end
19621
+ SpawnTemplate.units[UnitID].datalinks.Link16.network.teamMembers=team
19622
+ end
18754
19623
  end
18755
19624
  self:T3({"Template:",SpawnTemplate})
18756
19625
  return SpawnTemplate
@@ -19437,11 +20306,11 @@ function SPOT:onafterLasing(From,Event,To)
19437
20306
  self:T({From,Event,To})
19438
20307
  if self.Lasing then
19439
20308
  if self.Target and self.Target:IsAlive()then
19440
- self.SpotIR:setPoint(self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3())
20309
+ self.SpotIR:setPoint(self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/200):AddX(math.random(-100,100)/200):GetVec3())
19441
20310
  self.SpotLaser:setPoint(self.Target:GetPointVec3():AddY(1):GetVec3())
19442
20311
  self:__Lasing(0.2)
19443
20312
  elseif self.TargetCoord then
19444
- local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100,y=self.TargetCoord.y+math.random(-100,100)/100,z=self.TargetCoord.z}
20313
+ local irvec3={x=self.TargetCoord.x+math.random(-100,100)/200,y=self.TargetCoord.y+math.random(-100,100)/200,z=self.TargetCoord.z}
19445
20314
  local lsvec3={x=self.TargetCoord.x,y=self.TargetCoord.y,z=self.TargetCoord.z}
19446
20315
  self.SpotIR:setPoint(irvec3)
19447
20316
  self.SpotLaser:setPoint(lsvec3)
@@ -19824,7 +20693,7 @@ OBJECT={
19824
20693
  ClassName="OBJECT",
19825
20694
  ObjectName="",
19826
20695
  }
19827
- function OBJECT:New(ObjectName,Test)
20696
+ function OBJECT:New(ObjectName)
19828
20697
  local self=BASE:Inherit(self,BASE:New())
19829
20698
  self:F2(ObjectName)
19830
20699
  self.ObjectName=ObjectName
@@ -19836,7 +20705,7 @@ if DCSObject then
19836
20705
  local ObjectID=DCSObject:getID()
19837
20706
  return ObjectID
19838
20707
  end
19839
- BASE:E({"Cannot GetID",Name=self.ObjectName,Class=self:GetClassName()})
20708
+ self:E({"Cannot GetID",Name=self.ObjectName,Class=self:GetClassName()})
19840
20709
  return nil
19841
20710
  end
19842
20711
  function OBJECT:Destroy()
@@ -19845,7 +20714,7 @@ if DCSObject then
19845
20714
  DCSObject:destroy(false)
19846
20715
  return true
19847
20716
  end
19848
- BASE:E({"Cannot Destroy",Name=self.ObjectName,Class=self:GetClassName()})
20717
+ self:E({"Cannot Destroy",Name=self.ObjectName,Class=self:GetClassName()})
19849
20718
  return nil
19850
20719
  end
19851
20720
  IDENTIFIABLE={
@@ -23055,7 +23924,7 @@ if DCSControllable then
23055
23924
  local Controller=self:_GetController()
23056
23925
  if Controller then
23057
23926
  if self:IsAir()then
23058
- self:SetOption(AI.Option.Air.val.MISSILE_ATTACK,range)
23927
+ self:SetOption(AI.Option.Air.id.MISSILE_ATTACK,range)
23059
23928
  end
23060
23929
  end
23061
23930
  return self
@@ -24577,6 +25446,7 @@ if vec3 then
24577
25446
  local coord=COORDINATE:NewFromVec3(vec3)
24578
25447
  local Heading=self:GetHeading()
24579
25448
  coord.Heading=Heading
25449
+ return coord
24580
25450
  else
24581
25451
  BASE:E({"Cannot GetAverageCoordinate",Group=self,Alive=self:IsAlive()})
24582
25452
  return nil
@@ -25553,6 +26423,34 @@ local tankertask=self:EnRouteTaskTanker()
25553
26423
  self:PushTask(tankertask,delay+2)
25554
26424
  return self
25555
26425
  end
26426
+ function GROUP:GetGroupSTN()
26427
+ local tSTN={}
26428
+ local units=self:GetUnits()
26429
+ local gname=self:GetName()
26430
+ gname=string.gsub(gname,"(#%d+)$","")
26431
+ local report=REPORT:New()
26432
+ report:Add("Link16 S/TN Report")
26433
+ report:Add("Group: "..gname)
26434
+ report:Add("==================")
26435
+ for _,_unit in pairs(units)do
26436
+ local unit=_unit
26437
+ if unit and unit:IsAlive()then
26438
+ local STN,VCL,VCN,Lead=unit:GetSTN()
26439
+ local name=unit:GetName()
26440
+ tSTN[name]={
26441
+ STN=STN,
26442
+ VCL=VCL,
26443
+ VCN=VCN,
26444
+ Lead=Lead,
26445
+ }
26446
+ local lead=Lead==true and"(*)"or""
26447
+ report:Add(string.format("| %s%s %s %s",tostring(VCL),tostring(VCN),tostring(STN),lead))
26448
+ end
26449
+ end
26450
+ report:Add("==================")
26451
+ local text=report:Text()
26452
+ return tSTN,text
26453
+ end
25556
26454
  UNIT={
25557
26455
  ClassName="UNIT",
25558
26456
  UnitName=nil,
@@ -26347,6 +27245,30 @@ local name=self.UnitName
26347
27245
  local skill=_DATABASE.Templates.Units[name].Template.skill or"Random"
26348
27246
  return skill
26349
27247
  end
27248
+ function UNIT:GetSTN()
27249
+ self:F2(self.UnitName)
27250
+ local STN=nil
27251
+ local VCL=nil
27252
+ local VCN=nil
27253
+ local FGL=false
27254
+ local template=self:GetTemplate()
27255
+ if template.AddPropAircraft then
27256
+ if template.AddPropAircraft.STN_L16 then
27257
+ STN=template.AddPropAircraft.STN_L16
27258
+ elseif template.AddPropAircraft.SADL_TN then
27259
+ STN=template.AddPropAircraft.SADL_TN
27260
+ end
27261
+ VCN=template.AddPropAircraft.VoiceCallsignNumber
27262
+ VCL=template.AddPropAircraft.VoiceCallsignLabel
27263
+ end
27264
+ if template.datalinks and template.datalinks.Link16 and template.datalinks.Link16.settings then
27265
+ FGL=template.datalinks.Link16.settings.flightLead
27266
+ end
27267
+ if template.datalinks and template.datalinks.SADL and template.datalinks.SADL.settings then
27268
+ FGL=template.datalinks.SADL.settings.flightLead
27269
+ end
27270
+ return STN,VCL,VCN,FGL
27271
+ end
26350
27272
  CLIENT={
26351
27273
  ClassName="CLIENT",
26352
27274
  ClientName=nil,
@@ -26870,6 +27792,13 @@ AIRBASE.Normandy={
26870
27792
  ["Broglie"]="Broglie",
26871
27793
  ["Bernay_Saint_Martin"]="Bernay Saint Martin",
26872
27794
  ["Saint_Andre_de_lEure"]="Saint-Andre-de-lEure",
27795
+ ["Biggin_Hill"]="Biggin Hill",
27796
+ ["Manston"]="Manston",
27797
+ ["Detling"]="Detling",
27798
+ ["Lympne"]="Lympne",
27799
+ ["Abbeville_Drucat"]="Abbeville Drucat",
27800
+ ["Merville_Calonne"]="Merville Calonne",
27801
+ ["Saint_Omer_Wizernes"]="Saint-Omer Wizernes",
26873
27802
  }
26874
27803
  AIRBASE.PersianGulf={
26875
27804
  ["Abu_Dhabi_International_Airport"]="Abu Dhabi Intl",
@@ -27140,7 +28069,7 @@ end
27140
28069
  function AIRBASE:GetWarehouse()
27141
28070
  local warehouse=nil
27142
28071
  local airbase=self:GetDCSObject()
27143
- if airbase then
28072
+ if airbase and Airbase.getWarehouse then
27144
28073
  warehouse=airbase:getWarehouse()
27145
28074
  end
27146
28075
  return warehouse
@@ -28390,6 +29319,13 @@ self.launcherUnit=UNIT:Find(self.launcher)
28390
29319
  end
28391
29320
  self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
28392
29321
  self.lid=string.format("[%s] %s | ",self.typeName,self.name)
29322
+ if self.launcherUnit then
29323
+ self.releaseHeading=self.launcherUnit:GetHeading()
29324
+ self.releaseAltitudeASL=self.launcherUnit:GetAltitude()
29325
+ self.releaseAltitudeAGL=self.launcherUnit:GetAltitude(true)
29326
+ self.releaseCoordinate=self.launcherUnit:GetCoordinate()
29327
+ self.releasePitch=self.launcherUnit:GetPitch()
29328
+ end
28393
29329
  self:SetTimeStepTrack()
28394
29330
  self:SetDistanceInterceptPoint()
28395
29331
  local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
@@ -28445,7 +29381,7 @@ local target=nil
28445
29381
  if self.weapon then
28446
29382
  local object=self.weapon:getTarget()
28447
29383
  if object then
28448
- local category=object:getCategory()
29384
+ local category=Object.getCategory(object)
28449
29385
  local name=object:getName()
28450
29386
  self:T(self.lid..string.format("Got Target Object %s, category=%d",object:getName(),category))
28451
29387
  if category==Object.Category.UNIT then
@@ -28535,6 +29471,26 @@ end
28535
29471
  function WEAPON:GetImpactCoordinate()
28536
29472
  return self.impactCoord
28537
29473
  end
29474
+ function WEAPON:GetReleaseHeading(AccountForMagneticInclination)
29475
+ AccountForMagneticInclination=AccountForMagneticInclination or true
29476
+ if AccountForMagneticInclination then return UTILS.ClampAngle(self.releaseHeading-UTILS.GetMagneticDeclination())else return UTILS.ClampAngle(self.releaseHeading)end
29477
+ end
29478
+ function WEAPON:GetReleaseAltitudeASL()
29479
+ return self.releaseAltitudeASL
29480
+ end
29481
+ function WEAPON:GetReleaseAltitudeAGL()
29482
+ return self.releaseAltitudeAGL
29483
+ end
29484
+ function WEAPON:GetReleaseCoordinate()
29485
+ return self.releaseCoordinate
29486
+ end
29487
+ function WEAPON:GetReleasePitch()
29488
+ return self.releasePitch
29489
+ end
29490
+ function WEAPON:GetImpactHeading(AccountForMagneticInclination)
29491
+ AccountForMagneticInclination=AccountForMagneticInclination or true
29492
+ if AccountForMagneticInclination then return UTILS.ClampAngle(self.impactHeading-UTILS.GetMagneticDeclination())else return self.impactHeading end
29493
+ end
28538
29494
  function WEAPON:InAir()
28539
29495
  local inAir=nil
28540
29496
  if self.weapon then
@@ -28606,6 +29562,7 @@ if status then
28606
29562
  self.pos3=pos3
28607
29563
  self.vec3=UTILS.DeepCopy(self.pos3.p)
28608
29564
  self.coordinate:UpdateFromVec3(self.vec3)
29565
+ self.last_velocity=self.weapon:getVelocity()
28609
29566
  self.tracking=true
28610
29567
  if self.trackFunc then
28611
29568
  self.trackFunc(self,unpack(self.trackArg))
@@ -28633,6 +29590,7 @@ self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters",d))
28633
29590
  end
28634
29591
  self.impactVec3=ip or self.vec3
28635
29592
  self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
29593
+ self.impactHeading=UTILS.VecHdg(self.last_velocity)
28636
29594
  if self.impactMark then
28637
29595
  self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s",self.name,self.typeName,self.launcherName))
28638
29596
  end
@@ -29102,7 +30060,9 @@ STORAGE.version="0.0.1"
29102
30060
  function STORAGE:New(AirbaseName)
29103
30061
  local self=BASE:Inherit(self,BASE:New())
29104
30062
  self.airbase=Airbase.getByName(AirbaseName)
30063
+ if Airbase.getWarehouse then
29105
30064
  self.warehouse=self.airbase:getWarehouse()
30065
+ end
29106
30066
  self.lid=string.format("STORAGE %s",AirbaseName)
29107
30067
  return self
29108
30068
  end
@@ -32248,7 +33208,7 @@ self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
32248
33208
  end
32249
33209
  return self
32250
33210
  end
32251
- local targetcat=_target:getCategory()
33211
+ local targetcat=Object.getCategory(_target)
32252
33212
  local _targetUnit=nil
32253
33213
  local _targetgroup=nil
32254
33214
  self:T(string.format("*** Targetcat = %d",targetcat))
@@ -33391,7 +34351,7 @@ AirbaseNames=nil,
33391
34351
  }
33392
34352
  function ATC_GROUND:New(Airbases,AirbaseList)
33393
34353
  local self=BASE:Inherit(self,BASE:New())
33394
- self:E({self.ClassName,Airbases})
34354
+ self:T({self.ClassName,Airbases})
33395
34355
  self.Airbases=Airbases
33396
34356
  self.AirbaseList=AirbaseList
33397
34357
  self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
@@ -33470,7 +34430,7 @@ function(Client)
33470
34430
  if Client:IsAlive()then
33471
34431
  local IsOnGround=Client:InAir()==false
33472
34432
  for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
33473
- self:E(AirbaseID,AirbaseMeta.KickSpeed)
34433
+ self:T(AirbaseID,AirbaseMeta.KickSpeed)
33474
34434
  if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
33475
34435
  local NotInRunwayZone=true
33476
34436
  for ZoneRunwayID,ZoneRunway in pairs(AirbaseMeta.ZoneRunways)do
@@ -33479,7 +34439,7 @@ end
33479
34439
  if NotInRunwayZone then
33480
34440
  if IsOnGround then
33481
34441
  local Taxi=Client:GetState(self,"Taxi")
33482
- self:E(Taxi)
34442
+ self:T(Taxi)
33483
34443
  if Taxi==false then
33484
34444
  local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
33485
34445
  Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
@@ -33599,12 +34559,18 @@ KickSpeed=nil,
33599
34559
  }
33600
34560
  function ATC_GROUND_UNIVERSAL:New(AirbaseList)
33601
34561
  local self=BASE:Inherit(self,BASE:New())
33602
- self:E({self.ClassName})
34562
+ self:T({self.ClassName})
33603
34563
  self.Airbases={}
33604
34564
  for _name,_ in pairs(_DATABASE.AIRBASES)do
33605
34565
  self.Airbases[_name]={}
33606
34566
  end
33607
34567
  self.AirbaseList=AirbaseList
34568
+ if not self.AirbaseList then
34569
+ self.AirbaseList={}
34570
+ for _name,_ in pairs(_DATABASE.AIRBASES)do
34571
+ self.AirbaseList[_name]=_name
34572
+ end
34573
+ end
33608
34574
  self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
33609
34575
  for AirbaseID,Airbase in pairs(self.Airbases)do
33610
34576
  if Airbase.ZoneBoundary then
@@ -33703,12 +34669,13 @@ self:SetMaximumKickSpeed(UTILS.MiphToMps(MaximumKickSpeedMiph),Airbase)
33703
34669
  return self
33704
34670
  end
33705
34671
  function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
34672
+ self:I("_AirbaseMonitor")
33706
34673
  self.SetClient:ForEachClient(
33707
34674
  function(Client)
33708
34675
  if Client:IsAlive()then
33709
34676
  local IsOnGround=Client:InAir()==false
33710
34677
  for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
33711
- self:E(AirbaseID,AirbaseMeta.KickSpeed)
34678
+ self:T(AirbaseID,AirbaseMeta.KickSpeed)
33712
34679
  if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
33713
34680
  local NotInRunwayZone=true
33714
34681
  if AirbaseMeta.ZoneRunways then
@@ -33720,7 +34687,7 @@ end
33720
34687
  if NotInRunwayZone then
33721
34688
  if IsOnGround then
33722
34689
  local Taxi=Client:GetState(self,"Taxi")
33723
- self:E(Taxi)
34690
+ self:T(Taxi)
33724
34691
  if Taxi==false then
33725
34692
  local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
33726
34693
  Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
@@ -33832,7 +34799,7 @@ return true
33832
34799
  end
33833
34800
  function ATC_GROUND_UNIVERSAL:Start(RepeatScanSeconds)
33834
34801
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33835
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34802
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33836
34803
  return self
33837
34804
  end
33838
34805
  ATC_GROUND_CAUCASUS={
@@ -33846,7 +34813,7 @@ return self
33846
34813
  end
33847
34814
  function ATC_GROUND_CAUCASUS:Start(RepeatScanSeconds)
33848
34815
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33849
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34816
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33850
34817
  end
33851
34818
  ATC_GROUND_NEVADA={
33852
34819
  ClassName="ATC_GROUND_NEVADA",
@@ -33859,7 +34826,7 @@ return self
33859
34826
  end
33860
34827
  function ATC_GROUND_NEVADA:Start(RepeatScanSeconds)
33861
34828
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33862
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34829
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33863
34830
  end
33864
34831
  ATC_GROUND_NORMANDY={
33865
34832
  ClassName="ATC_GROUND_NORMANDY",
@@ -33872,7 +34839,7 @@ return self
33872
34839
  end
33873
34840
  function ATC_GROUND_NORMANDY:Start(RepeatScanSeconds)
33874
34841
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33875
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34842
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33876
34843
  end
33877
34844
  ATC_GROUND_PERSIANGULF={
33878
34845
  ClassName="ATC_GROUND_PERSIANGULF",
@@ -33884,20 +34851,20 @@ self:SetMaximumKickSpeedKmph(150)
33884
34851
  end
33885
34852
  function ATC_GROUND_PERSIANGULF:Start(RepeatScanSeconds)
33886
34853
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33887
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34854
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33888
34855
  end
33889
34856
  ATC_GROUND_MARIANAISLANDS={
33890
34857
  ClassName="ATC_GROUND_MARIANAISLANDS",
33891
34858
  }
33892
34859
  function ATC_GROUND_MARIANAISLANDS:New(AirbaseNames)
33893
- local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(self.Airbases,AirbaseNames))
34860
+ local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
33894
34861
  self:SetKickSpeedKmph(50)
33895
34862
  self:SetMaximumKickSpeedKmph(150)
33896
34863
  return self
33897
34864
  end
33898
34865
  function ATC_GROUND_MARIANAISLANDS:Start(RepeatScanSeconds)
33899
34866
  RepeatScanSeconds=RepeatScanSeconds or 0.05
33900
- self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,2,RepeatScanSeconds)
34867
+ self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
33901
34868
  end
33902
34869
  do
33903
34870
  DETECTION_BASE={
@@ -34050,6 +35017,28 @@ DetectionAccepted=false
34050
35017
  end
34051
35018
  end
34052
35019
  end
35020
+ if self.RadarBlur then
35021
+ MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose)
35022
+ local minheight=self.RadarBlurMinHeight or 250
35023
+ local thresheight=self.RadarBlurThresHeight or 90
35024
+ local thresblur=self.RadarBlurThresBlur or 85
35025
+ local dist=math.floor(Distance)
35026
+ if dist<=20 then
35027
+ thresheight=(((dist*dist)/400)*thresheight)
35028
+ thresblur=(((dist*dist)/400)*thresblur)
35029
+ end
35030
+ local fheight=math.floor(math.random(1,10000)/100)
35031
+ local fblur=math.floor(math.random(1,10000)/100)
35032
+ local unit=UNIT:FindByName(DetectedObjectName)
35033
+ if unit and unit:IsAlive()then
35034
+ local AGL=unit:GetAltitude(true)
35035
+ MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose)
35036
+ MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose)
35037
+ if fblur>thresblur then DetectionAccepted=false end
35038
+ if AGL<=minheight and fheight<thresheight then DetectionAccepted=false end
35039
+ MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose)
35040
+ end
35041
+ end
34053
35042
  if not self.DetectedObjects[DetectedObjectName]and TargetIsVisible and self.DistanceProbability then
34054
35043
  local DistanceFactor=Distance/4
34055
35044
  local DistanceProbabilityReversed=(1-self.DistanceProbability)*DistanceFactor
@@ -34210,6 +35199,13 @@ self._.FilterCategories[FilterCategories]=FilterCategories
34210
35199
  end
34211
35200
  return self
34212
35201
  end
35202
+ function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur)
35203
+ self.RadarBlur=true
35204
+ self.RadarBlurMinHeight=minheight or 250
35205
+ self.RadarBlurThresHeight=thresheight or 90
35206
+ self.RadarBlurThresBlur=thresblur or 85
35207
+ return self
35208
+ end
34213
35209
  end
34214
35210
  do
34215
35211
  function DETECTION_BASE:SetRefreshTimeInterval(RefreshTimeInterval)
@@ -39511,6 +40507,7 @@ soundpath="Range Soundfiles/",
39511
40507
  targetsheet=nil,
39512
40508
  targetpath=nil,
39513
40509
  targetprefix=nil,
40510
+ Coalition=nil,
39514
40511
  }
39515
40512
  RANGE.Defaults={
39516
40513
  goodhitrange=25,
@@ -39580,10 +40577,11 @@ IRExitRange={filename="IR-ExitRange.ogg",duration=3.10},
39580
40577
  RANGE.Names={}
39581
40578
  RANGE.MenuF10={}
39582
40579
  RANGE.MenuF10Root=nil
39583
- RANGE.version="2.7.1"
39584
- function RANGE:New(RangeName)
40580
+ RANGE.version="2.7.3"
40581
+ function RANGE:New(RangeName,Coalition)
39585
40582
  local self=BASE:Inherit(self,FSM:New())
39586
40583
  self.rangename=RangeName or"Practice Range"
40584
+ self.Coalition=Coalition
39587
40585
  self.lid=string.format("RANGE %s | ",self.rangename)
39588
40586
  local text=string.format("Script version %s - creating new RANGE object %s.",RANGE.version,self.rangename)
39589
40587
  self:I(self.lid..text)
@@ -39838,6 +40836,10 @@ end
39838
40836
  return self
39839
40837
  end
39840
40838
  function RANGE:SetSRSRangeControl(frequency,modulation,voice,culture,gender,relayunitname)
40839
+ if not self.instructmsrs then
40840
+ self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!")
40841
+ return self
40842
+ end
39841
40843
  self.rangecontrolfreq=frequency or 256
39842
40844
  self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
39843
40845
  self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
@@ -39853,6 +40855,10 @@ end
39853
40855
  return self
39854
40856
  end
39855
40857
  function RANGE:SetSRSRangeInstructor(frequency,modulation,voice,culture,gender,relayunitname)
40858
+ if not self.instructmsrs then
40859
+ self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!")
40860
+ return self
40861
+ end
39856
40862
  self.instructorfreq=frequency or 305
39857
40863
  self.instructmsrs:SetFrequencies(self.instructorfreq)
39858
40864
  self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
@@ -40118,7 +41124,13 @@ local _callsign=_unit:GetCallsign()
40118
41124
  local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)",_playername,_callsign,_unitName,_uid,_group:GetName(),_gid)
40119
41125
  self:T(self.lid..text)
40120
41126
  self.strafeStatus[_uid]=nil
41127
+ if self.Coalition then
41128
+ if EventData.IniCoalition==self.Coalition then
40121
41129
  self:ScheduleOnce(0.1,self._AddF10Commands,self,_unitName)
41130
+ end
41131
+ else
41132
+ self:ScheduleOnce(0.1,self._AddF10Commands,self,_unitName)
41133
+ end
40122
41134
  self.PlayerSettings[_playername]={}
40123
41135
  self.PlayerSettings[_playername].smokebombimpact=self.defaultsmokebomb
40124
41136
  self.PlayerSettings[_playername].flaredirecthits=false
@@ -48155,11 +49167,22 @@ local _assetattribute
48155
49167
  local _assetcategory
48156
49168
  local _assetairstart=false
48157
49169
  if _nassets>0 then
49170
+ local asset=_assets[1]
48158
49171
  _assetattribute=_assets[1].attribute
48159
49172
  _assetcategory=_assets[1].category
48160
49173
  _assetairstart=_assets[1].takeoffType and _assets[1].takeoffType==COORDINATE.WaypointType.TurningPoint or false
48161
49174
  if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
48162
49175
  if self.airbase and self.airbase:GetCoalition()==self:GetCoalition()then
49176
+ if self.airbase.storage then
49177
+ local nS=self.airbase.storage:GetAmount(asset.unittype)
49178
+ local nA=asset.nunits*request.nasset
49179
+ if nS<nA then
49180
+ local text=string.format("Warehouse %s: Request denied! DCS Warehouse has only %d assets of type %s ==> NOT enough to spawn the requested %d asset units (%d groups)",
49181
+ self.alias,nS,asset.unittype,nA,request.nasset)
49182
+ self:_InfoMessage(text,5)
49183
+ return false
49184
+ end
49185
+ end
48163
49186
  if self:IsRunwayOperational()or _assetairstart then
48164
49187
  if _assetairstart then
48165
49188
  else
@@ -48221,6 +49244,7 @@ local text=string.format("Warehouse %s: Request denied! Not close enough to spaw
48221
49244
  self:_InfoMessage(text,5)
48222
49245
  return false
48223
49246
  end
49247
+ elseif _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
48224
49248
  end
48225
49249
  end
48226
49250
  request.cargoassets=_assets
@@ -50089,7 +51113,9 @@ MANTIS.SamData={
50089
51113
  ["SA-20A"]={Range=150,Blindspot=5,Height=27,Type="Long",Radar="S-300PMU1"},
50090
51114
  ["SA-20B"]={Range=200,Blindspot=4,Height=27,Type="Long",Radar="S-300PMU2"},
50091
51115
  ["HQ-2"]={Range=50,Blindspot=6,Height=35,Type="Medium",Radar="HQ_2_Guideline_LN"},
50092
- ["SHORAD"]={Range=3,Blindspot=0,Height=3,Type="Short",Radar="Igla"}
51116
+ ["SHORAD"]={Range=3,Blindspot=0,Height=3,Type="Short",Radar="Igla"},
51117
+ ["TAMIR IDFA"]={Range=20,Blindspot=0.6,Height=12.3,Type="Short",Radar="IRON_DOME_LN"},
51118
+ ["STUNNER IDFA"]={Range=250,Blindspot=1,Height=45,Type="Long",Radar="DAVID_SLING_LN"},
50093
51119
  }
50094
51120
  MANTIS.SamDataHDS={
50095
51121
  ["SA-2 HDS"]={Range=56,Blindspot=7,Height=30,Type="Medium",Radar="V759"},
@@ -50237,7 +51263,7 @@ end
50237
51263
  if self.HQ_Template_CC then
50238
51264
  self.HQ_CC=GROUP:FindByName(self.HQ_Template_CC)
50239
51265
  end
50240
- self.version="0.8.14"
51266
+ self.version="0.8.15"
50241
51267
  self:I(string.format("***** Starting MANTIS Version %s *****",self.version))
50242
51268
  self:SetStartState("Stopped")
50243
51269
  self:AddTransition("Stopped","Start","Running")
@@ -50268,11 +51294,13 @@ local radius=radius or 5000
50268
51294
  self.grouping=radius
50269
51295
  return self
50270
51296
  end
50271
- function MANTIS:AddScootZones(ZoneSet,Number)
51297
+ function MANTIS:AddScootZones(ZoneSet,Number,Random,Formation)
50272
51298
  self:T(self.lid.." AddScootZones")
50273
51299
  self.SkateZones=ZoneSet
50274
51300
  self.SkateNumber=Number or 3
50275
51301
  self.shootandscoot=true
51302
+ self.ScootRandom=Random
51303
+ self.ScootFormation=Formation or"Cone"
50276
51304
  return self
50277
51305
  end
50278
51306
  function MANTIS:AddZones(AcceptZones,RejectZones,ConflictZones)
@@ -50972,8 +52000,8 @@ self.ShoradLink=true
50972
52000
  self.Shorad.Groupset=self.ShoradGroupSet
50973
52001
  self.Shorad.debug=self.debug
50974
52002
  end
50975
- if self.shootandscoot and self.SkateZones then
50976
- self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3)
52003
+ if self.shootandscoot and self.SkateZones and self.Shorad then
52004
+ self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3,self.ScootRandom,self.ScootFormation)
50977
52005
  end
50978
52006
  self:__Status(-math.random(1,10))
50979
52007
  return self
@@ -51080,6 +52108,9 @@ UseEmOnOff=true,
51080
52108
  shootandscoot=false,
51081
52109
  SkateNumber=3,
51082
52110
  SkateZones=nil,
52111
+ minscootdist=100,
52112
+ minscootdist=3000,
52113
+ scootrandomcoord=false,
51083
52114
  }
51084
52115
  do
51085
52116
  SHORAD.Harms={
@@ -51123,7 +52154,7 @@ self.DefenseLowProb=70
51123
52154
  self.DefenseHighProb=90
51124
52155
  self.UseEmOnOff=true
51125
52156
  if UseEmOnOff==false then self.UseEmOnOff=UseEmOnOff end
51126
- self:I("*** SHORAD - Started Version 0.3.2")
52157
+ self:I("*** SHORAD - Started Version 0.3.4")
51127
52158
  self.lid=string.format("SHORAD %s | ",self.name)
51128
52159
  self:_InitState()
51129
52160
  self:HandleEvent(EVENTS.Shot,self.HandleEventShot)
@@ -51153,11 +52184,13 @@ math.random()
51153
52184
  end
51154
52185
  return self
51155
52186
  end
51156
- function SHORAD:AddScootZones(ZoneSet,Number)
52187
+ function SHORAD:AddScootZones(ZoneSet,Number,Random,Formation)
51157
52188
  self:T(self.lid.." AddScootZones")
51158
52189
  self.SkateZones=ZoneSet
51159
52190
  self.SkateNumber=Number or 3
51160
52191
  self.shootandscoot=true
52192
+ self.scootrandomcoord=Random
52193
+ self.scootformation=Formation or"Cone"
51161
52194
  return self
51162
52195
  end
51163
52196
  function SHORAD:SwitchDebug(onoff)
@@ -51421,8 +52454,8 @@ end
51421
52454
  function SHORAD:onafterShootAndScoot(From,Event,To,Shorad)
51422
52455
  self:T({From,Event,To})
51423
52456
  local possibleZones={}
51424
- local mindist=100
51425
- local maxdist=3000
52457
+ local mindist=self.minscootdist or 100
52458
+ local maxdist=self.maxscootdist or 3000
51426
52459
  if Shorad and Shorad:IsAlive()then
51427
52460
  local NowCoord=Shorad:GetCoordinate()
51428
52461
  for _,_zone in pairs(self.SkateZones.Set)do
@@ -51438,7 +52471,11 @@ local rand=math.floor(math.random(1,#possibleZones*1000)/1000+0.5)
51438
52471
  if rand==0 then rand=1 end
51439
52472
  self:T(self.lid.." ShootAndScoot to zone "..rand)
51440
52473
  local ToCoordinate=possibleZones[rand]:GetCoordinate()
51441
- Shorad:RouteGroundTo(ToCoordinate,20,"Cone",1)
52474
+ if self.scootrandomcoord then
52475
+ ToCoordinate=possibleZones[rand]:GetRandomCoordinate(nil,nil,{land.SurfaceType.LAND,land.SurfaceType.ROAD})
52476
+ end
52477
+ local formation=self.scootformation or"Cone"
52478
+ Shorad:RouteGroundTo(ToCoordinate,20,formation,1)
51442
52479
  end
51443
52480
  end
51444
52481
  return self
@@ -51469,7 +52506,7 @@ self:__CalculateHitZone(20,ShootingWeapon,pos0,fheight,EventData.IniGroup)
51469
52506
  end
51470
52507
  return self
51471
52508
  end
51472
- local targetcat=targetdata:getCategory()
52509
+ local targetcat=Object.getCategory(targetdata)
51473
52510
  self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
51474
52511
  self:T({targetdata})
51475
52512
  local targetunit=nil
@@ -61519,7 +62556,7 @@ DELIMITER="Punto",
61519
62556
  }
61520
62557
  ATIS.locale="en"
61521
62558
  _ATIS={}
61522
- ATIS.version="0.10.3"
62559
+ ATIS.version="0.10.4"
61523
62560
  function ATIS:New(AirbaseName,Frequency,Modulation)
61524
62561
  local self=BASE:Inherit(self,FSM:New())
61525
62562
  self.airbasename=AirbaseName
@@ -61828,7 +62865,16 @@ self:E(self.lid..string.format("EXPERIMENTAL: Starting ATIS for Helipad %s! SRS
61828
62865
  self.ATISforFARPs=true
61829
62866
  self.useSRS=true
61830
62867
  end
62868
+ if type(self.frequency)=="table"then
62869
+ local frequency=table.concat(self.frequency,"/")
62870
+ local modulation=self.modulation
62871
+ if type(self.modulation)=="table"then
62872
+ modulation=table.concat(self.modulation,"/")
62873
+ end
62874
+ self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %s MHz Modulation=%s",ATIS.version,self.airbasename,frequency,modulation))
62875
+ else
61831
62876
  self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d",ATIS.version,self.airbasename,self.frequency,self.modulation))
62877
+ end
61832
62878
  if not self.useSRS then
61833
62879
  self.radioqueue=RADIOQUEUE:New(self.frequency,self.modulation,string.format("ATIS %s",self.airbasename))
61834
62880
  self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
@@ -61860,7 +62906,17 @@ if ru then
61860
62906
  relayunitstatus=tostring(ru:IsAlive())
61861
62907
  end
61862
62908
  end
61863
- local text=string.format("State %s: Freq=%.3f MHz %s",fsmstate,self.frequency,UTILS.GetModulationName(self.modulation))
62909
+ local text=""
62910
+ if type(self.frequency)=="table"then
62911
+ local frequency=table.concat(self.frequency,"/")
62912
+ local modulation=self.modulation
62913
+ if type(self.modulation)=="table"then
62914
+ modulation=table.concat(self.modulation,"/")
62915
+ end
62916
+ text=string.format("State %s: Freq=%s MHz %s",fsmstate,frequency,modulation)
62917
+ else
62918
+ text=string.format("State %s: Freq=%.3f MHz %s",fsmstate,self.frequency,UTILS.GetModulationName(self.modulation))
62919
+ end
61864
62920
  if self.useSRS then
61865
62921
  text=text..string.format(", SRS path=%s (%s), gender=%s, culture=%s, voice=%s",tostring(self.msrs.path),tostring(self.msrs.port),tostring(self.msrs.gender),tostring(self.msrs.culture),tostring(self.msrs.voice))
61866
62922
  else
@@ -62457,7 +63513,7 @@ end
62457
63513
  alltext=alltext..";\n"..subtitle
62458
63514
  local _RUNACT
62459
63515
  if not self.ATISforFARPs then
62460
- local subtitle
63516
+ local subtitle=""
62461
63517
  if runwayLanding then
62462
63518
  local actrun=self.gettext:GetEntry("ACTIVELANDING",self.locale)
62463
63519
  subtitle=string.format("%s %s",actrun,runwayLanding)
@@ -62745,7 +63801,17 @@ function ATIS:UpdateMarker(information,runact,wind,altimeter,temperature)
62745
63801
  if self.markerid then
62746
63802
  self.airbase:GetCoordinate():RemoveMark(self.markerid)
62747
63803
  end
62748
- local text=string.format("ATIS on %.3f %s, %s:\n",self.frequency,UTILS.GetModulationName(self.modulation),tostring(information))
63804
+ local text=""
63805
+ if type(self.frequency)=="table"then
63806
+ local frequency=table.concat(self.frequency,"/")
63807
+ local modulation=self.modulation
63808
+ if type(modulation)=="table"then
63809
+ modulation=table.concat(self.modulation,"/")
63810
+ end
63811
+ text=string.format("ATIS on %s %s, %s:\n",tostring(frequency),tostring(modulation),tostring(information))
63812
+ else
63813
+ text=string.format("ATIS on %.3f %s, %s:\n",self.frequency,UTILS.GetModulationName(self.modulation),tostring(information))
63814
+ end
62749
63815
  text=text..string.format("%s\n",tostring(runact))
62750
63816
  text=text..string.format("%s\n",tostring(wind))
62751
63817
  text=text..string.format("%s\n",tostring(altimeter))
@@ -63192,7 +64258,7 @@ MOVE="move",
63192
64258
  SHIP="ship",
63193
64259
  BEACON="beacon",
63194
64260
  }
63195
- CTLD.UnitTypes={
64261
+ CTLD.UnitTypeCapabilities={
63196
64262
  ["SA342Mistral"]={type="SA342Mistral",crates=false,troops=true,cratelimit=0,trooplimit=4,length=12,cargoweightlimit=400},
63197
64263
  ["SA342L"]={type="SA342L",crates=false,troops=true,cratelimit=0,trooplimit=2,length=12,cargoweightlimit=400},
63198
64264
  ["SA342M"]={type="SA342M",crates=false,troops=true,cratelimit=0,trooplimit=4,length=12,cargoweightlimit=400},
@@ -63209,7 +64275,7 @@ CTLD.UnitTypes={
63209
64275
  ["AH-64D_BLK_II"]={type="AH-64D_BLK_II",crates=false,troops=true,cratelimit=0,trooplimit=2,length=17,cargoweightlimit=200},
63210
64276
  ["Bronco-OV-10A"]={type="Bronco-OV-10A",crates=false,troops=true,cratelimit=0,trooplimit=5,length=13,cargoweightlimit=1450},
63211
64277
  }
63212
- CTLD.version="1.0.41"
64278
+ CTLD.version="1.0.44"
63213
64279
  function CTLD:New(Coalition,Prefixes,Alias)
63214
64280
  local self=BASE:Inherit(self,FSM:New())
63215
64281
  BASE:T({Coalition,Prefixes,Alias})
@@ -63254,6 +64320,8 @@ self:AddTransition("*","TroopsRTB","*")
63254
64320
  self:AddTransition("*","CratesDropped","*")
63255
64321
  self:AddTransition("*","CratesBuild","*")
63256
64322
  self:AddTransition("*","CratesRepaired","*")
64323
+ self:AddTransition("*","CratesBuildStarted","*")
64324
+ self:AddTransition("*","CratesRepairStarted","*")
63257
64325
  self:AddTransition("*","Load","*")
63258
64326
  self:AddTransition("*","Save","*")
63259
64327
  self:AddTransition("*","Stop","Stopped")
@@ -63349,7 +64417,7 @@ function CTLD:_GetUnitCapabilities(Unit)
63349
64417
  self:T(self.lid.." _GetUnitCapabilities")
63350
64418
  local _unit=Unit
63351
64419
  local unittype=_unit:GetTypeName()
63352
- local capabilities=self.UnitTypes[unittype]
64420
+ local capabilities=self.UnitTypeCapabilities[unittype]
63353
64421
  if not capabilities or capabilities=={}then
63354
64422
  capabilities={}
63355
64423
  capabilities.troops=false
@@ -63673,6 +64741,7 @@ local desttimer=TIMER:New(function()NearestGroup:Destroy(false)end,self)
63673
64741
  desttimer:Start(self.repairtime-1)
63674
64742
  local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,object,true,NearestGroup:GetCoordinate())
63675
64743
  buildtimer:Start(self.repairtime)
64744
+ self:__CratesRepairStarted(1,Group,Unit)
63676
64745
  else
63677
64746
  if not Engineering then
63678
64747
  self:_SendMessage("Can't repair this unit with "..build.Name,10,false,Group)
@@ -63997,6 +65066,34 @@ self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddis
63997
65066
  end
63998
65067
  return self
63999
65068
  end
65069
+ function CTLD:_RemoveCratesNearby(_group,_unit)
65070
+ self:T(self.lid.." _RemoveCratesNearby")
65071
+ local finddist=self.CrateDistance or 35
65072
+ local crates,number=self:_FindCratesNearby(_group,_unit,finddist,true)
65073
+ if number>0 then
65074
+ local text=REPORT:New("Removing Crates Found Nearby:")
65075
+ text:Add("------------------------------------------------------------")
65076
+ for _,_entry in pairs(crates)do
65077
+ local entry=_entry
65078
+ local name=entry:GetName()
65079
+ local dropped=entry:WasDropped()
65080
+ if dropped then
65081
+ text:Add(string.format("Crate for %s, %dkg removed",name,entry.PerCrateMass))
65082
+ else
65083
+ text:Add(string.format("Crate for %s, %dkg removed",name,entry.PerCrateMass))
65084
+ end
65085
+ entry:GetPositionable():Destroy(false)
65086
+ end
65087
+ if text:GetCount()==1 then
65088
+ text:Add(" N O N E")
65089
+ end
65090
+ text:Add("------------------------------------------------------------")
65091
+ self:_SendMessage(text:Text(),30,true,_group)
65092
+ else
65093
+ self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist),10,false,_group)
65094
+ end
65095
+ return self
65096
+ end
64000
65097
  function CTLD:_GetDistance(_point1,_point2)
64001
65098
  self:T(self.lid.." _GetDistance")
64002
65099
  if _point1 and _point2 then
@@ -64335,6 +65432,26 @@ else
64335
65432
  return false
64336
65433
  end
64337
65434
  end
65435
+ function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
65436
+ local Positions={}
65437
+ local template=_DATABASE:GetGroupTemplate(Template)
65438
+ UTILS.PrintTableToLog(template)
65439
+ local numbertroops=#template.units
65440
+ local newcenter=Coordinate:Translate(Radius,((Heading+270)%360))
65441
+ for i=1,360,math.floor(360/numbertroops)do
65442
+ local phead=((Heading+270+i)%360)
65443
+ local post=newcenter:Translate(Radius,phead)
65444
+ local pos1=post:GetVec2()
65445
+ local p1t={
65446
+ x=pos1.x,
65447
+ y=pos1.y,
65448
+ heading=phead,
65449
+ }
65450
+ table.insert(Positions,p1t)
65451
+ end
65452
+ UTILS.PrintTableToLog(Positions)
65453
+ return Positions
65454
+ end
64338
65455
  function CTLD:_UnloadTroops(Group,Unit)
64339
65456
  self:T(self.lid.." _UnloadTroops")
64340
65457
  local droppingatbase=false
@@ -64375,14 +65492,25 @@ factor=cargo:GetCratesNeeded()or 1
64375
65492
  zoneradius=Unit:GetVelocityMPS()or 100
64376
65493
  end
64377
65494
  local zone=ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
64378
- local randomcoord=zone:GetRandomCoordinate(10,30*factor):GetVec2()
65495
+ local randomcoord=zone:GetRandomCoordinate(10,30*factor)
65496
+ local heading=Group:GetHeading()or 0
65497
+ if hoverunload or grounded then
65498
+ randomcoord=Group:GetCoordinate()
65499
+ local Angle=(heading+270)%360
65500
+ local offset=hoverunload and 1.5 or 5
65501
+ randomcoord:Translate(offset,Angle,nil,true)
65502
+ end
65503
+ local tempcount=0
64379
65504
  for _,_template in pairs(temptable)do
64380
65505
  self.TroopCounter=self.TroopCounter+1
65506
+ tempcount=tempcount+1
64381
65507
  local alias=string.format("%s-%d",_template,math.random(1,100000))
65508
+ local rad=2.5+tempcount
65509
+ local Positions=self:_GetUnitPositions(randomcoord,rad,heading,_template)
64382
65510
  self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
64383
- :InitRandomizeUnits(true,20,2)
64384
65511
  :InitDelayOff()
64385
- :SpawnFromVec2(randomcoord)
65512
+ :InitSetUnitAbsolutePositions(Positions)
65513
+ :SpawnFromVec2(randomcoord:GetVec2())
64386
65514
  self:__TroopsDeployed(1,Group,Unit,self.DroppedTroops[self.TroopCounter],type)
64387
65515
  end
64388
65516
  cargo:SetWasDropped(true)
@@ -64581,6 +65709,7 @@ if self.buildtime and self.buildtime>0 then
64581
65709
  local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate())
64582
65710
  buildtimer:Start(self.buildtime)
64583
65711
  self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
65712
+ self:__CratesBuildStarted(1,Group,Unit)
64584
65713
  else
64585
65714
  self:_BuildObjectFromCrates(Group,Unit,build)
64586
65715
  end
@@ -64825,8 +65954,14 @@ local capabilities=self:_GetUnitCapabilities(_unit)
64825
65954
  local cantroops=capabilities.troops
64826
65955
  local cancrates=capabilities.crates
64827
65956
  local topmenu=MENU_GROUP:New(_group,"CTLD",nil)
64828
- local toptroops=MENU_GROUP:New(_group,"Manage Troops",topmenu)
64829
- local topcrates=MENU_GROUP:New(_group,"Manage Crates",topmenu)
65957
+ local toptroops=nil
65958
+ local topcrates=nil
65959
+ if cantroops then
65960
+ toptroops=MENU_GROUP:New(_group,"Manage Troops",topmenu)
65961
+ end
65962
+ if cancrates then
65963
+ topcrates=MENU_GROUP:New(_group,"Manage Crates",topmenu)
65964
+ end
64830
65965
  local listmenu=MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu,self._ListCargo,self,_group,_unit)
64831
65966
  local invtry=MENU_GROUP_COMMAND:New(_group,"Inventory",topmenu,self._ListInventory,self,_group,_unit)
64832
65967
  local rbcns=MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu,self._ListRadioBeacons,self,_group,_unit)
@@ -64868,6 +66003,7 @@ if cancrates then
64868
66003
  local loadmenu=MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates,self._LoadCratesNearby,self,_group,_unit)
64869
66004
  local cratesmenu=MENU_GROUP:New(_group,"Get Crates",topcrates)
64870
66005
  local packmenu=MENU_GROUP_COMMAND:New(_group,"Pack crates",topcrates,self._PackCratesNearby,self,_group,_unit)
66006
+ local removecratesmenu=MENU_GROUP:New(_group,"Remove crates",topcrates)
64871
66007
  if self.usesubcats then
64872
66008
  local subcatmenus={}
64873
66009
  for _name,_entry in pairs(self.subcats)do
@@ -64902,6 +66038,7 @@ menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrat
64902
66038
  end
64903
66039
  end
64904
66040
  listmenu=MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates,self._ListCratesNearby,self,_group,_unit)
66041
+ removecrates=MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu,self._RemoveCratesNearby,self,_group,_unit)
64905
66042
  local unloadmenu=MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates,self._UnloadCrates,self,_group,_unit)
64906
66043
  if not self.nobuildmenu then
64907
66044
  local buildmenu=MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates,self._BuildCrates,self,_group,_unit)
@@ -65402,7 +66539,7 @@ self:_SendMessage(string.format("Negative, need to be closer than %dnm to a zone
65402
66539
  end
65403
66540
  return self
65404
66541
  end
65405
- function CTLD:UnitCapabilities(Unittype,Cancrates,Cantroops,Cratelimit,Trooplimit,Length,Maxcargoweight)
66542
+ function CTLD:SetUnitCapabilities(Unittype,Cancrates,Cantroops,Cratelimit,Trooplimit,Length,Maxcargoweight)
65406
66543
  self:T(self.lid.." UnitCapabilities")
65407
66544
  local unittype=nil
65408
66545
  local unit=nil
@@ -65416,7 +66553,7 @@ return self
65416
66553
  end
65417
66554
  local length=20
65418
66555
  local maxcargo=500
65419
- local existingcaps=self.UnitTypes[unittype]
66556
+ local existingcaps=self.UnitTypeCapabilities[unittype]
65420
66557
  if existingcaps then
65421
66558
  length=existingcaps.length or 20
65422
66559
  maxcargo=existingcaps.cargoweightlimit or 500
@@ -65429,7 +66566,12 @@ capabilities.cratelimit=Cratelimit or 0
65429
66566
  capabilities.trooplimit=Trooplimit or 0
65430
66567
  capabilities.length=Length or length
65431
66568
  capabilities.cargoweightlimit=Maxcargoweight or maxcargo
65432
- self.UnitTypes[unittype]=capabilities
66569
+ self.UnitTypeCapabilities[unittype]=capabilities
66570
+ return self
66571
+ end
66572
+ function CTLD:UnitCapabilities(Unittype,Cancrates,Cantroops,Cratelimit,Trooplimit,Length,Maxcargoweight)
66573
+ self:I(self.lid.."This function been replaced with `SetUnitCapabilities()` - pls use the new one going forward!")
66574
+ self:SetUnitCapabilities(Unittype,Cancrates,Cantroops,Cratelimit,Trooplimit,Length,Maxcargoweight)
65433
66575
  return self
65434
66576
  end
65435
66577
  function CTLD:IsCorrectHover(Unit)
@@ -77804,8 +78946,9 @@ AltBackend=nil,
77804
78946
  ConfigFileName="Moose_MSRS.lua",
77805
78947
  ConfigFilePath="Config\\",
77806
78948
  ConfigLoaded=false,
78949
+ ttsprovider="Microsoft",
77807
78950
  }
77808
- MSRS.version="0.1.2"
78951
+ MSRS.version="0.1.3"
77809
78952
  MSRS.Voices={
77810
78953
  Microsoft={
77811
78954
  ["Hedda"]="Microsoft Hedda Desktop",
@@ -77926,8 +79069,7 @@ Backend.Vars.Volume=Volume
77926
79069
  Backend.Functions=Backend.Functions or{}
77927
79070
  return self:_NewAltBackend(Backend)
77928
79071
  end
77929
- local success=self:LoadConfigFile(nil,nil,self.ConfigLoaded)
77930
- if(not success)and(not self.ConfigLoaded)then
79072
+ if not self.ConfigLoaded then
77931
79073
  self:SetPath(PathToSRS)
77932
79074
  self:SetPort()
77933
79075
  self:SetFrequencies(Frequency)
@@ -77966,7 +79108,7 @@ while(self.path:sub(-1)=="/"or self.path:sub(-1)==[[\]])and n<=nmax do
77966
79108
  self.path=self.path:sub(1,#self.path-1)
77967
79109
  n=n+1
77968
79110
  end
77969
- self:I(string.format("SRS path=%s",self:GetPath()))
79111
+ self:T(string.format("SRS path=%s",self:GetPath()))
77970
79112
  end
77971
79113
  return self
77972
79114
  end
@@ -78072,6 +79214,7 @@ self.APIKey=PathToCredentials
78072
79214
  self.provider="gcloud"
78073
79215
  self.GRPCOptions.DefaultProvider="gcloud"
78074
79216
  self.GRPCOptions.gcloud.key=PathToCredentials
79217
+ self.ttsprovider="Google"
78075
79218
  end
78076
79219
  return self
78077
79220
  end
@@ -78084,6 +79227,14 @@ self.GRPCOptions.gcloud.key=APIKey
78084
79227
  end
78085
79228
  return self
78086
79229
  end
79230
+ function MSRS:SetTTSProviderGoogle()
79231
+ self.ttsprovider="Google"
79232
+ return self
79233
+ end
79234
+ function MSRS:SetTTSProviderMicrosoft()
79235
+ self.ttsprovider="Microsoft"
79236
+ return self
79237
+ end
78087
79238
  function MSRS:Help()
78088
79239
  local path=self:GetPath()or STTS.DIRECTORY
78089
79240
  local exe=STTS.EXECUTABLE or"DCS-SR-ExternalAudio.exe"
@@ -78269,16 +79420,22 @@ if coordinate then
78269
79420
  local lat,lon,alt=self:_GetLatLongAlt(coordinate)
78270
79421
  command=command..string.format(" -L %.4f -O %.4f -A %d",lat,lon,alt)
78271
79422
  end
78272
- if self.google then
79423
+ if self.google and self.ttsprovider=="Google"then
78273
79424
  command=command..string.format(' --ssml -G "%s"',self.google)
78274
79425
  end
78275
79426
  self:T("MSRS command="..command)
78276
79427
  return command
78277
79428
  end
78278
- function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded)
79429
+ function MSRS:LoadConfigFile(Path,Filename)
79430
+ if lfs==nil then
79431
+ env.info("*****Note - lfs and os need to be desanitized for MSRS to work!")
79432
+ return false
79433
+ end
78279
79434
  local path=Path or lfs.writedir()..MSRS.ConfigFilePath
78280
79435
  local file=Filename or MSRS.ConfigFileName or"Moose_MSRS.lua"
78281
- if UTILS.CheckFileExists(path,file)and not ConfigLoaded then
79436
+ local pathandfile=path..file
79437
+ local filexsists=UTILS.FileExists(pathandfile)
79438
+ if filexsists and not MSRS.ConfigLoaded then
78282
79439
  assert(loadfile(path..file))()
78283
79440
  if MSRS_Config then
78284
79441
  if self then
@@ -78293,8 +79450,11 @@ end
78293
79450
  self.culture=MSRS_Config.Culture or"en-GB"
78294
79451
  self.gender=MSRS_Config.Gender or"male"
78295
79452
  self.google=MSRS_Config.Google
79453
+ if MSRS_Config.Provider then
79454
+ self.ttsprovider=MSRS_Config.Provider
79455
+ end
78296
79456
  self.Label=MSRS_Config.Label or"MSRS"
78297
- self.voice=MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel
79457
+ self.voice=MSRS_Config.Voice
78298
79458
  if MSRS_Config.GRPC then
78299
79459
  self.provider=MSRS_Config.GRPC.DefaultProvider
78300
79460
  if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider]then
@@ -78317,8 +79477,11 @@ end
78317
79477
  MSRS.culture=MSRS_Config.Culture or"en-GB"
78318
79478
  MSRS.gender=MSRS_Config.Gender or"male"
78319
79479
  MSRS.google=MSRS_Config.Google
79480
+ if MSRS_Config.Provider then
79481
+ MSRS.ttsprovider=MSRS_Config.Provider
79482
+ end
78320
79483
  MSRS.Label=MSRS_Config.Label or"MSRS"
78321
- MSRS.voice=MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel
79484
+ MSRS.voice=MSRS_Config.Voice
78322
79485
  if MSRS_Config.GRPC then
78323
79486
  MSRS.provider=MSRS_Config.GRPC.DefaultProvider
78324
79487
  if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider]then
@@ -78331,9 +79494,10 @@ end
78331
79494
  MSRS.ConfigLoaded=true
78332
79495
  end
78333
79496
  end
78334
- env.info("MSRS - Sucessfully loaded default configuration from disk!",false)
78335
- else
78336
- env.info("MSRS - Cannot load default configuration from disk!",false)
79497
+ env.info("MSRS - Successfully loaded default configuration from disk!",false)
79498
+ end
79499
+ if not filexsists then
79500
+ env.info("MSRS - Cannot find default configuration file!",false)
78337
79501
  return false
78338
79502
  end
78339
79503
  return true
@@ -78502,7 +79666,7 @@ self.lid=string.format("MSRSQUEUE %s | ",self.alias)
78502
79666
  return self
78503
79667
  end
78504
79668
  function MSRSQUEUE:Clear()
78505
- self:I(self.lid.."Clearning MSRSQUEUE")
79669
+ self:I(self.lid.."Clearing MSRSQUEUE")
78506
79670
  self.queue={}
78507
79671
  return self
78508
79672
  end
@@ -78559,7 +79723,7 @@ end
78559
79723
  transmission.gender=gender
78560
79724
  transmission.culture=culture
78561
79725
  transmission.voice=voice
78562
- transmission.gender=volume
79726
+ transmission.volume=volume
78563
79727
  transmission.label=label
78564
79728
  transmission.coordinate=coordinate
78565
79729
  self:AddTransmission(transmission)
@@ -78666,6 +79830,7 @@ end
78666
79830
  self:_CheckRadioQueue(dt)
78667
79831
  end
78668
79832
  end
79833
+ MSRS.LoadConfigFile()
78669
79834
  COMMANDCENTER={
78670
79835
  ClassName="COMMANDCENTER",
78671
79836
  CommandCenterName="",