@jtff/miztemplate-lib 3.1.7 → 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.
- package/lua/lib/Moose_.lua +1123 -103
- package/lua/lib/{mist_4_5_107.lua → mist_4_5_122.lua} +773 -351
- package/package.json +1 -1
- package/scripts/inject-scripts.js +1 -1
package/lua/lib/Moose_.lua
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
env.info('*** MOOSE GITHUB Commit Hash ID: 2023-
|
|
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,20 +1283,32 @@ 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
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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"
|
|
1290
1300
|
else
|
|
1291
|
-
|
|
1301
|
+
local value
|
|
1302
|
+
if tostring(v)=="true"or tostring(v)=="false"or tonumber(v)~=nil then
|
|
1303
|
+
value=v
|
|
1304
|
+
else
|
|
1305
|
+
value='"'..tostring(v)..'"'
|
|
1306
|
+
end
|
|
1307
|
+
env.info(string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n")
|
|
1308
|
+
text=text..string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n"
|
|
1292
1309
|
end
|
|
1293
1310
|
end
|
|
1311
|
+
return text
|
|
1294
1312
|
end
|
|
1295
1313
|
function UTILS.TableShow(tbl,loc,indent,tableshow_tbls)
|
|
1296
1314
|
tableshow_tbls=tableshow_tbls 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:
|
|
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={},
|
|
@@ -6618,7 +6936,7 @@ if Event.weapon then
|
|
|
6618
6936
|
Event.Weapon=Event.weapon
|
|
6619
6937
|
Event.WeaponName=Event.Weapon:getTypeName()
|
|
6620
6938
|
Event.WeaponUNIT=CLIENT:Find(Event.Weapon,'',true)
|
|
6621
|
-
Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
|
6939
|
+
Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
|
|
6622
6940
|
Event.WeaponCoalition=Event.WeaponUNIT and Event.Weapon:getCoalition()
|
|
6623
6941
|
Event.WeaponCategory=Event.WeaponUNIT and Event.Weapon:getDesc().category
|
|
6624
6942
|
Event.WeaponTypeName=Event.WeaponUNIT and Event.Weapon:getTypeName()
|
|
@@ -8166,7 +8484,13 @@ if Delay and Delay>0 then
|
|
|
8166
8484
|
self:ScheduleOnce(Delay,ZONE_BASE.UndrawZone,self)
|
|
8167
8485
|
else
|
|
8168
8486
|
if self.DrawID then
|
|
8487
|
+
if type(self.DrawID)~="table"then
|
|
8169
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
|
|
8170
8494
|
end
|
|
8171
8495
|
end
|
|
8172
8496
|
return self
|
|
@@ -8881,8 +9205,68 @@ local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2())
|
|
|
8881
9205
|
self:T3({PointVec2})
|
|
8882
9206
|
return PointVec2
|
|
8883
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
|
|
8884
9265
|
ZONE_POLYGON_BASE={
|
|
8885
9266
|
ClassName="ZONE_POLYGON_BASE",
|
|
9267
|
+
_Triangles={},
|
|
9268
|
+
SurfaceArea=0,
|
|
9269
|
+
DrawID={}
|
|
8886
9270
|
}
|
|
8887
9271
|
function ZONE_POLYGON_BASE:New(ZoneName,PointsArray)
|
|
8888
9272
|
local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName))
|
|
@@ -8894,9 +9278,84 @@ self._.Polygon[i]={}
|
|
|
8894
9278
|
self._.Polygon[i].x=PointsArray[i].x
|
|
8895
9279
|
self._.Polygon[i].y=PointsArray[i].y
|
|
8896
9280
|
end
|
|
9281
|
+
self._Triangles=self:_Triangulate()
|
|
9282
|
+
self.SurfaceArea=self:_CalculateSurfaceArea()
|
|
8897
9283
|
end
|
|
8898
9284
|
return self
|
|
8899
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
|
|
8900
9359
|
function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
|
|
8901
9360
|
self._.Polygon={}
|
|
8902
9361
|
for i=1,#Vec2Array do
|
|
@@ -8904,6 +9363,8 @@ self._.Polygon[i]={}
|
|
|
8904
9363
|
self._.Polygon[i].x=Vec2Array[i].x
|
|
8905
9364
|
self._.Polygon[i].y=Vec2Array[i].y
|
|
8906
9365
|
end
|
|
9366
|
+
self._Triangles=self:_Triangulate()
|
|
9367
|
+
self.SurfaceArea=self:_CalculateSurfaceArea()
|
|
8907
9368
|
return self
|
|
8908
9369
|
end
|
|
8909
9370
|
function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
|
|
@@ -8913,8 +9374,17 @@ self._.Polygon[i]={}
|
|
|
8913
9374
|
self._.Polygon[i].x=Vec3Array[i].x
|
|
8914
9375
|
self._.Polygon[i].y=Vec3Array[i].z
|
|
8915
9376
|
end
|
|
9377
|
+
self._Triangles=self:_Triangulate()
|
|
9378
|
+
self.SurfaceArea=self:_CalculateSurfaceArea()
|
|
8916
9379
|
return self
|
|
8917
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
|
|
8918
9388
|
function ZONE_POLYGON_BASE:GetVec2()
|
|
8919
9389
|
self:F(self.ZoneName)
|
|
8920
9390
|
local Bounds=self:GetBoundingSquare()
|
|
@@ -8997,32 +9467,42 @@ i=i+1
|
|
|
8997
9467
|
end
|
|
8998
9468
|
return self
|
|
8999
9469
|
end
|
|
9000
|
-
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)
|
|
9001
9471
|
if self._.Polygon and#self._.Polygon>=3 then
|
|
9002
|
-
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
|
9003
9472
|
Coalition=Coalition or self:GetDrawCoalition()
|
|
9004
9473
|
self:SetDrawCoalition(Coalition)
|
|
9005
9474
|
Color=Color or self:GetColorRGB()
|
|
9006
9475
|
Alpha=Alpha or 1
|
|
9007
9476
|
self:SetColor(Color,Alpha)
|
|
9008
9477
|
FillColor=FillColor or self:GetFillColorRGB()
|
|
9009
|
-
if not FillColor then
|
|
9478
|
+
if not FillColor then
|
|
9479
|
+
UTILS.DeepCopy(Color)
|
|
9480
|
+
end
|
|
9010
9481
|
FillAlpha=FillAlpha or self:GetFillColorAlpha()
|
|
9011
|
-
if not FillAlpha then
|
|
9482
|
+
if not FillAlpha then
|
|
9483
|
+
FillAlpha=0.15
|
|
9484
|
+
end
|
|
9012
9485
|
self:SetFillColor(FillColor,FillAlpha)
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
local
|
|
9017
|
-
self.DrawID
|
|
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
|
|
9018
9492
|
else
|
|
9019
|
-
local
|
|
9020
|
-
|
|
9021
|
-
|
|
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
|
|
9022
9499
|
end
|
|
9023
9500
|
end
|
|
9024
9501
|
return self
|
|
9025
9502
|
end
|
|
9503
|
+
function ZONE_POLYGON_BASE:GetSurfaceArea()
|
|
9504
|
+
return self.SurfaceArea
|
|
9505
|
+
end
|
|
9026
9506
|
function ZONE_POLYGON_BASE:GetRadius()
|
|
9027
9507
|
local center=self:GetVec2()
|
|
9028
9508
|
local radius=0
|
|
@@ -9131,17 +9611,18 @@ local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
|
|
|
9131
9611
|
return InZone
|
|
9132
9612
|
end
|
|
9133
9613
|
function ZONE_POLYGON_BASE:GetRandomVec2()
|
|
9134
|
-
local
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
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()
|
|
9140
9624
|
end
|
|
9141
|
-
n=n+1
|
|
9142
9625
|
end
|
|
9143
|
-
self:E("Could not find a random point in the polygon zone!")
|
|
9144
|
-
return nil
|
|
9145
9626
|
end
|
|
9146
9627
|
function ZONE_POLYGON_BASE:GetRandomPointVec2()
|
|
9147
9628
|
self:F2()
|
|
@@ -9220,6 +9701,7 @@ i=i+1
|
|
|
9220
9701
|
end
|
|
9221
9702
|
return self
|
|
9222
9703
|
end
|
|
9704
|
+
do
|
|
9223
9705
|
ZONE_POLYGON={
|
|
9224
9706
|
ClassName="ZONE_POLYGON",
|
|
9225
9707
|
}
|
|
@@ -9244,6 +9726,36 @@ self:F({GroupName,ZoneGroup,self._.Polygon})
|
|
|
9244
9726
|
_EVENTDISPATCHER:CreateEventNewZone(self)
|
|
9245
9727
|
return self
|
|
9246
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
|
|
9247
9759
|
function ZONE_POLYGON:FindByName(ZoneName)
|
|
9248
9760
|
local ZoneFound=_DATABASE:FindZone(ZoneName)
|
|
9249
9761
|
return ZoneFound
|
|
@@ -9430,6 +9942,7 @@ end
|
|
|
9430
9942
|
function ZONE_POLYGON:IsNoneInZone()
|
|
9431
9943
|
return self:CountScannedCoalitions()==0
|
|
9432
9944
|
end
|
|
9945
|
+
end
|
|
9433
9946
|
do
|
|
9434
9947
|
ZONE_ELASTIC={
|
|
9435
9948
|
ClassName="ZONE_ELASTIC",
|
|
@@ -9523,6 +10036,124 @@ table.remove(h,#h)
|
|
|
9523
10036
|
return h
|
|
9524
10037
|
end
|
|
9525
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
|
|
9526
10157
|
do
|
|
9527
10158
|
ZONE_AIRBASE={
|
|
9528
10159
|
ClassName="ZONE_AIRBASE",
|
|
@@ -10834,7 +11465,10 @@ self:T3({LastObject})
|
|
|
10834
11465
|
return LastObject
|
|
10835
11466
|
end
|
|
10836
11467
|
function SET_BASE:GetRandom()
|
|
10837
|
-
local tablemax=
|
|
11468
|
+
local tablemax=0
|
|
11469
|
+
for _,_ind in pairs(self.Index)do
|
|
11470
|
+
tablemax=tablemax+1
|
|
11471
|
+
end
|
|
10838
11472
|
local RandomItem=self.Set[self.Index[math.random(1,tablemax)]]
|
|
10839
11473
|
self:T3({RandomItem})
|
|
10840
11474
|
return RandomItem
|
|
@@ -11983,46 +12617,40 @@ self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText})
|
|
|
11983
12617
|
return MaxThreatLevelA2G,MaxThreatText
|
|
11984
12618
|
end
|
|
11985
12619
|
function SET_UNIT:GetCoordinate()
|
|
11986
|
-
local
|
|
11987
|
-
local
|
|
11988
|
-
|
|
11989
|
-
|
|
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()
|
|
11990
12629
|
end
|
|
11991
|
-
if
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
local y2=Coordinate.y
|
|
11997
|
-
local z1=Coordinate.z
|
|
11998
|
-
local z2=Coordinate.z
|
|
11999
|
-
local MaxVelocity=0
|
|
12000
|
-
local AvgHeading=nil
|
|
12001
|
-
local MovingCount=0
|
|
12002
|
-
for UnitName,UnitData in pairs(self:GetAliveSet())do
|
|
12003
|
-
local Unit=UnitData
|
|
12004
|
-
local Coordinate=Unit:GetCoordinate()
|
|
12005
|
-
x1=(Coordinate.x<x1)and Coordinate.x or x1
|
|
12006
|
-
x2=(Coordinate.x>x2)and Coordinate.x or x2
|
|
12007
|
-
y1=(Coordinate.y<y1)and Coordinate.y or y1
|
|
12008
|
-
y2=(Coordinate.y>y2)and Coordinate.y or y2
|
|
12009
|
-
z1=(Coordinate.y<z1)and Coordinate.z or z1
|
|
12010
|
-
z2=(Coordinate.y>z2)and Coordinate.z or z2
|
|
12011
|
-
local Velocity=Coordinate:GetVelocity()
|
|
12012
|
-
if Velocity~=0 then
|
|
12013
|
-
MaxVelocity=(MaxVelocity<Velocity)and Velocity or MaxVelocity
|
|
12014
|
-
local Heading=Coordinate:GetHeading()
|
|
12015
|
-
AvgHeading=AvgHeading and(AvgHeading+Heading)or Heading
|
|
12016
|
-
MovingCount=MovingCount+1
|
|
12630
|
+
if vec3 then
|
|
12631
|
+
x=x+vec3.x
|
|
12632
|
+
y=y+vec3.y
|
|
12633
|
+
z=z+vec3.z
|
|
12634
|
+
n=n+1
|
|
12017
12635
|
end
|
|
12018
12636
|
end
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
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))
|
|
12026
12654
|
end
|
|
12027
12655
|
return Coordinate
|
|
12028
12656
|
end
|
|
@@ -16213,6 +16841,9 @@ end
|
|
|
16213
16841
|
end
|
|
16214
16842
|
return BRAANATO
|
|
16215
16843
|
end
|
|
16844
|
+
function COORDINATE.GetBullseyeCoordinate(Coalition)
|
|
16845
|
+
return COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
|
|
16846
|
+
end
|
|
16216
16847
|
function COORDINATE:ToStringBULLS(Coalition,Settings,MagVar)
|
|
16217
16848
|
local BullsCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
|
|
16218
16849
|
local DirectionVec3=BullsCoordinate:GetDirectionVec3(self)
|
|
@@ -16792,7 +17423,7 @@ end
|
|
|
16792
17423
|
if CoalitionSide then
|
|
16793
17424
|
if self.MessageDuration~=0 then
|
|
16794
17425
|
self:T(self.MessageCategory..self.MessageText:gsub("\n$",""):gsub("\n$","").." / "..self.MessageDuration)
|
|
16795
|
-
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)
|
|
16796
17427
|
end
|
|
16797
17428
|
end
|
|
16798
17429
|
self.CoalitionSide=CoalitionSide
|
|
@@ -17439,6 +18070,7 @@ self.SpawnInitModexPrefix=nil
|
|
|
17439
18070
|
self.SpawnInitModexPostfix=nil
|
|
17440
18071
|
self.SpawnInitAirbase=nil
|
|
17441
18072
|
self.TweakedTemplate=false
|
|
18073
|
+
self.SpawnRandomCallsign=false
|
|
17442
18074
|
self.SpawnGroups={}
|
|
17443
18075
|
else
|
|
17444
18076
|
error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'")
|
|
@@ -17728,6 +18360,18 @@ self:_RandomizeZones(SpawnGroupID)
|
|
|
17728
18360
|
end
|
|
17729
18361
|
return self
|
|
17730
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
|
|
17731
18375
|
function SPAWN:InitPositionCoordinate(Coordinate)
|
|
17732
18376
|
self:T({self.SpawnTemplatePrefix,Coordinate:GetVec2()})
|
|
17733
18377
|
self:InitPositionVec2(Coordinate:GetVec2())
|
|
@@ -18849,19 +19493,133 @@ SpawnTemplate.units[UnitID].name=string.format('%s#%03d-%02d',UnitPrefix,SpawnIn
|
|
|
18849
19493
|
SpawnTemplate.units[UnitID].unitId=nil
|
|
18850
19494
|
end
|
|
18851
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
|
|
18852
19549
|
for UnitID=1,#SpawnTemplate.units do
|
|
18853
19550
|
local Callsign=SpawnTemplate.units[UnitID].callsign
|
|
18854
19551
|
if Callsign then
|
|
18855
|
-
if type(Callsign)~="number"then
|
|
19552
|
+
if type(Callsign)~="number"and not self.SpawnInitCallSign then
|
|
18856
19553
|
Callsign[2]=((SpawnIndex-1)%10)+1
|
|
18857
19554
|
local CallsignName=SpawnTemplate.units[UnitID].callsign["name"]
|
|
18858
19555
|
CallsignName=string.match(CallsignName,"^(%a+)")
|
|
18859
19556
|
local CallsignLen=CallsignName:len()
|
|
19557
|
+
SpawnTemplate.units[UnitID].callsign[2]=UnitID
|
|
18860
19558
|
SpawnTemplate.units[UnitID].callsign["name"]=CallsignName:sub(1,CallsignLen)..SpawnTemplate.units[UnitID].callsign[2]..SpawnTemplate.units[UnitID].callsign[3]
|
|
18861
|
-
|
|
19559
|
+
elseif type(Callsign)=="number"then
|
|
18862
19560
|
SpawnTemplate.units[UnitID].callsign=Callsign+SpawnIndex
|
|
18863
19561
|
end
|
|
18864
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
|
|
18865
19623
|
end
|
|
18866
19624
|
self:T3({"Template:",SpawnTemplate})
|
|
18867
19625
|
return SpawnTemplate
|
|
@@ -23166,7 +23924,7 @@ if DCSControllable then
|
|
|
23166
23924
|
local Controller=self:_GetController()
|
|
23167
23925
|
if Controller then
|
|
23168
23926
|
if self:IsAir()then
|
|
23169
|
-
self:SetOption(AI.Option.Air.
|
|
23927
|
+
self:SetOption(AI.Option.Air.id.MISSILE_ATTACK,range)
|
|
23170
23928
|
end
|
|
23171
23929
|
end
|
|
23172
23930
|
return self
|
|
@@ -24688,6 +25446,7 @@ if vec3 then
|
|
|
24688
25446
|
local coord=COORDINATE:NewFromVec3(vec3)
|
|
24689
25447
|
local Heading=self:GetHeading()
|
|
24690
25448
|
coord.Heading=Heading
|
|
25449
|
+
return coord
|
|
24691
25450
|
else
|
|
24692
25451
|
BASE:E({"Cannot GetAverageCoordinate",Group=self,Alive=self:IsAlive()})
|
|
24693
25452
|
return nil
|
|
@@ -25664,6 +26423,34 @@ local tankertask=self:EnRouteTaskTanker()
|
|
|
25664
26423
|
self:PushTask(tankertask,delay+2)
|
|
25665
26424
|
return self
|
|
25666
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
|
|
25667
26454
|
UNIT={
|
|
25668
26455
|
ClassName="UNIT",
|
|
25669
26456
|
UnitName=nil,
|
|
@@ -26458,6 +27245,30 @@ local name=self.UnitName
|
|
|
26458
27245
|
local skill=_DATABASE.Templates.Units[name].Template.skill or"Random"
|
|
26459
27246
|
return skill
|
|
26460
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
|
|
26461
27272
|
CLIENT={
|
|
26462
27273
|
ClassName="CLIENT",
|
|
26463
27274
|
ClientName=nil,
|
|
@@ -26981,6 +27792,13 @@ AIRBASE.Normandy={
|
|
|
26981
27792
|
["Broglie"]="Broglie",
|
|
26982
27793
|
["Bernay_Saint_Martin"]="Bernay Saint Martin",
|
|
26983
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",
|
|
26984
27802
|
}
|
|
26985
27803
|
AIRBASE.PersianGulf={
|
|
26986
27804
|
["Abu_Dhabi_International_Airport"]="Abu Dhabi Intl",
|
|
@@ -28501,6 +29319,13 @@ self.launcherUnit=UNIT:Find(self.launcher)
|
|
|
28501
29319
|
end
|
|
28502
29320
|
self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
|
|
28503
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
|
|
28504
29329
|
self:SetTimeStepTrack()
|
|
28505
29330
|
self:SetDistanceInterceptPoint()
|
|
28506
29331
|
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
|
|
@@ -28646,6 +29471,26 @@ end
|
|
|
28646
29471
|
function WEAPON:GetImpactCoordinate()
|
|
28647
29472
|
return self.impactCoord
|
|
28648
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
|
|
28649
29494
|
function WEAPON:InAir()
|
|
28650
29495
|
local inAir=nil
|
|
28651
29496
|
if self.weapon then
|
|
@@ -28717,6 +29562,7 @@ if status then
|
|
|
28717
29562
|
self.pos3=pos3
|
|
28718
29563
|
self.vec3=UTILS.DeepCopy(self.pos3.p)
|
|
28719
29564
|
self.coordinate:UpdateFromVec3(self.vec3)
|
|
29565
|
+
self.last_velocity=self.weapon:getVelocity()
|
|
28720
29566
|
self.tracking=true
|
|
28721
29567
|
if self.trackFunc then
|
|
28722
29568
|
self.trackFunc(self,unpack(self.trackArg))
|
|
@@ -28744,6 +29590,7 @@ self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters",d))
|
|
|
28744
29590
|
end
|
|
28745
29591
|
self.impactVec3=ip or self.vec3
|
|
28746
29592
|
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
|
|
29593
|
+
self.impactHeading=UTILS.VecHdg(self.last_velocity)
|
|
28747
29594
|
if self.impactMark then
|
|
28748
29595
|
self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s",self.name,self.typeName,self.launcherName))
|
|
28749
29596
|
end
|
|
@@ -33504,7 +34351,7 @@ AirbaseNames=nil,
|
|
|
33504
34351
|
}
|
|
33505
34352
|
function ATC_GROUND:New(Airbases,AirbaseList)
|
|
33506
34353
|
local self=BASE:Inherit(self,BASE:New())
|
|
33507
|
-
self:
|
|
34354
|
+
self:T({self.ClassName,Airbases})
|
|
33508
34355
|
self.Airbases=Airbases
|
|
33509
34356
|
self.AirbaseList=AirbaseList
|
|
33510
34357
|
self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
|
|
@@ -33583,7 +34430,7 @@ function(Client)
|
|
|
33583
34430
|
if Client:IsAlive()then
|
|
33584
34431
|
local IsOnGround=Client:InAir()==false
|
|
33585
34432
|
for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
|
|
33586
|
-
self:
|
|
34433
|
+
self:T(AirbaseID,AirbaseMeta.KickSpeed)
|
|
33587
34434
|
if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
|
|
33588
34435
|
local NotInRunwayZone=true
|
|
33589
34436
|
for ZoneRunwayID,ZoneRunway in pairs(AirbaseMeta.ZoneRunways)do
|
|
@@ -33592,7 +34439,7 @@ end
|
|
|
33592
34439
|
if NotInRunwayZone then
|
|
33593
34440
|
if IsOnGround then
|
|
33594
34441
|
local Taxi=Client:GetState(self,"Taxi")
|
|
33595
|
-
self:
|
|
34442
|
+
self:T(Taxi)
|
|
33596
34443
|
if Taxi==false then
|
|
33597
34444
|
local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
|
|
33598
34445
|
Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
|
|
@@ -33712,12 +34559,18 @@ KickSpeed=nil,
|
|
|
33712
34559
|
}
|
|
33713
34560
|
function ATC_GROUND_UNIVERSAL:New(AirbaseList)
|
|
33714
34561
|
local self=BASE:Inherit(self,BASE:New())
|
|
33715
|
-
self:
|
|
34562
|
+
self:T({self.ClassName})
|
|
33716
34563
|
self.Airbases={}
|
|
33717
34564
|
for _name,_ in pairs(_DATABASE.AIRBASES)do
|
|
33718
34565
|
self.Airbases[_name]={}
|
|
33719
34566
|
end
|
|
33720
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
|
|
33721
34574
|
self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
|
|
33722
34575
|
for AirbaseID,Airbase in pairs(self.Airbases)do
|
|
33723
34576
|
if Airbase.ZoneBoundary then
|
|
@@ -33816,12 +34669,13 @@ self:SetMaximumKickSpeed(UTILS.MiphToMps(MaximumKickSpeedMiph),Airbase)
|
|
|
33816
34669
|
return self
|
|
33817
34670
|
end
|
|
33818
34671
|
function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
|
34672
|
+
self:I("_AirbaseMonitor")
|
|
33819
34673
|
self.SetClient:ForEachClient(
|
|
33820
34674
|
function(Client)
|
|
33821
34675
|
if Client:IsAlive()then
|
|
33822
34676
|
local IsOnGround=Client:InAir()==false
|
|
33823
34677
|
for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
|
|
33824
|
-
self:
|
|
34678
|
+
self:T(AirbaseID,AirbaseMeta.KickSpeed)
|
|
33825
34679
|
if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
|
|
33826
34680
|
local NotInRunwayZone=true
|
|
33827
34681
|
if AirbaseMeta.ZoneRunways then
|
|
@@ -33833,7 +34687,7 @@ end
|
|
|
33833
34687
|
if NotInRunwayZone then
|
|
33834
34688
|
if IsOnGround then
|
|
33835
34689
|
local Taxi=Client:GetState(self,"Taxi")
|
|
33836
|
-
self:
|
|
34690
|
+
self:T(Taxi)
|
|
33837
34691
|
if Taxi==false then
|
|
33838
34692
|
local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
|
|
33839
34693
|
Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
|
|
@@ -33945,7 +34799,7 @@ return true
|
|
|
33945
34799
|
end
|
|
33946
34800
|
function ATC_GROUND_UNIVERSAL:Start(RepeatScanSeconds)
|
|
33947
34801
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
33948
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34802
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
33949
34803
|
return self
|
|
33950
34804
|
end
|
|
33951
34805
|
ATC_GROUND_CAUCASUS={
|
|
@@ -33959,7 +34813,7 @@ return self
|
|
|
33959
34813
|
end
|
|
33960
34814
|
function ATC_GROUND_CAUCASUS:Start(RepeatScanSeconds)
|
|
33961
34815
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
33962
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34816
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
33963
34817
|
end
|
|
33964
34818
|
ATC_GROUND_NEVADA={
|
|
33965
34819
|
ClassName="ATC_GROUND_NEVADA",
|
|
@@ -33972,7 +34826,7 @@ return self
|
|
|
33972
34826
|
end
|
|
33973
34827
|
function ATC_GROUND_NEVADA:Start(RepeatScanSeconds)
|
|
33974
34828
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
33975
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34829
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
33976
34830
|
end
|
|
33977
34831
|
ATC_GROUND_NORMANDY={
|
|
33978
34832
|
ClassName="ATC_GROUND_NORMANDY",
|
|
@@ -33985,7 +34839,7 @@ return self
|
|
|
33985
34839
|
end
|
|
33986
34840
|
function ATC_GROUND_NORMANDY:Start(RepeatScanSeconds)
|
|
33987
34841
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
33988
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34842
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
33989
34843
|
end
|
|
33990
34844
|
ATC_GROUND_PERSIANGULF={
|
|
33991
34845
|
ClassName="ATC_GROUND_PERSIANGULF",
|
|
@@ -33997,20 +34851,20 @@ self:SetMaximumKickSpeedKmph(150)
|
|
|
33997
34851
|
end
|
|
33998
34852
|
function ATC_GROUND_PERSIANGULF:Start(RepeatScanSeconds)
|
|
33999
34853
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
34000
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34854
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
34001
34855
|
end
|
|
34002
34856
|
ATC_GROUND_MARIANAISLANDS={
|
|
34003
34857
|
ClassName="ATC_GROUND_MARIANAISLANDS",
|
|
34004
34858
|
}
|
|
34005
34859
|
function ATC_GROUND_MARIANAISLANDS:New(AirbaseNames)
|
|
34006
|
-
local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(
|
|
34860
|
+
local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
|
|
34007
34861
|
self:SetKickSpeedKmph(50)
|
|
34008
34862
|
self:SetMaximumKickSpeedKmph(150)
|
|
34009
34863
|
return self
|
|
34010
34864
|
end
|
|
34011
34865
|
function ATC_GROUND_MARIANAISLANDS:Start(RepeatScanSeconds)
|
|
34012
34866
|
RepeatScanSeconds=RepeatScanSeconds or 0.05
|
|
34013
|
-
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,
|
|
34867
|
+
self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
|
|
34014
34868
|
end
|
|
34015
34869
|
do
|
|
34016
34870
|
DETECTION_BASE={
|
|
@@ -34163,6 +35017,28 @@ DetectionAccepted=false
|
|
|
34163
35017
|
end
|
|
34164
35018
|
end
|
|
34165
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
|
|
34166
35042
|
if not self.DetectedObjects[DetectedObjectName]and TargetIsVisible and self.DistanceProbability then
|
|
34167
35043
|
local DistanceFactor=Distance/4
|
|
34168
35044
|
local DistanceProbabilityReversed=(1-self.DistanceProbability)*DistanceFactor
|
|
@@ -34323,6 +35199,13 @@ self._.FilterCategories[FilterCategories]=FilterCategories
|
|
|
34323
35199
|
end
|
|
34324
35200
|
return self
|
|
34325
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
|
|
34326
35209
|
end
|
|
34327
35210
|
do
|
|
34328
35211
|
function DETECTION_BASE:SetRefreshTimeInterval(RefreshTimeInterval)
|
|
@@ -39953,6 +40836,10 @@ end
|
|
|
39953
40836
|
return self
|
|
39954
40837
|
end
|
|
39955
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
|
|
39956
40843
|
self.rangecontrolfreq=frequency or 256
|
|
39957
40844
|
self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
|
|
39958
40845
|
self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
|
|
@@ -39968,6 +40855,10 @@ end
|
|
|
39968
40855
|
return self
|
|
39969
40856
|
end
|
|
39970
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
|
|
39971
40862
|
self.instructorfreq=frequency or 305
|
|
39972
40863
|
self.instructmsrs:SetFrequencies(self.instructorfreq)
|
|
39973
40864
|
self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
|
|
@@ -48276,11 +49167,22 @@ local _assetattribute
|
|
|
48276
49167
|
local _assetcategory
|
|
48277
49168
|
local _assetairstart=false
|
|
48278
49169
|
if _nassets>0 then
|
|
49170
|
+
local asset=_assets[1]
|
|
48279
49171
|
_assetattribute=_assets[1].attribute
|
|
48280
49172
|
_assetcategory=_assets[1].category
|
|
48281
49173
|
_assetairstart=_assets[1].takeoffType and _assets[1].takeoffType==COORDINATE.WaypointType.TurningPoint or false
|
|
48282
49174
|
if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
|
|
48283
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
|
|
48284
49186
|
if self:IsRunwayOperational()or _assetairstart then
|
|
48285
49187
|
if _assetairstart then
|
|
48286
49188
|
else
|
|
@@ -48342,6 +49244,7 @@ local text=string.format("Warehouse %s: Request denied! Not close enough to spaw
|
|
|
48342
49244
|
self:_InfoMessage(text,5)
|
|
48343
49245
|
return false
|
|
48344
49246
|
end
|
|
49247
|
+
elseif _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
|
|
48345
49248
|
end
|
|
48346
49249
|
end
|
|
48347
49250
|
request.cargoassets=_assets
|
|
@@ -61653,7 +62556,7 @@ DELIMITER="Punto",
|
|
|
61653
62556
|
}
|
|
61654
62557
|
ATIS.locale="en"
|
|
61655
62558
|
_ATIS={}
|
|
61656
|
-
ATIS.version="0.10.
|
|
62559
|
+
ATIS.version="0.10.4"
|
|
61657
62560
|
function ATIS:New(AirbaseName,Frequency,Modulation)
|
|
61658
62561
|
local self=BASE:Inherit(self,FSM:New())
|
|
61659
62562
|
self.airbasename=AirbaseName
|
|
@@ -61962,7 +62865,16 @@ self:E(self.lid..string.format("EXPERIMENTAL: Starting ATIS for Helipad %s! SRS
|
|
|
61962
62865
|
self.ATISforFARPs=true
|
|
61963
62866
|
self.useSRS=true
|
|
61964
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
|
|
61965
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
|
|
61966
62878
|
if not self.useSRS then
|
|
61967
62879
|
self.radioqueue=RADIOQUEUE:New(self.frequency,self.modulation,string.format("ATIS %s",self.airbasename))
|
|
61968
62880
|
self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
|
|
@@ -61994,7 +62906,17 @@ if ru then
|
|
|
61994
62906
|
relayunitstatus=tostring(ru:IsAlive())
|
|
61995
62907
|
end
|
|
61996
62908
|
end
|
|
61997
|
-
local text=
|
|
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
|
|
61998
62920
|
if self.useSRS then
|
|
61999
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))
|
|
62000
62922
|
else
|
|
@@ -62879,7 +63801,17 @@ function ATIS:UpdateMarker(information,runact,wind,altimeter,temperature)
|
|
|
62879
63801
|
if self.markerid then
|
|
62880
63802
|
self.airbase:GetCoordinate():RemoveMark(self.markerid)
|
|
62881
63803
|
end
|
|
62882
|
-
local text=
|
|
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
|
|
62883
63815
|
text=text..string.format("%s\n",tostring(runact))
|
|
62884
63816
|
text=text..string.format("%s\n",tostring(wind))
|
|
62885
63817
|
text=text..string.format("%s\n",tostring(altimeter))
|
|
@@ -63343,7 +64275,7 @@ CTLD.UnitTypeCapabilities={
|
|
|
63343
64275
|
["AH-64D_BLK_II"]={type="AH-64D_BLK_II",crates=false,troops=true,cratelimit=0,trooplimit=2,length=17,cargoweightlimit=200},
|
|
63344
64276
|
["Bronco-OV-10A"]={type="Bronco-OV-10A",crates=false,troops=true,cratelimit=0,trooplimit=5,length=13,cargoweightlimit=1450},
|
|
63345
64277
|
}
|
|
63346
|
-
CTLD.version="1.0.
|
|
64278
|
+
CTLD.version="1.0.44"
|
|
63347
64279
|
function CTLD:New(Coalition,Prefixes,Alias)
|
|
63348
64280
|
local self=BASE:Inherit(self,FSM:New())
|
|
63349
64281
|
BASE:T({Coalition,Prefixes,Alias})
|
|
@@ -63388,6 +64320,8 @@ self:AddTransition("*","TroopsRTB","*")
|
|
|
63388
64320
|
self:AddTransition("*","CratesDropped","*")
|
|
63389
64321
|
self:AddTransition("*","CratesBuild","*")
|
|
63390
64322
|
self:AddTransition("*","CratesRepaired","*")
|
|
64323
|
+
self:AddTransition("*","CratesBuildStarted","*")
|
|
64324
|
+
self:AddTransition("*","CratesRepairStarted","*")
|
|
63391
64325
|
self:AddTransition("*","Load","*")
|
|
63392
64326
|
self:AddTransition("*","Save","*")
|
|
63393
64327
|
self:AddTransition("*","Stop","Stopped")
|
|
@@ -63807,6 +64741,7 @@ local desttimer=TIMER:New(function()NearestGroup:Destroy(false)end,self)
|
|
|
63807
64741
|
desttimer:Start(self.repairtime-1)
|
|
63808
64742
|
local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,object,true,NearestGroup:GetCoordinate())
|
|
63809
64743
|
buildtimer:Start(self.repairtime)
|
|
64744
|
+
self:__CratesRepairStarted(1,Group,Unit)
|
|
63810
64745
|
else
|
|
63811
64746
|
if not Engineering then
|
|
63812
64747
|
self:_SendMessage("Can't repair this unit with "..build.Name,10,false,Group)
|
|
@@ -64131,6 +65066,34 @@ self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddis
|
|
|
64131
65066
|
end
|
|
64132
65067
|
return self
|
|
64133
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
|
|
64134
65097
|
function CTLD:_GetDistance(_point1,_point2)
|
|
64135
65098
|
self:T(self.lid.." _GetDistance")
|
|
64136
65099
|
if _point1 and _point2 then
|
|
@@ -64469,6 +65432,26 @@ else
|
|
|
64469
65432
|
return false
|
|
64470
65433
|
end
|
|
64471
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
|
|
64472
65455
|
function CTLD:_UnloadTroops(Group,Unit)
|
|
64473
65456
|
self:T(self.lid.." _UnloadTroops")
|
|
64474
65457
|
local droppingatbase=false
|
|
@@ -64509,14 +65492,25 @@ factor=cargo:GetCratesNeeded()or 1
|
|
|
64509
65492
|
zoneradius=Unit:GetVelocityMPS()or 100
|
|
64510
65493
|
end
|
|
64511
65494
|
local zone=ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
|
|
64512
|
-
local randomcoord=zone:GetRandomCoordinate(10,30*factor)
|
|
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
|
|
64513
65504
|
for _,_template in pairs(temptable)do
|
|
64514
65505
|
self.TroopCounter=self.TroopCounter+1
|
|
65506
|
+
tempcount=tempcount+1
|
|
64515
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)
|
|
64516
65510
|
self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
|
|
64517
|
-
:InitRandomizeUnits(true,20,2)
|
|
64518
65511
|
:InitDelayOff()
|
|
64519
|
-
:
|
|
65512
|
+
:InitSetUnitAbsolutePositions(Positions)
|
|
65513
|
+
:SpawnFromVec2(randomcoord:GetVec2())
|
|
64520
65514
|
self:__TroopsDeployed(1,Group,Unit,self.DroppedTroops[self.TroopCounter],type)
|
|
64521
65515
|
end
|
|
64522
65516
|
cargo:SetWasDropped(true)
|
|
@@ -64715,6 +65709,7 @@ if self.buildtime and self.buildtime>0 then
|
|
|
64715
65709
|
local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate())
|
|
64716
65710
|
buildtimer:Start(self.buildtime)
|
|
64717
65711
|
self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
|
|
65712
|
+
self:__CratesBuildStarted(1,Group,Unit)
|
|
64718
65713
|
else
|
|
64719
65714
|
self:_BuildObjectFromCrates(Group,Unit,build)
|
|
64720
65715
|
end
|
|
@@ -65008,6 +66003,7 @@ if cancrates then
|
|
|
65008
66003
|
local loadmenu=MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates,self._LoadCratesNearby,self,_group,_unit)
|
|
65009
66004
|
local cratesmenu=MENU_GROUP:New(_group,"Get Crates",topcrates)
|
|
65010
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)
|
|
65011
66007
|
if self.usesubcats then
|
|
65012
66008
|
local subcatmenus={}
|
|
65013
66009
|
for _name,_entry in pairs(self.subcats)do
|
|
@@ -65042,6 +66038,7 @@ menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrat
|
|
|
65042
66038
|
end
|
|
65043
66039
|
end
|
|
65044
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)
|
|
65045
66042
|
local unloadmenu=MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates,self._UnloadCrates,self,_group,_unit)
|
|
65046
66043
|
if not self.nobuildmenu then
|
|
65047
66044
|
local buildmenu=MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates,self._BuildCrates,self,_group,_unit)
|
|
@@ -77949,8 +78946,9 @@ AltBackend=nil,
|
|
|
77949
78946
|
ConfigFileName="Moose_MSRS.lua",
|
|
77950
78947
|
ConfigFilePath="Config\\",
|
|
77951
78948
|
ConfigLoaded=false,
|
|
78949
|
+
ttsprovider="Microsoft",
|
|
77952
78950
|
}
|
|
77953
|
-
MSRS.version="0.1.
|
|
78951
|
+
MSRS.version="0.1.3"
|
|
77954
78952
|
MSRS.Voices={
|
|
77955
78953
|
Microsoft={
|
|
77956
78954
|
["Hedda"]="Microsoft Hedda Desktop",
|
|
@@ -78071,8 +79069,7 @@ Backend.Vars.Volume=Volume
|
|
|
78071
79069
|
Backend.Functions=Backend.Functions or{}
|
|
78072
79070
|
return self:_NewAltBackend(Backend)
|
|
78073
79071
|
end
|
|
78074
|
-
|
|
78075
|
-
if(not success)and(not self.ConfigLoaded)then
|
|
79072
|
+
if not self.ConfigLoaded then
|
|
78076
79073
|
self:SetPath(PathToSRS)
|
|
78077
79074
|
self:SetPort()
|
|
78078
79075
|
self:SetFrequencies(Frequency)
|
|
@@ -78111,7 +79108,7 @@ while(self.path:sub(-1)=="/"or self.path:sub(-1)==[[\]])and n<=nmax do
|
|
|
78111
79108
|
self.path=self.path:sub(1,#self.path-1)
|
|
78112
79109
|
n=n+1
|
|
78113
79110
|
end
|
|
78114
|
-
self:
|
|
79111
|
+
self:T(string.format("SRS path=%s",self:GetPath()))
|
|
78115
79112
|
end
|
|
78116
79113
|
return self
|
|
78117
79114
|
end
|
|
@@ -78217,6 +79214,7 @@ self.APIKey=PathToCredentials
|
|
|
78217
79214
|
self.provider="gcloud"
|
|
78218
79215
|
self.GRPCOptions.DefaultProvider="gcloud"
|
|
78219
79216
|
self.GRPCOptions.gcloud.key=PathToCredentials
|
|
79217
|
+
self.ttsprovider="Google"
|
|
78220
79218
|
end
|
|
78221
79219
|
return self
|
|
78222
79220
|
end
|
|
@@ -78229,6 +79227,14 @@ self.GRPCOptions.gcloud.key=APIKey
|
|
|
78229
79227
|
end
|
|
78230
79228
|
return self
|
|
78231
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
|
|
78232
79238
|
function MSRS:Help()
|
|
78233
79239
|
local path=self:GetPath()or STTS.DIRECTORY
|
|
78234
79240
|
local exe=STTS.EXECUTABLE or"DCS-SR-ExternalAudio.exe"
|
|
@@ -78414,16 +79420,22 @@ if coordinate then
|
|
|
78414
79420
|
local lat,lon,alt=self:_GetLatLongAlt(coordinate)
|
|
78415
79421
|
command=command..string.format(" -L %.4f -O %.4f -A %d",lat,lon,alt)
|
|
78416
79422
|
end
|
|
78417
|
-
if self.google then
|
|
79423
|
+
if self.google and self.ttsprovider=="Google"then
|
|
78418
79424
|
command=command..string.format(' --ssml -G "%s"',self.google)
|
|
78419
79425
|
end
|
|
78420
|
-
self:
|
|
79426
|
+
self:T("MSRS command="..command)
|
|
78421
79427
|
return command
|
|
78422
79428
|
end
|
|
78423
|
-
function MSRS:LoadConfigFile(Path,Filename
|
|
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
|
|
78424
79434
|
local path=Path or lfs.writedir()..MSRS.ConfigFilePath
|
|
78425
79435
|
local file=Filename or MSRS.ConfigFileName or"Moose_MSRS.lua"
|
|
78426
|
-
|
|
79436
|
+
local pathandfile=path..file
|
|
79437
|
+
local filexsists=UTILS.FileExists(pathandfile)
|
|
79438
|
+
if filexsists and not MSRS.ConfigLoaded then
|
|
78427
79439
|
assert(loadfile(path..file))()
|
|
78428
79440
|
if MSRS_Config then
|
|
78429
79441
|
if self then
|
|
@@ -78438,6 +79450,9 @@ end
|
|
|
78438
79450
|
self.culture=MSRS_Config.Culture or"en-GB"
|
|
78439
79451
|
self.gender=MSRS_Config.Gender or"male"
|
|
78440
79452
|
self.google=MSRS_Config.Google
|
|
79453
|
+
if MSRS_Config.Provider then
|
|
79454
|
+
self.ttsprovider=MSRS_Config.Provider
|
|
79455
|
+
end
|
|
78441
79456
|
self.Label=MSRS_Config.Label or"MSRS"
|
|
78442
79457
|
self.voice=MSRS_Config.Voice
|
|
78443
79458
|
if MSRS_Config.GRPC then
|
|
@@ -78462,6 +79477,9 @@ end
|
|
|
78462
79477
|
MSRS.culture=MSRS_Config.Culture or"en-GB"
|
|
78463
79478
|
MSRS.gender=MSRS_Config.Gender or"male"
|
|
78464
79479
|
MSRS.google=MSRS_Config.Google
|
|
79480
|
+
if MSRS_Config.Provider then
|
|
79481
|
+
MSRS.ttsprovider=MSRS_Config.Provider
|
|
79482
|
+
end
|
|
78465
79483
|
MSRS.Label=MSRS_Config.Label or"MSRS"
|
|
78466
79484
|
MSRS.voice=MSRS_Config.Voice
|
|
78467
79485
|
if MSRS_Config.GRPC then
|
|
@@ -78476,9 +79494,10 @@ end
|
|
|
78476
79494
|
MSRS.ConfigLoaded=true
|
|
78477
79495
|
end
|
|
78478
79496
|
end
|
|
78479
|
-
env.info("MSRS -
|
|
78480
|
-
|
|
78481
|
-
|
|
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)
|
|
78482
79501
|
return false
|
|
78483
79502
|
end
|
|
78484
79503
|
return true
|
|
@@ -78811,6 +79830,7 @@ end
|
|
|
78811
79830
|
self:_CheckRadioQueue(dt)
|
|
78812
79831
|
end
|
|
78813
79832
|
end
|
|
79833
|
+
MSRS.LoadConfigFile()
|
|
78814
79834
|
COMMANDCENTER={
|
|
78815
79835
|
ClassName="COMMANDCENTER",
|
|
78816
79836
|
CommandCenterName="",
|