|
|
- extends Node # Networking functionality
-
- var player
-
- var is_placing = false
- var placing_node
- var placed = []
- var max_placed = 5
-
- var start_action
- var confirm_action
- var delete_action
-
- var scene
-
- signal start_placement
- signal confirm_placement
-
- func _init(parent, scene_path):
- player = parent
- player.add_child(self)
- # Set the network master to the player's network master, which happens to be its name
- # This allows it to use master, slave keywords appropriately
- var net_id = int(player.get_name())
- set_name("Placement") # We need to share common name for networking
- set_network_master(net_id)
- scene = load(scene_path)
- rpc("request_placed")
-
- master func request_placed():
- for node in placed:
- rpc_id(get_tree().get_rpc_sender_id(), "slave_place", node.transform)
-
- func place_input(radius=-1, can_build=true, require_ghost=false):
-
- # We allow you to just click to place, without needing to press E
- var confirm = Input.is_action_just_pressed(confirm_action)
-
- if can_build and Input.is_action_just_pressed(start_action) or (confirm and not is_placing and not require_ghost):
- # Press button twice to cancel
- if is_placing:
- # We changed our mind, delete the placing wall
- placing_node.queue_free()
- is_placing = false
- else:
- # Make a floating placement wall
- placing_node = create()
- is_placing = true
-
- if Input.is_action_just_pressed(delete_action):
- var pick = player.pick_from(placed)
- if pick != -1:
- rpc("remove_placed", placed[pick].get_name())
-
- if is_placing:
- position_placement(placing_node)
- if radius > 0: # A radius is specified
- var distance = placing_node.get_translation() - player.get_translation()
- if distance.length() > radius:
- placing_node.out_of_range()
- confirm = false
- else:
- placing_node.within_range()
-
- if can_build and (confirm and not require_ghost) or (confirm and is_placing):
- # Order matters here: confirm_placement resets placing_node so we have to do anything with it first
- rpc("slave_place", placing_node.transform)
- confirm_placement(placing_node)
- return true
- return false
-
- func confirm_placement(node, tf=null):
- if tf:
- node.set_transform(tf)
- node.place()
- placed.append(node)
- check_count()
- placing_node = null
- is_placing = false
-
- func check_count():
- # If we've made max_walls, remove the first we made
- if placed.size() > max_placed:
- placed[0].queue_free()
- placed.pop_front()
- # When placing, make the about-to-disappear wall translucent
- if placed.size() >= max_placed:
- placed[0].make_last()
-
- # Find the point we're looking at, and put the wall there
- func position_placement(node):
- var aim = player.get_node("Yaw/Pitch").get_global_transform().basis
- var look_ray = player.get_node("TPCamera/Camera/Ray")
- var pos = look_ray.get_collision_point()
- node.set_translation(pos)
- var normal = look_ray.get_collision_normal()
- var towards = normal + pos
- var up = aim[2] # Wall should be horizontal to my view
- # This helps nearly horizontal walls be easier to make flat
- # We have two methods I'm deciding between
- var use_method_two = true
- if not use_method_two:
- # Method one: only allow horizontal or vertical, based on whether the surface faces you
- var on_wall_margin = 0.75
- if normal.dot(Vector3(0,1,0)) < on_wall_margin:
- var margin = 0.8
- if up.dot(normal) > margin: # The wall is facing us
- # We want flat
- up = Vector3(0,1,0)
- else:
- # We want straight
- up.y = 0
- else:
- # Method two: Make y more aggressive than other dimensions
- up.y = 3 * tanh(up.y)
- up = up.normalized()
- node.look_at(towards, up)
-
- func clear():
- for node in placed:
- node.queue_free()
- placed = []
-
- slave func slave_place(tf):
- var node = create()
- confirm_placement(node, tf)
-
- sync func remove_placed(name):
- var what = get_node("/root/Level").get_node(name)
- placed.erase(what)
- what.queue_free()
-
- func create():
- var node = scene.instance()
- player.get_node("/root/Level").add_child(node)
- node.init(player)
- return node
-
|