This repository has been archived on 2020-09-30. You can view files and clone it, but cannot push or open issues or pull requests.
odyssey-old/Scenes/MapEditor.gd

350 lines
9.7 KiB
GDScript3
Raw Permalink Normal View History

2020-09-11 09:29:06 +00:00
extends Control
2020-09-15 23:24:15 +00:00
onready var map_menu := ($menu/menubar/MapMenu as MenuButton).get_popup()
onready var map_node := $map
onready var cursor := $map/cursor
onready var cursor_sprite := $map/cursor/preview
2020-09-11 09:29:06 +00:00
2020-09-15 23:24:15 +00:00
const MAP_SCALE_MAX := 8.0
const MAP_SCALE_MIN := 0.25
const TileTabScene := preload("res://Scenes/Editor/TileTab.tscn")
2020-09-11 09:29:06 +00:00
2020-09-22 08:06:17 +00:00
const objects = {
"Machines": [
2020-09-24 10:56:33 +00:00
GameObjectComputer,
GameObjectDoor,
GameObjectEngine,
GameObjectLightbulb,
GameObjectPowerStorage
2020-09-22 08:06:17 +00:00
]
}
2020-09-11 09:29:06 +00:00
func _ready():
2020-09-22 08:06:17 +00:00
# Add tiles
2020-09-15 23:24:15 +00:00
add_tile_tab("Base", $map/tiles/base)
2020-09-22 08:06:17 +00:00
add_tile_tab("Cables", $map/tiles/cables)
2020-09-15 23:24:15 +00:00
add_tile_tab("Floors", $map/tiles/floor)
add_tile_tab("Walls", $map/tiles/walls)
2020-09-22 08:06:17 +00:00
# Add objects
for cat in objects:
add_object_list(cat, objects[cat])
2020-09-15 23:24:15 +00:00
enum PlacingMode {
NONE,
TILEMAP,
OBJECT
}
2020-09-11 09:29:06 +00:00
2020-09-24 10:56:33 +00:00
enum Tool {
NONE
TILE_FREEHAND,
TILE_RECT,
TILE_FILL,
OBJ_PLACE,
OBJ_EDIT,
OBJ_REMOVE
}
2020-09-11 09:29:06 +00:00
# Prevent input handler from running when other dialogs/actions are focused
2020-09-15 23:24:15 +00:00
var input_lock := false
2020-09-11 09:29:06 +00:00
# Drag variables
2020-09-15 23:24:15 +00:00
var dragging := false
var view_origin := Vector2.ZERO
var mouse_origin := Vector2.ZERO
# Placing variables
var placing := false
var deleting := false
var placing_mode = PlacingMode.NONE
var placing_layer = null
2020-09-17 23:47:16 +00:00
var placing_tile_id := -1
2020-09-24 10:56:33 +00:00
var current_tool = Tool.NONE
2020-09-15 23:24:15 +00:00
# Cursor variables
var cursor_pos := Vector2.ZERO
2020-09-17 23:47:16 +00:00
var pressed_pos := Vector2.ZERO
2020-09-15 23:24:15 +00:00
const TILE_SIZE := 32
2020-09-11 09:29:06 +00:00
func _input(ev: InputEvent):
if input_lock:
return
2020-09-15 23:24:15 +00:00
2020-09-11 09:29:06 +00:00
if ev is InputEventMouseMotion:
if dragging:
2020-09-15 23:24:15 +00:00
map_node.global_position = view_origin - (mouse_origin - ev.global_position)
2020-09-11 09:29:06 +00:00
else:
# Map cursor location to grid
2020-09-15 23:24:15 +00:00
var tile_snap: Vector2 = map_node.scale * TILE_SIZE
var mouse_offset: Vector2 = (ev.global_position - map_node.global_position) / tile_snap
var new_cursor_pos := Vector2(floor(mouse_offset.x), floor(mouse_offset.y))
if new_cursor_pos != cursor_pos:
2020-09-17 23:47:16 +00:00
var old_pos = cursor_pos
2020-09-15 23:24:15 +00:00
cursor_pos = new_cursor_pos
2020-09-17 23:47:16 +00:00
if placing or deleting:
2020-09-24 10:56:33 +00:00
if current_tool != Tool.NONE:
tile_mouse_move(old_pos, cursor_pos)
2020-09-15 23:24:15 +00:00
cursor.position = cursor_pos * TILE_SIZE
2020-09-11 09:29:06 +00:00
if ev is InputEventMouseButton:
2020-09-15 23:24:15 +00:00
var mouse := ev as InputEventMouseButton
2020-09-11 09:29:06 +00:00
if mouse.pressed:
match ev.button_index:
2020-09-24 10:56:33 +00:00
BUTTON_LEFT, BUTTON_RIGHT:
if current_tool != Tool.NONE:
tile_pressed(ev.button_index, cursor_pos)
2020-09-11 09:29:06 +00:00
BUTTON_WHEEL_UP:
# Zoom in
2020-09-15 23:24:15 +00:00
var old_scale = map_node.scale
2020-09-11 09:29:06 +00:00
if map_node.scale.x < MAP_SCALE_MAX:
if map_node.scale.x < 1:
map_node.scale *= 2
else:
map_node.scale += Vector2.ONE
2020-09-15 23:24:15 +00:00
map_node.position -= (map_node.position + mouse.position * map_node.scale)- (map_node.position + mouse.position * old_scale)
2020-09-11 09:29:06 +00:00
BUTTON_WHEEL_DOWN:
# Zoom out
2020-09-15 23:24:15 +00:00
var old_scale = map_node.scale
2020-09-11 09:29:06 +00:00
if map_node.scale.x > MAP_SCALE_MIN:
if map_node.scale.x <= 1:
map_node.scale /= 2
else:
map_node.scale -= Vector2.ONE
2020-09-15 23:24:15 +00:00
map_node.position -= (map_node.position + mouse.position * map_node.scale)- (map_node.position + mouse.position * old_scale)
2020-09-11 09:29:06 +00:00
BUTTON_MIDDLE:
2020-09-15 23:24:15 +00:00
view_origin = map_node.global_position
2020-09-11 09:29:06 +00:00
mouse_origin = ev.global_position
dragging = true
else:
match ev.button_index:
2020-09-24 10:56:33 +00:00
BUTTON_LEFT, BUTTON_RIGHT:
if current_tool != Tool.NONE:
tile_released(ev.button_index, cursor_pos)
2020-09-11 09:29:06 +00:00
BUTTON_MIDDLE:
dragging = false
2020-09-15 23:24:15 +00:00
map_node.global_position = view_origin - (mouse_origin - ev.global_position)
2020-09-17 23:47:16 +00:00
func place_rect(a: Vector2, b: Vector2, id: int):
if placing_layer == null:
return
var layer := placing_layer as TileMap
# Sort coordinates
var x_ord = Vector2(a.x, b.x)
var y_ord = Vector2(a.y, b.y)
if a.x > b.x:
x_ord = Vector2(b.x, a.x)
if a.y > b.y:
y_ord = Vector2(b.y, a.y)
for x in range(x_ord.x, x_ord.y+1):
2020-09-22 08:06:17 +00:00
place_tile(layer, Vector2(x, a.y), id)
place_tile(layer, Vector2(x, b.y), id)
2020-09-17 23:47:16 +00:00
for y in range(y_ord.x, y_ord.y+1):
2020-09-22 08:06:17 +00:00
place_tile(layer, Vector2(a.x, y), id)
place_tile(layer, Vector2(b.x, y), id)
2020-09-17 23:47:16 +00:00
layer.update_bitmask_region(a, b)
2020-09-22 08:06:17 +00:00
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):
2020-09-17 23:47:16 +00:00
for pos in positions:
2020-09-22 08:06:17 +00:00
place_tile(tilemap, pos, id)
2020-09-17 23:47:16 +00:00
for pos in positions:
# Update bitmask
2020-09-22 08:06:17 +00:00
tilemap.update_bitmask_area(pos)
2020-09-15 23:24:15 +00:00
var group := ButtonGroup.new()
func add_tile_tab(name: String, tilemap: TileMap):
var tab := add_tab(name)
tab.connect("tile_selected", self, "_tile_selected", [name, tilemap])
var tileset := tilemap.tile_set
var ids := tileset.get_tiles_ids()
for id in ids:
var tile_icon := make_tile_texture(tileset, id)
tab.add_entry(id, group, tile_icon)
func add_tab(name: String) -> TileTab:
var tab := TileTabScene.instance() as TileTab
tab.name = name
$layers/tabs.add_child(tab)
return tab
2020-09-22 08:06:17 +00:00
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:
2020-09-24 10:56:33 +00:00
var editor_info = obj.editor_info()
2020-09-22 08:06:17 +00:00
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
2020-09-15 23:24:15 +00:00
func _toggle_tile_input(enable: bool):
input_lock = not enable
2020-09-24 10:59:40 +00:00
func _tile_selected(id: int, _name: String, tilemap: TileMap):
2020-09-15 23:24:15 +00:00
cursor_sprite.texture = make_tile_texture(tilemap.tile_set, id)
placing_mode = PlacingMode.TILEMAP
placing_layer = tilemap
placing_tile_id = id
func make_tile_texture(tileset: TileSet, id: int) -> AtlasTexture:
var tile_mode := tileset.tile_get_tile_mode(id)
var tile_icon := AtlasTexture.new()
tile_icon.atlas = tileset.tile_get_texture(id)
match tile_mode:
TileSet.AUTO_TILE:
var tile_size := tileset.autotile_get_size(id)
tile_icon.region = Rect2(
tileset.autotile_get_icon_coordinate(id) * tile_size,
tile_size
)
TileSet.SINGLE_TILE:
tile_icon.region = tileset.tile_get_region(id)
return tile_icon
2020-09-17 23:47:16 +00:00
onready var layers_panel = $layers
2020-09-22 08:06:17 +00:00
onready var objects_panel = $objects
2020-09-17 23:47:16 +00:00
onready var brush_panel = $tools/brushPanel
2020-09-22 08:06:17 +00:00
onready var obj_action_panel = $tools/objPanel
2020-09-17 23:47:16 +00:00
func _tool_selected(tool_type):
layers_panel.visible = false
brush_panel.visible = false
2020-09-22 08:06:17 +00:00
objects_panel.visible = false
obj_action_panel.visible = false
2020-09-24 10:56:33 +00:00
placing_mode = PlacingMode.NONE
2020-09-17 23:47:16 +00:00
match tool_type:
"tile":
2020-09-24 10:56:33 +00:00
layers_panel.visible = tool_type(current_tool) == "brush"
2020-09-17 23:47:16 +00:00
brush_panel.visible = true
placing_mode = PlacingMode.TILEMAP
2020-09-22 08:06:17 +00:00
"obj":
obj_action_panel.visible = true
2020-09-24 10:56:33 +00:00
objects_panel.visible = tool_type(current_tool) == "obj"
placing_mode = PlacingMode.OBJECT
2020-09-17 23:47:16 +00:00
func _set_brush(brush_type: String):
2020-09-24 10:56:33 +00:00
match brush_type:
"freehand":
current_tool = Tool.TILE_FREEHAND
"rect":
current_tool = Tool.TILE_RECT
"fill":
current_tool = Tool.TILE_FILL
layers_panel.visible = true
func tool_type(brush):
match brush:
Tool.TILE_FILL, Tool.TILE_FREEHAND, Tool.TILE_RECT:
return "brush"
Tool.OBJ_EDIT, Tool.OBJ_PLACE, Tool.OBJ_REMOVE:
return "obj"
_:
return "none"
2020-09-22 08:06:17 +00:00
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):
2020-09-24 10:56:33 +00:00
match action:
"add":
current_tool = Tool.OBJ_PLACE
"edit":
current_tool = Tool.OBJ_EDIT
"remove":
current_tool = Tool.OBJ_REMOVE
objects_panel.visible = true
func tile_pressed(button: int, pos: Vector2):
match button:
BUTTON_LEFT:
placing = true
BUTTON_RIGHT:
deleting = true
match current_tool:
Tool.TILE_FREEHAND:
if placing_layer != null:
place_tiles(placing_layer, [pos], get_active_tile())
Tool.OBJ_PLACE:
pass
pressed_pos = pos
func tile_released(button: int, pos: Vector2):
match current_tool:
Tool.TILE_RECT:
if placing_layer != null:
2020-09-24 10:59:40 +00:00
place_rect(pressed_pos, pos, get_active_tile())
2020-09-24 10:56:33 +00:00
Tool.TILE_FILL:
if placing_layer != null:
2020-09-24 10:59:40 +00:00
flood_fill(placing_layer, pos, get_active_tile())
2020-09-24 10:56:33 +00:00
match button:
BUTTON_LEFT:
placing = false
BUTTON_RIGHT:
deleting = false
2020-09-24 10:59:40 +00:00
func tile_mouse_move(_from: Vector2, to: Vector2):
2020-09-24 10:56:33 +00:00
match current_tool:
Tool.TILE_FREEHAND:
if placing_layer != null:
place_tiles(placing_layer, [to], get_active_tile())
func get_active_tile():
if deleting:
return -1
return placing_tile_id