Page 2 of 2

RE: Locating my units?

Posted: Fri Jun 28, 2019 9:18 pm
by Rory Noonan
Just to expand on lumiere's excellent example above, here is a special action that will match partial names and allows retries if the unit name isn't correct or produces multiple results:
local function Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end

local function ConvertDecimalPositionToDegrees(latitude,longitude)
local latitidePrefix, longitudePrefix
if latitude > 0 then
latitidePrefix = "N"
else
latitidePrefix = "S"
latitude = latitude*-1
end

local latitudeDegrees = math.floor(latitude)
local latitudeMinutes = math.floor((latitude - latitudeDegrees)*60)
local latitudeSeconds = (((latitude-latitudeDegrees)*60) - latitudeMinutes)*60
latitudeSeconds = Round(latitudeSeconds, 0)

if longitude > 0 then
longitudePrefix = "E"
else
longitudePrefix = "W"
longitude = longitude*-1
end

local longitudeDegrees = math.floor(longitude)
local longitudeMinutes = math.floor((longitude - longitudeDegrees)*60)
local longitudeSeconds = (((longitude-longitudeDegrees)*60) - longitudeMinutes)*60
longitudeSeconds = Round(longitudeSeconds, 0)

local result = (latitidePrefix..latitudeDegrees .. "°" .. latitudeMinutes .. "'" .. latitudeSeconds .. '", '..
longitudePrefix..longitudeDegrees .. "°" .. longitudeMinutes .. "'" .. longitudeSeconds .. '"')
return result
end

local function GeneratePlayerSideUnitDataTable()
local result, unitList = {}, VP_GetSide({side='playerside'}).units
for k,v in ipairs (unitList) do
local unit = ScenEdit_GetUnit({guid=v.guid})
table.insert(result, unit)
end
return result
end

::retry::

local sideUnits = GeneratePlayerSideUnitDataTable()

local userInput = ScenEdit_InputBox('Which unit do you want to locate? \n\nType any unique part of the unit name, e.g. 73, col or collins will match SSG 73 Collins; case insensitive.')
userInput = string.lower(userInput)
if userInput == '' then goto finish end

local matchCount, matchedUnit = 0, nil

for k,v in ipairs (sideUnits) do
match = string.find(string.lower(v.name),userInput)
if match ~= nil then
matchedUnit = ScenEdit_GetUnit({guid=v.guid})
matchCount = matchCount + 1
end
end

local choice, retry = nil, nil

if matchCount == 0 then

retry = ScenEdit_MsgBox('No matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end

elseif matchCount == 1 then

choice = ScenEdit_MsgBox("Is the "..matchedUnit.type..", "..matchedUnit.classname.." '"..matchedUnit.name.."' the unit you're looking for ?",4)

elseif matchCount > 1 then

retry = ScenEdit_MsgBox('Multiple matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end

end

if choice == 'Yes' then
local unit = ScenEdit_GetUnit({guid=matchedUnit.guid})
local unitPositionDescription = ConvertDecimalPositionToDegrees(unit.latitude,unit.longitude)
ScenEdit_SpecialMessage('playerside',unit.name..' is currently located at '..unitPositionDescription..'<BR><BR> Click "Jump to Location" to go to this position.',{latitude=unit.latitude,longitude=unit.longitude})
end

::finish::


Also attached is a demo .scen file and a separate .lua file with the above code nicely formatted.

RE: Locating my units?

Posted: Fri Jun 28, 2019 9:27 pm
by magi
there are some really smart guys here......

RE: Locating my units?

Posted: Sat Jun 29, 2019 3:00 am
by lumiere
ORIGINAL: apache85

Just to expand on lumiere's excellent example above, here is a special action that will match partial names and allows retries if the unit name isn't correct or produces multiple results:
local function Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end

local function ConvertDecimalPositionToDegrees(latitude,longitude)
local latitidePrefix, longitudePrefix
if latitude > 0 then
latitidePrefix = "N"
else
latitidePrefix = "S"
latitude = latitude*-1
end

local latitudeDegrees = math.floor(latitude)
local latitudeMinutes = math.floor((latitude - latitudeDegrees)*60)
local latitudeSeconds = (((latitude-latitudeDegrees)*60) - latitudeMinutes)*60
latitudeSeconds = Round(latitudeSeconds, 0)

if longitude > 0 then
longitudePrefix = "E"
else
longitudePrefix = "W"
longitude = longitude*-1
end

local longitudeDegrees = math.floor(longitude)
local longitudeMinutes = math.floor((longitude - longitudeDegrees)*60)
local longitudeSeconds = (((longitude-longitudeDegrees)*60) - longitudeMinutes)*60
longitudeSeconds = Round(longitudeSeconds, 0)

local result = (latitidePrefix..latitudeDegrees .. "°" .. latitudeMinutes .. "'" .. latitudeSeconds .. '", '..
longitudePrefix..longitudeDegrees .. "°" .. longitudeMinutes .. "'" .. longitudeSeconds .. '"')
return result
end

local function GeneratePlayerSideUnitDataTable()
local result, unitList = {}, VP_GetSide({side='playerside'}).units
for k,v in ipairs (unitList) do
local unit = ScenEdit_GetUnit({guid=v.guid})
table.insert(result, unit)
end
return result
end

::retry::

local sideUnits = GeneratePlayerSideUnitDataTable()

local userInput = ScenEdit_InputBox('Which unit do you want to locate? \n\nType any unique part of the unit name, e.g. 73, col or collins will match SSG 73 Collins; case insensitive.')
userInput = string.lower(userInput)
if userInput == '' then goto finish end

local matchCount, matchedUnit = 0, nil

for k,v in ipairs (sideUnits) do
match = string.find(string.lower(v.name),userInput)
if match ~= nil then
matchedUnit = ScenEdit_GetUnit({guid=v.guid})
matchCount = matchCount + 1
end
end

local choice, retry = nil, nil

if matchCount == 0 then

retry = ScenEdit_MsgBox('No matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end

elseif matchCount == 1 then

choice = ScenEdit_MsgBox("Is the "..matchedUnit.type..", "..matchedUnit.classname.." '"..matchedUnit.name.."' the unit you're looking for ?",4)

elseif matchCount > 1 then

retry = ScenEdit_MsgBox('Multiple matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end

end

if choice == 'Yes' then
local unit = ScenEdit_GetUnit({guid=matchedUnit.guid})
local unitPositionDescription = ConvertDecimalPositionToDegrees(unit.latitude,unit.longitude)
ScenEdit_SpecialMessage('playerside',unit.name..' is currently located at '..unitPositionDescription..'<BR><BR> Click "Jump to Location" to go to this position.',{latitude=unit.latitude,longitude=unit.longitude})
end

::finish::


Also attached is a demo .scen file and a separate .lua file with the above code nicely formatted.

[X(] [:)]

RE: Locating my units?

Posted: Sat Jun 29, 2019 6:08 am
by goldfinger35
lumiere and apache [&o]

RE: Locating my units?

Posted: Sat Jun 29, 2019 9:40 am
by Phoenix100
I do remember asking for this - click unit in message log to centre in main map - around 4 years ago. Is it coming anytime soon, then, Dimitris? That would be a really cool addition.

RE: Locating my units?

Posted: Sat Jun 29, 2019 9:54 am
by Dimitris
ORIGINAL: Phoenix100
I do remember asking for this - click unit in message log to centre in main map - around 4 years ago. Is it coming anytime soon, then, Dimitris? That would be a really cool addition.

I wish I could say more right now.

Not long yet, though.

RE: Locating my units?

Posted: Sat Jun 29, 2019 8:02 pm
by Whicker
This works for contacts too - just change out lumiere's line 2 with:

local unit = ScenEdit_GetContact({side=ScenEdit_PlayerSide(), name=name})

RE: Locating my units?

Posted: Sun Jun 30, 2019 8:25 pm
by Whicker
modified to work with contacts - though the contact name must be an exact match - you can just copy it from the log (before running the action). Added trim function so if you copy a space at the end it will still match (only trimming on contacts). Can't do partial match on contacts I don't think since they all seem to start at #10 - so if you put in 13 there would likely be multiple #13s.
local function sanitize(str)
local s = string.gsub(str, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1")
return s
end

local function trim(s)
-- from PiL2 20.4
return (s:gsub("^%s*(.-)%s*$", "%1"))
end

local function Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end

local function ConvertDecimalPositionToDegrees(latitude,longitude)
local latitidePrefix, longitudePrefix
if latitude > 0 then
latitidePrefix = "N"
else
latitidePrefix = "S"
latitude = latitude*-1
end --if
local latitudeDegrees = math.floor(latitude)
local latitudeMinutes = math.floor((latitude - latitudeDegrees)*60)
local latitudeSeconds = (((latitude-latitudeDegrees)*60) - latitudeMinutes)*60
latitudeSeconds = Round(latitudeSeconds, 0)
if longitude > 0 then
longitudePrefix = "E"
else
longitudePrefix = "W"
longitude = longitude*-1
end --if
local longitudeDegrees = math.floor(longitude)
local longitudeMinutes = math.floor((longitude - longitudeDegrees)*60)
local longitudeSeconds = (((longitude-longitudeDegrees)*60) - longitudeMinutes)*60
longitudeSeconds = Round(longitudeSeconds, 0)
local result = (latitidePrefix..latitudeDegrees .. "°" .. latitudeMinutes .. "'" .. latitudeSeconds .. '", '..
longitudePrefix..longitudeDegrees .. "°" .. longitudeMinutes .. "'" .. longitudeSeconds .. '"')
return result
end --end function

local function GeneratePlayerSideUnitDataTable()
local result, unitList = {}, VP_GetSide({side='playerside'}).units
for k,v in ipairs (unitList) do
local unit = ScenEdit_GetUnit({guid=v.guid})
table.insert(result, unit)
end
return result
end

local function unitFound(u)
local unitPositionDescription = ConvertDecimalPositionToDegrees(u.latitude,u.longitude)
ScenEdit_SpecialMessage('playerside',u.name..' is currently located at '..unitPositionDescription..'<BR><BR> Click "Jump to Location" to go to this position.',{latitude=u.latitude,longitude=u.longitude})
end --function

local function listOfMatches(tableList)
local list = ''
for k,v in ipairs(tableList) do
list = list.. '\n' ..v
end
return list
end --function

::retry::

local userInput = ScenEdit_InputBox('Which unit do you want to locate? \n\nType any unique part of the unit name, e.g. 73, col or collins will match SSG 73 Collins; case insensitive.')
userInput = string.lower(userInput)
if userInput == '' then goto finish end --nothing entered

userInputTrimmed= trim(userInput) --trim only is doing exact comparison, spaces may be valuable in fuzzy search
-- check units for exact match, needed because fuzzy search returns both unit 1 and unit 11
local exactUnit = ScenEdit_GetUnit({side=ScenEdit_PlayerSide(), name=userInputTrimmed})
if exactUnit ~= nil then
unitFound(exactUnit)
return
end
--check contacts for exact match
local exactContact = ScenEdit_GetContact({side=ScenEdit_PlayerSide(), name=userInputTrimmed})
if exactContact ~= nil then
unitFound(exactContact)
return
end

local sideUnits = GeneratePlayerSideUnitDataTable()
local matchCount, matchedUnit, matches = 0, nil, {}
userInput = sanitize(userInput)

for k,v in ipairs (sideUnits) do
match = string.find(string.lower(v.name),userInput)
if match ~= nil then
matchedUnit = ScenEdit_GetUnit({guid=v.guid})
matchCount = matchCount + 1
table.insert(matches, matchedUnit.name)
end
end

local choice, retry = nil, nil

if matchCount == 0 then

retry = ScenEdit_MsgBox('No matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end

elseif matchCount == 1 then
choice = ScenEdit_MsgBox("Is the "..matchedUnit.type..", "..matchedUnit.classname.." '"..matchedUnit.name.."' the unit you're looking for ?",4)

elseif matchCount > 1 then
local possibleMatches = listOfMatches(matches)
retry = ScenEdit_MsgBox('Multiple matches found. \n\nTry typing a more specific part of the unit name. Case is not important, but at least some of the name needs to be a match! \nPossible Matches:\n'..possibleMatches..'\n\nDo you want to try again?',4)

if retry == 'Yes' then goto retry end
end

if choice == 'Yes' then
local unit = ScenEdit_GetUnit({guid=matchedUnit.guid})
unitFound(unit)
end

::finish::

RE: Locating my units?

Posted: Mon Jul 01, 2019 5:37 am
by michaelm75au
Contact number should be unique for a side; not sure if the uniqueness is overall or just for the contact type (e.g. SKUNK #1234, VAMPIRE #1234). I had a feeling it was side based so you shouldn't get SKUNK #1234 and VAMPIRE #1234 at same time for a single side.

RE: Locating my units?

Posted: Mon Jul 01, 2019 3:43 pm
by Whicker
I edited my code above to fix some things:

- first it tries to match an exact unit name, without it you cannot get a result with units named 'Flanker #1' and 'Flanker #11' as the the string.match method can't tell the difference
- next it tries to match an exact contact name - that is the only try for contacts, string.match would find too many units as it would match anything with 21 in it*
- added the list of possible matches to the message that says multiple matches found
- units with a special character in the name like a hyphen would not match anything cause special characters need to be escaped (added sanitize function)

* I wonder if the use case for searching for a contact would always be to find the most recent contact, which would be a larger number than existing contacts so maybe the string match would work if the contact was #123 and there was no 1123 or 1230. Even then you would still have to be careful as you would need to combine the results of units and contacts in order to know which one the user is looking for. '318' could be a contact or it could be unit '318 LR ASW Regt #2'.

I tested this a bit with one of gunners large scenarios and it seemed to perform well, I was worried loading all of a sides units into a table would eat up all the memory but it didn't. Last time I did something like this it did so maybe something was fixed.

this is a really neat lua example for anyone looking for a fairly advanced but doable code sample.

RE: Locating my units?

Posted: Tue Jul 16, 2019 8:50 am
by Dimitris
Added ORBAT keyword search in a future release.

RE: Locating my units?

Posted: Tue Jul 16, 2019 12:38 pm
by goldfinger35

RE: Locating my units?

Posted: Thu Jul 18, 2019 12:16 am
by magi
ORIGINAL: goldfinger35

That. Or a simple search function in order of battle.
i actually heartily agree.... i asked about this in a post years ago.....

RE: Locating my units?

Posted: Thu Jul 18, 2019 4:21 am
by magi
ORIGINAL: Dimitris

ORIGINAL: spike2071
I would love a Jump To Location ability from the message log directly. Similar to the pop up, but without the actual pop up.

Stick around.
cool.....

RE: Locating my units?

Posted: Sat Jul 27, 2019 3:23 am
by MagpieS
ORIGINAL: magi

ORIGINAL: Dimitris

ORIGINAL: spike2071
I would love a Jump To Location ability from the message log directly. Similar to the pop up, but without the actual pop up.

Stick around.
cool.....


Why not just hot link the callsign in the message window ?