From d0440314897435e3c420ea8e502cf31eeb783ea4 Mon Sep 17 00:00:00 2001 From: Valerie Date: Mon, 13 May 2024 18:16:23 -0400 Subject: [PATCH] init --- .gitattributes | 2 + .gitignore | 2 + .vscode/settings.json | 3 + Scripts/Entity.gd | 20 ++++ Scripts/Grid.gd | 192 +++++++++++++++++++++++++++++++ Scripts/Highlight.gd | 58 ++++++++++ Scripts/Lib.gd | 5 + Scripts/Positional.gd | 87 ++++++++++++++ Scripts/SelectionManager.gd | 41 +++++++ Scripts/TileRenderer.gd | 30 +++++ Scripts/Util.gd | 25 ++++ Tiles/farm.tscn | 10 ++ World.tscn | 32 ++++++ addons/tile_helper/TileHelper.gd | 13 +++ addons/tile_helper/plugin.cfg | 7 ++ icon.svg | 1 + icon.svg.import | 37 ++++++ project.godot | 34 ++++++ 18 files changed, 599 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Scripts/Entity.gd create mode 100644 Scripts/Grid.gd create mode 100644 Scripts/Highlight.gd create mode 100644 Scripts/Lib.gd create mode 100644 Scripts/Positional.gd create mode 100644 Scripts/SelectionManager.gd create mode 100644 Scripts/TileRenderer.gd create mode 100644 Scripts/Util.gd create mode 100644 Tiles/farm.tscn create mode 100644 World.tscn create mode 100644 addons/tile_helper/TileHelper.gd create mode 100644 addons/tile_helper/plugin.cfg create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4709183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Godot 4+ specific ignores +.godot/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8157773 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "godotTools.editorPath.godot4": "c:\\Users\\Valerie\\Desktop\\Godot_v4.2.1-stable_win64.exe" +} \ No newline at end of file diff --git a/Scripts/Entity.gd b/Scripts/Entity.gd new file mode 100644 index 0000000..4c137e9 --- /dev/null +++ b/Scripts/Entity.gd @@ -0,0 +1,20 @@ +@tool + +extends Node2D +class_name WorldEntity + +var grid: Grid + +func _ready(): + if Engine.is_editor_hint(): + grid = get_tree().edited_scene_root.find_child("Grid") + else: + grid = $/root/Root/Grid + + Util.find(Grid) + +func _process(_delta): + pass + +func _draw(): + draw_circle(Vector2.ZERO, 100, Color.WHITE) \ No newline at end of file diff --git a/Scripts/Grid.gd b/Scripts/Grid.gd new file mode 100644 index 0000000..f2214bd --- /dev/null +++ b/Scripts/Grid.gd @@ -0,0 +1,192 @@ +@tool +class_name Grid +extends Node2D + +@export var cells: int: + set(val): + cells = val + _calc_dims() +@export var cell_size: int: + set(val): + cell_size = val + _calc_dims() + +var total_width: int +var total_height: int + +var thickness: int +var off: Vector2 + +@onready var selection_manager: SelectionManager = Util.find(SelectionManager) + +func _enter_tree(): + _calc_dims() + +func _ready(): + _calc_dims() + Engine.is_editor_hint() + + +func _calc_dims(): + total_width = cells * cell_size + total_height = cells * cell_size + + thickness = 1 + off = Vector2(total_width / -2, total_height / -2) + +func _process(_delta): + queue_redraw() + +func _draw(): + var r = Rect2(off, Vector2(total_width, total_height)) + draw_rect(r, Color("222222"), true) + var line_color = Color(1, 1, 1, 0.3) + for i in range(cells + 1): + draw_line(Vector2(i * cell_size, 0) + off, Vector2(i * cell_size, total_height) + off, line_color, thickness, false) + for i in range(cells + 1): + draw_line(Vector2(0, i * cell_size) + off, Vector2(total_width, i * cell_size) + off, line_color, thickness, false) + for x in range(cells): + for y in range(cells): + var world_pos = Vector2i(x, y) + var tile = _get_tile_at_tile_pos(world_pos) + if tile == null: + continue + var t = Rect2( + tile2world(world_pos), + Vector2.ONE * cell_size + ) + draw_rect(t, Color("773322")) + +func tile2world(tile: Vector2i) -> Vector2i: + return Vector2(tile) * cell_size + off + +func tile2world_rect(tile: Rect2i) -> Rect2: + return Rect2(Vector2(tile.position) * cell_size + off, tile.size * cell_size) + +func tile2world_size(tile_size: Vector2i) -> Vector2: + return Vector2(tile_size * cell_size) + +func world2tile(world_pos: Vector2) -> Vector2i: + return Vector2i(((world_pos - off) / cell_size).floor()) + +func world2tile_f(world_pos: Vector2) -> Vector2: + return (world_pos - off) / cell_size + +func world2tile_rect(rect: Rect2) -> Rect2i: + var start = world2tile(rect.position) + var end = world2tile(rect.end) + return Rect2i(start, end - start) + +func quantize(world_position: Vector2, tile_size: Vector2i) -> Rect2: + var q_pos = tile2world(world2tile(world_position)) + return Rect2(q_pos, tile_size * cell_size) + +func quantize_on_objects(world_position: Vector2) -> Rect2: + var tile_pos = world2tile(world_position) + var size = Vector2i.ONE + for tile in things: + # we subtract ONE here, because the encloses thing is a >= test and goes OOB with integers + if Rect2(tile.tile_pos, tile.size - Vector2i.ONE).encloses(Rect2(tile_pos, Vector2.ZERO)): + size = tile.size + tile_pos = tile.tile_pos + break + return tile2world_rect(Rect2i(tile_pos, size)) + +func _get_tile_at_tile_pos(pos: Vector2i) -> Positional: + for tile in things: + if tile.get_tilebox().has_point(pos): + return tile + return null + +func _get_tile_at_world_pos(pos: Vector2) -> Positional: + #var world_pos = world2tile_f(pos) + for tile in things: + if tile.worldbox.has_point(pos): + return tile + return null + +func contains_world_pos(pos: Vector2) -> bool: + var tile = world2tile(pos) + if tile.x < 0 or tile.x > cells - 1: + return false + if tile.y < 0 or tile.y > cells - 1: + return false + return true + +func _shift_rect_half(rect: Rect2) -> Rect2: + return Rect2(rect.position + rect.size / -2, rect.size) + +var left_dragging = false +var left_mouse_down = false +var left_mouse_down_start_pos: Vector2 = Vector2.ZERO + +func _is_mouse_pos_significant() -> bool: + return (get_local_mouse_position() - left_mouse_down_start_pos).length_squared() > 50 + +func _input(event: InputEvent): + if event is InputEventMouseMotion: + if left_mouse_down: + if not left_dragging and _is_mouse_pos_significant(): + left_dragging = true + _begin_drag(get_local_mouse_position()) + elif left_dragging: + _dragging(get_local_mouse_position()) + elif event is InputEventMouseButton: + if event.button_index == 1: + left_mouse_down = event.pressed + if event.pressed: + left_mouse_down_start_pos = get_local_mouse_position() + else: + if left_dragging: + left_dragging = false + _end_drag(get_local_mouse_position()) + else: + _click(get_local_mouse_position()) + +var things: Array[Positional] = [] +var things_dict: Dictionary = {} + +func register_tile(tile: Positional) -> void: + things.append(tile) + +func remove_tile(tile: Positional) -> void: + things.erase(tile) + +func _begin_drag(pos: Vector2): + print("drag from", pos) + +func _dragging(pos: Vector2): + pass + +func _end_drag(pos: Vector2): + print("to", pos) + +func _click(pos: Vector2): + print("click at", pos) + var tile = _get_tile_at_world_pos(pos) + if tile == null: + selection_manager.clear() + return + selection_manager.select_array([tile]) + +func _double_click(pos: Vector2): + pass + +func _right_click(pos: Vector2): + pass + + + + + + + + + + + + + + + + diff --git a/Scripts/Highlight.gd b/Scripts/Highlight.gd new file mode 100644 index 0000000..5cabd0d --- /dev/null +++ b/Scripts/Highlight.gd @@ -0,0 +1,58 @@ +extends Node2D + +@onready var grid: Grid = $/root/Root/Grid +@export var highlight_color: Color = Color(1, 1, 1, 0.2) + +@onready var d_pos: Rect2 = grid.quantize(get_local_mouse_position(), Vector2i.ONE) +@onready var c_pos: Rect2 = d_pos +@onready var d_opacity = 1.0 if grid.contains_world_pos(get_local_mouse_position()) else 0.0 +@onready var c_opacity = 0.0 + +#eventually, replace this weird shit with an array of old rects that are fading out +#let them fade out, and just remove them. have a method to abandon the current box +#and start a new one that just follows the normal box-booting procedure. +#if you're not in the grid, it simply wont be created yet. have to have a has_box prop +#or something like that. but this is good enough for now + +func _lerp_opacity(c: Color, t: float) -> Color: + return Color(c.r, c.g, c.b, c.a * t) + +func _process(delta): + queue_redraw() + + var a_center = c_pos.get_center() + + if c_opacity >= 0.01: + var b_center = d_pos.get_center() + # i know its not right but stfu + var n_center = lerp(a_center, b_center, delta * 60) + var a_size = c_pos.size + var b_size = d_pos.size + var n_size: Vector2 + if a_size.length_squared() > b_size.length_squared(): + n_size = lerp(a_size, b_size, delta * 20) + else: + #getting bigger needs to be ffaster because it looks nice but also lerping + #bigger makes it last longer at the very end + n_size = lerp(a_size, b_size, delta * 60) + + var n_pos = n_center - n_size / 2 + c_pos = Rect2(n_pos, n_size) + else: + c_pos = Rect2(d_pos) + + var mouse = get_local_mouse_position() + if grid.contains_world_pos(mouse) and not grid.left_dragging: + d_pos = grid.quantize_on_objects(get_local_mouse_position()) + if grid.contains_world_pos(a_center): + d_opacity = 1.0 + else: + d_opacity = 0.0 + + if d_opacity > c_opacity or grid.left_dragging: + c_opacity = lerp(c_opacity, d_opacity, delta * 30) + else: + c_opacity = lerp(c_opacity, d_opacity, delta * 3) + +func _draw(): + draw_rect(c_pos, _lerp_opacity(highlight_color, c_opacity), true) diff --git a/Scripts/Lib.gd b/Scripts/Lib.gd new file mode 100644 index 0000000..471c183 --- /dev/null +++ b/Scripts/Lib.gd @@ -0,0 +1,5 @@ +@tool +extends Node + +func _center_rect(rect: Rect2) -> Rect2: + return Rect2(rect.size / -2, rect.size) diff --git a/Scripts/Positional.gd b/Scripts/Positional.gd new file mode 100644 index 0000000..6e259ba --- /dev/null +++ b/Scripts/Positional.gd @@ -0,0 +1,87 @@ +@tool +extends Node2D +class_name Positional + +enum State { + CLEAN, + UPDATE_POSITION + # UPDATE_TILE_POS +} + +@export var quantize_in_editor = true: + set(val): + quantize_in_editor = val + _quantize() +@export var size: Vector2i = Vector2i.ONE: + set(val): + size = val + _quantize() + +# - - - - - - - - - - - - - - - - + +@onready var grid = Util.find(Grid) +var _worldbox: Rect2 = Rect2(0, 0, 0, 0) +var _tilebox: Rect2i = Rect2i(0, 0, 0, 0) + +var state: State = State.CLEAN +var has_adjusted_positions_this_frame = false + +func _ready(): + set_notify_transform(true) + _quantize() + +func _process(_delta): + has_adjusted_positions_this_frame = false + if state == State.UPDATE_POSITION: + _quantize() + +func _enter_tree(): + set_notify_transform(true) + if grid != null: + grid.register_tile(self) + +func _exit_tree(): + if grid != null: + grid.remove_tile(self) + +func _notification(what: int) -> void: + if what == NOTIFICATION_TRANSFORM_CHANGED: + _quantize() + +func _tile_pos_updated(): + pass + +func _size_updated(): + pass + +func _position_updated(): + pass + +func _quantize(): + if grid == null || !Engine.is_editor_hint() || !quantize_in_editor: + return + if has_adjusted_positions_this_frame: + state = State.UPDATE_POSITION + return + + var top_left_world_pos = position - grid.tile2world_size(size - Vector2i.ONE) / 2 + var tile_pos = grid.world2tile(top_left_world_pos) + _worldbox = grid.tile2world_rect(Rect2i(tile_pos, size)) + position = _worldbox.get_center() + state = State.CLEAN + has_adjusted_positions_this_frame = true + +func _derive(): + pass + +func _update_boxes(): + pass + +# func set_tile_pos(pos: Vector2i): +# pass + +func get_worldbox(): + return Rect2(_worldbox) + +func get_tilebox(): + return Rect2i(_tilebox) \ No newline at end of file diff --git a/Scripts/SelectionManager.gd b/Scripts/SelectionManager.gd new file mode 100644 index 0000000..e30dff6 --- /dev/null +++ b/Scripts/SelectionManager.gd @@ -0,0 +1,41 @@ +extends Node2D +class_name SelectionManager + +const SIGNAL_SELECTION_CHANGED = "selection-changed" + +@export var selected_box_hidden_color = Color(1, 1, 1, 0) +@export var selected_box_color = Color(1, 1, 1, 0.6) +@onready var selection: Array[Positional] = [] +var anim_t: float = 0.0 + +func _process(delta): + anim_t += delta * 8 + anim_t = clamp(anim_t, 0, 1) + queue_redraw() + +func clear(): + anim_t = 0 + selection = [] + emit_signal(SIGNAL_SELECTION_CHANGED) + print("clear selection ", len(selection)) + +func append_array(items: Array[Positional]): + selection.append_array(items) + emit_signal(SIGNAL_SELECTION_CHANGED) + +func select_array(items: Array[Positional]): + clear() + if items.is_empty(): + return + append_array(items) + print("selected ", len(selection)) + +func _draw(): + if selection.is_empty(): + return + for tile in selection: + draw_rect( + tile.worldbox.grow(lerp(-16, 0, anim_t)), + lerp(selected_box_hidden_color, selected_box_color, anim_t ** 1.3), + false, 3 + ) diff --git a/Scripts/TileRenderer.gd b/Scripts/TileRenderer.gd new file mode 100644 index 0000000..1b93b09 --- /dev/null +++ b/Scripts/TileRenderer.gd @@ -0,0 +1,30 @@ +@tool + +extends Node2D +class_name TileRenderer + +@export var target: Positional +@export var base_color: Color = Color.SPRING_GREEN +@export var top_color: Color = Color.WEB_GREEN + +var drawbox: Rect2 +var drawbox_top: Rect2 + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta): + queue_redraw() + +func _draw(): + draw_circle(Vector2.ZERO, 10, Color.ANTIQUE_WHITE) + + +func _update_drawbox(): + if not is_node_ready(): + return + # drawbox = _center_rect(_inset_rect(worldbox, 5)) + # drawbox_top = _center_rect(_inset_rect(worldbox, 7)) + # drawbox_top.size.y -= 8 \ No newline at end of file diff --git a/Scripts/Util.gd b/Scripts/Util.gd new file mode 100644 index 0000000..37830cf --- /dev/null +++ b/Scripts/Util.gd @@ -0,0 +1,25 @@ +@tool + +extends Node + +func find(type): + var root = null + if Engine.is_editor_hint(): + root = get_tree().edited_scene_root + else: + root = get_tree().root + + return _find_type_on_base(root, type) + +func _find_type_on_base(base: Node, type): + if is_instance_of(base, type): + return base + for child in base.get_children(): + var found_node = _find_type_on_base(child, type) + if found_node != null: + return found_node + return null + + +func is_debug(): + return true \ No newline at end of file diff --git a/Tiles/farm.tscn b/Tiles/farm.tscn new file mode 100644 index 0000000..69b86c9 --- /dev/null +++ b/Tiles/farm.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=3 format=3 uid="uid://cgygrdlj7ty6s"] + +[ext_resource type="Script" path="res://Scripts/Positional.gd" id="1_jjdrc"] +[ext_resource type="Script" path="res://Scripts/TileRenderer.gd" id="2_p4boj"] + +[node name="Farm" type="Node2D"] +script = ExtResource("1_jjdrc") + +[node name="TileRenderer" type="Node2D" parent="."] +script = ExtResource("2_p4boj") diff --git a/World.tscn b/World.tscn new file mode 100644 index 0000000..302d2fe --- /dev/null +++ b/World.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=5 format=3 uid="uid://co6md8v2b8hhu"] + +[ext_resource type="Script" path="res://Scripts/Grid.gd" id="1_2s04l"] +[ext_resource type="Script" path="res://Scripts/SelectionManager.gd" id="1_anesy"] +[ext_resource type="PackedScene" uid="uid://cgygrdlj7ty6s" path="res://Tiles/farm.tscn" id="2_7ixwv"] +[ext_resource type="Script" path="res://Scripts/Highlight.gd" id="3_62nr3"] + +[node name="Root" type="Node2D"] +position = Vector2(13, -2) + +[node name="Camera2D" type="Camera2D" parent="."] + +[node name="Grid" type="Node2D" parent="."] +script = ExtResource("1_2s04l") +cells = 32 +cell_size = 24 + +[node name="Farm" parent="Grid" instance=ExtResource("2_7ixwv")] +position = Vector2(204, -132) + +[node name="Farm2" parent="Grid" instance=ExtResource("2_7ixwv")] +position = Vector2(-108, -108) + +[node name="Farm3" parent="Grid" instance=ExtResource("2_7ixwv")] +position = Vector2(-12, -156) + +[node name="Highlight" type="Node2D" parent="."] +script = ExtResource("3_62nr3") +highlight_color = Color(1, 1, 1, 0.301961) + +[node name="SelectionManager" type="Node2D" parent="."] +script = ExtResource("1_anesy") diff --git a/addons/tile_helper/TileHelper.gd b/addons/tile_helper/TileHelper.gd new file mode 100644 index 0000000..7d874e3 --- /dev/null +++ b/addons/tile_helper/TileHelper.gd @@ -0,0 +1,13 @@ +@tool +extends EditorPlugin + +func _handles(object): + return object is Tile + +func _enter_tree(): + # Initialization of the plugin goes here. + pass + +func _exit_tree(): + # Clean-up of the plugin goes here. + pass diff --git a/addons/tile_helper/plugin.cfg b/addons/tile_helper/plugin.cfg new file mode 100644 index 0000000..111b378 --- /dev/null +++ b/addons/tile_helper/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Tile Helper" +description="" +author="" +version="" +script="TileHelper.gd" diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..b370ceb --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..85f2f06 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://txyjconsf31o" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..3b168cd --- /dev/null +++ b/project.godot @@ -0,0 +1,34 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="MobileGame" +run/main_scene="res://World.tscn" +config/features=PackedStringArray("4.2", "Mobile") +config/icon="res://icon.svg" + +[autoload] + +Util="*res://Scripts/Util.gd" + +[display] + +window/size/viewport_width=1600 +window/size/viewport_height=900 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/tile_helper/plugin.cfg") + +[rendering] + +renderer/rendering_method="mobile" +environment/defaults/default_clear_color=Color(0, 0, 0, 1)