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