Detect nearby buildings
19 replies



26.05.23 11:22:07 am
I'm working on a custom health script, but I'm having trouble figuring out how to make the script detect if the player is near a dispenser so they can gain +10 health every second.
edited 1×, last 28.05.23 09:30:13 am
The darkest nights produce the brightest stars.
@
Mami Tomoe:
I couldn't figure out how to use this. Could you please provide an example, like a simple script that allows the player to gain 20 HP every second when he is near a Dispenser?

I couldn't figure out how to use this. Could you please provide an example, like a simple script that allows the player to gain 20 HP every second when he is near a Dispenser?
The darkest nights produce the brightest stars.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
addhook("second","_second")
function _second()
for id = 1, 32 do
if player(id,"exists") then
-- First check for any dispensers in a 1 tile (= 32 pixels) radius around the player
for _, oid in pairs(closeobjects(player(id,"x"),player(id,"y"),32,7)) do
-- Next check which team the dispensers belong to
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
-- If the dispenser belongs to the player's team or is neutral, heal
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end
function _second()
for id = 1, 32 do
if player(id,"exists") then
-- First check for any dispensers in a 1 tile (= 32 pixels) radius around the player
for _, oid in pairs(closeobjects(player(id,"x"),player(id,"y"),32,7)) do
-- Next check which team the dispensers belong to
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
-- If the dispenser belongs to the player's team or is neutral, heal
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end
Just a quick draft. Important to note is the comments though, so you know the logic of it.


@
Cure Pikachu:
It works only when you are up the dispenser, but not when you are down it, even if you are touching it.

It works only when you are up the dispenser, but not when you are down it, even if you are touching it.
The darkest nights produce the brightest stars.
@
muslim: That's interesting what you said. The code provided should work as intended. I am assuming there might be something wrong with the internal position checks relating to
closeobjects.
You should either increase the radius size or add small numbers to the player's Y position.
@
Cure Pikachu: Iterating through 1 to 32 and checking if the player exists are all bad habits stuck to the veteran CS2D coders. I would ditch it all with good old


You should either increase the radius size or add small numbers to the player's Y position.
@

for _, id in pairs(player(0, "tableliving")) do
. This way you use fewer lines of code, and make it more performant. Shit your pants:
Outlast II Mod (29) | Create your UI faster: CS2D UI Framework


@
Masea:
It's not working as expected... Like I mentioned earlier, I only receive 20 HP when I'm on top of the dispenser.
Take a look at this picture: imgur.com/jc5eCTM
The green area is where I gain HP when standing on it, but I don't get the same results in other areas around the dispenser.
However, I made some changes to the code and now it looks like this:
This didn't completely fix the problem, but it's somewhat better now... More areas are receiving the additional 20 HP.
Check out this image: imgur.com/nIrcRgk

It's not working as expected... Like I mentioned earlier, I only receive 20 HP when I'm on top of the dispenser.
Take a look at this picture: imgur.com/jc5eCTM
The green area is where I gain HP when standing on it, but I don't get the same results in other areas around the dispenser.
However, I made some changes to the code and now it looks like this:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
addhook("second", "_second")
function _second()
for id = 1, 32 do
if player(id, "exists") then
for _, offset in ipairs({{0, 0}, {-32, -32}}) do
local x, y = player(id, "x") + offset[1], player(id, "y") + offset[2]
for _, oid in pairs(closeobjects(x, y, 32, 7)) do
local dteam, pteam = math.min(object(oid, "team"), 2), math.min(player(id, "team"), 2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id, "health") + 20, player(id, "maxhealth"))
parse("sethealth " .. id .. " " .. hp)
end
end
end
end
end
end
function _second()
for id = 1, 32 do
if player(id, "exists") then
for _, offset in ipairs({{0, 0}, {-32, -32}}) do
local x, y = player(id, "x") + offset[1], player(id, "y") + offset[2]
for _, oid in pairs(closeobjects(x, y, 32, 7)) do
local dteam, pteam = math.min(object(oid, "team"), 2), math.min(player(id, "team"), 2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id, "health") + 20, player(id, "maxhealth"))
parse("sethealth " .. id .. " " .. hp)
end
end
end
end
end
end
This didn't completely fix the problem, but it's somewhat better now... More areas are receiving the additional 20 HP.
Check out this image: imgur.com/nIrcRgk
edited 1×, last 01.06.23 06:26:31 pm
The darkest nights produce the brightest stars.
All you should do now is double or triple the radius size.
Edit: Uh, you have some major issues within your code. I'd suggest you use this code instead:
Edit: Uh, you have some major issues within your code. I'd suggest you use this code instead:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
addhook("second","_second")
function _second()
for _, id in pairs(player(0, "tableliving")) do
for _, oid in pairs(closeobjects(player(id,"x") + 32, player(id,"y") + 32, 64, 7)) do
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
function _second()
for _, id in pairs(player(0, "tableliving")) do
for _, oid in pairs(closeobjects(player(id,"x") + 32, player(id,"y") + 32, 64, 7)) do
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
Shit your pants:
Outlast II Mod (29) | Create your UI faster: CS2D UI Framework



@
Cure Pikachu: Iterating through 1 to 32 and checking if the player exists are all bad habits stuck to the veteran CS2D coders. I would ditch it all with good old

for _, id in pairs(player(0, "tableliving")) do
. This way you use fewer lines of code, and make it more performant.Truth is I was using that, but hearing discussions about it actually being less performant in the past was giving me skeptism in a sense

Oh and I was just recycling whatever

Back for real, though... If even expanding the radius and such results in incosistencies in the heals (since you plan to implement a custom health system) then we might have to resort to the primitive method of iterating through both the player's x and y tiles from -1 to +1 current tile position for any dispenser... Let's hope it doesn't get down to that
edited 1×, last 02.06.23 03:10:40 pm


If expanding the radius still result in inconsistencies, then it's likely CS2D bug.
CS2D Bug Reports




With the following code:
I got the following result:

It seems that it doesn't account for anything above the player or to the left of the player.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
addhook('always', '_always_hook')
function _always_hook()
if player(1, 'health') == 0 then
return
end
local objs = closeobjects(player(1, 'x'), player(1, 'y'), 32, 7)
for i = 1, #objs do
local obj = objs[i]
parse('killobject ' .. obj)
end
end
function _always_hook()
if player(1, 'health') == 0 then
return
end
local objs = closeobjects(player(1, 'x'), player(1, 'y'), 32, 7)
for i = 1, #objs do
local obj = objs[i]
parse('killobject ' .. obj)
end
end
I got the following result:

It seems that it doesn't account for anything above the player or to the left of the player.
It's hard being the best girl in the whole entire world
There are a few problems with this unfortunately (god, it's ugly and I'm sorry about it):
Buildings have their pivot point in the top left corner and not in their center. To compensate for that you would have to use the player position -16 (on x and y) if you still want to use
closeobjects:
It may STILL fail because
closeobjects uses < and not <= for the radial distance check. Also it would only work when the player stands in the perfect center of the tile. Would be safer to add 17 to the radius (+16 for half a tile and +1 to compensate for the < which should probably be a <=).
Dispensers do NOT use a radial distance check internally. They just check the absolute distance on X and Y separately. That's for performance and simplicity reasons. Their check looks like this:
With do = dynamic object / dispenser and p = player. So it's not a radial AoE but a square one.
Sooo... what would be the best approach to solve the issue?
Still use
closeobjects because it's fast!
Use the -16 offset (because of building offset)
Increase the radius to 48 * sqrt(2) to get a radius which includes the full square area. That's a radius of ~67.882. You can round it up to 68.
Iterate on all results of
closeobjects and use a Lua version of the original dispenser distance check to find out if any of the detected dispensers is really within the distance the game actually uses. Also don't forget to check if the team is right.


Code:
1
local objs = closeobjects(player(1, 'x') - 16, player(1, 'y') - 16, 32, 7)



Code:
1
If Abs((do.x+16)-p.x)<48 And Abs((do.y+16)-p.y)<48 Then
With do = dynamic object / dispenser and p = player. So it's not a radial AoE but a square one.
Sooo... what would be the best approach to solve the issue?






edited 3×, last 04.06.23 12:23:45 pm
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
addhook('always', '_always_hook')
function _always_hook()
if player(1, 'health') == 0 then
return
end
local objs = closeobjects(player(1, 'x')-16, player(1, 'y')-16, 48, 7)
for i = 1, #objs do
local obj = objs[i]
parse('killobject ' .. obj)
end
end
function _always_hook()
if player(1, 'health') == 0 then
return
end
local objs = closeobjects(player(1, 'x')-16, player(1, 'y')-16, 48, 7)
for i = 1, #objs do
local obj = objs[i]
parse('killobject ' .. obj)
end
end
@

Tested, worked.
EDIT: Oh sht. U have edited it. NVM.
Sorry, had do edit it AGAIN. I now took a look into the code and the dispenser AoE is actually a square. So the 16 pixel offset and a radius of 48 is still not enough for perfect results. 
I added a little red message to the
closeobjects documentation to make people aware of the building pivot offset.

I added a little red message to the

By the way, since the problem is solved(I think is it)
Instead of making new topic I'd like to know how can I catch "mine" and "lasermine" explosion, which hook might tell me "X" and "Y" of the building explosion? I'd like to add effect, for example. But in my case I need to check "closeobjects(mineX,mineY,rad,..?)"
Projectile hook and Projectile_impact is not triggered when mine/lasermine is exploded.
/in case with HE grenade on projectile/projectile_impact hook it works, but with mine it doesnt:
https://youtu.be/wCO9atZXu5M
Instead of making new topic I'd like to know how can I catch "mine" and "lasermine" explosion, which hook might tell me "X" and "Y" of the building explosion? I'd like to add effect, for example. But in my case I need to check "closeobjects(mineX,mineY,rad,..?)"
Projectile hook and Projectile_impact is not triggered when mine/lasermine is exploded.
/in case with HE grenade on projectile/projectile_impact hook it works, but with mine it doesnt:
https://youtu.be/wCO9atZXu5M
@
Mora: We have no limitation on threads so it would still be better to create a new thread for a new topic instead of mixing things for no good reason
(Laser)mines are dynamic objects and not projectiles. They should trigger the
objectkill hook. They basically kill themselves when they explode. You should be able to still retrieve ther values using
object inside that hook.


(Laser)mines are dynamic objects and not projectiles. They should trigger the



@user Mora: We have no limitation on threads so it would still be better to create a new thread for a new topic instead of mixing things for no good reason
oh, excuse me. Just thought in good way to save time.
Thank You for the hint bro. This thing is working as I needed.
edited 1×, last 04.06.23 01:11:56 pm

Sooo... what would be the best approach to solve the issue?
Still use
closeobjects because it's fast!
Use the -16 offset (because of building offset)
Increase the radius to 48 * sqrt(2) to get a radius which includes the full square area. That's a radius of ~67.882. You can round it up to 68.
Iterate on all results of
closeobjects and use a Lua version of the original dispenser distance check to find out if any of the detected dispensers is really within the distance the game actually uses. Also don't forget to check if the team is right.






So like this?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
addhook("second","_second")
function _second()
for _, id in pairs(player(0,"tableliving")) do
local x, y = player(id,"x"), player(id,"y")
for _, oid in pairs(closeobjects(x-16, y-16, 68, 7)) do
if math.abs((object(oid,"x")+16)-x) < 48 and math.abs((object(oid,"y")+16)-y) < 48 then
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end
function _second()
for _, id in pairs(player(0,"tableliving")) do
local x, y = player(id,"x"), player(id,"y")
for _, oid in pairs(closeobjects(x-16, y-16, 68, 7)) do
if math.abs((object(oid,"x")+16)-x) < 48 and math.abs((object(oid,"y")+16)-y) < 48 then
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end


@
Cure Pikachu: Yes, that's what I meant. I can't guarantee that it works but it looks legit in theory.
You should never call CS2D api methods in loops though if you don't have too. Especially not the heavy ones which return tables. Talking about line 3 and 5 in your snippet. Instead you should cache both return values in variables once and then loop on the variables.
(yes, methods in loop conditions are called and evaluated for each loop iteration)

You should never call CS2D api methods in loops though if you don't have too. Especially not the heavy ones which return tables. Talking about line 3 and 5 in your snippet. Instead you should cache both return values in variables once and then loop on the variables.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
addhook("second","_second")
function _second()
local players = player(0,"tableliving")
for _, id in pairs(players) do
local x, y = player(id,"x"), player(id,"y")
local objs = closeobjects(x-16, y-16, 68, 7)
for _, oid in pairs(objs) do
if math.abs((object(oid,"x")+16)-x) < 48 and math.abs((object(oid,"y")+16)-y) < 48 then
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end
function _second()
local players = player(0,"tableliving")
for _, id in pairs(players) do
local x, y = player(id,"x"), player(id,"y")
local objs = closeobjects(x-16, y-16, 68, 7)
for _, oid in pairs(objs) do
if math.abs((object(oid,"x")+16)-x) < 48 and math.abs((object(oid,"y")+16)-y) < 48 then
local dteam, pteam = math.min(object(oid,"team"),2), math.min(player(id,"team"),2)
if dteam == 0 or dteam == pteam then
local hp = math.min(player(id,"health")+20,player(id,"maxhealth"))
parse("sethealth "..id.." "..hp)
end
end
end
end
end
(yes, methods in loop conditions are called and evaluated for each loop iteration)
@
DC: Lua
However, I still prefer
DC code as it looks cleaner with the locals. Both should compile to same Lua bytecode though (which means there's no difference).
A better optimization would be caching class-1 dynamic object positions (and invalidating it out on
objectkill), but this is left as an exercise to OP or other people.

for
-loop construct (both numeric and iterative construct) does not re-evaluate the values if it's a function. Perhaps you're confusing it with while
loop where values are re-evaluated everytime?However, I still prefer

A better optimization would be caching class-1 dynamic object positions (and invalidating it out on






