diff --git a/Actors/Objects/Computer/Computer.gd b/Actors/Objects/Computer/Computer.gd index d414957..f575e10 100644 --- a/Actors/Objects/Computer/Computer.gd +++ b/Actors/Objects/Computer/Computer.gd @@ -17,6 +17,16 @@ var open := false onready var activationRange := $ActivationRange as ActivationRange onready var manager := $PowerManager as PowerManager +static func editor_info(): + var editor_icon = AtlasTexture.new() + editor_icon.atlas = preload("res://Graphics/tgstation/computer.png") + editor_icon.region = Rect2(0, 0, 32, 32) + return { + "name": "Computer", + "scene": load("res://Actors/Objects/Computer/Computer.tscn"), + "icon": editor_icon + } + func _ready(): if not Engine.editor_hint: activationRange.visible = true diff --git a/Graphics/UI/editor-icons.png b/Graphics/UI/editor-icons.png index 416ef72..ce5a92f 100644 Binary files a/Graphics/UI/editor-icons.png and b/Graphics/UI/editor-icons.png differ diff --git a/Scenes/MapEditor.gd b/Scenes/MapEditor.gd index 06c3ec9..7dde97e 100644 --- a/Scenes/MapEditor.gd +++ b/Scenes/MapEditor.gd @@ -10,10 +10,24 @@ const MAP_SCALE_MIN := 0.25 const TileTabScene := preload("res://Scenes/Editor/TileTab.tscn") +const objects = { + "Machines": [ + { + "scene": preload("res://Actors/Objects/Computer/Computer.tscn"), + "script": GameObjectComputer + } + ] +} + func _ready(): + # Add tiles add_tile_tab("Base", $map/tiles/base) + add_tile_tab("Cables", $map/tiles/cables) add_tile_tab("Floors", $map/tiles/floor) add_tile_tab("Walls", $map/tiles/walls) + # Add objects + for cat in objects: + add_object_list(cat, objects[cat]) enum PlacingMode { NONE, @@ -36,6 +50,7 @@ var placing_mode = PlacingMode.NONE var placing_layer = null var placing_tile_id := -1 var current_brush := "none" +var current_obj_action := "none" # Cursor variables var cursor_pos := Vector2.ZERO @@ -68,12 +83,15 @@ func _input(ev: InputEvent): match ev.button_index: BUTTON_LEFT: if current_brush == "freehand": - place_tiles([cursor_pos], placing_tile_id) + if placing_layer != null: + place_tiles(placing_layer, [cursor_pos], placing_tile_id) placing = true pressed_pos = cursor_pos BUTTON_RIGHT: if current_brush == "freehand": - place_tiles([cursor_pos], -1) + if placing_layer != null: + place_tiles(placing_layer, [cursor_pos], -1) + pressed_pos = cursor_pos deleting = true BUTTON_WHEEL_UP: # Zoom in @@ -100,10 +118,20 @@ func _input(ev: InputEvent): else: match ev.button_index: BUTTON_LEFT: - if current_brush == "rect": - place_rect(pressed_pos, cursor_pos, placing_tile_id) + match current_brush: + "rect": + place_rect(pressed_pos, cursor_pos, placing_tile_id) + "fill": + if placing_layer != null: + flood_fill(placing_layer, cursor_pos, placing_tile_id) placing = false BUTTON_RIGHT: + match current_brush: + "rect": + place_rect(pressed_pos, cursor_pos, -1) + "fill": + if placing_layer != null: + flood_fill(placing_layer, cursor_pos, -1) deleting = false BUTTON_MIDDLE: dragging = false @@ -116,7 +144,8 @@ func handle_held_cursor_move(old_pos: Vector2, new_pos: Vector2): # If deleting, null tile instead if deleting: id = -1 - place_tiles([new_pos], id) + if placing_layer != null: + place_tiles(placing_layer, [new_pos], id) func place_rect(a: Vector2, b: Vector2, id: int): if placing_layer == null: @@ -131,23 +160,27 @@ func place_rect(a: Vector2, b: Vector2, id: int): y_ord = Vector2(b.y, a.y) var positions = [] for x in range(x_ord.x, x_ord.y+1): - layer.set_cellv(Vector2(x, a.y), id) - layer.set_cellv(Vector2(x, b.y), id) + place_tile(layer, Vector2(x, a.y), id) + place_tile(layer, Vector2(x, b.y), id) for y in range(y_ord.x, y_ord.y+1): - layer.set_cellv(Vector2(a.x, y), id) - layer.set_cellv(Vector2(b.x, y), id) + place_tile(layer, Vector2(a.x, y), id) + place_tile(layer, Vector2(b.x, y), id) layer.update_bitmask_region(a, b) -func place_tiles(positions: Array, id: int): - if placing_layer == null: - return - var layer := placing_layer as TileMap +func place_tile(tilemap: TileMap, pos: Vector2, id: int): + # Place tile + tilemap.set_cellv(pos, id) + # If no base is under that tile, add it + if id != -1 and tilemap != $map/tiles/base: + if $map/tiles/base.get_cellv(pos) == -1: + $map/tiles/base.set_cellv(pos, 1) + +func place_tiles(tilemap: TileMap, positions: Array, id: int): for pos in positions: - # Place tile - layer.set_cellv(pos, id) + place_tile(tilemap, pos, id) for pos in positions: # Update bitmask - layer.update_bitmask_area(pos) + tilemap.update_bitmask_area(pos) var group := ButtonGroup.new() @@ -167,6 +200,20 @@ func add_tab(name: String) -> TileTab: $layers/tabs.add_child(tab) return tab +func add_object_list(name: String, objects: Array): + var tab := ItemList.new() + tab.connect("item_selected", self, "_item_selected", [tab]) + tab.name = name + for obj in objects: + var editor_info = obj.script.editor_info() + tab.add_item(editor_info.name, editor_info.icon) + tab.set_item_metadata(tab.get_item_count()-1, editor_info) + $objects/tabs.add_child(tab) + +func _item_selected(idx: int, tab: ItemList): + var meta = tab.get_item_metadata(idx) + cursor_sprite.texture = meta.icon + func _toggle_tile_input(enable: bool): input_lock = not enable @@ -192,16 +239,73 @@ func make_tile_texture(tileset: TileSet, id: int) -> AtlasTexture: return tile_icon onready var layers_panel = $layers +onready var objects_panel = $objects onready var brush_panel = $tools/brushPanel +onready var obj_action_panel = $tools/objPanel func _tool_selected(tool_type): layers_panel.visible = false brush_panel.visible = false placing_mode = PlacingMode.NONE + objects_panel.visible = false + obj_action_panel.visible = false match tool_type: "tile": - layers_panel.visible = true + layers_panel.visible = current_brush != "none" brush_panel.visible = true placing_mode = PlacingMode.TILEMAP + "obj": + obj_action_panel.visible = true + objects_panel.visible = current_obj_action == "add" func _set_brush(brush_type: String): current_brush = brush_type + layers_panel.visible = current_brush != "none" + +var ff_cells = {} +var ff_origin = Vector2.ZERO + +const BOUNDS_MAX_X = 40 +const BOUNDS_MAX_Y = 40 + +# Sanity check: force bounds for cell +func ff_is_inbound(cell: Vector2): + return abs(cell.x-ff_origin.x) < BOUNDS_MAX_X and abs(cell.y-ff_origin.y) < BOUNDS_MAX_Y + +func ff_is_valid(tilemap: TileMap, cell: Vector2, matching_tile: int) -> bool: + return ff_is_inbound(cell) and (not ff_cells.has(cell)) and tilemap.get_cellv(cell) == matching_tile + +func ff_get_neighbours(tilemap: TileMap, cell: Vector2, matching_tile: int) -> Array: + var neighbours = [Vector2(cell.x, cell.y-1),Vector2(cell.x-1, cell.y),Vector2(cell.x+1, cell.y), Vector2(cell.x, cell.y+1)] + var out = [] + for neighbour in neighbours: + # Have we checked this already? + if not ff_is_valid(tilemap, neighbour, matching_tile): + continue + out.push_back(neighbour) + return out + +func flood_fill(tilemap: TileMap, pos: Vector2, id: int): + # Reset lists + ff_cells = {} + ff_origin = pos + var current_tile = tilemap.get_cellv(pos) + var queue = ff_get_neighbours(tilemap, pos, current_tile) + place_tile(tilemap, pos, id) + ff_cells[pos] = true + # Depth-first search + while not queue.empty(): + var current = queue.pop_front() + # Have we checked this already? + if not ff_is_valid(tilemap, current, current_tile): + continue + var tile_id = tilemap.get_cellv(current) + if tile_id == current_tile: + ff_cells[current] = true + place_tile(tilemap, current, id) + for neighbour in ff_get_neighbours(tilemap, current, current_tile): + queue.push_front(neighbour) + tilemap.update_bitmask_region() + +func _set_obj_action(action): + current_obj_action = action + objects_panel.visible = action == "add" diff --git a/Scenes/MapEditor.tscn b/Scenes/MapEditor.tscn index c9e2245..b08f060 100644 --- a/Scenes/MapEditor.tscn +++ b/Scenes/MapEditor.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=25 format=2] +[gd_scene load_steps=28 format=2] [ext_resource path="res://Graphics/deepspace_mat.tres" type="Material" id=1] [ext_resource path="res://Scenes/InEditorMap.gd" type="Script" id=2] @@ -63,14 +63,26 @@ region = Rect2( 16, 0, 16, 16 ) atlas = ExtResource( 4 ) region = Rect2( 0, 16, 16, 16 ) -[sub_resource type="AtlasTexture" id=11] +[sub_resource type="AtlasTexture" id=10] atlas = ExtResource( 4 ) region = Rect2( 32, 16, 16, 16 ) -[sub_resource type="AtlasTexture" id=10] +[sub_resource type="AtlasTexture" id=11] atlas = ExtResource( 4 ) region = Rect2( 16, 16, 16, 16 ) +[sub_resource type="AtlasTexture" id=12] +atlas = ExtResource( 4 ) +region = Rect2( 0, 32, 16, 16 ) + +[sub_resource type="AtlasTexture" id=13] +atlas = ExtResource( 4 ) +region = Rect2( 16, 32, 16, 16 ) + +[sub_resource type="AtlasTexture" id=14] +atlas = ExtResource( 4 ) +region = Rect2( 32, 32, 16, 16 ) + [node name="Control" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 @@ -315,7 +327,7 @@ margin_bottom = 56.0 theme = ExtResource( 13 ) toggle_mode = true group = SubResource( 8 ) -icon = SubResource( 11 ) +icon = SubResource( 10 ) flat = false [node name="Fill" type="ToolButton" parent="tools/brushPanel/brush"] @@ -325,7 +337,52 @@ margin_bottom = 86.0 theme = ExtResource( 13 ) toggle_mode = true group = SubResource( 8 ) -icon = SubResource( 10 ) +icon = SubResource( 11 ) +flat = false + +[node name="objPanel" type="PanelContainer" parent="tools"] +visible = false +margin_left = 59.0 +margin_right = 93.0 +margin_bottom = 94.0 +size_flags_vertical = 0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="brush" type="VBoxContainer" parent="tools/objPanel"] +margin_left = 4.0 +margin_top = 4.0 +margin_right = 30.0 +margin_bottom = 90.0 + +[node name="add" type="ToolButton" parent="tools/objPanel/brush"] +margin_right = 26.0 +margin_bottom = 26.0 +theme = ExtResource( 13 ) +toggle_mode = true +group = SubResource( 8 ) +icon = SubResource( 12 ) +flat = false + +[node name="edit" type="ToolButton" parent="tools/objPanel/brush"] +margin_top = 30.0 +margin_right = 26.0 +margin_bottom = 56.0 +theme = ExtResource( 13 ) +toggle_mode = true +group = SubResource( 8 ) +icon = SubResource( 13 ) +flat = false + +[node name="remove" type="ToolButton" parent="tools/objPanel/brush"] +margin_top = 60.0 +margin_right = 26.0 +margin_bottom = 86.0 +theme = ExtResource( 13 ) +toggle_mode = true +group = SubResource( 8 ) +icon = SubResource( 14 ) flat = false [node name="layers" type="MarginContainer" parent="."] @@ -351,6 +408,29 @@ tab_align = 0 __meta__ = { "_edit_use_anchors_": false } + +[node name="objects" type="MarginContainer" parent="."] +visible = false +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 5.0 +margin_top = -166.0 +margin_right = 172.0 +margin_bottom = -5.0 +size_flags_vertical = 13 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="tabs" type="TabContainer" parent="objects"] +margin_right = 167.0 +margin_bottom = 161.0 +rect_min_size = Vector2( 0, 80 ) +theme = ExtResource( 12 ) +tab_align = 0 +__meta__ = { +"_edit_use_anchors_": false +} [connection signal="mouse_entered" from="background" to="." method="_toggle_tile_input" binds= [ true ]] [connection signal="mouse_exited" from="background" to="." method="_toggle_tile_input" binds= [ false ]] [connection signal="pressed" from="tools/toolsPanel/tools/placeTile" to="." method="_tool_selected" binds= [ "tile" ]] @@ -358,3 +438,6 @@ __meta__ = { [connection signal="pressed" from="tools/brushPanel/brush/FreeHand" to="." method="_set_brush" binds= [ "freehand" ]] [connection signal="pressed" from="tools/brushPanel/brush/Rect" to="." method="_set_brush" binds= [ "rect" ]] [connection signal="pressed" from="tools/brushPanel/brush/Fill" to="." method="_set_brush" binds= [ "fill" ]] +[connection signal="pressed" from="tools/objPanel/brush/add" to="." method="_set_obj_action" binds= [ "add" ]] +[connection signal="pressed" from="tools/objPanel/brush/edit" to="." method="_set_obj_action" binds= [ "edit" ]] +[connection signal="pressed" from="tools/objPanel/brush/remove" to="." method="_set_obj_action" binds= [ "remove" ]]