Initial commit

This commit is contained in:
Matt Wagner 2020-10-24 23:38:05 -07:00
commit 560768bc61
48 changed files with 2401 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

4
mfile Normal file
View File

@ -0,0 +1,4 @@
{
"package": "lotj-ui",
"version": "1.0"
}

View File

@ -0,0 +1,6 @@
[
{
"name": "gmap-refresh",
"regex": "^gmap refresh$"
}
]

View File

@ -0,0 +1,5 @@
lotj.galaxyMap.resetData()
enableTrigger("galaxy-map-refresh")
send("starsystems", false)
send("planets", false)

View File

@ -0,0 +1,7 @@
[
{
"name": "map",
"regex": "^map( ?.*)$",
"script": "lotj.mapper.mapCommand(matches[2])"
}
]

View File

@ -0,0 +1,6 @@
[
{
"name": "radar",
"regex": "^r(a(d(a(r)?)?)?)?$"
}
]

View File

@ -0,0 +1,4 @@
-- Intercept the radar command to enable our radar scraping triggers.
-- We don't want them to potentially fire on unrelated lines.
enableTrigger("system-map-radar")
send("radar", false)

BIN
src/resources/space.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

34
src/scripts/chat/chat.lua Normal file
View File

@ -0,0 +1,34 @@
lotj = lotj or {}
lotj.chat = lotj.chat or {}
registerAnonymousEventHandler("lotjUICreated", function()
for keyword, contentsContainer in pairs(lotj.layout.lowerRightTabData.contents) do
lotj.chat[keyword] = Geyser.MiniConsole:new({
x = "1%", y = "1%",
width = "98%",
height = "98%",
autoWrap = false,
color = "black",
scrollBar = true,
fontSize = 14,
}, contentsContainer)
-- Set the wrap at a few characters short of the full width to avoid the scroll bar showing over text
local charsPerLine = lotj.chat[keyword]:getColumnCount()-3
lotj.chat[keyword]:setWrap(charsPerLine)
registerAnonymousEventHandler("sysWindowResizeEvent", function()
local charsPerLine = lotj.chat[keyword]:getColumnCount()-3
lotj.chat[keyword]:setWrap(charsPerLine)
end)
end
end)
function lotj.chat.routeMessage(type)
selectCurrentLine()
copy()
lotj.chat[type]:cecho("<reset>"..getTime(true, "hh:mm:ss").." ")
lotj.chat[type]:appendBuffer()
lotj.chat.all:cecho("<reset>"..getTime(true, "hh:mm:ss").." ")
lotj.chat.all:appendBuffer()
end

View File

@ -0,0 +1,5 @@
[
{
"name": "chat"
}
]

View File

@ -0,0 +1,319 @@
lotj = lotj or {}
lotj.galaxyMap = lotj.galaxyMap or {
data = {
labelAsPlanets = true,
systems = {},
planets = {},
govToColor = {
["Neutral Government"] = "#AAAAAA",
},
}
}
local dataFileName = getMudletHomeDir().."/galaxyMap"
registerAnonymousEventHandler("lotjUICreated", function()
lotj.galaxyMap.container = Geyser.Label:new({
name = "galaxy",
x = 0, y = 0,
width = "100%",
height = "100%",
}, lotj.layout.upperRightTabData.contents["galaxy"])
lotj.galaxyMap.container:setBackgroundImage(getMudletHomeDir().."/@PKGNAME@/space.jpg")
lotj.galaxyMap.refreshButton = Geyser.Label:new({
name = "galaxyMapRefresh",
x = "20%", y = "35%",
width = "60%", height = 40,
}, lotj.galaxyMap.container)
lotj.galaxyMap.refreshButton:setStyleSheet([[
background-color: grey;
border: 1px solid white;
]])
lotj.galaxyMap.refreshButton:echo("Click here to populate this map.", "white", "c14")
lotj.galaxyMap.refreshButton:setClickCallback(function()
expandAlias("gmap refresh", false)
end)
disableTrigger("galaxy-map-refresh")
if io.exists(dataFileName) then
table.load(dataFileName, lotj.galaxyMap.data)
lotj.galaxyMap.log("Loaded map data.")
lotj.galaxyMap.drawSystems()
end
registerAnonymousEventHandler("msdp.SHIPGALY", lotj.galaxyMap.setShipGalCoords)
end)
function lotj.galaxyMap.log(text)
cecho("[<cyan>LOTJ Galaxy Map<reset>] "..text.."\n")
end
function lotj.galaxyMap.setShipGalCoords()
-- Assume "0" in both values means we don't have valid coordinates and leave them what they were.
-- TODO: Find a way to support a system actually located at 0, 0
if msdp.SHIPGALX ~= "0" or msdp.SHIPGALY ~= "0" then
lotj.galaxyMap.currentX = tonumber(msdp.SHIPGALX)
lotj.galaxyMap.currentY = tonumber(msdp.SHIPGALY)
lotj.galaxyMap.drawSystems()
end
end
local function container()
return lotj.galaxyMap.container
end
-- Fire off any showplanet commands we still need to run to load data for all known planets
function lotj.galaxyMap.enqueuePendingRefreshCommands()
for planet in pairs(gatherPlanetsState.pendingBasic) do
send("showplanet \""..planet.."\"", false)
gatherPlanetsState.pendingCommands = gatherPlanetsState.pendingCommands + 1
end
for planet in pairs(gatherPlanetsState.pendingResources) do
send("showplanet \""..planet.."\" resources", false)
gatherPlanetsState.pendingCommands = gatherPlanetsState.pendingCommands + 1
end
-- We didn't have to retry any, so we're done getting info.
if gatherPlanetsState.pendingCommands == 0 then
disableTrigger("galaxy-map-refresh")
return
end
echo("\n")
lotj.galaxyMap.log("Enqueueing "..gatherPlanetsState.pendingCommands.." showplanet commands.")
end
function lotj.galaxyMap.resetData()
lotj.galaxyMap.data.planets = {}
lotj.galaxyMap.data.systems = {}
end
local govColorIdx = 1
local govColorList = {}
table.insert(govColorList, "#E69F00")
table.insert(govColorList, "#56B4E9")
table.insert(govColorList, "#009E73")
table.insert(govColorList, "#F0E442")
table.insert(govColorList, "#D55E00")
table.insert(govColorList, "#CC79A7")
function lotj.galaxyMap.recordSystem(name, x, y)
lotj.galaxyMap.data.systems = lotj.galaxyMap.data.systems or {}
lotj.galaxyMap.data.systems[name] = {
name = name,
planets = {},
gov = "Neutral Government",
x = x,
y = y,
}
table.save(dataFileName, lotj.galaxyMap.data)
lotj.galaxyMap.drawSystems()
end
function lotj.galaxyMap.recordPlanet(planetData)
if not lotj.galaxyMap.data.planets[planetData.name] then
lotj.galaxyMap.data.planets[planetData.name] = {
name = planetData.name,
gov = planetData.gov,
system = planetData.system,
}
local system = lotj.galaxyMap.data.systems[planetData.system]
if system ~= nil then
system.gov = planetData.gov
table.insert(system.planets, planetData.name)
else
lotj.galaxyMap.log("Unable to find system "..planetData.system.." for planet "..planetData.name)
end
if lotj.galaxyMap.data.govToColor[planetData.gov] == nil then
govColorIdx = govColorIdx+1
lotj.galaxyMap.data.govToColor[planetData.gov] = govColorList[govColorIdx]
end
end
if planetData.coords ~= nil then
lotj.galaxyMap.data.planets[planetData.name].coords = planetData.coords
end
if planetData.freeport ~= nil then
lotj.galaxyMap.data.planets[planetData.name].freeport = planetData.freeport
end
if planetData.taxRate ~= nil then
lotj.galaxyMap.data.planets[planetData.name].taxRate = planetData.taxRate
end
if planetData.resources ~= nil then
lotj.galaxyMap.data.planets[planetData.name].resources = planetData.resources
end
table.save(dataFileName, lotj.galaxyMap.data)
lotj.galaxyMap.drawSystems()
end
local systemPointSize = 14
local function stylePoint(point, gov, currentSystem)
local backgroundColor = lotj.galaxyMap.data.govToColor[gov] or "#AAAAAA"
local borderStyle = ""
if currentSystem then
borderStyle = "border: 2px solid red;"
end
point:setStyleSheet([[
border-radius: ]]..math.floor(systemPointSize/2)..[[px;
background-color: ]]..backgroundColor..[[;
]]..borderStyle..[[
]])
end
local function systemDisplayName(system)
if lotj.galaxyMap.data.labelAsPlanets and #(system.planets or {}) > 0 then
local labelString = ""
for _, planet in ipairs(system.planets) do
if #labelString > 0 then
labelString = labelString..", "
end
labelString = labelString..planet
end
return labelString
else
-- Cut off common extra words from the system name to keep labels short
local labelString = system.name
labelString = string.gsub(labelString, " System$", "")
labelString = string.gsub(labelString, " Sector$", "")
return labelString
end
end
function lotj.galaxyMap.drawSystems()
local minX, _, _, maxY = lotj.galaxyMap.coordRange()
local xOffset, yOffset, pxPerCoord = lotj.galaxyMap.calculateSizing()
lotj.galaxyMap.systemPoints = lotj.galaxyMap.systemPoints or {}
for _, point in pairs(lotj.galaxyMap.systemPoints) do
point:hide()
end
lotj.galaxyMap.systemLabels = lotj.galaxyMap.systemLabels or {}
for _, label in pairs(lotj.galaxyMap.systemLabels) do
label:hide()
end
local foundCurrentLocation = false
local systemsToDraw = {}
for _, system in pairs(lotj.galaxyMap.data.systems) do
table.insert(systemsToDraw, system)
if system.x == lotj.galaxyMap.currentX and system.y == lotj.galaxyMap.currentY then
foundCurrentLocation = true
end
end
if not foundCurrentLocation and lotj.galaxyMap.currentX and lotj.galaxyMap.currentY then
table.insert(systemsToDraw, {
name = "Current",
x = lotj.galaxyMap.currentX,
y = lotj.galaxyMap.currentY
})
end
-- Hide or show the refresh button accordingly, based on whether we have any data.
if #systemsToDraw > 0 then
lotj.galaxyMap.refreshButton:hide()
else
lotj.galaxyMap.refreshButton:show()
end
for _, system in ipairs(systemsToDraw) do
local point = lotj.galaxyMap.systemPoints[system.name]
if point == nil then
point = Geyser.Label:new({width=systemPointSize, height=systemPointSize}, container())
stylePoint(point, system.gov, false)
lotj.galaxyMap.systemPoints[system.name] = point
else
point:show()
end
local label = lotj.galaxyMap.systemLabels[system.name]
if label == nil then
label = Geyser.Label:new({
height = 16, width = 100,
fillBg = 0,
}, container())
lotj.galaxyMap.systemLabels[system.name] = label
else
label:show()
end
label:echo(systemDisplayName(system), "white", "12c")
local sysX = math.floor(xOffset + (system.x-minX)*pxPerCoord - systemPointSize/2 + 0.5)
local sysY = math.floor(yOffset + (maxY-system.y)*pxPerCoord - systemPointSize/2 + 0.5)
point:move(sysX, sysY)
if system.x == lotj.galaxyMap.currentX and system.y == lotj.galaxyMap.currentY then
stylePoint(point, system.gov, true)
else
stylePoint(point, system.gov, false)
end
label:move(math.max(sysX-45, 0), sysY+systemPointSize)
end
end
-- Returns X starting point, Y starting point, and pixels per coordinate
function lotj.galaxyMap.calculateSizing()
local minX, maxX, minY, maxY = lotj.galaxyMap.coordRange()
local xRange = maxX-minX
local yRange = maxY-minY
local contWidth = container():get_width()
local contHeight = container():get_height()
-- Determine whether the map would be limited by height or width first.
local mapWidth = nil
local mapHeight = nil
local pxPerCoord = nil
local pxHeightIfLimitedByWidth = (contWidth/xRange)*yRange
local pxWidthIfLimitedByHeight = (contHeight/yRange)*xRange
if pxHeightIfLimitedByWidth <= contHeight then
-- Width was the limiting factor, so use it to determine sizing
mapWidth = contWidth
mapHeight = pxHeightIfLimitedByWidth
pxPerCoord = contWidth/xRange
elseif pxWidthIfLimitedByHeight <= contWidth then
-- Width was the limiting factor, so use it to determine sizing
mapWidth = pxWidthIfLimitedByHeight
mapHeight = contHeight
pxPerCoord = contHeight/yRange
else
echo("Unable to determine appropriate galaxy map dimensions. This is a script bug.\n")
end
local mapAnchorX = (contWidth-mapWidth)/2
local mapAnchorY = (contHeight-mapHeight)/2
return mapAnchorX, mapAnchorY, pxPerCoord
end
function lotj.galaxyMap.coordRange()
local minX = 0
local maxX = 0
local minY = 0
local maxY = 0
for _, system in pairs(lotj.galaxyMap.data.systems) do
if minX > system.x then
minX = system.x
end
if maxX < system.x then
maxX = system.x
end
if minY > system.y then
minY = system.y
end
if maxY < system.y then
maxY = system.y
end
end
-- Pad all values by 10 to ensure points are displayed reasonably.
return minX-10, maxX+10, minY-10, maxY+10
end

View File

@ -0,0 +1,5 @@
[
{
"name": "galaxy-map"
}
]

View File

@ -0,0 +1,369 @@
lotj = lotj or {}
lotj.infoPanel = lotj.infoPanel or {}
registerAnonymousEventHandler("lotjUICreated", function()
local basicStatsContainer = Geyser.Label:new({
h_stretch_factor = 0.9
}, lotj.layout.lowerInfoPanel)
local combatContainer = Geyser.Label:new({
h_stretch_factor = 0.9
}, lotj.layout.lowerInfoPanel)
local chatContainer = Geyser.Label:new({
h_stretch_factor = 1
}, lotj.layout.lowerInfoPanel)
local spaceContainer = Geyser.Label:new({
h_stretch_factor = 2.2
}, lotj.layout.lowerInfoPanel)
lotj.infoPanel.createBasicStats(basicStatsContainer)
lotj.infoPanel.createOpponentStats(combatContainer)
lotj.infoPanel.createChatInfo(chatContainer)
lotj.infoPanel.createSpaceStats(spaceContainer)
end)
-- Utility functions
local function gaugeFrontStyle(step1, step2, step3, step4, step5)
return [[
background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 ]]..step1..[[, stop: 0.1 ]]..step2..[[, stop: 0.49 ]]..step3..[[, stop: 0.5 ]]..step4..[[, stop: 1 ]]..step5..[[);
border-top: 1px black solid;
border-left: 1px black solid;
border-bottom: 1px black solid;
padding: 3px;
]]
end
local function gaugeBackStyle(step1, step2, step3, step4, step5)
return [[
background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 ]]..step1..[[, stop: 0.1 ]]..step2..[[, stop: 0.49 ]]..step3..[[, stop: 0.5 ]]..step4..[[, stop: 1 ]]..step5..[[);
border-width: 1px;
border-color: black;
border-style: solid;
padding: 3px;
]]
end
local function styleGaugeText(gauge, fontSize)
gauge.text:setStyleSheet([[
padding-right: 10px;
]])
gauge:setAlignment("r")
gauge:setFontSize(fontSize)
end
-- Wires up MSDP subscriptions for a gauge.
-- statName is the short version of the stat name to show after the value (mv, hp, etc)
local function wireGaugeUpdate(gauge, valueVarName, maxVarName, statName)
local function doUpdate()
local current = tonumber(msdp[valueVarName] or "0")
local max = tonumber(msdp[maxVarName] or "0")
if max > 0 then
gauge:setValue(current, max, current.."/"..max.." "..statName)
else
gauge:setValue(0, 1, "")
end
end
registerAnonymousEventHandler("msdp."..valueVarName, doUpdate)
registerAnonymousEventHandler("msdp."..maxVarName, doUpdate)
end
function lotj.infoPanel.createBasicStats(container)
-- Health gauge
local healthGauge = Geyser.Gauge:new({
x="5%", y=4,
width="90%", height=16,
}, container)
healthGauge.front:setStyleSheet(gaugeFrontStyle("#f04141", "#ef2929", "#cc0000", "#a40000", "#cc0000"))
healthGauge.back:setStyleSheet(gaugeBackStyle("#3f1111", "#3f0707", "#330000", "#220000", "#330000"))
styleGaugeText(healthGauge, 12)
wireGaugeUpdate(healthGauge, "HEALTH", "HEALTHMAX", "hp")
local wimpyBar = Geyser.Label:new({
x=0, y=0,
width=2, height="100%",
}, healthGauge.front)
wimpyBar:setStyleSheet([[
background-color: yellow;
]])
registerAnonymousEventHandler("msdp.WIMPY", function()
local health = tonumber(msdp.HEALTH or 0)
local healthMax = tonumber(msdp.HEALTHMAX or 0)
local wimpy = tonumber(msdp.WIMPY or 0)
if healthMax > 0 then
if wimpy > 0 and health > 0 and wimpy < health then
wimpyBar:show()
wimpyBar:move(math.floor(wimpy*100/health).."%", nil)
else
wimpyBar:hide()
end
end
end)
-- Movement gauge
local movementGauge = Geyser.Gauge:new({
x="5%", y=23,
width="90%", height=16,
}, container)
movementGauge.front:setStyleSheet(gaugeFrontStyle("#41f041", "#29ef29", "#00cc00", "#00a400", "#00cc00"))
movementGauge.back:setStyleSheet(gaugeBackStyle("#113f11", "#073f07", "#003300", "#002200", "#003300"))
styleGaugeText(movementGauge, 12)
wireGaugeUpdate(movementGauge, "MOVEMENT", "MOVEMENTMAX", "mv")
-- Mana gauge (will be hidden later if we do not have mana)
local manaGauge = Geyser.Gauge:new({
x="5%", y=42,
width="90%", height=16,
}, container)
manaGauge.front:setStyleSheet(gaugeFrontStyle("#4141f0", "#2929ef", "#0000cc", "#0000a4", "#0000cc"))
manaGauge.back:setStyleSheet(gaugeBackStyle("#11113f", "#07073f", "#000033", "#000022", "#000011"))
styleGaugeText(manaGauge, 12)
wireGaugeUpdate(manaGauge, "MANA", "MANAMAX", "mn")
registerAnonymousEventHandler("msdp.MANAMAX", function()
local manaMax = tonumber(msdp.MANAMAX or 0)
if manaMax > 0 then
healthGauge:move(nil, 4)
healthGauge:resize(nil, 16)
healthGauge:setFontSize("12")
movementGauge:move(nil, 23)
movementGauge:resize(nil, 16)
movementGauge:setFontSize("12")
manaGauge:show()
manaGauge:move(nil, 42)
manaGauge:resize(nil, 16)
manaGauge:setFontSize("12")
else
healthGauge:move(nil, 6)
healthGauge:resize(nil, 22)
healthGauge:setFontSize("13")
movementGauge:move(nil, 32)
movementGauge:resize(nil, 22)
movementGauge:setFontSize("13")
manaGauge:hide()
end
end)
end
function lotj.infoPanel.createOpponentStats(container)
-- Opponent health gauge
local opponentGauge = Geyser.Gauge:new({
x="5%", y=6,
width="90%", height=48,
}, container)
opponentGauge.front:setStyleSheet(gaugeFrontStyle("#bd7833", "#bd6e20", "#994c00", "#703800", "#994c00"))
opponentGauge.back:setStyleSheet(gaugeBackStyle("#442511", "#441d08", "#331100", "#200900", "#331100"))
opponentGauge.text:setStyleSheet("padding: 10px;")
opponentGauge:setAlignment("c")
opponentGauge:setFontSize("12")
local function update()
local opponentName = string.gsub(msdp.OPPONENTNAME or "", "&.", "")
-- Opponent name seems to always be empty string even when fighting, so fall back to something reasonable
if opponentName == "" then
opponentName = "Current target"
end
local opponentHealth = tonumber(msdp.OPPONENTHEALTH or "0")
local opponentHealthMax = tonumber(msdp.OPPONENTHEALTHMAX or "0")
if opponentHealth > 0 then
opponentGauge:setValue(opponentHealth, opponentHealthMax, opponentName.."<br>"..opponentHealth.."%")
else
opponentGauge:setValue(0, 1, "Not fighting")
end
end
registerAnonymousEventHandler("msdp.OPPONENTNAME", update)
registerAnonymousEventHandler("msdp.OPPONENTHEALTH", update)
registerAnonymousEventHandler("msdp.OPPONENTHEALTHMAX", update)
end
function lotj.infoPanel.createChatInfo(container)
-- Commnet channel/code
local commnetLabel = Geyser.Label:new({
x="3%", y=6,
width="20%", height=24,
}, container)
commnetLabel:echo("Comm:", nil, "rb13")
local commnetInfo = Geyser.Label:new({
x = "25%", y = 6,
width = "75%", height = 24
}, container)
local function updateCommnet()
local commChannel = msdp.COMMCHANNEL or "0"
local commEncrypt = msdp.COMMENCRYPT or "0"
if commEncrypt == "0" then
commnetInfo:echo(commChannel, nil, "l13")
else
commnetInfo:echo(commChannel.." (E "..commEncrypt..")", nil, "l13")
end
end
registerAnonymousEventHandler("msdp.COMMCHANNEL", updateCommnet)
registerAnonymousEventHandler("msdp.COMMENCRYPT", updateCommnet)
-- OOC meter
local oocLabel = Geyser.Label:new({
x="3%", y=32,
width="20%", height=24,
}, container)
oocLabel:echo("OOC:", nil, "rb13")
local oocGauge = Geyser.Gauge:new({
x="25%", y=32,
width="40%", height=20,
}, container)
oocGauge.front:setStyleSheet(gaugeFrontStyle("#31d0d0", "#22cfcf", "#00b2b2", "#009494", "#00b2b2"))
oocGauge.back:setStyleSheet(gaugeBackStyle("#113f3f", "#073f3f", "#003333", "#002222", "#001111"))
registerAnonymousEventHandler("msdp.OOCLIMIT", function()
local oocLeft = tonumber(msdp.OOCLIMIT or 0)
local oocMax = 6
oocGauge:setValue(oocLeft, oocMax)
end)
end
function lotj.infoPanel.createSpaceStats(container)
local energyGauge = Geyser.Gauge:new({
x="3%", y=4,
width="30%", height=16,
}, container)
energyGauge.front:setStyleSheet(gaugeFrontStyle("#7a7a7a", "#777777", "#656565", "#505050", "#656565"))
energyGauge.back:setStyleSheet(gaugeBackStyle("#383838", "#303030", "#222222", "#151515", "#222222"))
styleGaugeText(energyGauge, 12)
wireGaugeUpdate(energyGauge, "SHIPENERGY", "SHIPMAXENERGY", "en")
local hullGauge = Geyser.Gauge:new({
x="3%", y=23,
width="30%", height=16,
}, container)
hullGauge.front:setStyleSheet(gaugeFrontStyle("#bd7833", "#bd6e20", "#994c00", "#703800", "#994c00"))
hullGauge.back:setStyleSheet(gaugeBackStyle("#442511", "#441d08", "#331100", "#200900", "#331100"))
styleGaugeText(hullGauge, 12)
wireGaugeUpdate(hullGauge, "SHIPHULL", "SHIPMAXHULL", "hl")
local shieldGauge = Geyser.Gauge:new({
x="3%", y=42,
width="30%", height=16,
}, container)
shieldGauge.front:setStyleSheet(gaugeFrontStyle("#31d0d0", "#22cfcf", "#00b2b2", "#009494", "#00b2b2"))
shieldGauge.back:setStyleSheet(gaugeBackStyle("#113f3f", "#073f3f", "#003333", "#002222", "#001111"))
styleGaugeText(shieldGauge, 12)
wireGaugeUpdate(shieldGauge, "SHIPSHIELD", "SHIPMAXSHIELD", "sh")
-- Piloting indicator
local pilotLabel = Geyser.Label:new({
x="35%", y=6,
width="13%", height=24
}, container)
pilotLabel:echo("Pilot:", nil, "lb12")
local pilotBoxCont = Geyser.Label:new({
x="48%", y=10,
width="8%", height=16
}, container)
local pilotBox = Geyser.Label:new({
x=2, y=0,
width=16, height=16
}, pilotBoxCont)
registerAnonymousEventHandler("msdp.PILOTING", function()
local piloting = tonumber(msdp.PILOTING or "0")
if piloting == 1 then
pilotBox:setStyleSheet("background-color: #29efef; border: 2px solid #eeeeee;")
else
pilotBox:setStyleSheet("background-color: #073f3f; border: 2px solid #eeeeee;")
end
end)
local speedGauge = Geyser.Label:new({
x="56%", y=6,
width="19%", height=24,
}, container)
local function updateSpeed()
local speed = tonumber(msdp.SHIPSPEED or "0")
local maxSpeed = tonumber(msdp.SHIPMAXSPEED or "0")
speedGauge:echo("<b>Sp:</b> "..speed.."<b>/</b>"..maxSpeed, nil, "l12")
end
registerAnonymousEventHandler("msdp.SHIPSPEED", updateSpeed)
registerAnonymousEventHandler("msdp.SHIPMAXSPEED", updateSpeed)
local coordsInfo = Geyser.Label:new({
x="35%", y=32,
width="40%", height=24,
}, container)
local function updateCoords()
local shipX = tonumber(msdp.SHIPSYSX or "0")
local shipY = tonumber(msdp.SHIPSYSY or "0")
local shipZ = tonumber(msdp.SHIPSYSZ or "0")
coordsInfo:echo("<b>Coords:</b> "..shipX.." "..shipY.." "..shipZ, nil, "l12")
end
registerAnonymousEventHandler("msdp.SHIPSYSX", updateCoords)
registerAnonymousEventHandler("msdp.SHIPSYSY", updateCoords)
registerAnonymousEventHandler("msdp.SHIPSYSZ", updateCoords)
lotj.infoPanel.spaceTickCounter = Geyser.Label:new({
x="77%", y=6,
width="20%", height=24,
}, container)
lotj.infoPanel.chaffIndicator = Geyser.Label:new({
x="77%", y=32,
width="20%", height=24,
}, container)
lotj.infoPanel.chaffIndicator:echo("[Chaff]", "yellow", "c13b")
lotj.infoPanel.chaffIndicator:hide()
end
-- Sets up timers to refresh the space tick counter
function lotj.infoPanel.markSpaceTick()
for _, timerId in ipairs(lotj.infoPanel.spaceTickTimers or {}) do
killTimer(timerId)
end
lotj.infoPanel.spaceTickCounter:show()
lotj.infoPanel.spaceTickTimers = {}
for i = 0,20,1 do
local timerId = tempTimer(i, function()
lotj.infoPanel.spaceTickCounter:echo("<b>Tick:</b> "..20-i, nil, "c13")
end)
table.insert(lotj.infoPanel.spaceTickTimers, timerId)
end
-- A few seconds after the next tick should happen, hide the counter.
-- This will be canceled if we see another tick before then.
local timerId = tempTimer(23, function()
lotj.infoPanel.spaceTickCounter:hide()
end)
table.insert(lotj.infoPanel.spaceTickTimers, timerId)
end
function lotj.infoPanel.markChaff()
lotj.infoPanel.clearChaff()
lotj.infoPanel.chaffIndicator:show()
-- In case we miss the "chaff cleared" message somehow, set a 20 second timer to get rid of this
lotj.infoPanel.chaffTimer = tempTimer(20, function()
lotj.infoPanel.clearChaff()
end)
end
function lotj.infoPanel.clearChaff()
if lotj.infoPanel.chaffTimer ~= nil then
killTimer(lotj.infoPanel.chaffTimer)
lotj.infoPanel.chaffTimer = nil
end
lotj.infoPanel.chaffIndicator:hide()
end

View File

@ -0,0 +1,5 @@
[
{
"name": "info-panel"
}
]

View File

@ -0,0 +1,140 @@
lotj = lotj or {}
lotj.layout = lotj.layout or {}
local rightPanelWidthPct = 40
local upperRightHeightPct = 50
local inactiveTabStyle = [[
background-color: #333333;
border: 1px solid #00aaaa;
margin: 3px 3px 0px 3px;
font-family: "Bitstream Vera Sans Mono";
]]
local activeTabStyle = [[
background-color: #336666;
border: 1px solid #00aaaa;
border-bottom: none;
margin: 3px 3px 0px 3px;
font-family: "Bitstream Vera Sans Mono";
]]
local function createTabbedPanel(tabData, container, tabList)
tabData.tabs = {}
tabData.contents = {}
local tabContainer = Geyser.HBox:new({
x = "2%", y = 0,
width = "96%", height = 30,
}, container)
local contentsContainer = Geyser.Label:new({
x = 0, y = 30,
width = "100%",
}, container)
lotj.layout.resizeTabContents(container, tabContainer, contentsContainer)
registerAnonymousEventHandler("sysWindowResizeEvent", function()
lotj.layout.resizeTabContents(container, tabContainer, contentsContainer)
end)
for _, tabInfo in ipairs(tabList) do
local keyword = tabInfo.keyword
local label = tabInfo.label
tabData.tabs[keyword] = Geyser.Label:new({}, tabContainer)
tabData.tabs[keyword]:setClickCallback("lotj.layout.selectTab", tabData, keyword)
tabData.tabs[keyword]:setFontSize(12)
tabData.tabs[keyword]:echo("<center>"..label)
tabData.contents[keyword] = Geyser.Label:new({
x = 0, y = 0,
width = "100%",
height = "100%",
}, contentsContainer)
end
end
function lotj.layout.selectTab(tabData, tabName)
for _, tab in pairs(tabData.tabs) do
tab:setStyleSheet(inactiveTabStyle)
tab:setBold(false)
end
for _, contents in pairs(tabData.contents) do
contents:hide()
end
tabData.tabs[tabName]:setStyleSheet(activeTabStyle)
tabData.tabs[tabName]:setBold(true)
tabData.contents[tabName]:show()
end
function lotj.layout.resizeTabContents(parentContainer, tabContainer, contentsContainer)
local newHeight = parentContainer:get_height()-tabContainer:get_height()
contentsContainer:resize(nil, newHeight)
end
registerAnonymousEventHandler("sysLoadEvent", function()
local rightPanel = Geyser.Container:new({
width = rightPanelWidthPct.."%",
x = (100-rightPanelWidthPct).."%",
y = 0, height = "100%",
})
registerAnonymousEventHandler("sysWindowResizeEvent", function()
local newBorder = math.floor(rightPanel:get_width())
if getBorderRight() ~= newBorder then
setBorderRight(newBorder)
end
end)
-- Upper-right pane, for maps
local upperContainer = Geyser.Container:new({
x = 0, y = 0,
width = "100%",
height = upperRightHeightPct.."%",
}, rightPanel)
local upperTabList = {}
table.insert(upperTabList, {keyword = "map", label = "Map"})
table.insert(upperTabList, {keyword = "system", label = "System"})
table.insert(upperTabList, {keyword = "galaxy", label = "Galaxy"})
lotj.layout.upperRightTabData = {}
createTabbedPanel(lotj.layout.upperRightTabData, upperContainer, upperTabList)
-- Lower-right panel, for chat history
local lowerContainer = Geyser.Container:new({
x = 0, y = upperRightHeightPct.."%",
width = "100%",
height = (100-upperRightHeightPct).."%",
}, rightPanel)
local lowerTabList = {}
table.insert(lowerTabList, {keyword = "all", label = "All"})
table.insert(lowerTabList, {keyword = "commnet", label = "CommNet"})
table.insert(lowerTabList, {keyword = "clan", label = "Clan"})
table.insert(lowerTabList, {keyword = "ooc", label = "OOC"})
table.insert(lowerTabList, {keyword = "tell", label = "Tell"})
table.insert(lowerTabList, {keyword = "imm", label = "Imm"})
lotj.layout.lowerRightTabData = {}
createTabbedPanel(lotj.layout.lowerRightTabData, lowerContainer, lowerTabList)
-- Lower info panel, for prompt hp/move gauges and other basic status
lotj.layout.lowerInfoPanel = Geyser.HBox:new({
x = 0, y = -60,
width = (100-rightPanelWidthPct).."%",
height = 60,
})
setBorderBottom(60)
raiseEvent("lotjUICreated")
lotj.layout.selectTab(lotj.layout.upperRightTabData, "map")
lotj.layout.selectTab(lotj.layout.lowerRightTabData, "all")
end)

View File

@ -0,0 +1,5 @@
[
{
"name": "layout"
}
]

View File

@ -0,0 +1,527 @@
mudlet = mudlet or {}; mudlet.mapper_script = true
lotj = lotj or {}
lotj.mapper = lotj.mapper or {}
local dirs = {}
-- The order of these is important. The indices of the directions must match
-- https://github.com/Mudlet/Mudlet/blob/9c13f8f946f5b82c0c2e817dab5f42588cee17e0/src/TRoom.h#L38
table.insert(dirs, {short="n", long="north", rev="s", xyzDiff = { 0, 1, 0}})
table.insert(dirs, {short="ne", long="northeast", rev="sw", xyzDiff = { 1, 1, 0}})
table.insert(dirs, {short="nw", long="northwest", rev="se", xyzDiff = {-1, 1, 0}})
table.insert(dirs, {short="e", long="east", rev="w", xyzDiff = { 1, 0, 0}})
table.insert(dirs, {short="w", long="west", rev="e", xyzDiff = {-1, 0, 0}})
table.insert(dirs, {short="s", long="south", rev="n", xyzDiff = { 0,-1, 0}})
table.insert(dirs, {short="se", long="southeast", rev="nw", xyzDiff = { 1,-1, 0}})
table.insert(dirs, {short="sw", long="southwest", rev="ne", xyzDiff = {-1,-1, 0}})
table.insert(dirs, {short="u", long="up", rev="d", xyzDiff = { 0, 0, 1}})
table.insert(dirs, {short="d", long="down", rev="u", xyzDiff = { 0, 0,-1}})
-- Given a direction short or long name, or a direction number, return an object representing it.
local function dirObj(arg)
if dirs[arg] ~= nil then
return dirs[arg]
end
for _, dir in ipairs(dirs) do
if arg == dir.short or arg == dir.long then
return dir
end
end
return nil
end
-- Given a direction short or long name, or a direction number, return an object representing its opposite
local function revDirObj(arg)
local dir = dirObj(arg)
if dir ~= nil then
return dirObj(dir.rev)
end
return nil
end
-- Configuration of an amenity name to the environment code to use on rooms with it
local amenityEnvCodes = {
bacta = 269,
bank = 267,
broadcast = 270,
hotel = 265,
library = 261,
locker = 263,
package = 262,
workshop = 266,
}
local function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
------------------------------------------------------------------------------
-- Command Handlers
------------------------------------------------------------------------------
-- Main "map" command handler
function lotj.mapper.mapCommand(input)
input = trim(input)
if #input == 0 then
lotj.mapper.printMainMenu()
return
end
_, _, cmd, args = string.find(input, "([^%s]+)%s*(.*)")
cmd = string.lower(cmd)
if cmd == "help" then
lotj.mapper.printHelp()
elseif cmd == "start" then
lotj.mapper.startMapping(args)
elseif cmd == "stop" then
lotj.mapper.stopMapping()
elseif cmd == "deletearea" then
lotj.mapper.deleteArea(args)
elseif cmd == "shift" then
lotj.mapper.shiftCurrentRoom(args)
elseif cmd == "save" then
lotj.mapper.saveMap()
else
lotj.mapper.logError("Unknown map command. Try <yellow>map help<reset>.")
end
end
function lotj.mapper.printMainMenu()
lotj.mapper.log("Mapper Introduction and Status")
cecho([[
The LOTJ Mapper plugin tracks movement using MSDP variables. To begin, try <yellow>map start <current area><reset>.
Once mapping is started, move <red>slowly<reset> between rooms to map them. Moving too quickly will cause the
mapper to skip rooms. You should wait for the map to reflect your movements before moving again
whenever you are in mapping mode.
When you are finished mapping, use <yellow>map stop<reset> to stop recording your movements, and be sure to
<yellow>map save<reset>! Map data will not be saved automatically.
Other commands are available to adjust mapping as you go. <yellow>map shift <direction><reset>, for example,
will move your current room. See <yellow>map help<reset> for a full list of available commands.
The map GUI also offers editing functionality and is ideal for moving groups of rooms, deleting
or coloring rooms, etc.
]])
if lotj.mapper.mappingArea ~= nil then
cecho("Mapper status: <green>Mapping<reset> in area <yellow>"..lotj.mapper.mappingArea.."<reset>\n")
else
cecho("Mapper status: <red>Off<reset>\n")
end
end
function lotj.mapper.printHelp()
lotj.mapper.log("Mapper Command List")
cecho([[
<yellow>map start <area name><reset>
Begin mapping. Any new rooms you enter while mapping will be added to this area name, so you
should be sure to stop mapping before entering a ship or moving to a different zone.
Some tips to remember:
- <white>Move slowly<reset>, and wait for the map to reflect your movements before going to the next room.
The MUD sends data about your current room after some delay, so moving too fast will make the
mapper skip rooms or draw exits which aren't there.
- Use a light while mapping. Entering a dark room where you can't see will not update the map.
- Use <yellow>map shift<reset> to adjust room positioning, especially after going through turbolifts or
voice-activated doors. It's faster to click-and-drag with the GUI to move large blocks of
rooms, though.
- Rooms in ships are all unique, even if they are the same model. In practice, mapping ships
really isn't supported yet, although platforms or ships you use frequently may be worth it.
<yellow>map stop<reset>
Stop editing the map based on your movements.
<yellow>map save<reset>
Save the map to the map.dat file in your Mudlet profile's directory.
<yellow>map deletearea <area name><reset>
Deletes all data for an area. There's no confirmation and no undo!
<yellow>map shift <direction><reset>
Moves the current room in whichever direction you enter. Useful for adjusting placement of
rooms when you need to space them out.
]])
end
function lotj.mapper.startMapping(areaName)
areaName = trim(areaName)
if #areaName == 0 then
lotj.mapper.log("Syntax: map start <yellow><area name><reset>")
return
end
if lotj.mapper.mappingArea ~= nil then
lotj.mapper.logError("Mapper already running in <yellow>"..lotj.mapper.mappingArea.."<reset>.")
return
end
local areaTable = getAreaTable()
if areaTable[areaName] == nil then
addAreaName(areaName)
lotj.mapper.log("Mapping in new area <yellow>"..areaName.."<reset>.")
if lotj.mapper.noAreasPrompt ~= nil then
lotj.mapper.noAreasPrompt:hide()
lotj.mapper.noAreasPrompt = nil
lotj.mapper.mapperInstance:show()
end
else
lotj.mapper.log("Mapping in existing area <yellow>"..areaName.."<reset>.")
end
lotj.mapper.mappingArea = areaName
lotj.mapper.processCurrentRoom()
end
function lotj.mapper.stopMapping()
if lotj.mapper.mappingArea == nil then
lotj.mapper.logError("Mapper not running.")
return
end
lotj.mapper.mappingArea = nil
lotj.mapper.log("Mapping <red>stopped<reset>. Don't forget to <yellow>map save<reset>!")
end
function lotj.mapper.deleteArea(areaName)
areaName = trim(areaName)
if #areaName == 0 then
lotj.mapper.log("Syntax: map deletearea <yellow><area name><reset>")
return
end
local areaTable = getAreaTable()
if areaTable[areaName] == nil then
lotj.mapper.logError("Area <yellow>"..areaName.."<reset> does not exist.")
return
end
if areaName == lotj.mapper.mappingArea then
lotj.mapper.stopMapping()
end
deleteArea(areaName)
lotj.mapper.log("Area <yellow>"..areaName.."<reset> deleted.")
end
function lotj.mapper.shiftCurrentRoom(direction)
direction = trim(direction)
if #direction == 0 then
lotj.mapper.log("Syntax: map shift <yellow><direction><reset>")
return
end
local dir = dirObj(direction)
if dir == nil then
lotj.mapper.logError("Direction unknown: <yellow>"..direction.."<reset>")
return
end
local vnum = lotj.mapper.current.vnum
local room = lotj.mapper.getRoomByVnum(vnum)
if room ~= nil then
currentX, currentY, currentZ = getRoomCoordinates(vnum)
dx, dy, dz = unpack(dir.xyzDiff)
setRoomCoordinates(vnum, currentX+dx, currentY+dy, currentZ+dz)
updateMap()
centerview(vnum)
end
end
function lotj.mapper.saveMap()
saveMap(getMudletHomeDir() .. "/map.dat")
lotj.mapper.log("Map saved.")
end
------------------------------------------------------------------------------
-- Event Handlers
------------------------------------------------------------------------------
registerAnonymousEventHandler("lotjUICreated", function()
lotj.mapper.mapperInstance = Geyser.Mapper:new({
x = 0, y = 0,
width = "100%",
height = "100%",
}, lotj.layout.upperRightTabData.contents["map"])
setMapZoom(15)
local hasAnyAreas = false
for name, id in pairs(getAreaTable()) do
if name ~= "Default Area" then
hasAnyAreas = true
end
end
if not hasAnyAreas then
lotj.mapper.mapperInstance:hide()
lotj.mapper.noAreasPrompt = Geyser.Label:new({
x = 0, y = 0,
width = "100%",
height = "100%"
}, lotj.layout.upperRightTabData.contents["map"])
lotj.mapper.noAreasPrompt:echo("No map data.<br><br>Use <b>map help</b> to get started.", nil, "c14")
end
registerAnonymousEventHandler("sysDataSendRequest", "lotj.mapper.handleSentCommand")
registerAnonymousEventHandler("msdp.ROOMVNUM", "lotj.mapper.onEnterRoom")
end)
-- Track the most recent movement command so we know which direction we moved when automapping
function lotj.mapper.handleSentCommand(event, cmd)
-- If we're not mapping, don't bother
if lotj.mapper.mappingArea == nil then
return
end
local dir = dirObj(trim(cmd))
if dir ~= nil then
lotj.mapper.lastMoveDir = dir
end
end
-- Function used to handle a room that we've moved into. This will use the data on
-- lotj.mapper.current, compared with lotj.mapper.last, to potentially create a new room and
-- link it with an exit on the previous room.
function lotj.mapper.processCurrentRoom()
local vnum = lotj.mapper.current.vnum
local moveDir = lotj.mapper.lastMoveDir
local room = lotj.mapper.getRoomByVnum(vnum)
if lotj.mapper.mappingArea == nil and room == nil then
lotj.mapper.logDebug("Room not found, but mapper not running.")
return
end
local lastRoom = nil
if lotj.mapper.last ~= nil then
lastRoom = lotj.mapper.getRoomByVnum(lotj.mapper.last.vnum)
end
-- Create the room if we don't have it yet
if room == nil then
lotj.mapper.log("Added new room: <yellow>"..lotj.mapper.current.name.."<reset>")
addRoom(vnum)
setRoomArea(vnum, lotj.mapper.mappingArea)
setRoomCoordinates(vnum, 0, 0, 0)
setRoomName(vnum, lotj.mapper.current.name)
room = lotj.mapper.getRoomByVnum(vnum)
-- Create stub exits in any known direction we see
for dir, state in pairs(lotj.mapper.current.exits) do
local exitDir = dirObj(dir)
if exitDir ~= nil then
setExitStub(vnum, exitDir.short, true)
if state == "C" then
setDoor(vnum, exitDir.short, 2)
end
end
end
-- Position the room relative to the room we came from
if lastRoom ~= nil then
local lastX, lastY, lastZ = getRoomCoordinates(lotj.mapper.last.vnum)
-- If we recorded a valid movement command, use that direction to position this room
if moveDir ~= nil then
local dx, dy, dz = unpack(moveDir.xyzDiff)
setRoomCoordinates(vnum, lastX+dx, lastY+dy, lastZ+dz)
else
-- We didn't have a valid movement command but we still changed rooms, so try to guess
-- where this room should be relative to the last.
-- Find a stub with a door on the last room which matches a stub with a door on this room
-- This aims to handle cases where you've used a voice-activated locked door
local lastDoors = getDoors(lotj.mapper.last.vnum)
local currentDoors = getDoors(vnum)
local matchingStubDir = nil
for _, lastRoomStubDirNum in ipairs(getExitStubs1(lotj.mapper.last.vnum) or {}) do
local lastRoomStubDir = dirObj(lastRoomStubDirNum)
for _, currentRoomStubDirNum in ipairs(getExitStubs1(vnum) or {}) do
local currentRoomStubDir = dirObj(currentRoomStubDirNum)
if lastRoomStubDir.short == currentRoomStubDir.rev
and lastDoors[lastRoomStubDir.short] == 2
and currentDoors[currentRoomStubDir.short] == 2 then
matchingStubDir = lastRoomStubDir
end
end
end
if matchingStubDir ~= nil then
local dx, dy, dz = unpack(matchingStubDir.xyzDiff)
setRoomCoordinates(vnum, lastX+dx, lastY+dy, lastZ+dz)
lotj.mapper.log("Positioning new room "..matchingStubDir.long.." of the previous room based on matching closed doors.")
else
-- If no matching stubs were found, just find a nearby location which isn't taken by either a stub or a real room.
for dir in pairs({"n", "e", "w", "s", "ne", "nw", "se", "sw", "u", "d"}) do
local dx, dy, dz = unpack(dirObj(dir).xyzDiff)
local overlappingRoomId = lotj.mapper.getRoomByCoords(lotj.mapper.mappingArea, lastX+dx, lastY+dy, lastZ+dz)
local hasOverlappingStub = false
for _, stubDirNum in ipairs(getExitStubs1(lotj.mapper.last.vnum) or {}) do
if dirObj(stubDirNum) == dirObj(dir) then
hasOverlappingStub = true
end
end
if overlappingRoomId == nil and not hasOverlappingStub then
lotj.mapper.log("Exit unknown. Positioning new room "..dirObj(dir).long.." of the previous room.")
setRoomCoordinates(vnum, lastX+dx, lastY+dy, lastZ+dz)
break
end
end
end
end
end
end
-- Link this room with the previous one if they have a matching set of exit stubs
if lastRoom ~= nil and moveDir ~= nil then
-- Always set the exit we took even if it wasn't a stub. The direction we just moved is our best
-- evidence of how rooms are connected, overriding any reverse-created exits made earlier if they
-- are different.
setExit(lotj.mapper.last.vnum, vnum, moveDir.short)
-- Only set the reverse exit (from current room back to where we came from) if it's a stub.
-- In the case of mazes or asymmetrical exits, this may be wrong but will be fixed on moving back
-- out through this exit.
for _, currentRoomStubDirNum in ipairs(getExitStubs1(vnum) or {}) do
local currentRoomStubDir = dirObj(currentRoomStubDirNum)
if moveDir.rev == currentRoomStubDir.short then
setExit(vnum, lotj.mapper.last.vnum, moveDir.rev)
end
end
end
centerview(vnum)
end
function lotj.mapper.checkAmenityLine(roomName, amenityName, wasPending)
if lotj.mapper.mappingArea == nil then
return
end
envCode = amenityEnvCodes[string.lower(amenityName)]
if envCode == nil then
return
end
local addAmenityRoom = nil
if lotj.mapper.current.name == roomName then
addAmenityRoom = lotj.mapper.current
elseif lotj.mapper.last.name == roomName then
addAmenityRoom = lotj.mapper.last
end
-- If this wasn't stored for later use, we need a newline since this is being invoked on
-- seeing a room name and we don't want it mushed into that line.
if not wasPending then
echo("\n")
end
if addAmenityRoom == nil then
-- The room name we're triggering on might be the room we just entered but we haven't
-- received the MSDP event yet, so we'll store this for the next time we do.
lotj.mapper.pendingAmenity = {
roomName = roomName,
amenityName = amenityName,
}
else
lotj.mapper.log("Set amenity <yellow>"..amenityName.."<reset> on room <yellow>"..addAmenityRoom.name.."<reset>")
setRoomEnv(addAmenityRoom.vnum, envCode)
updateMap()
end
end
-- The vnum is always sent after the name and exits, so we can use it as a trigger for
-- handling movement to a new room
function lotj.mapper.onEnterRoom()
if lotj.mapper.current ~= nil then
lotj.mapper.last = lotj.mapper.current
end
local exits = {}
if msdp.ROOMEXITS ~= "" then
exits = msdp.ROOMEXITS
end
lotj.mapper.current = {
vnum = tonumber(msdp.ROOMVNUM),
name = string.gsub(msdp.ROOMNAME, "&.", ""),
exits = exits,
}
lotj.mapper.processCurrentRoom()
-- Since we've handled the move, we don't want the last move command to get
-- used by anything else.
lotj.mapper.lastMoveDir = nil
local pendingAmenity = lotj.mapper.pendingAmenity
if pendingAmenity ~= nil then
lotj.mapper.checkAmenityLine(pendingAmenity.roomName, pendingAmenity.amenityName, true)
lotj.mapper.pendingAmenity = nil
end
end
------------------------------------------------------------------------------
-- Utility Functions
------------------------------------------------------------------------------
function lotj.mapper.log(text)
cecho("[<cyan>LOTJ Mapper<reset>] "..text.."\n")
end
function lotj.mapper.logDebug(text)
if lotj.mapper.debug then
lotj.mapper.log("<green>Debug:<reset> "..text)
end
end
function lotj.mapper.logError(text)
lotj.mapper.log("<red>Error:<reset> "..text)
end
function lotj.mapper.getRoomByVnum(vnum)
return getRooms()[vnum]
end
function lotj.mapper.getRoomByCoords(areaName, x, y, z)
local areaRooms = getAreaRooms(getAreaTable()[areaName]) or {}
for _, roomId in pairs(areaRooms) do
local roomX, roomY, roomZ = getRoomCoordinates(roomId)
if roomX == x and roomY == y and roomZ == z then
return roomId
end
end
return nil
end
function doSpeedWalk()
echo("Path we need to take: " .. table.concat(speedWalkDir, ", ") .. "\n")
echo("A future version of the mapper script might actually execute these commands.\n")
end

View File

@ -0,0 +1,5 @@
[
{
"name": "mapper"
}
]

45
src/scripts/msdp/msdp.lua Normal file
View File

@ -0,0 +1,45 @@
registerAnonymousEventHandler("sysLoadEvent", function()
registerAnonymousEventHandler("msdp.COMMANDS", function()
local msdpVars = {}
table.insert(msdpVars, "HEALTH")
table.insert(msdpVars, "HEALTHMAX")
table.insert(msdpVars, "WIMPY")
table.insert(msdpVars, "MOVEMENT")
table.insert(msdpVars, "MOVEMENTMAX")
table.insert(msdpVars, "MANA")
table.insert(msdpVars, "MANAMAX")
table.insert(msdpVars, "OPPONENTNAME")
table.insert(msdpVars, "OPPONENTHEALTH")
table.insert(msdpVars, "OPPONENTHEALTHMAX")
table.insert(msdpVars, "COMMCHANNEL")
table.insert(msdpVars, "COMMENCRYPT")
table.insert(msdpVars, "OOCLIMIT")
table.insert(msdpVars, "ROOMNAME")
table.insert(msdpVars, "ROOMEXITS")
table.insert(msdpVars, "ROOMVNUM")
table.insert(msdpVars, "PILOTING")
table.insert(msdpVars, "SHIPSPEED")
table.insert(msdpVars, "SHIPMAXSPEED")
table.insert(msdpVars, "SHIPHULL")
table.insert(msdpVars, "SHIPMAXHULL")
table.insert(msdpVars, "SHIPSHIELD")
table.insert(msdpVars, "SHIPMAXSHIELD")
table.insert(msdpVars, "SHIPENERGY")
table.insert(msdpVars, "SHIPMAXENERGY")
table.insert(msdpVars, "SHIPSYSX")
table.insert(msdpVars, "SHIPSYSY")
table.insert(msdpVars, "SHIPSYSZ")
table.insert(msdpVars, "SHIPGALX")
table.insert(msdpVars, "SHIPGALY")
table.insert(msdpVars, "SHIPSYSNAME")
for _, varName in ipairs(msdpVars) do
sendMSDP("REPORT", varName)
end
end)
end)

View File

@ -0,0 +1,5 @@
[
{
"name": "msdp"
}
]

View File

@ -0,0 +1,5 @@
[
{
"name": "system-map"
}
]

View File

@ -0,0 +1,326 @@
lotj = lotj or {}
lotj.systemMap = lotj.systemMap or {
mapRange = 2000,
radarItems = {},
genPoints = {},
genLabels = {}
}
local pointSize = 12
local labelWidth = 200
local labelHeight = 16
local controlButtonStyle = [[
background-color: grey;
border: 2px solid white;
]]
registerAnonymousEventHandler("lotjUICreated", function()
disableTrigger("system-map-radar")
local tabContents = lotj.layout.upperRightTabData.contents["system"]
lotj.systemMap.container = Geyser.Label:new({}, tabContents)
lotj.systemMap.container:setStyleSheet([[
background-color: black;
]])
lotj.systemMap.resizeToSquare()
registerAnonymousEventHandler("sysWindowResizeEvent", lotj.systemMap.resizeToSquare)
local zoomInButton = Geyser.Label:new({
x = "2%", y = 10,
width = 28, height = 28,
}, tabContents)
zoomInButton:setStyleSheet(controlButtonStyle)
zoomInButton:echo("+", "white", "c16b")
zoomInButton:setClickCallback(function()
if lotj.systemMap.mapRange > 1000 then
lotj.systemMap.mapRange = lotj.systemMap.mapRange - 1000
elseif lotj.systemMap.mapRange == 1000 then
lotj.systemMap.mapRange = 500
else
return
end
lotj.systemMap.drawMap()
end)
local zoomOutButton = Geyser.Label:new({
x = "2%", y = 44,
width = 28, height = 28,
}, tabContents)
zoomOutButton:setStyleSheet(controlButtonStyle)
zoomOutButton:echo("-", "white", "c16b")
zoomOutButton:setClickCallback(function()
if lotj.systemMap.mapRange >= 1000 then
lotj.systemMap.mapRange = lotj.systemMap.mapRange + 1000
elseif lotj.systemMap.mapRange == 500 then
lotj.systemMap.mapRange = 1000
else
return
end
lotj.systemMap.drawMap()
end)
local refreshButton = Geyser.Label:new({
x = "2%", y = 78,
width = 28, height = 28,
}, tabContents)
refreshButton:setStyleSheet(controlButtonStyle)
refreshButton:echo("R", "white", "c16b")
refreshButton:setClickCallback(function()
lotj.systemMap.maskNextRadarOutput = true
expandAlias("radar", false)
end)
local rangeCircle = rangeCircle or Geyser.Label:new({fillBg = 0}, lotj.systemMap.container)
rangeCircle:move(0, 0)
lotj.systemMap.rangeLabel = lotj.systemMap.rangeLabel or Geyser.Label:new({fillBg = 0}, lotj.systemMap.container)
lotj.systemMap.rangeLabel:resize(50, 20)
lotj.systemMap.rangeLabel:echo(lotj.systemMap.mapRange, "green", "10c")
local function positionRangeCircle()
local containerSize = lotj.systemMap.container:get_height()
lotj.systemMap.rangeLabel:move(containerSize-math.ceil(containerSize/10)-25, math.ceil(containerSize/10))
rangeCircle:resize(containerSize, containerSize)
rangeCircle:setStyleSheet([[
border: 1px dashed green;
border-radius: ]]..math.floor(containerSize/2)..[[px;
]])
end
positionRangeCircle()
registerAnonymousEventHandler("sysWindowResizeEvent", positionRangeCircle)
registerAnonymousEventHandler("msdp.SHIPSYSX", "lotj.systemMap.drawMap")
registerAnonymousEventHandler("msdp.SHIPSYSY", "lotj.systemMap.drawMap")
registerAnonymousEventHandler("msdp.SHIPSYSZ", "lotj.systemMap.drawMap")
end)
function lotj.systemMap.resetItems()
lotj.systemMap.radarItems = {}
end
function lotj.systemMap.addItem(item)
table.insert(lotj.systemMap.radarItems, item)
end
function lotj.systemMap.drawMap()
lotj.systemMap.rangeLabel:echo(lotj.systemMap.mapRange, "green", "10c")
-- Hide any previously generated elements which we may be displaying
for _, elem in ipairs(lotj.systemMap.genPoints) do
elem:hide()
end
for _, elem in ipairs(lotj.systemMap.genLabels) do
elem:hide()
end
-- We use ship max speed as a proxy for "do we have ship data at all"
if tonumber(msdp.SHIPMAXSPEED or "0") == 0 then
return
end
local shipX = tonumber(msdp.SHIPSYSX or "0")
local shipY = tonumber(msdp.SHIPSYSY or "0")
local shipZ = tonumber(msdp.SHIPSYSZ or "0")
local selfData = {name="You", x=shipX, y=shipY, z=shipZ}
local itemsToDraw = {}
table.insert(itemsToDraw, selfData)
for _, item in ipairs(lotj.systemMap.radarItems) do
if lotj.systemMap.dist(selfData, item) <= lotj.systemMap.mapRange then
table.insert(itemsToDraw, item)
end
end
local drawnItems = {}
for i, item in ipairs(itemsToDraw) do
local point, label = lotj.systemMap.pointAndLabel(i)
local color = "yellow"
if item.class and string.match(item.class, "Pirated") then
color = "red"
elseif item == selfData then
color = "white"
end
point:resize(pointSize, pointSize)
point:setStyleSheet([[
border-radius: ]]..math.floor(pointSize/2)..[[px;
background-color: ]]..color..[[;
]])
local x, y = lotj.systemMap.computeXY(selfData, item)
point:show()
point:move(x, y)
local labelXOffset, labelYOffset = lotj.systemMap.computeLabelPos(drawnItems, x, y)
local labelX = x+labelXOffset
local labelY = y+labelYOffset
label:show()
label:move(labelX, labelY)
lotj.systemMap.printLabels(point, label, color, labelXOffset, item, selfData)
table.insert(drawnItems, {x = x, y = y, labelX = labelX, labelY = labelY, labelXOffset = labelXOffset, labelYOffset = labelYOffset})
end
end
-- Add appropriate text to the point and label
function lotj.systemMap.printLabels(point, label, color, labelXOffset, item, selfData)
local labelStr = item.name
-- Prepend an up/down arrow to show Z diff
local zDiff = item.z - selfData.z
if zDiff ~= 0 then
local arrowFontSize = 3 + math.floor(11 * math.abs(zDiff) / lotj.systemMap.mapRange + 0.5)
local arrowChar = "&#x25B2;" -- Up arrow
if zDiff < 0 then
arrowChar = "&#x25BC;" -- Down arrow
end
point:echo(arrowChar, "black", arrowFontSize.."c")
else
point:echo("")
end
-- Append proximity and class
if item ~= selfData then
labelStr = labelStr.." ("..math.floor(lotj.systemMap.dist(item, selfData) + 0.5)
if item.class ~= nil then
labelStr = labelStr..", "..item.class
end
labelStr = labelStr..")"
end
-- Set alignment based on whether the label is to the right or left of the point
if labelXOffset > 0 then
label:echo(labelStr, color, "11l")
else
label:echo(labelStr, color, "11r")
end
end
-- Based on the position, size of map, and zoom level, determine the X and Y placement for the given item
function lotj.systemMap.computeXY(selfData, item)
local mapMinX = selfData.x-lotj.systemMap.mapRange
local mapMinY = selfData.y-lotj.systemMap.mapRange
local containerSize = lotj.systemMap.container:get_height()
local x = math.floor(containerSize * ((item.x - mapMinX) / (lotj.systemMap.mapRange*2)) + 0.5)
local y = math.floor(containerSize * (1 - (item.y - mapMinY) / (lotj.systemMap.mapRange*2)) + 0.5)
return x-pointSize/2, y-pointSize/2
end
-- Return true if the first rectancle overlaps with any part of the second rectangle
local function overlaps(x1, y1, w1, h1, x2, y2, w2, h2)
local xOverlap = (x1 >= x2 and x1 <= x2+w2) or (x1+w1 >= x2 and x1+w1 <= x2+w2)
local yOverlap = (y1 >= y2 and y1 <= y2+h2) or (y1+h1 >= y2 and y1+h1 <= y2+h2)
return xOverlap and yOverlap
end
-- Determine whether a given rectangle overlaps with the points or labels in the given array of items
local function anyOverlaps(x, y, w, h, items)
for _, item in ipairs(items) do
-- Overlap with this item's point?
if overlaps(x, y, w, h, item.x, item.y, pointSize, pointSize) then
return true
end
-- Overlap with this item's label?
if overlaps(x, y, w, h, item.labelX, item.labelY, labelWidth, labelHeight) then
return true
end
end
return false
end
-- Find a suitable label X and Y offset for a new label, attempting to avoid overlap with anything previously drawn
function lotj.systemMap.computeLabelPos(drawnItems, itemX, itemY)
local labelXOffset = pointSize
local labelYOffset = pointSize
-- If we find an item at the same coords, simply put the label below the last one for those coords
local foundSameCoords = false
for _, item in ipairs(drawnItems) do
if item.x == itemX and item.y == itemY then
foundSameCoords = true
if item.labelY > item.y then
labelYOffset = item.labelYOffset + labelHeight
else
labelYOffset = item.labelYOffset - labelHeight
end
end
end
if foundSameCoords then
return labelXOffset, labelYOffset
end
-- Try four different label positions to find one without any overlap
local offsetsToTry = {}
table.insert(offsetsToTry, {x = pointSize, y = pointSize})
table.insert(offsetsToTry, {x = pointSize, y = labelHeight * -1})
table.insert(offsetsToTry, {x = labelWidth * -1, y = labelHeight * -1})
table.insert(offsetsToTry, {x = labelWidth * -1, y = pointSize})
for _, offsets in ipairs(offsetsToTry) do
if not anyOverlaps(itemX+offsets.x, itemY+offsets.y, labelWidth, labelHeight, drawnItems) then
return offsets.x, offsets.y
end
end
-- We couldn't find a non-overlapping position, so just put it to the lower right
return labelXOffset, labelYOffset
end
-- Return existing (or create new) Geyser labels for a given point and label
-- We store and reuse these so that we don't accumulate infinite label objects, since Geyser doesn't give us a way to delete elements, only hide them
function lotj.systemMap.pointAndLabel(idx)
lotj.systemMap.genPoints[idx] = lotj.systemMap.genPoints[idx] or Geyser.Label:new({}, lotj.systemMap.container)
lotj.systemMap.genLabels[idx] = lotj.systemMap.genLabels[idx] or Geyser.Label:new({fillBg = 0, width = labelWidth, height = labelHeight}, lotj.systemMap.container)
return lotj.systemMap.genPoints[idx], lotj.systemMap.genLabels[idx]
end
-- Compute distance between one X/Y/Z coord and another
function lotj.systemMap.dist(coordsA, coordsB)
local xDiff = math.abs(coordsA.x-coordsB.x)
local yDiff = math.abs(coordsA.y-coordsB.y)
local zDiff = math.abs(coordsA.z-coordsB.z)
local xyDiff = math.sqrt(xDiff*xDiff + yDiff*yDiff)
local totalDiff = math.sqrt(xyDiff*xyDiff + zDiff*zDiff)
return totalDiff
end
-- Resize the map to the largest possible square when the window dimensions change
function lotj.systemMap.resizeToSquare()
local tabContents = lotj.layout.upperRightTabData.contents["system"]
local contH = tabContents:get_height()
local contW = tabContents:get_width()
local x = 0
local y = 0
local width = "100%"
local height = "100%"
if contW >= contH then
width = contH
x = (contW-contH)/2
else
height = contW
y = (contH-contW)/2
end
lotj.systemMap.container:move(x, y)
lotj.systemMap.container:resize(width, height)
end
function lotj.systemMap.findTarget(targetName)
targetName = targetName:lower()
local target = nil
for _, item in ipairs(lotj.systemMap.radarItems) do
if item.name:lower():sub(1, #targetName) == targetName then
target = item
end
end
return target
end
function lotj.systemMap.log(text)
cecho("[<cyan>System Map<reset>] "..text.."\n")
end

View File

@ -0,0 +1,6 @@
if lotj.chat.commnetLastChannel == matches[2] and lotj.chat.commnetLastMessage == matches[3] then
deleteLine()
echo(" (Translated)")
else
lotj.chat.routeMessage("commnet")
end

View File

@ -0,0 +1,6 @@
lotj.chat.routeMessage("commnet")
-- Track commnet messages to potentially squash a redundant translation
-- message on the next line
lotj.chat.commnetLastChannel = matches[2]
lotj.chat.commnetLastMessage = matches[3]

View File

@ -0,0 +1,76 @@
[
{
"name": "commnet",
"patterns": [
{
"pattern": "^CommNet ([0-9]+) \\[.*\\][()a-zA-Z<> ]*: (.*)",
"type": "regex"
}
]
},
{
"name": "commnet-translated",
"patterns": [
{
"pattern": "^.* buzzes '\\(Translating channel ([0-9]+)\\) (.*)'$",
"type": "regex"
}
]
},
{
"name": "clan",
"patterns": [
{
"pattern": "{.*}<.*>\\[[a-zA-Z ]+\\][()<>A-Za-z ]*: ",
"type": "regex"
},
{
"pattern": "[Incoming Transmission from",
"type": "substring"
},
{
"pattern": "[Outgoing Transmission to",
"type": "substring"
}
],
"script": "lotj.chat.routeMessage(\"clan\")"
},
{
"name": "ooc",
"patterns": [
{
"pattern": "^\\((OOC|IMM|RPC|NEWBIE)\\) [*]?[A-Za-z]+: .*$",
"type": "regex"
}
],
"script": "lotj.chat.routeMessage(\"ooc\")"
},
{
"name": "immchat",
"patterns": [
{
"pattern": "^\\( IMM \\| CHAT \\) .* mortchats to you '.*'$",
"type": "regex"
},
{
"pattern": "^\\( IMM \\| CHAT \\)\\[.*\\]: '.*'$",
"type": "regex"
}
],
"script": "lotj.chat.routeMessage(\"imm\")"
},
{
"name": "tell",
"patterns": [
{
"pattern": "^\\(OOC\\) .* tells you '.*'$",
"type": "regex"
},
{
"pattern": "^\\(OOC\\) You tell .* '.*'$",
"type": "regex"
}
],
"script": "lotj.chat.routeMessage(\"tell\")"
}
]

View File

@ -0,0 +1,5 @@
-- Swallow lines and extend the triggers as long as we haven't found the end of the planet yet
if gatherPlanetState ~= nil then
setTriggerStayOpen("gather-planet", 1)
end
deleteLine()

View File

@ -0,0 +1,7 @@
gatherPlanetState = {
section = "basics"
}
deleteLine()
moveCursor(0,getLineNumber()-1)
deleteLine()

View File

@ -0,0 +1,7 @@
deleteLine()
gatherPlanetsState = {
pendingBasic = {},
pendingResources = {},
pendingCommands = 0,
}

View File

@ -0,0 +1,3 @@
echo("\n")
lotj.galaxyMap.log("Error gathering galaxy map data. Please fix the problem and try again.")
disableTrigger("galaxy-map-refresh")

View File

@ -0,0 +1,2 @@
gatherPlanetState.section = "resources"
gatherPlanetState.resources = {}

View File

@ -0,0 +1,5 @@
gatherPlanetState.coords = {
x = tonumber(matches[2]),
y = tonumber(matches[3]),
z = tonumber(matches[4]),
}

View File

@ -0,0 +1,14 @@
-- If we've gotten into the list of resources, an empty line means we're done
if gatherPlanetState and gatherPlanetState.section == "resources" then
echo("\n")
lotj.galaxyMap.log("Collected resource data for "..gatherPlanetState.name)
lotj.galaxyMap.recordPlanet(gatherPlanetState)
gatherPlanetsState.pendingResources[gatherPlanetState.name] = nil
gatherPlanetsState.pendingCommands = gatherPlanetsState.pendingCommands - 1
if gatherPlanetsState.pendingCommands == 0 then
lotj.galaxyMap.enqueuePendingRefreshCommands()
end
gatherPlanetState = nil
end

View File

@ -0,0 +1,11 @@
echo("\n")
lotj.galaxyMap.log("Collected basic data for "..gatherPlanetState.name)
lotj.galaxyMap.recordPlanet(gatherPlanetState)
gatherPlanetsState.pendingBasic[gatherPlanetState.name] = nil
gatherPlanetsState.pendingCommands = gatherPlanetsState.pendingCommands - 1
if gatherPlanetsState.pendingCommands == 0 then
lotj.galaxyMap.enqueuePendingRefreshCommands()
end
gatherPlanetState = nil

View File

@ -0,0 +1,29 @@
deleteLine()
local line = matches[2]
local function starts_with(str, start)
return str:sub(1, #start) == start
end
if starts_with(line, "Use SHOWPLANET for more information.") then
lotj.galaxyMap.enqueuePendingRefreshCommands()
return
end
line = line:gsub("%(UFG%)", "")
line = line:gsub(" +", ";")
local _, _, planet, system, gov, support = line:find("([^;]+);([^;]+);([^;]+);([^;]+)")
if planet ~= "Planet" then
lotj.galaxyMap.recordPlanet({
name = planet,
system = system,
gov = gov,
})
gatherPlanetsState.pendingBasic[planet] = true
gatherPlanetsState.pendingResources[planet] = true
end
setTriggerStayOpen("gather-planets", 1)

View File

@ -0,0 +1,7 @@
gatherPlanetState.section = "resources"
local resource = matches[2]:match "^%s*(.-)%s*$"
local price = tonumber(matches[3])
gatherPlanetState.resources = gatherPlanetState.resources or {}
gatherPlanetState.resources[resource] = price

View File

@ -0,0 +1,4 @@
gatherPlanetsState.pendingCommands = gatherPlanetsState.pendingCommands - 1
if gatherPlanetsState.pendingCommands == 0 then
lotj.galaxyMap.enqueuePendingRefreshCommands()
end

View File

@ -0,0 +1,8 @@
local systemName = matches[2]:match "^%s*(.-)%s*$"
local xCoord = tonumber(matches[3])
local yCoord = tonumber(matches[4])
lotj.galaxyMap.recordSystem(systemName, xCoord, yCoord)
setTriggerStayOpen("gather-starsystems", 1)
deleteLine()

View File

@ -0,0 +1,211 @@
[
{
"name": "galaxy-map-refresh",
"isActive": "no",
"isFolder": "yes",
"children": [
{
"name": "gather-planets",
"fireLength": 1,
"patterns": [
{
"pattern": "Planet\\s+Starsystem\\s+Governed By\\s+Popular Support",
"type": "regex"
}
],
"children": [
{
"name": "planets-line",
"patterns": [
{
"pattern": "(.*)",
"type": "regex"
}
]
}
]
},
{
"name": "gather-planet",
"multiline": "yes",
"multilineDelta": 1,
"fireLength": 4,
"patterns": [
{
"pattern": "You use the datapad to lookup the information.",
"type": "substring"
},
{
"pattern": "--Planet Data: -------------------------------",
"type": "substring"
}
],
"children": [
{
"name": "planet-name",
"patterns": [
{
"pattern": "Planet: (.*)",
"type": "regex"
}
],
"script": "gatherPlanetState.name = matches[2]"
},
{
"name": "planet-starsys",
"patterns": [
{
"pattern": "Starsystem: (.*)",
"type": "regex"
}
],
"script": "gatherPlanetState.system = matches[2]"
},
{
"name": "planet-coords",
"patterns": [
{
"pattern": "Coordinates: ([0-9-]+) ([0-9-]+) ([0-9-]+)",
"type": "regex"
}
]
},
{
"name": "planet-gov",
"patterns": [
{
"pattern": "Governed By: (.*)",
"type": "regex"
}
],
"script": "gatherPlanetState.gov = matches[2]"
},
{
"name": "planet-description-hdr",
"patterns": [
{
"pattern": "--Planetary Information: ---------------------",
"type": "substring"
}
],
"script": "gatherPlanetState.section = \"description\""
},
{
"name": "planet-resources-hdr",
"patterns": [
{
"pattern": "--Planetary Resources: -----------------------",
"type": "substring"
}
],
"script": "gatherPlanetState.section = \"resources-basic\""
},
{
"name": "resource-price",
"patterns": [
{
"pattern": "^([\\w ]*)\\s+\\( Price per unit: ([0-9.]+)\\s*\\)",
"type": "regex"
}
]
},
{
"name": "no-resources",
"patterns": [
{
"pattern": "(No resources available on this planet)",
"type": "substring"
}
]
},
{
"name": "freeport",
"patterns": [
{
"pattern": "(.*) is a freeport.",
"type": "regex"
}
],
"script": "gatherPlanetState.freeport = true"
},
{
"name": "tax",
"patterns": [
{
"pattern": "Tax Rate: ([0-9]+)%",
"type": "regex"
}
],
"script": "gatherPlanetState.taxRate = tonumber(matches[2])"
},
{
"name": "any-planet-line",
"patterns": [
{
"pattern": "(.*)",
"type": "regex"
}
]
},
{
"name": "planet-empty-line",
"patterns": [
{
"pattern": "^$",
"type": "regex"
}
]
},
{
"name": "planet-end",
"patterns": [
{
"pattern": "Use 'SHOWPLANET <planet> RESOURCES' for current resources.",
"type": "prefix"
}
]
}
]
},
{
"name": "gather-starsystems",
"fireLength": 1,
"patterns": [
{
"pattern": "Listing publicly known starsystems:",
"type": "substring"
}
],
"children": [
{
"name": "system-line",
"patterns": [
{
"pattern": "^(.*) \\( ([0-9-]+), ([0-9-]+) \\)$",
"type": "regex"
}
]
}
]
},
{
"name": "no-datapad",
"patterns": [
{
"pattern": "You must hold a datapad to do this.",
"type": "substring"
}
]
},
{
"name": "showplanet-fail",
"patterns": [
{
"pattern": "^You fail.$",
"type": "regex"
}
]
}
]
}
]

View File

@ -0,0 +1,32 @@
[
{
"name": "spacetick",
"patterns": [
{
"pattern": "^Remaining jump time:",
"type": "regex"
}
],
"script": "lotj.infoPanel.markSpaceTick()"
},
{
"name": "chaff",
"patterns": [
{
"pattern": "A burst of chaff is released from the ship.",
"type": "substring"
}
],
"script": "lotj.infoPanel.markChaff()"
},
{
"name": "chaff-cleared",
"patterns": [
{
"pattern": "The chaff has cleared, leaving the ship vulnerable again.",
"type": "substring"
}
],
"script": "lotj.infoPanel.clearChaff()"
}
]

View File

@ -0,0 +1 @@
lotj.layout.selectTab(lotj.layout.upperRightTabData, "map")

View File

@ -0,0 +1 @@
lotj.layout.selectTab(lotj.layout.upperRightTabData, "system")

View File

@ -0,0 +1,20 @@
[
{
"name": "leave-ship",
"patterns": [
{
"pattern": "You exit the ship.",
"type": "substring"
}
]
},
{
"name": "ship-launched",
"patterns": [
{
"pattern": "The ship leaves the platform far behind as it flies into space",
"type": "substring"
}
]
}
]

View File

@ -0,0 +1,12 @@
[
{
"name": "room-amenities",
"patterns": [
{
"pattern": "^(.*) \\[([^]]+)\\]$",
"type": "regex"
}
],
"script": "lotj.mapper.checkAmenityLine(matches[2], matches[3])"
}
]

View File

@ -0,0 +1,2 @@
lotj.systemMap.maskNextRadarOutput = true
expandAlias("radar", false)

View File

@ -0,0 +1,5 @@
if lotj.systemMap.maskNextRadarOutput then
deleteLine()
end
setTriggerStayOpen("system-map-radar", 1)

View File

@ -0,0 +1,32 @@
local trimName = matches[2]:gsub("^%s*(.-)%s*$", "%1")
if trimName == "Your Coordinates:" then
setTriggerStayOpen("system-map-radar", 0)
disableTrigger("system-map-radar")
echo("\n")
lotj.systemMap.log("Radar data collected.")
lotj.systemMap.maskNextRadarOutput = false
lotj.systemMap.inRadarOutput = false
lotj.systemMap.drawMap()
return
end
_, _, class, name = trimName:find("(.*) '(.*)'")
if name == nil then
name = trimName
end
lotj.systemMap.addItem({
class = class,
name = name,
x = tonumber(matches[3]),
y = tonumber(matches[4]),
z = tonumber(matches[5]),
})
if lotj.systemMap.maskNextRadarOutput then
deleteLine()
end
setTriggerStayOpen("system-map-radar", 1)

View File

@ -0,0 +1,22 @@
-- Trigger groups seem to fire the parent trigger on child matches
if not matches or #matches == 0 then
return
end
-- Occasionally we catch the space prompt here. We want to ignore that.
if string.match(matches[2], "Fuel Level:") then
return
end
if lotj.systemMap.maskNextRadarOutput then
deleteLine()
end
-- If we're already in the block of radar output, don't do any top-level setup
if lotj.systemMap.inRadarOutput then
return
end
setTriggerStayOpen("system-map-radar", 1)
lotj.systemMap.resetItems()
lotj.systemMap.inRadarOutput = true

View File

@ -0,0 +1,45 @@
[
{
"name": "system-map-radar",
"isActive": "no",
"patterns": [
{
"pattern": "^(.*)\\s+([-0-9]+) ([-0-9]+) ([-0-9]+)$",
"type": "regex"
}
],
"children": [
{
"name": "radar-blank-line",
"patterns": [
{
"pattern": "^$",
"type": "regex"
}
]
},
{
"name": "radar-item",
"patterns": [
{
"pattern": "^(.*)\\s+([-0-9]+) ([-0-9]+) ([-0-9]+)$",
"type": "regex"
}
]
}
]
},
{
"name": "auto-radar",
"patterns": [
{
"pattern": "The ship leaves the platform far behind as it flies into space",
"type": "substring"
},
{
"pattern": "Hyperjump complete",
"type": "substring"
}
]
}
]