project-villain/places/base_place/base_place.gd

402 lines
11 KiB
GDScript

extends Node3D
class_name Place
var tile_grid_size: int = 32
var load_tile = preload("res://tiles/base_tile/base_tile.tscn")
var load_room = preload("res://places/base_place/base_room.tscn")
var load_door = preload("res://tiles/base_tile/base_door.tscn")
var load_actor = preload("res://actors/base_actor/base_actor.tscn")
@onready var task_list: Node = $TaskList
#Contains all tiles in a workplace.
var lookup_place_to_tile: Dictionary = {}
#Contains all rooms in a workplace.
var lookup_place_to_room: Array = []
#Contains the tiles in the current selection drag
var selection_drag_dict: Dictionary = {}
#Contains all the tiles that have been selected in the current build action
var selection_dict: Dictionary = {}
#Tracks the previous amount of tiles counted in a selection drac
var tile_count_x_hist: int = 0
var tile_count_z_hist: int = 0
#Tracks if confirming a build is allowed
var build_confirm_allowed: bool = true
#The object that's currently being built
var current_object: Object = null
#Where that object can be placed
var current_placement: Array = []
#Tracks parts of the room currently being built
var current_room: Object = null
var map = PlaceAStar3D.new()
func save():
var save_data = {
#Basics
"id": self.get_instance_id(),
"type": "Place",
"scene_file_path": scene_file_path,
"parent": get_parent().get_path(),
#Connections
#Data
}
return save_data
func init_grid():
#TEMP: Sets up a simple 2D grid of blank tiles.
for x in range(tile_grid_size):
for z in range (tile_grid_size):
var pos = Vector3i(x, 0, z)
var tile: Object = load_tile.instantiate()
tile.set_position(pos)
tile.grid_pos = pos
tile.name = str("Tile", tile.get_instance_id())
tile.place_id = self.get_instance_id()
tile.selection_mode = Tile.SEL_MODE.NONE
tile.construction_mode = Tile.CON_MODE.CLOSED
$TileContainer.add_child(tile)
func draw_tile_click(click_pos):
#starts a selection drag
var build_start_pos: Vector3i = click_pos.floor()
if lookup_place_to_tile.has(build_start_pos):
if not lookup_place_to_tile[build_start_pos].construction_mode == Tile.CON_MODE.BUILT:
select_tile(build_start_pos)
func init_select_drag(float_build_start_pos, float_build_mouse_pos):
#Creats an array of dragged tiles between mouse start and current position
var select_drag_array: Array = []
var build_start_pos: Vector3i = float_build_start_pos.floor()
var build_mouse_pos: Vector3i = float_build_mouse_pos.floor()
var tile_count_x: int = build_mouse_pos.x - build_start_pos.x
var tile_count_z: int = build_mouse_pos.z - build_start_pos.z
if not tile_count_x_hist == tile_count_x or not tile_count_z_hist == tile_count_z:
tile_count_x_hist = tile_count_x
tile_count_z_hist = tile_count_z
for x in range(min(0, tile_count_x), max(0, tile_count_x) + 1):
for z in range(min(0, tile_count_z), max(0, tile_count_z) + 1):
var select_drag_pos: Vector3i = build_start_pos + Vector3i(x, 0, z)
if lookup_place_to_tile.has(select_drag_pos):
if not lookup_place_to_tile[select_drag_pos].construction_mode == Tile.CON_MODE.BUILT:
select_drag_array.append(select_drag_pos)
if select_drag_array:
draw_select_drag(select_drag_array)
func draw_select_drag(array):
#Clears previous drag, then calls tile selection on all currently dragged tiles
for i in lookup_place_to_tile:
if not selection_dict.has(i):
var tile: Object = lookup_place_to_tile[i]
tile.selection_mode = Tile.SEL_MODE.NONE
selection_drag_dict.clear()
for i in array:
select_tile(i)
func select_tile(pos):
#Tells tiles to be selected
var tile: Object = lookup_place_to_tile[pos]
selection_drag_dict[pos] = tile
if build_confirm_allowed:
tile.selection_mode = Tile.SEL_MODE.BUILD
else:
tile.selection_mode = Tile.SEL_MODE.INVALID
func verify_room():
#Verifies that a given selection is fully contiguous
var verify_array: Array = selection_dict.keys()
var verify_queue_array: Array = [verify_array[0]]
var verify_checked_array: Array = []
while verify_array:
if not verify_queue_array:
return false
var verify_pos: Vector3i = verify_queue_array.pop_back()
var verify_neighbor_array: Array = lookup_place_to_tile[verify_pos].direction_vector_dict.values()
for n in verify_neighbor_array:
if selection_dict.has(verify_pos + n):
if not verify_checked_array.has(verify_pos + n):
if not verify_queue_array.has(verify_pos + n):
verify_queue_array.append(verify_pos + n)
verify_checked_array.append(verify_pos)
verify_array.erase(verify_pos)
return true
func end_select_drag():
#Adds dragged tiles to the current selection on mouse-up
if not selection_drag_dict:
return
tile_count_x_hist = 0
tile_count_z_hist = 0
selection_dict.merge(selection_drag_dict)
if verify_room():
build_confirm_allowed = true
for i in selection_dict:
lookup_place_to_tile[i].selection_mode = Tile.SEL_MODE.BUILD
else:
build_confirm_allowed = false
for i in selection_dict:
lookup_place_to_tile[i].selection_mode = Tile.SEL_MODE.INVALID
selection_drag_dict.clear()
func build_selection():
#When the build button is clicked, changes the selected tiles to match the button's request
if not build_confirm_allowed:
return
if selection_dict:
var room: Object = create_room(selection_dict)
for i in selection_dict:
var tile: Object = lookup_place_to_tile[i]
tile.selection_mode = Tile.SEL_MODE.NONE
tile.construction_mode = Tile.CON_MODE.BUILT
selection_dict.clear()
return room
func clear_selection():
#When the clear button is clicked, it clears the selected tiles without doing anything.
for i in selection_dict:
var tile: Object = selection_dict[i]
tile.selection_mode = Tile.SEL_MODE.NONE
build_confirm_allowed = true
selection_dict.clear()
func create_room(selection):
#Creates a room from the selected tiles.
var room: Object = load_room.instantiate()
room.position = (selection.keys().min())
room.room_to_tile = selection.values()
room.place_id = self.get_instance_id()
room.name = str("Room", room.get_instance_id())
$RoomContainer.add_child(room)
for i in room.room_to_tile:
i.lookup_tile_to_room = room
lookup_place_to_room.append(room)
return room
func init_object(object):
#Instantiates the object to be placed
match object:
"door":
var door: Object = load_door.instantiate()
init_object_locations(door)
door.place_id = self.get_instance_id()
door.visible = false
$ObjectContainer.add_child(door)
current_object = door
"actor":
var actor: Actor = load_actor.instantiate()
current_object = actor
actor.visible = false
$ActorContainer.add_child(actor)
func init_object_locations(object):
#Gets the list of locations that the object can be placed
match object.placement:
GameObject.PLACEMENTS.DOOR:
for i in lookup_place_to_tile.keys():
for j in lookup_place_to_tile[i].face_dict.keys():
if lookup_place_to_tile[i].face_dict[j] is not Door:
current_placement.append((Vector3(i) + Vector3(0.5, 0, 0.5)) + Vector3(j) / 2)
func hover_object(mouse_pos):
#Hovers the object at the closest acceptable placement location to the mouse
current_object.visible = true
if current_placement:
var closest: Vector3
var closest_distance: float = INF
for i in current_placement:
var distance: float = mouse_pos.distance_to(i)
if closest_distance > distance:
closest_distance = distance
closest = i
current_object.position = closest
current_object.rotation_degrees = Vector3(0, 180 * fmod(closest.x, 1), 0)
else:
current_object.position = mouse_pos
func confirm_object():
#Places the object at the hovered location
#TODO: this is very sloppy
var check = current_object.get_class()
if current_object is Door:
var tile_1: Object
var tile_2: Object
if fmod(current_object.position.x, 1) == 0:
tile_1 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z)]
tile_2 = lookup_place_to_tile[Vector3i(current_object.position.x - 1, 0, current_object.position.z)]
else:
tile_1 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z)]
tile_2 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z - 1)]
var tile_1_new_dict = tile_1.face_dict.duplicate()
var tile_2_new_dict = tile_2.face_dict.duplicate()
var tile_1_door_face: Vector3i = tile_2.position - tile_1.position
tile_1_new_dict[tile_1_door_face] = current_object
tile_1.update_faces(tile_1_new_dict)
tile_1.lookup_tile_to_room.room_to_door.append(current_object)
tile_2.lookup_connected_rooms.append(tile_1.lookup_tile_to_room)
tile_2.lookup_tile_to_room.room_to_tile.append(tile_1)
var tile_2_door_face: Vector3i = tile_1.position - tile_2.position
tile_2_new_dict[tile_2_door_face] = current_object
tile_2.update_faces(tile_2_new_dict)
tile_2.lookup_tile_to_room.room_to_door.append(current_object)
tile_1.lookup_connected_rooms.append(tile_2.lookup_tile_to_room)
tile_1.lookup_tile_to_room.room_to_tile.append(tile_2)
current_object.lookup_door_to_room.append(tile_1.lookup_tile_to_room)
current_object.lookup_door_to_room.append(tile_2.lookup_tile_to_room)
current_object.lookup_door_to_tile[tile_1] = tile_1_door_face
current_object.lookup_door_to_tile[tile_2] = tile_2_door_face
var id = map.get_available_point_id()
map.add_point(id, current_object.position)
current_object.map_point = id
prints(map.get_point_ids())
for i in tile_1.lookup_tile_to_room.room_to_door:
if i.map_point != id:
map.connect_points(i.map_point, id)
for i in tile_2.lookup_tile_to_room.room_to_door:
if i.map_point != id:
map.connect_points(i.map_point, id)
prints(map.get_point_connections(id))
elif current_object is Actor:
current_object.actor_state = Actor.ACTOR_STATE.PAUSED
current_room = null
current_placement = []
current_object = null
func give_neighbors(tile, grid_pos, directions):
#Responds to a tile's request for it's neighbors
var neighbor_dict = {}
for i in directions.keys():
if lookup_place_to_tile.has(directions[i] + grid_pos):
neighbor_dict[directions[i]] = lookup_place_to_tile[directions[i] + grid_pos]
tile.neighbor_dict = neighbor_dict
func _on_tile_container_child_entered_tree(node: Node) -> void:
#Connects signals from created tiles
lookup_place_to_tile[node.grid_pos] = node
node.connect("neighbor_request", give_neighbors)
func _on_actor_container_child_entered_tree(node: Node) -> void:
#Connects signals from created actors
node.connect("current_tile_request", give_tile)
if node.has_node("TaskCreator"):
var tasker = node.get_node("TaskCreator")
tasker.connect("record_task", record_task)
tasker.connect("finish_task", finish_task)
func give_tile(actor):
#Responds to an actor's request for its current tile
#var closest: Vector3
#var closest_distance: float = INF
#
#for i in lookup_place_to_tile:
#var distance: float = actor.position.distance_to(i)
#if closest_distance > distance:
#closest_distance = distance
#closest = i
actor.current_tile = lookup_place_to_tile[Vector3i(floor(actor.position.x), 0, floor(actor.position.z))]
func record_task(task):
#Records created tasks in the task list.
task_list.record_task(task)
func finish_task(source, task):
#Marks tasks as completed in the task list.
task_list.finish_task(source, task)