Teleport a certain distance in players view direction without destination in wall

Please post any questions about developing your plugin here. Please use the search function before posting!
User avatar
Pudge90
Member
Posts: 32
Joined: Sun Jan 14, 2018 6:18 pm
Location: Germany

Teleport a certain distance in players view direction without destination in wall

Postby Pudge90 » Mon May 11, 2020 4:09 pm

I am trying to make a player teleport for a certain distance in the direction of his view vector.
I came across this post https://forums.sourcepython.com/viewtopic.php?f=20&t=742&hilit=solid+trace where L'In20Cible had already provided what I think is what I am looking for:
L'In20Cible wrote:I checked the history and it was not working cause you omitted the player's mins/maxs. Taking the code I linked to you, a little modification should do the trick:

Syntax: Select all

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python
# Engines
from engines.trace import ContentMasks
from engines.trace import engine_trace
from engines.trace import GameTrace
from engines.trace import Ray
from engines.trace import TraceFilterSimple
# Filters
from filters.players import PlayerIter
# Players
from players.helpers import playerinfo_from_index


# ============================================================================
# >> HELPER FUNCTIONS
# ============================================================================
def will_player_stuck(player_index, location):
'''Return whether or not the given player will stuck ay yhr given
location.'''

# Get the player's PlayerInfo instance...
player_info = playerinfo_from_index(player_index)

# Get a Ray object based on the player physic box...
ray = Ray(location, location, player_info.get_player_mins(),
player_info.get_player_maxs())

# Get a new GameTrace instance...
trace = GameTrace()

# Do the trace...
engine_trace.trace_ray(ray, ContentMasks.PLAYER_SOLID, TraceFilterSimple(), trace)

# Return whether or not the trace did hit...
return trace.did_hit()
Now you can simply do something like:

Syntax: Select all

def teleport_to_spawnpoint(player_index):
# Loop through all spawnpoints...
for spawnpoint in spawnpoints:

# Will the player stuck there?
if will_player_stuck(player_index, spawnpoint):

# Player will stuck here so move on...
continue

# We have a winner!
some_teleport_function_blah_blah(player_index)

# No need to go further...
return

# Uh oh, we didn't find a safe spawnpoint!
do_something_else(player_index)


Now since I would like to teleport for a certain distance it could happen that the coordinates of the destination are inside a wall or something. Basically what the code above does - checking if a player would be blocked. This is where I would need some explanation:

How does Ray() exactly work ? From the wiki(http://wiki.sourcepython.com/developing ... .trace.Ray) I can only tell that I can either provide 3 or 5 arguments and I am not sure what to pass as arguments.

Then for engine_trace.trace_ray() I could not find an entry in the wiki but in the source code (https://github.com/Source-Python-Dev-Team/Source.Python/blob/master/src/core/modules/engines/engines_trace_wrap.cpp) which tells me:

Syntax: Select all

.def("trace_ray",
&IEngineTrace::TraceRay,
"A version that simply accepts a ray (can work as a traceline or tracehull).",
args("ray", "mask", "filter", "trace")
)


The parameter "ray" is the ray instance, that is clear. But I don't really understand what "mask", "filter" and trace does.

Overall my understanding is that I create a ray instance with starting point and end point that somewhat will be the size of a players model and then either move the model(not the actual model but the sizes of the players model) along the 'trace' and check if it hit anything (like a wall) or only check the endpoint for any wall collision. If somebody could explain to me how this trace_ray works to check if the destination point would get the player stuck, that would be great.

The ultimate goal for me then is to make the teleport work in a way like this:
If the destination point would get the player stuck then try a destination point a little closer (I don't know, a fixed step like the player models width) and check again if that destination point would get the player stuck. Repeat that until a destination point will be valid and then teleport the player to those coordinates
User avatar
L'In20Cible
Project Leader
Posts: 1302
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Teleport a certain distance in players view direction without destination in wall

Postby L'In20Cible » Mon May 11, 2020 6:13 pm

Syntax: Select all

from engines.trace import engine_trace
from engines.trace import ContentMasks
from engines.trace import GameTrace
from engines.trace import Ray
from engines.trace import TraceFilterSimple
from players.entity import Player

def teleport_forward(player, distance):
"""Teleport the given player where he is looking at."""
# Get the destination coordinates
destination = player.eye_location + player.view_vector * distance

# Get a new trace instance
trace = GameTrace()

# Trace from the player's feet to the destination
engine_trace.trace_ray(
# This matches the player's bounding box from his feets to
# the destination
Ray(player.origin, destination, player.mins, player.maxs),

# This collides with everything
ContentMasks.ALL,

# This ignore nothing but the player himself
TraceFilterSimple((player,)),

# The trace will contains the results
trace
)

# If the trace did hit, that means there was obstruction along the way
if trace.did_hit():

# So the end of our trace becomes our destination
destination = trace.end_position

# Teleport the player to the destination
player.teleport(destination)
User avatar
Pudge90
Member
Posts: 32
Joined: Sun Jan 14, 2018 6:18 pm
Location: Germany

Re: Teleport a certain distance in players view direction without destination in wall

Postby Pudge90 » Mon May 11, 2020 8:00 pm

That is an excellent example, thanks for the nicely commented code.

I tried to illustrate the scenario, very art: https://imgur.com/Qy6wtxa

So from your example I understand that in the illustrated scenario

Syntax: Select all

trace.did_hit()
would return true since the trace goes through the wall. So if I would want the player to stop at walls then I would use the example as provided.

If the player is supposed to teleport behind the wall then I would do:

Syntax: Select all

Ray(destination, destination, player.mins, player.maxs)

so that the trace has a "length" of 0 an therefore only at the destination coordinates the player boundaries box is 'placed' and checked if it collides with obstruction.

unfortunately I cannot test the code right now.
User avatar
L'In20Cible
Project Leader
Posts: 1302
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Teleport a certain distance in players view direction without destination in wall

Postby L'In20Cible » Mon May 11, 2020 8:17 pm

Correct, you would use the same start and end coordinates for your ray if the goal is to know whether the entity fits at a certain location or not. Pretty much how Entity.is_in_solid was implemented. I would however not recommend allowing players to teleport through walls, as they would also be able to teleport through meshes outside the playable areas.
User avatar
Pudge90
Member
Posts: 32
Joined: Sun Jan 14, 2018 6:18 pm
Location: Germany

Re: Teleport a certain distance in players view direction without destination in wall

Postby Pudge90 » Mon May 11, 2020 8:43 pm

interesting that Entity.is_in_solid is implemented like that, thanks for mentioning.

Is there any way I can check if the destination location of the ray is outside/within the playable area ? If so then I could think of shortening the teleport distance recursively until the teleport destination is within the playable area before the actual teleport happens
User avatar
L'In20Cible
Project Leader
Posts: 1302
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Teleport a certain distance in players view direction without destination in wall

Postby L'In20Cible » Mon May 11, 2020 8:50 pm

I don't know of an easy way to figure that out, as it would be on a per-map basis with lot of edge cases different for each of them. Perhaps that is something you could figure out in the nav file though.
User avatar
Pudge90
Member
Posts: 32
Joined: Sun Jan 14, 2018 6:18 pm
Location: Germany

Re: Teleport a certain distance in players view direction without destination in wall

Postby Pudge90 » Thu May 14, 2020 4:28 pm

I found this: https://forums.alliedmods.net/showthread.php?p=2649393&fbclid=IwAR1pO79gnflPx_tkpqLrcAGVFSniB1DqGxV_oGwSX4G8akt5d4yYDIa_zYA which describes how to check if the teleport destination is within the map or not by sendin out beams in every direction (x,y,z) checking if they hit anything. If within the map each beam should hit a wall sooner or later. Of cource there are scenarios where this could go wrong but I'd guess that usually it should work (at least if there is a skybox to hit)

With NAV file I don't know how to properly check the z-coordinate, also kind of complicated.
User avatar
L'In20Cible
Project Leader
Posts: 1302
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Teleport a certain distance in players view direction without destination in wall

Postby L'In20Cible » Thu May 14, 2020 9:11 pm

Pudge90 wrote:I found this: https://forums.alliedmods.net/showthread.php?p=2649393&fbclid=IwAR1pO79gnflPx_tkpqLrcAGVFSniB1DqGxV_oGwSX4G8akt5d4yYDIa_zYA which describes how to check if the teleport destination is within the map or not by sendin out beams in every direction (x,y,z) checking if they hit anything. If within the map each beam should hit a wall sooner or later. Of cource there are scenarios where this could go wrong but I'd guess that usually it should work (at least if there is a skybox to hit)


That seems to be an unnecessary way to go about it to be honest. You can easily determine if the coordinates are within the world boundaries:

Syntax: Select all

from entities.constants import WORLD_ENTITY_INDEX
from entities.entity import Entity

def is_within_world_boundaries(vector):
"""Returns whether the given vector is within the world boundaries."""
# Get the world entity
world = Entity(WORLD_ENTITY_INDEX)

# Return whether the given vector is within its boundaries
return vector.is_within_box(world.world_mins, world.world_maxs)


I was more referring about air pockets within the world that would normally not be accessible. If that is not an issue for your plugin, then you don't have to do any extra checks because this will already be handled by the ray above.

Pudge90 wrote:With NAV file I don't know how to properly check the z-coordinate, also kind of complicated.


search.php?keywords=navlib
User avatar
Pudge90
Member
Posts: 32
Joined: Sun Jan 14, 2018 6:18 pm
Location: Germany

Re: Teleport a certain distance in players view direction without destination in wall

Postby Pudge90 » Fri May 15, 2020 10:57 pm

Okay so I used your code with this plugin:

Syntax: Select all

from events import Event
from entities.entity import Entity
from players.helpers import index_from_userid
from filters.entities import EntityIter
from messages import SayText2
from mathlib import Vector
from engines.trace import engine_trace
from engines.trace import ContentMasks
from engines.trace import GameTrace
from engines.trace import Ray
from engines.trace import TraceFilterSimple
from players.entity import Player



def load():
SayText2('\x07Debug loaded!').send()

def unload():
SayText2('\x07Debug unloaded!').send()

@Event('player_say')
def chat_based_trigger(game_event):
userid = game_event['userid']
index = index_from_userid(userid)
player = Player(index)
user = player.name
print(player.origin)
is_within_world_boundaries(player.origin)
print("player is in solid: " + str(player.is_in_solid()))



from entities.constants import WORLD_ENTITY_INDEX
from entities.entity import Entity

def is_within_world_boundaries(vector):
"""Returns whether the given vector is within the world boundaries."""
# Get the world entity
world = Entity(WORLD_ENTITY_INDEX)

# Return whether the given vector is within its boundaries
print("World.mins:")
print(world.world_mins)
print("World.maxs:")
print(world.world_maxs)

print("Is within world: " + str(vector.is_within_box(world.world_mins, world.world_maxs)))


I moved around in de_dust2 with noclip and there were a bunch of places that behave like an "air pocket" that is not reachable normally.
Output of the plugin for such an air pocket:

Code: Select all

Vector(1174.625, 3330.34130859375, 302.3838806152344)
World.mins:
Vector(-3168.0, -1344.0, -192.0)
World.maxs:
Vector(2176.0, 3840.0, 616.0)
Is within world: True
player is in solid: False

the view from that position:
https://imgur.com/a/iz5wmWg

So I'll check the NAV thing. For now I will stick to players not teleporting through walls.

Thanks for your explanations, slowly I learn how things work

Return to “Plugin Development Support”

Who is online

Users browsing this forum: Kami and 9 guests