Proper way of dealing with spawnpoints

Please post any questions about developing your plugin here. Please use the search function before posting!
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Proper way of dealing with spawnpoints

Postby BackRaw » Wed Jan 21, 2015 11:22 pm

Hi,

I'm struggling to find a way to create a spawnpoint list, and then randomly choose a spawnpoint that no player has spawned in yet. See the screenshot below...

The code for the spawnpoints: here
And for the admin menu: here

Following things work:
  • loading the <mapname>.txt file (parsing it from JSON list to Vector instances)
  • saving the <mapname>.txt file (parsing it from Vector instances to JSON lists)
  • Adding, removing and teleporting to vectors through the Admin menu

Contents of the dynamically saved spawnpoints file:

Code: Select all

[[-1663.1475830078125, 779.1383056640625, 32.03125], [-1922.8316650390625, 1980.5145263671875, 9.888980865478516], [-957.9427490234375, 2230.0693359375, -57.30064010620117], [-479.4043273925781, 1868.709716796875, -127.12945556640625], [-414.70361328125, 666.8245849609375, 3.628751039505005], [378.34857177734375, 25.55403709411621, 8.529182434082031], [1775.96875, 987.219482421875, 128.03125], [1670.6195068359375, 369.1476745605469, 64.03125], [1713.1978759765625, 1822.50537109375, 48.798667907714844]]
JSON, as you can see.

Actually and randomly choosing an empty spawnpoint however does not work, code:

Syntax: Select all

from random.choice import random_choice

from flashfunsp.spawnpoints import spawnpoints
from flashfunsp.spawnpoints import VECTOR_DISTANCE

class Player(PlayerEntity):

""" Adds features needed by this script to PlayerEntity """

def __init__(self, index):
"""
Instance initlaization
"""

# call PlayerEntity's __init__()
super(Player, self).__init__()

# ....

def teleport(self):
"""
Teleports the player to a random spawnpoint.
"""

possible = list()

for player in playeriter_alive:
for vector in filter(lambda x: x not in possible and x.get_distance(player.origin) > VECTOR_DISTANCE, spawnpoints):
possible.append(vector)

if possible:
self.origin = random_choice(possible)


def player_spawn(game_event):
player = Player(index_from_userid(game_event.get_int("userid")))

if not player.get_property_int("pl.deadflag") and player.team > 1:
player.teleport() # players just get "glued together"....
Player.teleport() needs to be refined, but I have no clue how lol. Any help?
Attachments
Screen Shot 2015-01-22 at 00.14.42.jpg
Screen Shot 2015-01-22 at 00.14.51.jpg
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
L'In20Cible
Project Leader
Posts: 1536
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Thu Jan 22, 2015 1:34 am

You could loop through all spawnpoints and test their location using this code to ensure players won't get stuck if teleported there and assign him the first safe one.
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Thu Jan 22, 2015 7:40 am

Thank you very much.
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Mon Jan 26, 2015 8:34 pm

Players still get stuck :(
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
L'In20Cible
Project Leader
Posts: 1536
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Mon Jan 26, 2015 8:35 pm

Code?
User avatar
satoon101
Project Leader
Posts: 2703
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Jan 26, 2015 9:26 pm

I believe this is what he is currently testing, but I could be wrong:
https://github.com/backraw/backraw.sp/blob/master/addons/source-python/plugins/flashfunsp/spawnpoints.py
Image
User avatar
L'In20Cible
Project Leader
Posts: 1536
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Mon Jan 26, 2015 9:36 pm

If so, he is not even using the link I pointed to him and wonder why this is not working, oh well. :)
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Mon Jan 26, 2015 9:56 pm

I tried many ways and I commit a lot lol. I tried your method, but this one works:

Syntax: Select all

# maximum distance for spawning players
DISTANCE_SPAWN = 100.0

# maximum distance to add vectors
DISTANCE_ADD = 200.0


class _SpawnPoints(list):

# .....

def check(self, vector):
"""
Returns whether the vector can be added to this list.
"""

# is the vector already in this list?
if vector in self:

# return False to stop the addition
return False

# loop through each vector in this list
for check in self:

# is the check too near?
if check.get_distance(vector) < DISTANCE_ADD:

# if yes, return False
return False

# else return True to add it
return True

def get_random(self):
"""
Returns a random vector to spawn on.
"""

count_max = len(self) - 1
count = 0

# get a random vector
start = random_choice(self)

# loop trough all alive players' origins
for vector in map(lambda x: x.origin, playeriter_alive):

# did we reach the maximum?
if count == count_max:

# if yes, return None
return None

# is a player too near?
if vector.get_distance(start) < DISTANCE_SPAWN:

# if yes, go recursive...
return self.get_random()

count += 1

# return the random vector
return start
Thanks for your help it got me to the right direction.
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2703
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Jan 26, 2015 10:11 pm

You should use enumerate so you don't have to keep a count yourself.

Also, a couple tips related to Path objects that I saw in your code:

Syntax: Select all

# don't continue if it doens't exist
if not isfile(file_name):
return

Since 'file_name' should be a Path object itself, there is no need to import isfile directly:

Syntax: Select all

# don't continue if it doens't exist
if not file_name.isfile():
return


Also:

Syntax: Select all

# get the file object for the txt file
file_object = open(file_name, "r")

# store the file's contents (lists of [X,Y,Z] coordinates)
contents = json_load(file_object)

# close the file object
file_object.close()

The same applies here, since file_name is a Path object. And, you should really look into using context management (the 'with' statement):

Syntax: Select all

# Open/close the file
# NOTE: the lack of "r" since read is the default for opening a file
# NOTE 2: the 'with' statement automatically closes on exit
with file_name.open() as file_object:

# Store the file's contents
contents = json_load(file_object)


And for dumping the file:

Syntax: Select all

# Open/close the file
with spawnpoints_path.joinpath("{0}.json".format(mapname)).open("w") as file_object:

# dump the vectors from this list as [X,Y,Z] lists to it
json_dump(list(map(lambda vector: [vector.x, vector.y, vector.z], self)), file_object, indent=4)
Image
User avatar
L'In20Cible
Project Leader
Posts: 1536
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Mon Jan 26, 2015 10:17 pm

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)
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Tue Jan 27, 2015 3:29 pm

satoon101 wrote:You should use enumerate so you don't have to keep a count yourself.

Also, a couple tips related to Path objects that I saw in your code:

Syntax: Select all

# don't continue if it doens't exist
if not isfile(file_name):
return

Since 'file_name' should be a Path object itself, there is no need to import isfile directly:

Syntax: Select all

# don't continue if it doens't exist
if not file_name.isfile():
return


Also:

Syntax: Select all

# get the file object for the txt file
file_object = open(file_name, "r")

# store the file's contents (lists of [X,Y,Z] coordinates)
contents = json_load(file_object)

# close the file object
file_object.close()

The same applies here, since file_name is a Path object. And, you should really look into using context management (the 'with' statement):

Syntax: Select all

# Open/close the file
# NOTE: the lack of "r" since read is the default for opening a file
# NOTE 2: the 'with' statement automatically closes on exit
with file_name.open() as file_object:

# Store the file's contents
contents = json_load(file_object)


And for dumping the file:

Syntax: Select all

# Open/close the file
with spawnpoints_path.joinpath("{0}.json".format(mapname)).open("w") as file_object:

# dump the vectors from this list as [X,Y,Z] lists to it
json_dump(list(map(lambda vector: [vector.x, vector.y, vector.z], self)), file_object, indent=4)


Thanks, I applied those tips :) makes the code more readable!

I use another method now, outside the _SpawnPoints class:

Syntax: Select all

def get_random_spawnpoint(check, vector):
"""
Returns a random vector to spawn on.
"""

# return None if there is nothing to check
if not check:
return None

# loop trough alive player origins
for origin in map(lambda x: x.origin, playeriter_alive):

# is a player far enough away?
if vector.get_distance(origin) >= DISTANCE_SPAWN:

# if yes, return the vector
return vector

# remove the vector from the check list
del check[vector]

# is there still anything to check?
if not check:

# if not, return None
return None

# else, go recursive to find another random vector...
return get_random_spawnpoint(check, random_choice(check))
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Tue Jan 27, 2015 9:14 pm

Finally I have the perfect function :smile:

Syntax: Select all

def get_random_spawnpoint(tried=0):
"""
Returns a random vector to spawn on.
"""

# is there anything to check?
if not spawnpoints:

# if not, return None
return None

# store the amount of times the for loop has run
tries = len(spawnpoints) - 1

# did we reach the maximum number of tries?
if tried == tries:

# if yes, return None
return None

# else, get a random vector
vector = random_choice(spawnpoints)

# loop trough alive player origins
for origin in map(lambda x: Player(x).origin, playeriter_alive):

# is a player too near?
if vector.get_distance(origin) < DISTANCE_SPAWN:

# if yes, go recursive to find another vector...
return get_random_spawnpoint(tried + 1)

# retrun the random vector
return vector


OT: Can we use PlayerIter() like this? I can't find the forum thread about PlayerIter..

Syntax: Select all

playeriter_origins = PlayerIter(return_types="origin")
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2703
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Tue Jan 27, 2015 9:32 pm

Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Tue Jan 27, 2015 10:36 pm

satoon101 wrote:Don't forget about the wiki:
http://wiki.sourcepython.com/pages/filters.players


True, I shouldn't :D thanks.
My Github repositories:

Source.Python: https://github.com/backraw

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 86 guests