Jump to content

Applying LUA to Make a Radar Screen


Recommended Posts

Hello all. I've noticed there doesn't seem to be any kind of script out there for a Radar Plot yet so I decided to take a crack at it. Unsurprisingly I immediately ran into issues. I have a bunch of questions, but I'll start off with what I hope is a simple one:

 

In this section of code I'm using getEntries() to get all the available IDs then looping through the IDs to get data for each contact. (I don't actually plan on using all this data, I just want to test and see what I can get to work and pare it down from there.)

I'm getting the contact ID's to pull in in the debug but I'm getting errors reading any kind of data about those IDs. 

Contacts = Radar.getEntries() --list of construct IDs
if #Contacts >= 1 then
	for i,v in ipairs(Contacts) do
		local cID = Contacts[i] -- construct ID for 'i'
		local cName = Radar.getConstructName(cID) --name of detected construct [id][name]
		local pID = Radar.getConstructOwner(cID) --player ID # of construct owner [id][player]
		local cOwner = database.getPlayer(pID) --resolve player name from player ID
		local cSize = vec3(Radar.getConstructSize(cID)) --size of the construct bounding box [id][x,y,z]
		local cType = Radar.getConstructType(cID) --type of construct [id][static/dynamic]
		local cPos = vec3(Radar.getConstructPos(cID)) --local coordinates of the construct [id][x,y,z]
		local cVel = vec3(Radar.getConstructVelocity(cID)) --local speed relative to absolute space [id][x,y,z]
		local cSpeed = cVel:len() --rendered speed based on cVel info above
		local cAcc = vec3(Radar.getConstructAcceleration(cID)) --local accel relative to absolute space [id][x,y,z]
		local cWorldPos = vec3(Radar.getConstructWorldPos(cID)) --world coordinates of the construct [id][x,y,z]
		local cWorldVel = vec3(Radar.getConstructWorldVelocity(cID)) --world speed relative to absolute space [id][x,y,z]
		local cWorldAcc = vec3(Radar.getConstructWorldAcceleration(cID)) --world accel relative to absolute space [id][x,y,z]

It's yelling at me for calling getConstructOwner() saying it's a nil variable. Testing all of the other variables relating to position and speed for the contacts yields the same error. Am I making a mistake in how I am calling them? I checked the API and it looks right to me but then I've been wrong before.

 

As a reference here is the whole code in its current state:

Spoiler

--RADAR DISPLAY INIT--
RadScreenButtons = {}
RadScreenMouseX = 0
RadScreenMouseY = 0
RadScreenMouseDown = false
RadScreenButtonSelected = 0
local scanX = 0
local scanY = 0
local t = 0

--GLOBAL VARS--
ThresholdVel = 200.0 --only show contacts above this velocity in m/s
SearchType = "Dynamic" --only show constructs of this type: Static/Dynamic/Both
RadarMode = "Atmospheric" --type of radar being queried: Atmospheric/Space
rcolor = "limegreen" --default color is for atmospheric mode
RadarRange = 0.0 --default scan range for radar, set to '0.0' to use max range

--Buttons for Velocity Up/Down, Search Type, Mode, Range Up/Down
for i = 1,1 do --velocity up
	local button = {id = ("b"..1), enabled=true, td="<td>", top=83/100, bottom=87/100, left=1/100, right=28/100}--~ height="50" width="250" y="850" x="15"
	table.insert(RadScreenButtons, button)
end
for i = 2,2 do --velocity down
	local button = {id = ("b"..2), enabled=true, td="<td>", top=89/100, bottom=93/100, left=1/100, right=30/100}--~ height="50" width="250" y="910" x="15"
	table.insert(RadScreenButtons, button)
end
for i = 3,3 do --switch search type (static/dynamic/both)
    local button = {id = ("b"..3), enabled=true, td="<td>", top=94/100, bottom=98/100, left=1/100, right=28/100}--~ height="50" width="250" y="970" x="15"
	table.insert(RadScreenButtons, button)
end
for i = 4,4 do --toggle mode (atmo/space)
    local button = {id = ("b"..4), enabled=true, td="<td>", top=83/100, bottom=87/100, left=75/100, right=100/100}--~ height="50" width="250" y="850" x="760"
	table.insert(RadScreenButtons, button)
end
for i = 5,5 do --range increase
    local button = {id = ("b"..5), enabled=true, td="<td>", top=89/100, bottom=93/100, left=75/100, right=100/100}--~ height="50" width="250" y="910" x="760"
	table.insert(RadScreenButtons, button)
end
for i = 6,6 do --range decrease
    local button = {id = ("b"..6), enabled=true, td="<td>", top=94/100, bottom=98/100, left=75/100, right=100/100}--~ height="50" width="250" y="970" x="760"
	table.insert(RadScreenButtons, button)
end
for i = 7,7 do
    local button = {id = ("b"..7), enabled=true, td="<td>", top=90/100, bottom=100/100, left=40/100, right=60/100}
    table.insert(RadScreenButtons, button)
end

function evaluateButtons()
  local selected = 0

  if #RadScreenButtons >= 1 the
        for i, button in ipairs(RadScreenButtons) do
            if button.left < RadScreenMouseX and RadScreenMouseX < button.right and button.top < RadScreenMouseY and RadScreenMouseY < button.bottom then
                if RadScreenMouseDown and RadScreenButtons == i then
                end
                selected = i
            end
            if not button.enabled then
            end

        end
  end
  return selected
end

function onButtonDown(buttonNo)
  local button = RadScreenButtons[buttonNo]
  if not button or not button.enabled then
	return
  end
end

function onButtonUp(buttonNo)
  local button = RadScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end

function onClick(buttonNo)
  local button = RadScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end
end

if buttonNo == 1 then --increase velocity search threshold
	ThresholdVel = ThresholdVel + 25.0
elseif buttonNo == 2 then --decrease velocity search threshold
	ThresholdVel = ThresholdVel - 25.0
elseif buttonNo == 3 then --toggle construct search type
	if SearchType == "Dynamic" then
		SearchType = "Both"
	elseif SearchType == "Both" then
		SearchType = "Static"
	elseif SearchType == "Both" then
		SearchType = "Dynamic"
	end
elseif buttonNo == 4 then --toggle radar search mode
	if RadarMode == "Atmospheric" then
		RadarMode = "Space"
	elseif RadarMode == "Space" then
		RadarMode = "Atmospheric"
	end
elseif buttonNo == 5 then --increase radar search range
	RadarRange = RadarRange + 100.0
elseif buttonNo == 6 then --decrease radar search range
	RadarRange = RadarRange - 100.0
elseif buttonNo == 7 then
  unit.exit()
  end
end

function updateScreen()
if t = 360 then
	t = 0
else
	t = t + 1
end
if RadarMode == "Atmpspheric" then
	rcolor = "limegreen"
elseif RadarMode == "Space" then
	rcolor = "aqua"
end

scanX = math.floor(400 * math.cos(t*(3.14/180)))
scanY = math.floor(400 * math.sin(t*(3.14/180)))

maxRange = Radar.getRange()
if RadarRange = 0.0 then
	scanRange = maxRange
elseif RadarRange > maxRange then
	scanRange = maxRange
	RadarRange = maxRange
elseif RadarRange > 0.0 then
	scanRange = RadarRange
end

html= ([[
<svg class="bootstrap" viewBox="0 0 1024 1024" style="width:100%; height:100%">
<circle cx="500" cy="500" r="400" stroke="]]..rcolor..[[" stroke-width="3" transform=""></circle>
<circle cx="500" cy="500" r="350" stroke="]]..rcolor..[[" stroke-width="3" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="300" stroke="]]..rcolor..[[" stroke-width="3" transform=""></circle>
<circle cx="500" cy="500" r="250" stroke="]]..rcolor..[[" stroke-width="3" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="200" stroke="]]..rcolor..[[" stroke-width="3" transform=""></circle>
<circle cx="500" cy="500" r="150" stroke="]]..rcolor..[[" stroke-width="3" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="100" stroke="]]..rcolor..[[" stroke-width="3" transform=""></circle>
<circle cx="500" cy="500" r="50" stroke="]]..rcolor..[[" stroke-width="3" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="20" stroke="]]..rcolor..[[" stroke-width="3" transform=""></circle>
<circle cx="-0.00" cy="0" r="3" stroke="]]..rcolor..[[" stroke-width="1" fill="]]..rcolor..[[" transform="translate(500,500)"></circle>

<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="1150" x="55" stroke-width="0" fill="]]..rcolor..[[">Increase Vel.</text>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="1230" x="55" stroke-width="0" fill="]]..rcolor..[[">Decrease Vel.</text>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="1310" x="55" stroke-width="0" fill="]]..rcolor..[[">Search Type</text>
<rect rx="10" id="svg_2" height="50" width="250" y="850" x="15" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>
<rect rx="10" id="svg_3" height="50" width="250" y="910" x="15" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>
<rect rx="10" id="svg_4" height="50" width="250" y="970" x="15" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="1150" x="997.163642" stroke-width="0" fill="]]..rcolor..[[">Radar Mode</text>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="1230" x="997.163642" stroke-width="0" fill="]]..rcolor..[[">Increase Range</text>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="1310" x="997.163642" stroke-width="0" fill="]]..rcolor..[[">Decrease Range</text>
<rect rx="10" id="svg_5" height="50" width="250" y="850" x="760" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>
<rect rx="10" id="svg_6" height="50" width="250" y="910" x="760" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>
<rect rx="10" id="svg_7" height="50" width="250" y="970" x="760" stroke-width="5" stroke="]]..rcolor..[[" fill="none"/>

<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..scanY..[[" x2="]]..scanX..[[" y1="0" x1="0" transform="translate(500,500)" stroke-width="3" stroke="]]..rcolor..[[" fill="none"/>

<text x="-470" y="-420" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "9vh" font-weight= "bold">TARGET VELOCITY:"]]..ThresholdVel..[["m/s</text>
<text x="-420" y="-450" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "9vh" font-weight= "bold">RADAR RANGE:"]]..scanRange..[["m</text>
<text x="-410" y="-480" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "9vh" font-weight= "bold">RADAR MODE:"]]..RadarMode..[["</text>

    ]])

Contacts = Radar.getEntries() --list of construct IDs
if #Contacts >= 1 then
	for i,v in ipairs(Contacts) do
		local cID = Contacts[i] -- construct ID for 'i'
		local cName = Radar.getConstructName(cID) --name of detected construct [id][name]
		local pID = Radar.getConstructOwner(cID) --player ID # of construct owner [id][player]
		local cOwner = database.getPlayer(pID) --resolve player name from player ID
		local cSize = vec3(Radar.getConstructSize(cID)) --size of the construct bounding box [id][x,y,z]
		local cType = Radar.getConstructType(cID) --type of construct [id][static/dynamic]
		local cPos = vec3(Radar.getConstructPos(cID)) --local coordinates of the construct [id][x,y,z]
		local cVel = vec3(Radar.getConstructVelocity(cID)) --local speed relative to absolute space [id][x,y,z]
		local cSpeed = cVel:len() --rendered speed based on cVel info above
		local cAcc = vec3(Radar.getConstructAcceleration(cID)) --local accel relative to absolute space [id][x,y,z]
		local cWorldPos = vec3(Radar.getConstructWorldPos(cID)) --world coordinates of the construct [id][x,y,z]
		local cWorldVel = vec3(Radar.getConstructWorldVelocity(cID)) --world speed relative to absolute space [id][x,y,z]
		local cWorldAcc = vec3(Radar.getConstructWorldAcceleration(cID)) --world accel relative to absolute space [id][x,y,z]

--~ 		rendered coords:
		if SearchType == "Both" then
			if cSpeed >= ThresholdVel then
				local newcoordx = (cPos.x/scanRange)*400
				local newcoordy = (cPos.y/scanRange)*400

				html = html..[[<circle cx="]]..newcoordx..[[" cy="]]..newcoordy..[[" r="5" stroke="black" stroke-width="1" fill="]]..rcolor..[[" transform="translate(500,500)"></circle>
				<text x="]]..newcoordx..[[" y="]]..newcoordy..[[" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "4.5vh" font-weight= "bold">"]]..cOwner..[["</text>]]
			end
		elseif SearchType = cType then
			if SearchType == "Dynamic" then
				if cSpeed >= ThresholdVel then
					local newcoordx = (cPos.x/scanRange)*400
					local newcoordy = (cPos.y/scanRange)*400

					html = html..[[<circle cx="]]..newcoordx..[[" cy="]]..newcoordy..[[" r="5" stroke="black" stroke-width="1" fill="]]..rcolor..[[" transform="translate(500,500)"></circle>
					<text x="]]..newcoordx..[[" y="]]..newcoordy..[[" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "4.5vh" font-weight= "bold">"]]..cOwner..[["</text>]]
				end
			elseif SearchType == "Static" then
				local newcoordx = (cPos.x/scanRange)*400
				local newcoordy = (cPos.y/scanRange)*400

				html = html..[[<circle cx="]]..newcoordx..[[" cy="]]..newcoordy..[[" r="5" stroke="black" stroke-width="1" fill="]]..rcolor..[[" transform="translate(500,500)"></circle>
				<text x="]]..newcoordx..[[" y="]]..newcoordy..[[" transform="translate(500,500)"fill="]]..rcolor..[[" font-size= "4.5vh" font-weight= "bold">"]]..cOwner..[["</text>]]
			end
		end
	end
end

html = html..[[
</svg>]]
screen.setHTML(html)
end
unit.setTimer("radardisplay",.08)

 

 

Link to post
Share on other sites

The removed the ability to get position information from the space radar as NQ felt that scripting would diminish game play. They added `getEntries()` recently I guess because 1) Its consensual, and 2) promotes gameplay(?).
BTW:

	for i,v in ipairs(Contacts) do
		local cID = Contacts[i] -- construct ID for 'i'

Can be simplified to:

	for i,cID in ipairs(Contacts) do

 

Link to post
Share on other sites
3 hours ago, JayleBreak said:

The removed the ability to get position information from the space radar as NQ felt that scripting would diminish game play. They added `getEntries()` recently I guess because 1) Its consensual, and 2) promotes gameplay(?).
BTW:


	for i,v in ipairs(Contacts) do
		local cID = Contacts[i] -- construct ID for 'i'

Can be simplified to:


	for i,cID in ipairs(Contacts) do

 

Thank you sir! Old table habits, I'm still learning Lua.

 

I hope they rethink that decision in the future because the logic doesn't compute.

Link to post
Share on other sites

I don't see them changing that decision, because being able to get the position of enemy ships would allow you to write combat autopilots that would be vastly superior to player pilots.  NQ is trying very hard to make the game not be dependent on lua, so I doubt they will add a feature that makes it mandatory in order to be competitive.

Link to post
Share on other sites
54 minutes ago, SGCam said:

I don't see them changing that decision, because being able to get the position of enemy ships would allow you to write combat autopilots that would be vastly superior to player pilots.  NQ is trying very hard to make the game not be dependent on lua, so I doubt they will add a feature that makes it mandatory in order to be competitive.

In that case they should really change the element titles to something more appropriate and adjust the API library where this subject is concerned. At the moment it's quite misleading since it implies capability that doesn't actually exist.

Link to post
Share on other sites
On 10/27/2020 at 8:42 PM, SGCam said:

I don't see them changing that decision, because being able to get the position of enemy ships would allow you to write combat autopilots that would be vastly superior to player pilots.  NQ is trying very hard to make the game not be dependent on lua, so I doubt they will add a feature that makes it mandatory in order to be competitive.

Maybe it is worth removing the possibility of creating individual ships? Because someone can build a better one, so that he will have an advantage over others )

Link to post
Share on other sites
1 hour ago, SeM said:

Maybe it is worth removing the possibility of creating individual ships? Because someone can build a better one, so that he will have an advantage over others )

Straw man aside, there is a huge difference between building ships and writing programs.  Pretty much anyone can build a decent ship if they work at it, and learning how is a core part of the game's learning curve.  People expect building things to be part of a sandbox game, and in general people find that building things they can see and use and show off is fun.  Knowing how to program is a much rarer skill and frankly isn't fun to most people; NQ has said that they do NOT want to make programming a mandatory thing, since it reduces the interested players for an already niche game.

Link to post
Share on other sites
38 minutes ago, SGCam said:

if they work at it, and learning

key phrase )


someone builds ships better, someone writes programs. they can exchange what they know how to do. using the market. everything is like in life. Isn't that what NQ wanted?

Then it's easier to remove the radar from the game altogether, what's the point of using it? )

 

upd:

lua cannot identify, cannot take a target, cannot shoot, cannot ... what can lua be more than a human? )

Link to post
Share on other sites

I mean really all I wanted was an updated minimap that was more visible, customizable and user friendly than the cluttered tiny mess we've got stuck in the corner. I'm not trying to build bloody SkyNet, just add something that's in basically every first-person game by default.

Link to post
Share on other sites
  • 2 weeks later...
  • 4 weeks later...
On 10/29/2020 at 6:47 PM, SGCam said:

Straw man aside, there is a huge difference between building ships and writing programs.

I don't agree with this. NQ want players to specialize. I know I'll never be able to build a really beautiful large ship, but if I have the quanta I can buy one off the market from a player who took the time learning voxelmancy and spent hours getting every curve perfect. I might not be much good at PvP, but if I have the quanta I can always hire some mercenaries to fight on my behalf. Why not do the same for programmers writing Lua scripts?

Dumbing the game down by nerfing or removing specializations which are "too hard for the average player" just seems insane.

 

Also, I'd like to make the point that writing human-level AI is really, really hard. If the PvP mechanics are so simple that a 1000 line Lua script can perform better than an experienced human then those mechanics are awful and need to be reworked. DU isn't a first-person shooter which can be won by a simple aimbot. Just as in EVE, the outcome of an engagement should be largely decided before the first shot is even fired.

Link to post
Share on other sites
1 hour ago, Pathalogical Functor said:

I don't agree with this. NQ want players to specialize. I know I'll never be able to build a really beautiful large ship, but if I have the quanta I can buy one off the market from a player who took the time learning voxelmancy and spent hours getting every curve perfect. I might not be much good at PvP, but if I have the quanta I can always hire some mercenaries to fight on my behalf. Why not do the same for programmers writing Lua scripts?

Dumbing the game down by nerfing or removing specializations which are "too hard for the average player" just seems insane.

I am 100% in favor of letting scripting do nice things, whether its hard to write or not.  I was simply pointing out that letting scripts access things that would allow PvP autopilots is a bad idea.

 

There is a reason NASA uses MechJeb - in a full 6 DoF environment, computers are better than humans.  They can take all the variables and compute an optimal course and orientation to maximize weapon time on target, minimize enemy hit chance, or even just have a perfect intercept.  That may be realistic, but it doesn't make for a fun game.  The issue isn't that PvP mechanics are too simple, its that Newtonian Physics is a solved problem.  You would be surprised at how much you can do in 1000 lines of Lua script.

 

I agree that the outcome of the engagement should typically be decided ahead of time - logistics, how much gun your side brought, how good your ships are, etc.  But in an otherwise equal 1v1 engagement, the ship with the PvP autopilot will win 9 times out of 10.  And since the script would increase the combat effectiveness of the ship so much, allowing them basically makes them mandatory.

Link to post
Share on other sites

What works for a NASA docking maneuver (cooperative, two-body problem in a controlled environment) won't work quite so well in a multi-ship PvP engagement where there are far more tactical decisions to be made. If you're just ganking an AFK freighter then sure, the autopilot will perform a nice intercept burn and drop you at your guns' optimal range then automatically bring you up along side the wreck so you can loot it. Now try designing a user interface which lets you tell your autopilot, "Get in weapons range of ships A and B, while avoiding the short-range weapons on C and maintaining at least 0.05 rad/s angular velocity around D to avoid getting hit by its heavy guns", then try using that interface in the middle of battle.

 

Anyway, as long as engine control is possible people will still end up automating those kind of intercept maneuvers. It will just require clicking in a user interface to tell the AI what direction the target is in (and possibly its range, if that isn't available through the API). There's no Lua API for the ore scanner either, but people still managed to write scripts to make quadrilateration of ore nodes trivial.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...