@jtff/miztemplate-lib 4.0.0 → 4.0.1

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.
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  do
14
14
  HOUND = {
15
- VERSION = "0.5.0",
15
+ VERSION = "0.5.1",
16
16
  DEBUG = false,
17
17
  ELLIPSE_PERCENTILE = 0.6,
18
18
  DATAPOINTS_NUM = 30,
@@ -148,6 +148,15 @@ do
148
148
  return count
149
149
  end
150
150
 
151
+ function HOUND.shallowCopy(T)
152
+ if type(T) ~= "table" then return {} end
153
+ local newTable = {}
154
+ for k,v in pairs(T) do
155
+ newTable[k] = v
156
+ end
157
+ return newTable
158
+ end
159
+
151
160
  function HOUND.setContains(set, key)
152
161
  if not set or not key then return false end
153
162
  return set[key] ~= nil
@@ -335,7 +344,7 @@ do
335
344
  HOUND.Mist.__index = HOUND.Mist
336
345
 
337
346
  function HOUND.Mist.getNorthCorrection(gPoint) --gets the correction needed for true north
338
- local point = HOUND.Mist.utils.deepCopy(gPoint)
347
+ local point = HOUND.Utils.Dcs.copyPoint(gPoint)
339
348
  if not point.z then --Vec2; convert to Vec3
340
349
  point.z = point.y
341
350
  point.y = 0
@@ -2579,8 +2588,8 @@ do
2579
2588
  ['Assigned'] = { "Naval" },
2580
2589
  ['Role'] = { HOUND.DB.RadarType.NAVAL },
2581
2590
  ['Band'] = {
2582
- [true] = { 31757675.635593, 203140.782317 },
2583
- [false] = { 31757675.635593, 203140.782317 }
2591
+ [true] = HOUND.DB.Bands.H,
2592
+ [false] = HOUND.DB.Bands.F
2584
2593
  },
2585
2594
  ['Primary'] = true,
2586
2595
  ['numDistinctFreqs'] = 0
@@ -2832,6 +2841,7 @@ do
2832
2841
  ['S-3B'] = { antenna = { size = 18, factor = 0.8 }, ins_error = 0 },
2833
2842
  ['E-3A'] = { antenna = { size = 9, factor = 0.5 }, ins_error = 0 },
2834
2843
  ['E-2C'] = { antenna = { size = 7, factor = 0.5 }, ins_error = 0 },
2844
+ ['E-2D'] = { antenna = { size = 7, factor = 0.5 }, ins_error = 0 },
2835
2845
  ['Tu-95MS'] = { antenna = { size = 50, factor = 1 }, ins_error = 50 },
2836
2846
  ['Tu-142'] = { antenna = { size = 50, factor = 1 }, ins_error = 0 },
2837
2847
  ['IL-76MD'] = { antenna = { size = 48, factor = 0.8 }, ins_error = 50 },
@@ -4058,12 +4068,9 @@ do
4058
4068
  if HOUND.DB.Platform[mainCategory][UnitType]['require'] then
4059
4069
  local platformData = HOUND.DB.Platform[mainCategory][UnitType]
4060
4070
  if HOUND.setContains(platformData['require'],'Payload') then
4061
- HOUND.Logger.debug(tostring(PayloadAdded).. " | " .. type(PayloadAdded))
4062
4071
  local required = platformData['require']['Payload']
4063
4072
  if type(PayloadAdded) == 'string' then
4064
- HOUND.Logger.debug("HOUND.DB.isValidPlatform: checking payload " .. PayloadAdded .. " for platform " .. UnitType)
4065
4073
  isValid = HOUND.setContainsValue(required,PayloadAdded)
4066
- HOUND.Logger.debug("HOUND.DB.isValidPlatform: isValid = " .. tostring(isValid))
4067
4074
  else
4068
4075
  isValid = HOUND.Utils.hasPayload(candidate,required)
4069
4076
  end
@@ -4084,7 +4091,7 @@ do
4084
4091
  if not HOUND.Utils.Dcs.isUnit(DcsObject) and not HOUND.Utils.Dcs.isStaticObject(DcsObject) then return end
4085
4092
 
4086
4093
  local platformData={
4087
- pos = l_mist.utils.deepCopy(DcsObject:getPosition().p),
4094
+ pos = HOUND.Utils.Dcs.copyPoint(DcsObject:getPosition().p),
4088
4095
  isStatic = false,
4089
4096
  isAerial = false,
4090
4097
  }
@@ -4547,11 +4554,11 @@ do
4547
4554
  end
4548
4555
 
4549
4556
  function HOUND.Utils.AzimuthAverage(azimuths)
4550
- if not azimuths or HOUND.Length(azimuths) == 0 then return nil end
4557
+ if not azimuths or #azimuths == 0 then return nil end
4551
4558
 
4552
4559
  local sumSin = 0
4553
4560
  local sumCos = 0
4554
- for i=1, HOUND.Length(azimuths) do
4561
+ for i=1, #azimuths do
4555
4562
  sumSin = sumSin + l_math.sin(azimuths[i])
4556
4563
  sumCos = sumCos + l_math.cos(azimuths[i])
4557
4564
  end
@@ -4624,7 +4631,7 @@ do
4624
4631
  "Come back with E T A, T O T, and B D A.",
4625
4632
  " "
4626
4633
  }
4627
- return response[l_math.max(1,l_math.min(l_math.ceil(timer.getAbsTime() % HOUND.Length(response)),HOUND.Length(response)))]
4634
+ return response[l_math.max(1,l_math.min(l_math.ceil(timer.getAbsTime() % #response),#response))]
4628
4635
  end
4629
4636
 
4630
4637
  function HOUND.Utils.getCoalitionString(coalitionID)
@@ -4706,10 +4713,16 @@ do
4706
4713
  override,flightMember = flightMember,override
4707
4714
  end
4708
4715
  local formationCallsign = string.gsub(player.callsign.name,"[%d%s]","")
4716
+ callsign = formationCallsign
4709
4717
 
4710
- callsign = formationCallsign .. " " .. player.callsign[2]
4711
- if flightMember then
4712
- callsign = callsign .. " " .. player.callsign[3]
4718
+ if string.len(formationCallsign) == 0 and type(player.callsign.name) == "string" then
4719
+ callsign = player.callsign.name
4720
+ end
4721
+ if HOUND.Length(player.callsign) > 1 then
4722
+ callsign = callsign .. " " .. player.callsign[2]
4723
+ if flightMember then
4724
+ callsign = callsign .. " " .. player.callsign[3]
4725
+ end
4713
4726
  end
4714
4727
 
4715
4728
  if type(override) == "table" then
@@ -4724,20 +4737,27 @@ do
4724
4737
  end
4725
4738
 
4726
4739
  if not DcsUnit then return string.upper(callsign:match( "^%s*(.-)%s*$" )) end
4740
+ local rawPlayerName = DcsUnit:getPlayerName()
4741
+ if rawPlayerName then
4742
+ local callsign = rawPlayerName
4727
4743
 
4728
- local playerName = DcsUnit:getPlayerName()
4729
- playerName = playerName:match("%a+%s%d+[?%p%s*]%d*")
4730
- if playerName then
4731
- callsign = playerName
4732
- local base = string.match(callsign,"%a+")
4733
- local num = tonumber(string.match(callsign,"%d+"))
4734
- local memberNum = string.gsub(callsign,"%a+%s%d+[%p%s*]","")
4735
- if memberNum:len() > 0 then
4736
- memberNum = tonumber(memberNum:match("%d+"))
4737
- else
4738
- memberNum = nil
4744
+ if rawPlayerName:find("|") then
4745
+ callsign = rawPlayerName:match("([^|]+)")
4746
+ end
4747
+
4748
+ callsign = callsign:match("^%s*(.-)%s*$")
4749
+
4750
+ local base = callsign:match("^(.-)%s+[%d]") or callsign
4751
+ base = base:match("^%s*(.-)%s*$")
4752
+
4753
+ local numbers = {}
4754
+ for n in callsign:gmatch("%d+") do
4755
+ table.insert(numbers, tonumber(n))
4739
4756
  end
4740
4757
 
4758
+ local num = numbers[1] -- First number found (e.g., the '1' in Carbon 1-1)
4759
+ local memberNum = numbers[2] -- Second number found (e.g., the '2' in Carbon 1-2)
4760
+
4741
4761
  callsign = base
4742
4762
  if type(num) == "number" and type(memberNum) == "number" then
4743
4763
  callsign = callsign .. " " .. num
@@ -4758,7 +4778,7 @@ do
4758
4778
 
4759
4779
  function HOUND.Utils.getHoundCallsign(namePool)
4760
4780
  local SelectedPool = HOUND.DB.CALLSIGNS[namePool] or HOUND.DB.CALLSIGNS.GENERIC
4761
- return SelectedPool[l_math.random(1, HOUND.Length(SelectedPool))]
4781
+ return SelectedPool[l_math.random(1, #SelectedPool)]
4762
4782
  end
4763
4783
 
4764
4784
  function HOUND.Utils.useDMM(DcsUnit)
@@ -4853,6 +4873,16 @@ do
4853
4873
  return (type(point.x) == "number") and (type(point.z) == "number")
4854
4874
  end
4855
4875
 
4876
+ function HOUND.Utils.Dcs.copyPoint(point)
4877
+ if type(point) ~= "table" or type(point.x) ~= "number" then return nil end
4878
+ if point.z ~= nil then
4879
+ return {x = point.x, y = point.y, z = point.z}
4880
+ elseif point.y ~= nil then
4881
+ return {x = point.x, y = 0, z = point.y}
4882
+ end
4883
+ return nil
4884
+ end
4885
+
4856
4886
  function HOUND.Utils.Dcs.isUnit(obj)
4857
4887
  if type(obj) ~= "table" then return false end
4858
4888
  return getmetatable(obj) == Unit
@@ -4876,6 +4906,7 @@ do
4876
4906
  function HOUND.Utils.Dcs.getPlayers(coalitionId)
4877
4907
  if type(coalitionId) ~= "number" or (coalitionId > 2 or coalitionId < 0) then return {} end
4878
4908
  local players = coalition.getPlayers(coalitionId)
4909
+
4879
4910
  local humanUnits = {}
4880
4911
  for i = 1, #players do
4881
4912
  local playerUnit = players[i]
@@ -5189,13 +5220,9 @@ do
5189
5220
 
5190
5221
  instance.remove = function(self)
5191
5222
  if self.id > 0 then
5192
- local GC = (self.id % 5 == 0)
5193
5223
  trigger.action.removeMark(self.id)
5194
5224
  self.id = -1
5195
5225
  self.type = HOUND.Utils.Marker.Type.NONE
5196
- if GC then
5197
- collectgarbage("collect")
5198
- end
5199
5226
  end
5200
5227
  end
5201
5228
 
@@ -5221,21 +5248,21 @@ do
5221
5248
  trigger.action.textToAll(coalition,self.id, pos,lineColor,fillColor,fontSize,true,HOUND.MARKER_TEXT_POINTER .. text)
5222
5249
  return true
5223
5250
  end
5224
-
5225
- if HOUND.Length(pos) == 2 and HOUND.Utils.Dcs.isPoint(pos.p) and type(pos.r) == "number" then
5251
+ local len_pos = HOUND.Length(pos)
5252
+ if len_pos == 2 and HOUND.Utils.Dcs.isPoint(pos.p) and type(pos.r) == "number" then
5226
5253
  self.type = HOUND.Utils.Marker.Type.CIRCLE
5227
5254
  trigger.action.circleToAll(coalition,self.id, pos.p,pos.r,lineColor,fillColor,lineType,true)
5228
5255
  return true
5229
5256
  end
5230
5257
 
5231
- if HOUND.Length(pos) == 4 then
5258
+ if len_pos == 4 then
5232
5259
  self.type = HOUND.Utils.Marker.Type.FREEFORM
5233
5260
  trigger.action.markupToAll(6,coalition,self.id,
5234
5261
  pos[1], pos[2], pos[3], pos[4],
5235
5262
  lineColor,fillColor,lineType,true)
5236
5263
  end
5237
5264
 
5238
- if HOUND.Length(pos) == 8 then
5265
+ if len_pos == 8 then
5239
5266
  self.type = HOUND.Utils.Marker.Type.FREEFORM
5240
5267
  trigger.action.markupToAll(7,coalition,self.id,
5241
5268
  pos[1], pos[2], pos[3], pos[4],
@@ -5243,7 +5270,7 @@ do
5243
5270
  lineColor,fillColor,lineType,true)
5244
5271
  end
5245
5272
 
5246
- if HOUND.Length(pos) == 16 then
5273
+ if len_pos == 16 then
5247
5274
  self.type = HOUND.Utils.Marker.Type.FREEFORM
5248
5275
  trigger.action.markupToAll(7,coalition,self.id,
5249
5276
  pos[1], pos[2], pos[3], pos[4],
@@ -5273,11 +5300,12 @@ do
5273
5300
  if HOUND.Utils.Dcs.isPoint(pos) then
5274
5301
  self:setPos(pos)
5275
5302
  end
5276
- if HOUND.Length(pos) == 2 and type(pos.r) == "number" and HOUND.Utils.Dcs.isPoint(pos.p) then
5303
+ local len_pos = HOUND.Length(pos)
5304
+ if len_pos == 2 and type(pos.r) == "number" and HOUND.Utils.Dcs.isPoint(pos.p) then
5277
5305
  self:setPos(pos.p)
5278
5306
  self:setRadius(pos.r)
5279
5307
  end
5280
- if type(pos) == "table" and HOUND.Length(pos) > 2 and HOUND.Utils.Dcs.isPoint(pos[1]) then
5308
+ if type(pos) == "table" and len_pos > 2 and HOUND.Utils.Dcs.isPoint(pos[1]) then
5281
5309
  return self:_replace(args)
5282
5310
  end
5283
5311
  end
@@ -5460,7 +5488,7 @@ do
5460
5488
  if drawObject["name"] == zoneName and drawObject["primitiveType"] == "Polygon" then
5461
5489
  local points = {}
5462
5490
  local theta = nil
5463
- if drawObject["polygonMode"] == "free" and HOUND.Length(drawObject["points"]) >2 then
5491
+ if drawObject["polygonMode"] == "free" and #drawObject["points"] >2 then
5464
5492
  points = l_mist.utils.deepCopy(drawObject["points"])
5465
5493
  table.remove(points)
5466
5494
  end
@@ -5494,7 +5522,7 @@ do
5494
5522
  point.y = x * l_math.sin(theta) + y * l_math.cos(theta)
5495
5523
  end
5496
5524
  end
5497
- if HOUND.Length(points) < 3 then return nil end
5525
+ if #points < 3 then return nil end
5498
5526
  local objectX,objecty = drawObject["mapX"],drawObject["mapY"]
5499
5527
  for _,point in pairs(points) do
5500
5528
  point.x = point.x + objectX
@@ -5610,7 +5638,6 @@ end
5610
5638
  do
5611
5639
  local l_mist = HOUND.Mist
5612
5640
  local l_math = math
5613
- local l_grpc = GRPC
5614
5641
  local l_stts = HoundTTS or STTS
5615
5642
  local l_houndTTS = HoundTTS
5616
5643
  local PI_2 = 2*l_math.pi
@@ -5620,7 +5647,6 @@ do
5620
5647
  function HOUND.Utils.TTS.isAvailable()
5621
5648
  for _,engine in ipairs(HOUND.TTS_ENGINE) do
5622
5649
  if engine == "HOUND" and l_houndTTS ~= nil then return true end
5623
- if engine == "GRPC" and (l_grpc ~= nil and type(l_grpc.tts) == "function") then return true end
5624
5650
  if engine == "STTS" and l_stts ~= nil then return true end
5625
5651
  end
5626
5652
  return false
@@ -5649,6 +5675,24 @@ do
5649
5675
  return "AM"
5650
5676
  end
5651
5677
 
5678
+ function HOUND.Utils.TTS.getTransmitterPos(dcsObject)
5679
+ if dcsObject == nil then return nil end
5680
+ if HOUND.Utils.Dcs.isPoint(dcsObject) then return dcsObject end
5681
+ if not HOUND.Utils.Dcs.isUnit(dcsObject) and not HOUND.Utils.Dcs.isStaticObject(dcsObject) then
5682
+ return nil
5683
+ end
5684
+ if (dcsObject:isExist() == false or dcsObject:getLife() < 1) then
5685
+ return false
5686
+ end
5687
+ local pos = dcsObject:getPoint()
5688
+ local transmitterObjectCat, transmitterSubCat = dcsObject:getCategory()
5689
+ if transmitterObjectCat == Object.Category.STATIC or (transmitterObjectCat == Object.Category.UNIT and transmitterSubCat == Unit.Category.GROUND_UNIT) then
5690
+ local verticalOffset = (dcsObject:getDesc()["box"]["max"]["y"] + 5) or 20
5691
+ pos.y = pos.y + verticalOffset
5692
+ end
5693
+ return pos
5694
+ end
5695
+
5652
5696
  function HOUND.Utils.TTS.Transmit(msg,coalitionID,args,transmitterPos)
5653
5697
  if not HOUND.Utils.TTS.isAvailable() then return end
5654
5698
  if msg == nil then return end
@@ -5664,24 +5708,29 @@ do
5664
5708
  args.tts_engine = engine
5665
5709
  break
5666
5710
  end
5667
- if engine == "GRPC" and (l_grpc ~= nil and type(l_grpc.tts) == "function") then
5668
- args.tts_engine = engine
5669
- break
5670
- end
5671
- if engine == "STTS" and l_stts ~= nil then
5711
+ if engine == "STTS" and (l_stts ~= nil and type(l_stts.TextToSpeech) == "function") then
5672
5712
  args.tts_engine = engine
5673
5713
  break
5674
5714
  end
5675
5715
  end
5676
5716
  end
5717
+ local dcsObject = nil
5718
+ if type(transmitterPos) == "string" then
5719
+ dcsObject = Unit.getByName(transmitterPos) or StaticObject.getByName(transmitterPos)
5720
+ transmitterPos = nil
5721
+ end
5722
+ if HOUND.Utils.Dcs.isUnit(transmitterPos) or HOUND.Utils.Dcs.isStaticObject(transmitterPos) then
5723
+ dcsObject = transmitterPos
5724
+ transmitterPos = HOUND.Utils.TTS.getTransmitterPos(dcsObject)
5725
+ end
5726
+ if transmitterPos == false then
5727
+ return
5728
+ end
5677
5729
  if args.tts_engine == "STTS" then
5678
5730
  return HOUND.Utils.TTS.TransmitSTTS(msg,coalitionID,args,transmitterPos)
5679
5731
  end
5680
- if args.tts_engine == "GRPC" then
5681
- return HOUND.Utils.TTS.TransmitGRPC(msg,coalitionID,args,transmitterPos)
5682
- end
5683
5732
  if args.tts_engine == "HOUND" then
5684
- return HOUND.Utils.TTS.TransmitHound(msg,coalitionID,args,transmitterPos)
5733
+ return HOUND.Utils.TTS.TransmitHound(msg,coalitionID,args,transmitterPos,dcsObject)
5685
5734
  end
5686
5735
  end
5687
5736
 
@@ -5691,11 +5740,12 @@ do
5691
5740
  return l_stts.TextToSpeech(msg,args.freq,args.modulation,args.volume,args.name,coalitionID,transmitterPos,args.speed,args.gender,args.culture,args.voice,args.googletts,args.azurecreds)
5692
5741
  end
5693
5742
 
5694
- function HOUND.Utils.TTS.TransmitHound(msg,coalitionID,args,transmitterPos)
5743
+ function HOUND.Utils.TTS.TransmitHound(msg,coalitionID,args,transmitterPos,dcsObject)
5695
5744
  local transmitter_params = {
5696
5745
  name = args.name,
5697
5746
  freq = args.freq,
5698
5747
  modulation = args.modulation or HOUND.Utils.TTS.getdefaultModulation(args.freq),
5748
+ dcsObject = dcsObject,
5699
5749
  point = transmitterPos,
5700
5750
  coalition = coalitionID,
5701
5751
  transmitter = args.transmitter or "srs"
@@ -5730,91 +5780,6 @@ do
5730
5780
  return l_houndTTS.Transmit(msg,transmitter_params,provider_params)
5731
5781
  end
5732
5782
 
5733
- function HOUND.Utils.TTS.TransmitGRPC(msg,coalitionID,args,transmitterPos)
5734
- local VOLUME = {"default","x-slow", "slow", "medium", "fast", "x-fast"}
5735
- local ssml_msg = msg
5736
-
5737
- local grpc_ttsArgs = {
5738
- srsClientName = args.name,
5739
- coalition = HOUND.Utils.getCoalitionString(coalitionID):lower(),
5740
- }
5741
- if type(transmitterPos) == "table" then
5742
- grpc_ttsArgs.position = {}
5743
- grpc_ttsArgs.position.lat, grpc_ttsArgs.position.lon, grpc_ttsArgs.position.alt = coord.LOtoLL( transmitterPos )
5744
- end
5745
- if type(args.provider) == "table" then
5746
- grpc_ttsArgs.provider = args.provider
5747
- end
5748
-
5749
- local readSpeed = 1.0
5750
- if args.speed ~= 0 then
5751
- if args.speed > 10 then
5752
- readSpeed = HOUND.Utils.Mapping.linear(args.speed,50,250,0.5,2.5,true)
5753
- else
5754
- if args.speed > 0 then
5755
- readSpeed = HOUND.Utils.Mapping.linear(args.speed,0,10,1.0,2.5,true)
5756
- else
5757
- readSpeed = HOUND.Utils.Mapping.linear(args.speed,-10,0,0.5,1.0,true)
5758
- end
5759
- end
5760
- end
5761
-
5762
- local ssml_prosody = ""
5763
- if readSpeed ~= 1.0 then
5764
- ssml_prosody = ssml_prosody .. " rate='"..readSpeed.."'"
5765
- end
5766
-
5767
- if args.volume ~= 1.0 then
5768
- local volume = ""
5769
-
5770
- if HOUND.setContainsValue(VOLUME,args.volume) then
5771
- volume = args.volume
5772
- end
5773
-
5774
- if type(args.volume)=="number" then
5775
- if args.volume ~= 0 then
5776
- volume = (args.volume*100)-100 .. "%"
5777
- if args.volume > 1 then
5778
- volume = "+" .. volume
5779
- end
5780
- else
5781
- volume = "slient"
5782
- end
5783
- end
5784
-
5785
- if string.len(volume) > 0 then
5786
- ssml_prosody = ssml_prosody .. " volume='"..volume.."'"
5787
- end
5788
- end
5789
- if string.len(ssml_prosody) > 0 then
5790
- ssml_msg = table.concat({"<prosody",ssml_prosody,">",ssml_msg,"</prosody>"},"")
5791
- end
5792
-
5793
- local ssml_voice = ""
5794
- if args.voice then
5795
- ssml_voice = ssml_voice.." name='"..args.voice.."'"
5796
- else
5797
- if args.gender then
5798
- ssml_voice = ssml_voice.." gender='"..args.gender.."'"
5799
- end
5800
- if args.culture then
5801
- ssml_voice = ssml_voice.." language='"..args.culture.."'"
5802
- end
5803
- end
5804
-
5805
- if string.len(ssml_voice) > 0 then
5806
- ssml_msg = table.concat({"<voice",ssml_voice,">",ssml_msg,"</voice>"},"")
5807
- end
5808
-
5809
- local freqs = string.split(args.freq,",")
5810
-
5811
- for _,freq in ipairs(freqs) do
5812
- freq = math.ceil(freq * 1000000)
5813
- l_grpc.tts(ssml_msg, freq, grpc_ttsArgs)
5814
- end
5815
- return HOUND.Utils.TTS.getReadTime(msg) / readSpeed -- read speed > 1.0 is fast
5816
- end
5817
-
5818
5783
  function HOUND.Utils.TTS.getTtsTime(timestamp)
5819
5784
  if timestamp == nil then timestamp = timer.getAbsTime() end
5820
5785
  local DHMS = l_mist.time.getDHMS(timestamp)
@@ -5936,11 +5901,11 @@ do
5936
5901
  return l_math.ceil(length/cps)
5937
5902
  end
5938
5903
 
5939
- function HOUND.Utils.TTS.simplfyDistance(distanceM)
5904
+ function HOUND.Utils.TTS.simplifyDistance(distanceM)
5940
5905
  local distanceUnit = "meters"
5941
5906
  local distance = HOUND.Utils.roundToNearest(distanceM,50) or 0
5942
5907
  if distance >= 1000 then
5943
- distance = string.format("%.1f",tostring(HOUND.Utils.roundToNearest(distanceM,100)/1000))
5908
+ distance = string.format("%.1f",HOUND.Utils.roundToNearest(distanceM,100)/1000)
5944
5909
  distanceUnit = "kilometers"
5945
5910
  end
5946
5911
  return distance .. " " .. distanceUnit
@@ -5955,7 +5920,7 @@ do
5955
5920
  HOUND.Utils.Cluster = {}
5956
5921
 
5957
5922
  function HOUND.Utils.Polygon.threatOnSector(polygon,point, radius)
5958
- if type(polygon) ~= "table" or HOUND.Length(polygon) < 3 or not HOUND.Utils.Dcs.isPoint(l_mist.utils.makeVec3(polygon[1])) then
5923
+ if type(polygon) ~= "table" or #polygon < 3 or not HOUND.Utils.Dcs.isPoint(l_mist.utils.makeVec3(polygon[1])) then
5959
5924
  return
5960
5925
  end
5961
5926
  if not HOUND.Utils.Dcs.isPoint(point) then
@@ -5972,7 +5937,7 @@ do
5972
5937
  end
5973
5938
 
5974
5939
  function HOUND.Utils.Polygon.azMinMax(poly,refPos)
5975
- if not HOUND.Utils.Dcs.isPoint(refPos) or type(poly) ~= "table" or HOUND.Length(poly) < 2 or l_mist.pointInPolygon(refPos,poly) then
5940
+ if not HOUND.Utils.Dcs.isPoint(refPos) or type(poly) ~= "table" or #poly < 2 or l_mist.pointInPolygon(refPos,poly) then
5976
5941
  return
5977
5942
  end
5978
5943
 
@@ -5992,9 +5957,12 @@ do
5992
5957
  end
5993
5958
 
5994
5959
  function HOUND.Utils.Cluster.getDeltaSubsetPercent(Table,referencePos,NthPercentile,returnRelative)
5995
- local t = l_mist.utils.deepCopy(Table)
5996
- local len_t = HOUND.Length(t)
5997
- t = HOUND.Utils.Geo.setHeight(t)
5960
+ local t = {}
5961
+ for _,pt in ipairs(Table) do
5962
+ table.insert(t, { x = pt.x, y = pt.y or 0, z = pt.z, dist = 0, score = pt.score})
5963
+ end
5964
+ local len_t = #t
5965
+
5998
5966
  if not referencePos then
5999
5967
  referencePos = l_mist.getAvgPoint(t)
6000
5968
  end
@@ -6834,7 +6802,7 @@ do
6834
6802
  end
6835
6803
 
6836
6804
  function HOUND.Contact.Emitter:getPos()
6837
- return self.pos.p
6805
+ return HoundUtils.Dcs.copyPoint(self.pos.p)
6838
6806
  end
6839
6807
 
6840
6808
  function HOUND.Contact.Emitter:getWavelenght(isTracking)
@@ -7132,7 +7100,7 @@ do
7132
7100
  local AllDataPoints = {}
7133
7101
 
7134
7102
  for _,platformDatapoints in pairs(self._dataPoints) do
7135
- if HOUND.Length(platformDatapoints) > 0 then
7103
+ if #platformDatapoints > 0 then
7136
7104
  for _,datapoint in pairs(platformDatapoints) do
7137
7105
  if datapoint:isStatic() then
7138
7106
  table.insert(staticDataPoints,datapoint)
@@ -7143,12 +7111,12 @@ do
7143
7111
  end
7144
7112
  end
7145
7113
  end
7146
- local numMobilepoints = HOUND.Length(mobileDataPoints)
7147
- local numStaticPoints = HOUND.Length(staticDataPoints)
7114
+ local numMobilepoints = #mobileDataPoints
7115
+ local numStaticPoints = #staticDataPoints
7148
7116
  table.sort(mobileDataPoints, function(a,b) return a.signalStrength < b.signalStrength end)
7149
7117
  table.sort(staticDataPoints, function(a,b) return a.signalStrength < b.signalStrength end)
7150
7118
 
7151
- if numMobilepoints+numStaticPoints < 2 and HOUND.Length(estimatePositions) == 0 then return end
7119
+ if numMobilepoints+numStaticPoints < 2 and #estimatePositions == 0 then return end
7152
7120
  if numStaticPoints > 1 then
7153
7121
  for i=1,numStaticPoints-1 do
7154
7122
  for j=i+1,numStaticPoints do
@@ -7176,7 +7144,7 @@ do
7176
7144
  end
7177
7145
  end
7178
7146
 
7179
- if HOUND.Length(estimatePositions) > 2 or (HOUND.Length(estimatePositions) > 0 and staticPlatformsOnly) then
7147
+ if #estimatePositions > 2 or (#estimatePositions > 0 and staticPlatformsOnly) then
7180
7148
  self.pos.p = HoundUtils.Cluster.WeightedCentroid(estimatePositions)
7181
7149
  self.uncertenty_data = self.calculateEllipse(estimatePositions,self.pos.p)
7182
7150
 
@@ -7336,7 +7304,7 @@ do
7336
7304
  local unitPos = self.DcsObject:getPosition()
7337
7305
  self:setPreBriefed(true)
7338
7306
 
7339
- self.pos.p = l_mist.utils.deepCopy(unitPos.p)
7307
+ self.pos.p = HoundUtils.Dcs.copyPoint(unitPos.p)
7340
7308
  self:calculateExtrasPosData(self.pos)
7341
7309
 
7342
7310
  self.uncertenty_data = {}
@@ -7356,7 +7324,7 @@ do
7356
7324
  contact.uid = self.uid % 100
7357
7325
  contact.DcsObjectName = self.DcsObject:getName()
7358
7326
  if self.pos.p ~= nil and self.uncertenty_data ~= nil then
7359
- contact.pos = self.pos.p
7327
+ contact.pos = HoundUtils.Dcs.copyPoint(self.pos.p)
7360
7328
  contact.LL = self.pos.LL
7361
7329
 
7362
7330
  contact.accuracy = HoundUtils.TTS.getVerbalConfidenceLevel( self.uncertenty_data.r )
@@ -7368,8 +7336,8 @@ do
7368
7336
  end
7369
7337
  contact.maxWeaponsRange = self.maxWeaponsRange
7370
7338
  contact.last_seen = self.last_seen
7371
- contact.detected_by = self.detected_by
7372
- return l_mist.utils.deepCopy(contact)
7339
+ contact.detected_by = HOUND.shallowCopy(self.detected_by)
7340
+ return contact
7373
7341
  end
7374
7342
  end
7375
7343
  do
@@ -7485,7 +7453,7 @@ do
7485
7453
  then
7486
7454
  msg = msg .. ", Reported " .. HoundUtils.TTS.getVerbalContactAge(self.first_seen) .. " ago"
7487
7455
  else
7488
- msg = msg .. ", ellipse " .. HoundUtils.TTS.simplfyDistance(self.uncertenty_data.major) .. " by " .. HoundUtils.TTS.simplfyDistance(self.uncertenty_data.minor) .. ", aligned bearing " .. HoundUtils.TTS.toPhonetic(string.format("%03d",self.uncertenty_data.az))
7456
+ msg = msg .. ", ellipse " .. HoundUtils.TTS.simplifyDistance(self.uncertenty_data.major) .. " by " .. HoundUtils.TTS.simplifyDistance(self.uncertenty_data.minor) .. ", aligned bearing " .. HoundUtils.TTS.toPhonetic(string.format("%03d",self.uncertenty_data.az))
7489
7457
  msg = msg .. ", Tracked for " .. HoundUtils.TTS.getVerbalContactAge(self.first_seen) .. ", last seen " .. HoundUtils.TTS.getVerbalContactAge(self.last_seen) .. " ago"
7490
7458
  end
7491
7459
  end
@@ -7808,7 +7776,7 @@ do
7808
7776
  self:ensurePrimaryHasPos()
7809
7777
  for _,emitter in ipairs(self.emitters) do
7810
7778
  if emitter:hasPos() then
7811
- self.pos.p = l_mist.utils.deepCopy(emitter:getPos())
7779
+ self.pos.p = emitter:getPos()
7812
7780
  break
7813
7781
  end
7814
7782
  end
@@ -7822,15 +7790,16 @@ do
7822
7790
  if ( not primary:hasPos() ) then
7823
7791
  for _,emitter in ipairs(self.emitters) do
7824
7792
  if ( emitter:hasPos() ) then
7825
- primary.pos = l_mist.utils.deepCopy(emitter.pos)
7826
- primary.uncertenty_data = l_mist.utils.deepCopy(emitter.uncertenty_data)
7793
+ primary.pos = HOUND.shallowCopy(emitter.pos)
7794
+ primary.pos.p = HoundUtils.Dcs.copyPoint(emitter.pos.p)
7795
+ primary.uncertenty_data = HOUND.shallowCopy(emitter.uncertenty_data)
7827
7796
  break
7828
7797
  end
7829
7798
  end
7830
7799
 
7831
7800
  if ( not primary:hasPos() and HoundUtils.Dcs.isPoint(refPos)) then
7832
7801
  local uncertenty = primary:getMaxWeaponsRange() * 0.75
7833
- primary.pos.p = l_mist.utils.deepCopy(refPos)
7802
+ primary.pos.p = HoundUtils.Dcs.copyPoint(refPos)
7834
7803
  primary.pos = primary:calculateExtrasPosData(primary.pos)
7835
7804
  primary.uncertenty_data = {}
7836
7805
  primary.uncertenty_data.major = uncertenty
@@ -8195,7 +8164,7 @@ do
8195
8164
  for _,emitter in ipairs(self.emitters) do
8196
8165
  table.insert(report.emitters,emitter:export())
8197
8166
  end
8198
- return l_mist.utils.deepCopy(report)
8167
+ return report
8199
8168
  end
8200
8169
  end--- Hound Comms Manager (Base class)
8201
8170
  do
@@ -8452,26 +8421,12 @@ do
8452
8421
  end
8453
8422
  end
8454
8423
 
8455
- function HOUND.Comms.Manager:getTransmitterPos()
8456
- if self.transmitter == nil then return nil end
8457
- if self.transmitter ~= nil and (self.transmitter:isExist() == false or self.transmitter:getLife() < 1) then
8458
- return false
8459
- end
8460
- local pos = self.transmitter:getPoint()
8461
- local transmitterObjectCat, transmitterSubCat = self.transmitter:getCategory()
8462
- if transmitterObjectCat == Object.Category.STATIC or (transmitterObjectCat == Object.Category.UNIT and transmitterSubCat == Unit.Category.GROUND_UNIT) then
8463
- local verticalOffset = (self.transmitter:getDesc()["box"]["max"]["y"] + 5) or 20
8464
- pos.y = pos.y + verticalOffset
8465
- end
8466
- return pos
8467
- end
8468
-
8469
8424
  function HOUND.Comms.Manager.TransmitFromQueue(gSelf)
8470
8425
  local msgObj = gSelf:getNextMsg()
8471
8426
  local readTime = gSelf.settings.interval
8472
8427
  if msgObj == nil then return timer.getTime() + readTime end
8473
8428
 
8474
- local transmitterPos = gSelf:getTransmitterPos()
8429
+ local transmitterPos = HoundUtils.TTS.getTransmitterPos(gSelf.transmitter)
8475
8430
 
8476
8431
  if transmitterPos == false then
8477
8432
  env.info("[Hound] - Transmitter destroyed")
@@ -8482,11 +8437,12 @@ do
8482
8437
  transmitter = gSelf.transmitter
8483
8438
  })
8484
8439
 
8440
+ gSelf.transmitter = nil
8485
8441
  return timer.getTime() + 10
8486
8442
  end
8487
8443
 
8488
8444
  if gSelf.enabled and HoundUtils.TTS.isAvailable() and msgObj.tts ~= nil and gSelf.preferences.enabletts then
8489
- HoundUtils.TTS.Transmit(msgObj.tts,msgObj.coalition,gSelf.settings,transmitterPos)
8445
+ HoundUtils.TTS.Transmit(msgObj.tts,msgObj.coalition,gSelf.settings,gSelf.transmitter)
8490
8446
  readTime = HoundUtils.TTS.getReadTime(msgObj.tts,gSelf.settings.speed,gSelf.settings.googletts)
8491
8447
 
8492
8448
  end
@@ -8744,7 +8700,7 @@ do
8744
8700
  end
8745
8701
 
8746
8702
  function HOUND.ElintWorker:platformRefresh()
8747
- if HOUND.Length(self.platforms) < 1 then return end
8703
+ if #self.platforms < 1 then return end
8748
8704
  for id,platform in ipairs(self.platforms) do
8749
8705
  if platform:isExist() == false or platform:getLife() <1 then
8750
8706
  table.remove(self.platforms, id)
@@ -8759,7 +8715,7 @@ do
8759
8715
  end
8760
8716
 
8761
8717
  function HOUND.ElintWorker:removeDeadPlatforms()
8762
- if HOUND.Length(self.platforms) < 1 then return end
8718
+ if #self.platforms < 1 then return end
8763
8719
  for id,platform in ipairs(self.platforms) do
8764
8720
  if platform:isExist() == false or platform:getLife() <1 or (platform:getCategory() ~= Object.Category.STATIC and platform:isActive() == false) then
8765
8721
  table.remove(self.platforms, id)
@@ -8774,7 +8730,7 @@ do
8774
8730
  end
8775
8731
 
8776
8732
  function HOUND.ElintWorker:countPlatforms()
8777
- return HOUND.Length(self.platforms)
8733
+ return #self.platforms
8778
8734
  end
8779
8735
 
8780
8736
  function HOUND.ElintWorker:listPlatforms()
@@ -8964,7 +8920,7 @@ do
8964
8920
  for _, contact in pairs(self.contacts) do
8965
8921
  contact:KalmanPredict()
8966
8922
  end
8967
- if HOUND.Length(self.platforms) == 0 then return end
8923
+ if #self.platforms == 0 then return end
8968
8924
  local Radars = {}
8969
8925
  if GroupName then
8970
8926
  Radars = HoundUtils.Elint.getActiveRadarsInGroup(GroupName)
@@ -9952,7 +9908,6 @@ function HOUND.Sector:notifySiteLaunching(site)
9952
9908
  end
9953
9909
 
9954
9910
  if gSelf.comms.controller:isEnabled() then
9955
- HOUND.Logger.debug(args["contact"].. ":\n" .. HOUND.Mist.utils.tableShow(contact))
9956
9911
 
9957
9912
  msgObj.contactId = contact:getId()
9958
9913
  msgObj.tts = contact:generateTtsReport(useDMM,preferMGRS)
@@ -9962,7 +9917,6 @@ function HOUND.Sector:notifySiteLaunching(site)
9962
9917
  if gSelf.comms.controller:getSettings("enableText") == true then
9963
9918
  msgObj.txt = contact:generateTextReport(useDMM)
9964
9919
  end
9965
- HOUND.Logger.debug("msg: \n"..HOUND.Mist.utils.tableShow(msgObj))
9966
9920
  gSelf.comms.controller:addMessageObj(msgObj)
9967
9921
  end
9968
9922
  end
@@ -10091,14 +10045,14 @@ do
10091
10045
  local sitesData = self:getRadioItemsText()
10092
10046
  local typesSpotted = {}
10093
10047
 
10094
- if HOUND.setContains(sitesData.noData) and
10048
+ if HOUND.setContains(sitesData, "noData") and
10095
10049
  not self.comms.menu.noData then
10096
10050
  self.comms.menu.noData = missionCommands.addCommandForCoalition(self._hSettings:getCoalition(),
10097
10051
  sitesData.noData,
10098
10052
  self.comms.menu.root, timer.getAbsTime)
10099
10053
  end
10100
10054
 
10101
- if not HOUND.setContains(sitesData.noData) then
10055
+ if not HOUND.setContains(sitesData, "noData") then
10102
10056
  if self.comms.menu.noData ~= nil then
10103
10057
  self.comms.menu.noData = missionCommands.removeItemForCoalition(self._hSettings:getCoalition(),
10104
10058
  self.comms.menu.noData)
@@ -10107,7 +10061,7 @@ do
10107
10061
 
10108
10062
  local grpMenuDone = {}
10109
10063
  if HOUND.Length(self.comms.enrolled) > 0 then
10110
- if HOUND.Length(sitesData) and not HOUND.setContains(sitesData.noData) then
10064
+ if HOUND.Length(sitesData) > 0 and not HOUND.setContains(sitesData, "noData") then
10111
10065
  for _,siteData in ipairs(sitesData) do
10112
10066
  if not HOUND.setContainsValue(typesSpotted,siteData.typeAssigned) then
10113
10067
  table.insert(typesSpotted,siteData.typeAssigned)
@@ -10224,12 +10178,12 @@ do
10224
10178
 
10225
10179
  local siteObj = typeMenu.objs[siteData.dcsName]
10226
10180
  if HOUND.setContains(siteObj,'items') then
10227
- for emitterName,emitter in (siteObj.items) do
10181
+ for emitterName,emitter in pairs(siteObj.items) do
10228
10182
  siteObj.items[emitterName] = missionCommands.removeItemForGroup(playerGid,emitter)
10229
10183
  end
10230
10184
  end
10231
10185
 
10232
- if HOUND.setContains(typeMenu.items[siteData.dcsName]) then
10186
+ if HOUND.setContains(typeMenu.items, siteData.dcsName) then
10233
10187
  typeMenu.items[siteData.dcsName] = missionCommands.removeItemForGroup(playerGid,typeMenu.items[siteData.dcsName] )
10234
10188
  end
10235
10189
  end
@@ -10274,7 +10228,7 @@ do
10274
10228
 
10275
10229
  function HoundElint:destroy()
10276
10230
  self:systemOff(false)
10277
- self:defaultEventHandler(false)
10231
+ self:defaultEventHandler(true)
10278
10232
 
10279
10233
  for name,sector in pairs(self.sectors) do
10280
10234
  self.sectors[name] = sector:destroy()
@@ -10409,7 +10363,6 @@ do
10409
10363
  function HoundElint:AlertOnLaunch(fireUnit)
10410
10364
  if not self:getAlertOnLaunch() or (not HoundUtils.Dcs.isGroup(fireUnit) and not HoundUtils.Dcs.isUnit(fireUnit)) then return end
10411
10365
  HOUND.Logger.debug("Launch Alert called for " .. fireUnit:getName())
10412
-
10413
10366
  self.contacts:AlertOnLaunch(fireUnit)
10414
10367
  end
10415
10368
 
@@ -11131,10 +11084,11 @@ do
11131
11084
  end
11132
11085
 
11133
11086
  function HoundElint:populateRadioMenu()
11134
- if not self:isRunning() or not self.contacts or self.contacts:countContacts() == 0 or self.settings:getCoalition() == nil then
11087
+ if not self:isRunning() or not self.contacts or type(self.contacts:countContacts()) ~= "number" or self.settings:getCoalition() == nil then
11135
11088
  return
11136
11089
  end
11137
11090
  HOUND.DB.updateHumanDb(self.settings:getCoalition())
11091
+
11138
11092
  local sectors = self:getSectors()
11139
11093
  table.sort(sectors,HoundUtils.Sort.sectorsByPriorityLowLast)
11140
11094
  for _,sector in pairs(sectors) do
@@ -11164,7 +11118,8 @@ do
11164
11118
  trigger.action.outTextForCoalition(self.settings:getCoalition(),
11165
11119
  "Hound ELINT system is now Operating", 10)
11166
11120
  end
11167
- env.info("Hound is now on")
11121
+ env.info("Hound instance " .. self.settings:getId() .. " is now on")
11122
+ self:populateRadioMenu()
11168
11123
  HOUND.EventHandler.publishEvent({
11169
11124
  id = HOUND.EVENTS.HOUND_ENABLED,
11170
11125
  houndId = self.settings:getId(),
@@ -11182,7 +11137,7 @@ do
11182
11137
  trigger.action.outTextForCoalition(self.settings:getCoalition(),
11183
11138
  "Hound ELINT system is now Offline", 10)
11184
11139
  end
11185
- env.info("Hound is now off")
11140
+ env.info("Hound instance " .. self.settings:getId() .. " is now off")
11186
11141
  HOUND.EventHandler.publishEvent({
11187
11142
  id = HOUND.EVENTS.HOUND_DISABLED,
11188
11143
  houndId = self.settings:getId(),
@@ -11406,8 +11361,8 @@ do
11406
11361
  end
11407
11362
  end
11408
11363
 
11409
- function HoundElint:defaultEventHandler(remove)
11410
- if remove == false then
11364
+ function HoundElint:defaultEventHandler(purge)
11365
+ if purge == true then
11411
11366
  HOUND.EventHandler.removeInternalEventHandler(self)
11412
11367
  world.removeEventHandler(self)
11413
11368
  return
@@ -11420,4 +11375,4 @@ do
11420
11375
  trigger.action.outText("Hound ELINT ("..HOUND.VERSION..") is loaded.", 15)
11421
11376
  env.info("[Hound] - finished loading (".. HOUND.VERSION..")")
11422
11377
  end
11423
- -- Hound version 0.5.0 - Compiled on 2026-02-24 22:19
11378
+ -- Hound version 0.5.1 - Compiled on 2026-04-29 10:28