commit c54bc5bf9bfbebd06d92ebee78999d4bc998f056 Author: wanp Date: Sun Sep 8 17:57:53 2024 -0300 initial commit diff --git a/gd/.gitignore b/gd/.gitignore new file mode 100644 index 0000000..d9aac21 --- /dev/null +++ b/gd/.gitignore @@ -0,0 +1,15 @@ +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json diff --git a/gd/assets/fonts/kenney-pixel.ttf b/gd/assets/fonts/kenney-pixel.ttf new file mode 100644 index 0000000..0020733 Binary files /dev/null and b/gd/assets/fonts/kenney-pixel.ttf differ diff --git a/gd/assets/fonts/kenney-pixel.ttf.import b/gd/assets/fonts/kenney-pixel.ttf.import new file mode 100644 index 0000000..21da73e --- /dev/null +++ b/gd/assets/fonts/kenney-pixel.ttf.import @@ -0,0 +1,39 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://y1lgw5ooyt4r" +path="res://.godot/imported/kenney-pixel.ttf-cfbdf074ccbd3e4253e0a9dc96ff7ecf.fontdata" + +[deps] + +source_file="res://assets/fonts/kenney-pixel.ttf" +dest_files=["res://.godot/imported/kenney-pixel.ttf-cfbdf074ccbd3e4253e0a9dc96ff7ecf.fontdata"] + +[params] + +Rendering=null +antialiasing=0 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0) +}] +language_support={} +script_support={} +opentype_features={} diff --git a/gd/assets/fonts/yoster.ttf b/gd/assets/fonts/yoster.ttf new file mode 100644 index 0000000..bbbeb51 Binary files /dev/null and b/gd/assets/fonts/yoster.ttf differ diff --git a/gd/assets/fonts/yoster.ttf.import b/gd/assets/fonts/yoster.ttf.import new file mode 100644 index 0000000..e6cf02a --- /dev/null +++ b/gd/assets/fonts/yoster.ttf.import @@ -0,0 +1,40 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://cj763w0tomglu" +path="res://.godot/imported/yoster.ttf-7cef9b7dce5ce91cdcc28263aa7977b8.fontdata" + +[deps] + +source_file="res://assets/fonts/yoster.ttf" +dest_files=["res://.godot/imported/yoster.ttf-7cef9b7dce5ce91cdcc28263aa7977b8.fontdata"] + +[params] + +Rendering=null +antialiasing=0 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0), +"variation_embolden": 0.0 +}] +language_support={} +script_support={} +opentype_features={} diff --git a/gd/assets/music/mod27_2.ogg b/gd/assets/music/mod27_2.ogg new file mode 100644 index 0000000..b540238 Binary files /dev/null and b/gd/assets/music/mod27_2.ogg differ diff --git a/gd/assets/music/mod27_2.ogg.import b/gd/assets/music/mod27_2.ogg.import new file mode 100644 index 0000000..5031ab4 --- /dev/null +++ b/gd/assets/music/mod27_2.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://bym65o117cs2m" +path="res://.godot/imported/mod27_2.ogg-4630225fd91daec63f201e096ccd9d65.oggvorbisstr" + +[deps] + +source_file="res://assets/music/mod27_2.ogg" +dest_files=["res://.godot/imported/mod27_2.ogg-4630225fd91daec63f201e096ccd9d65.oggvorbisstr"] + +[params] + +loop=true +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/gd/assets/music/sho-beat.ogg b/gd/assets/music/sho-beat.ogg new file mode 100644 index 0000000..c7bf0cc Binary files /dev/null and b/gd/assets/music/sho-beat.ogg differ diff --git a/gd/assets/music/sho-beat.ogg.import b/gd/assets/music/sho-beat.ogg.import new file mode 100644 index 0000000..30fc2bf --- /dev/null +++ b/gd/assets/music/sho-beat.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://d3r5mkwvjlxw7" +path="res://.godot/imported/sho-beat.ogg-afd8c5baf73cb191b247b303e8a7f650.oggvorbisstr" + +[deps] + +source_file="res://assets/music/sho-beat.ogg" +dest_files=["res://.godot/imported/sho-beat.ogg-afd8c5baf73cb191b247b303e8a7f650.oggvorbisstr"] + +[params] + +loop=true +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/gd/assets/music/sho.ogg b/gd/assets/music/sho.ogg new file mode 100644 index 0000000..08ce034 Binary files /dev/null and b/gd/assets/music/sho.ogg differ diff --git a/gd/assets/music/sho.ogg.import b/gd/assets/music/sho.ogg.import new file mode 100644 index 0000000..6f7d664 --- /dev/null +++ b/gd/assets/music/sho.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://hw8c84xk77tj" +path="res://.godot/imported/sho.ogg-f3829153b0715f844d46519ab2ce39db.oggvorbisstr" + +[deps] + +source_file="res://assets/music/sho.ogg" +dest_files=["res://.godot/imported/sho.ogg-f3829153b0715f844d46519ab2ce39db.oggvorbisstr"] + +[params] + +loop=true +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/gd/data/items/olive.tres b/gd/data/items/olive.tres new file mode 100644 index 0000000..48cd9ed --- /dev/null +++ b/gd/data/items/olive.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" script_class="Item" load_steps=3 format=3 uid="uid://n5vwskdrnk7y"] + +[ext_resource type="Texture2D" uid="uid://kl1g25keou1k" path="res://icon.svg" id="1_q6q32"] +[ext_resource type="Script" path="res://src/lib/item.gd" id="1_xy46h"] + +[resource] +script = ExtResource("1_xy46h") +icon = ExtResource("1_q6q32") +name = "Olive" +id = &"olive" +description = "It's supposed to look like that." +categories = 17 +intrinsic_value = 1 +weight = 1 +stackable = true +stack_size = 6 +spoilage_per_day = 0 +condition = 100 +states = 0 +amount = 1 diff --git a/gd/data/items/scented_hammer.tres b/gd/data/items/scented_hammer.tres new file mode 100644 index 0000000..d96af24 --- /dev/null +++ b/gd/data/items/scented_hammer.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" script_class="Item" load_steps=3 format=3 uid="uid://cy6581ixduqti"] + +[ext_resource type="Texture2D" uid="uid://kl1g25keou1k" path="res://icon.svg" id="1_ix6ik"] +[ext_resource type="Script" path="res://src/lib/item.gd" id="2_wiywl"] + +[resource] +script = ExtResource("2_wiywl") +icon = ExtResource("1_ix6ik") +name = "Scented Hammer" +id = &"scented_hammer" +description = "This is a great idea." +categories = 2 +intrinsic_value = 15 +weight = 20 +stackable = false +stack_size = 6 +spoilage_per_day = 0 +condition = 100 +states = 0 +amount = 1 diff --git a/gd/data/towns/goldthorn.tres b/gd/data/towns/goldthorn.tres new file mode 100644 index 0000000..2cce895 --- /dev/null +++ b/gd/data/towns/goldthorn.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" script_class="Town" load_steps=3 format=3 uid="uid://fr2sgjoprkgb"] + +[ext_resource type="Script" path="res://src/lib/town.gd" id="1_l2yn4"] +[ext_resource type="Resource" uid="uid://clvys3lr00hgd" path="res://data/towns/salmonmound.tres" id="2_qqgee"] + +[resource] +script = ExtResource("1_l2yn4") +name = "Goldthorn" +size = 0 +status = 0 +workshops = 0 +neighboring_towns = Array[ExtResource("1_l2yn4")]([ExtResource("2_qqgee")]) +has_dimension_gate = false +has_water = true +possible_npcs = Array[PackedScene]([]) diff --git a/gd/data/towns/salmonmound.tres b/gd/data/towns/salmonmound.tres new file mode 100644 index 0000000..815be8e --- /dev/null +++ b/gd/data/towns/salmonmound.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="Town" load_steps=2 format=3 uid="uid://clvys3lr00hgd"] + +[ext_resource type="Script" path="res://src/lib/town.gd" id="1_axw15"] + +[resource] +script = ExtResource("1_axw15") +name = "Salmonmound" +size = 1 +status = 1 +workshops = 1 +neighboring_towns = Array[ExtResource("1_axw15")]([]) +has_dimension_gate = false +has_water = true +possible_npcs = Array[PackedScene]([]) diff --git a/gd/icon.svg b/gd/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/gd/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gd/icon.svg.import b/gd/icon.svg.import new file mode 100644 index 0000000..31a2b01 --- /dev/null +++ b/gd/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kl1g25keou1k" +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=0 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/gd/project.godot b/gd/project.godot new file mode 100644 index 0000000..d563ecc --- /dev/null +++ b/gd/project.godot @@ -0,0 +1,93 @@ +; 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="Salesman" +run/main_scene="res://src/main.tscn" +config/features=PackedStringArray("4.3", "GL Compatibility") +boot_splash/bg_color=Color(0, 0, 0, 1) +boot_splash/show_image=false +config/icon="res://icon.svg" + +[autoload] + +MainEvents="*res://src/main_events.gd" + +[debug] + +gdscript/warnings/unused_private_class_variable=0 +gdscript/warnings/unused_signal=0 + +[display] + +window/size/viewport_width=640 +window/size/viewport_height=360 +window/size/window_width_override=1280 +window/size/window_height_override=720 +window/stretch/mode="viewport" +window/stretch/aspect="expand" + +[file_customization] + +folder_colors={ +"res://assets/": "green", +"res://data/": "blue", +"res://src/": "red" +} + +[input] + +toggle_fullscreen={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194342,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_forward={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +move_backward={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +strafe_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +strafe_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +interact={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +] +} + +[layer_names] + +3d_physics/layer_1="solid" + +[physics] + +common/physics_jitter_fix=0.0 +common/physics_interpolation=true + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/gd/src/config.gd b/gd/src/config.gd new file mode 100644 index 0000000..6fa09a1 --- /dev/null +++ b/gd/src/config.gd @@ -0,0 +1,20 @@ +## Holds game constants and variable settings. +class_name Config +extends Resource + +const ITEM_DB: Array[Item] = [ + preload("res://data/items/olive.tres"), + preload("res://data/items/scented_hammer.tres"), +] +const START_ITEMS = 8 +const START_TOWN = preload("res://data/towns/goldthorn.tres") + +static var instance := Config.new() + + +static func save() -> void: + ResourceSaver.save(instance, "user://config.tres") + + +static func fetch() -> Config: + return instance diff --git a/gd/src/game_state.gd b/gd/src/game_state.gd new file mode 100644 index 0000000..51dea64 --- /dev/null +++ b/gd/src/game_state.gd @@ -0,0 +1,24 @@ +class_name GameState +extends Resource + +# state +@export var player_inventory := PlayerInventory.new() +@export var town: Town = preload("res://data/towns/goldthorn.tres") +@export var day := 1 +@export var day_progress := 0 # 0 to 100 + +# overworld +@export var is_player_input_disabled := false + +# trade +@export var trade_npc_data: Script + +static var instance := GameState.new() + + +static func save() -> void: + ResourceSaver.save(instance, "user://game_state.tres") + + +static func fetch() -> GameState: + return instance diff --git a/gd/src/lib/c_interactable.gd b/gd/src/lib/c_interactable.gd new file mode 100644 index 0000000..23ffe63 --- /dev/null +++ b/gd/src/lib/c_interactable.gd @@ -0,0 +1,9 @@ +class_name CInteractable +extends Node + + +signal interacted(source: Node) + + +func interact(source: Node) -> void: + interacted.emit(source) diff --git a/gd/src/lib/components.gd b/gd/src/lib/components.gd new file mode 100644 index 0000000..c47f6a8 --- /dev/null +++ b/gd/src/lib/components.gd @@ -0,0 +1,41 @@ +class_name Components +extends Node +## Provides a set of methods to easily access component nodes externally. +## +## The owner of this node should have a variable named [code]components[/code] +## containing a reference to it. [br] +## This API does require component scripts to have global names (via [code]class_name[/code]). + +var _owned_cache := {} + + +func _ready() -> void: + child_order_changed.connect(_clear_cache) + + +## Returns the first component child node of the specified class. +func get_component(name_of_class: StringName) -> Node: + if name_of_class in _owned_cache: + return _owned_cache[name_of_class] + + var component := find_child(name_of_class + "*", true, false) + if component == null: + return null + + _owned_cache[name_of_class] = component + return component + + +## Returns all component child nodes of the specified class. +func get_components(name_of_class: StringName) -> Array[Node]: + return find_children(name_of_class + "*", "", true, false) + + +## Returns the value of a node's [code]components[/code] variable. +## Mainly exists to avoid leaving strings everywhere. +static func get_from(node: Node) -> Components: + return node.get("components") + + +func _clear_cache() -> void: + _owned_cache.clear() diff --git a/gd/src/lib/item.gd b/gd/src/lib/item.gd new file mode 100644 index 0000000..08646d1 --- /dev/null +++ b/gd/src/lib/item.gd @@ -0,0 +1,139 @@ +class_name Item +extends Resource + + +enum Category { + CATEGORY_NONE = 0, + CATEGORY_FOOD = 1 << 0, + CATEGORY_TOOL = 1 << 1, + CATEGORY_PAPER = 1 << 2, + CATEGORY_GEM = 1 << 3, + CATEGORY_CURRENCY = 1 << 4, + CATEGORY_CONTAINER = 1 << 5, + CATEGORY_MAGIC = 1 << 6, + CATEGORY_NUM = 1 << 7, +} + +enum State { + STATE_NONE = 0, + STATE_CURSED = 1 << 0, + STATE_BANNED = 1 << 1, + STATE_WET = 1 << 2, + STATE_NUM = 1 << 3, +} + +@export var icon: Texture2D = preload("res://icon.svg") +@export var name := "Static" +@export var id := &"static" +@export_multiline var description := "Your eyes begin to mist over." +@export_flags( + "Food", + "Tool", + "Paper", + "Gem", + "Currency", + "Container", + "Magic", +) var categories := 0 +@export_range(0, 255) var intrinsic_value := 0 + +@export var weight := 0 +@export var stackable := true +@export var stack_size := 6 + +## Affects condition of food items +@export var spoilage_per_day := 0 + +@export var condition := 100 # 0 (broken) to 100 (spotless) +@export var states := 0 +@export var amount := 1 # increased when stacked + + +func _init() -> void: + resource_name = id + + +func category_to_string(category: Category) -> String: + match category: + Category.CATEGORY_NONE: + return "none" + Category.CATEGORY_FOOD: + return "food" + Category.CATEGORY_TOOL: + return "tool" + Category.CATEGORY_PAPER: + return "paper" + Category.CATEGORY_GEM: + return "gem" + Category.CATEGORY_CURRENCY: + return "currency" + Category.CATEGORY_CONTAINER: + return "container" + Category.CATEGORY_MAGIC: + return "magic" + _: + assert(false, "tried to get string representation of invalid category bit") + return "bugged" + + +func category_list(capitalize := true, add_commas := true) -> String: + var strings: PackedStringArray + + var current_bit: int = Category.CATEGORY_NUM + while current_bit > Category.CATEGORY_NONE: + if categories & current_bit: + strings.push_back(category_to_string(current_bit)) + + current_bit >>= 1 + + if capitalize: + strings[0] = strings[0].to_pascal_case() + + if add_commas: + return ", ".join(strings) + else: + return " ".join(strings) + + +func state_to_string(state: State) -> String: + match state: + State.STATE_NONE: + return "none" + State.STATE_CURSED: + return "cursed" + State.STATE_BANNED: + return "banned" + State.STATE_WET: + return "wet" + _: + assert(false, "tried to get string representation of invalid state bit") + return "bugged" + + +func state_list(capitalize := true, add_commas := true) -> String: + var strings: PackedStringArray + + var current_bit: int = State.STATE_NUM + while current_bit > State.STATE_NONE: + if states & current_bit: + strings.push_back(state_to_string(current_bit)) + + current_bit >>= 1 + + if capitalize: + strings[0] = strings[0].to_pascal_case() + + if add_commas: + return ", ".join(strings) + else: + return " ".join(strings) + + +func worsen_condition(by: int) -> void: + condition -= by + condition = clamp(condition, 0, 100) + + +func improve_condition(by: int) -> void: + condition += by + condition = clamp(condition, 0, 100) diff --git a/gd/src/lib/item_box.gd b/gd/src/lib/item_box.gd new file mode 100644 index 0000000..39c8590 --- /dev/null +++ b/gd/src/lib/item_box.gd @@ -0,0 +1,32 @@ +extends MarginContainer + + +@export var highlight_panel: PanelContainer +@export var icon: TextureRect +@export var name_label: RichTextLabel +@export var amount_label: Label + + +func update_from_item(item: Item, show_amount: bool) -> void: + icon.texture = item.icon + name_label.text = "[center]%s[/center]" % item.name + + if item.stackable and show_amount: + amount_label.text = "x%d" % item.amount + else: + amount_label.text = "" + + +func _on_focus_entered() -> void: + const PANEL = preload("res://src/lib/item_box_highlight_panel.tres") + highlight_panel.add_theme_stylebox_override("panel", PANEL) + + +func _on_focus_exited() -> void: + highlight_panel.add_theme_stylebox_override("panel", StyleBoxEmpty.new()) + + +func _on_gui_input(event: InputEvent) -> void: + # ignore mouse clicks for focus, it's only for mouseless gameplay + if event is InputEventMouseButton: + release_focus() diff --git a/gd/src/lib/item_box.tscn b/gd/src/lib/item_box.tscn new file mode 100644 index 0000000..8cddceb --- /dev/null +++ b/gd/src/lib/item_box.tscn @@ -0,0 +1,68 @@ +[gd_scene load_steps=5 format=3 uid="uid://bdj734elapmqo"] + +[ext_resource type="Script" path="res://src/lib/item_box.gd" id="1_i5r36"] +[ext_resource type="Texture2D" uid="uid://kl1g25keou1k" path="res://icon.svg" id="1_knk66"] +[ext_resource type="FontFile" uid="uid://y1lgw5ooyt4r" path="res://assets/fonts/kenney-pixel.ttf" id="3_nm7hh"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_nhqyb"] + +[node name="ItemBox" type="MarginContainer" node_paths=PackedStringArray("highlight_panel", "icon", "name_label", "amount_label")] +anchors_preset = -1 +anchor_right = 0.0625 +anchor_bottom = 0.155556 +focus_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 +script = ExtResource("1_i5r36") +highlight_panel = NodePath("HighlightPanel") +icon = NodePath("HighlightPanel/VBoxContainer/Icon") +name_label = NodePath("HighlightPanel/VBoxContainer/Name") +amount_label = NodePath("HighlightPanel/VBoxContainer/Icon/Amount") +metadata/_edit_use_anchors_ = true + +[node name="HighlightPanel" type="PanelContainer" parent="."] +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxEmpty_nhqyb") + +[node name="VBoxContainer" type="VBoxContainer" parent="HighlightPanel"] +layout_mode = 2 + +[node name="Icon" type="TextureRect" parent="HighlightPanel/VBoxContainer"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +texture = ExtResource("1_knk66") +expand_mode = 1 +stretch_mode = 5 + +[node name="Amount" type="Label" parent="HighlightPanel/VBoxContainer/Icon"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -64.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +text = "x0" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="Name" type="RichTextLabel" parent="HighlightPanel/VBoxContainer"] +clip_contents = false +layout_mode = 2 +mouse_filter = 2 +theme_override_fonts/normal_font = ExtResource("3_nm7hh") +theme_override_font_sizes/normal_font_size = 16 +bbcode_enabled = true +text = "[center]Static[/center]" +fit_content = true +autowrap_mode = 2 + +[connection signal="focus_entered" from="." to="." method="_on_focus_entered"] +[connection signal="focus_exited" from="." to="." method="_on_focus_exited"] +[connection signal="gui_input" from="." to="." method="_on_gui_input"] diff --git a/gd/src/lib/item_box_highlight_panel.tres b/gd/src/lib/item_box_highlight_panel.tres new file mode 100644 index 0000000..03e8b93 --- /dev/null +++ b/gd/src/lib/item_box_highlight_panel.tres @@ -0,0 +1,14 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://drutubv0yxq37"] + +[resource] +bg_color = Color(1, 0.788235, 0.160784, 1) +border_color = Color(1, 0.788235, 0.160784, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 +anti_aliasing = false diff --git a/gd/src/lib/player_inventory.gd b/gd/src/lib/player_inventory.gd new file mode 100644 index 0000000..b7800fa --- /dev/null +++ b/gd/src/lib/player_inventory.gd @@ -0,0 +1,62 @@ +class_name PlayerInventory +extends Resource + + +@export var max_weight := 100 +@export var items: Array[Item] + + +# calculating it every time is less bug-prone than keeping track of it +func total_weight() -> int: + return items.reduce(_item_weight_sum, 0) + + +func add_item(new_item: Item) -> bool: + # can't carry this + # TODO: overburdening instead of this + if (total_weight() + new_item.weight) > max_weight: + return false + + # stacking is done purely automatically for now + var items_like_this: Array[Item] = items.filter(_filter_same_items.bind(new_item)) + items_like_this.sort_custom(_sort_amount_descending) + + var stacked := false + for item in items_like_this: + if not item.stackable or item.amount == item.stack_size: + continue + item.amount += 1 + stacked = true + break + if not stacked: + items.push_back(new_item) + + return true + + +func remove_item(item: Item) -> void: + var items_like_this: Array[Item] = items.filter(_filter_same_items.bind(item)) + var items_sorted: Array[Item] = items_like_this.duplicate() + items_sorted.sort_custom(_sort_amount_ascending) + var least := items_sorted[0] + + if not least.stackable or least.amount == 1: + items.erase(least) + else: + least.amount -= 1 + + +func _sort_amount_ascending(a: Item, b: Item) -> bool: + return a.amount < b.amount + + +func _sort_amount_descending(a: Item, b: Item) -> bool: + return a.amount > b.amount + + +func _item_weight_sum(accum: int, item: Item) -> int: + return accum + item.weight + + +func _filter_same_items(item: Item, original: Item) -> bool: + return item.id == original.id diff --git a/gd/src/lib/town.gd b/gd/src/lib/town.gd new file mode 100644 index 0000000..0bd78c1 --- /dev/null +++ b/gd/src/lib/town.gd @@ -0,0 +1,83 @@ +class_name Town +extends Resource + + +enum Status { + STATUS_POOR, + STATUS_WEALTHY, +} + +enum Size { + SIZE_SMALL, + SIZE_MEDIUM, + SIZE_LARGE, +} + +enum Workshop { + WORKSHOP_NONE = 0, + WORKSHOP_BLACKSMITH = 1 << 0, + WORKSHOP_NUM = 1 << 1, +} + +@export var name := "Nonamey" +@export var size := Size.SIZE_SMALL +@export var status := Status.STATUS_POOR +@export_flags("Blacksmith") var workshops := 0 +@export var neighboring_towns: Array[Town] +@export var has_dimension_gate := false +@export var has_water := false +@export var possible_npcs: Array[PackedScene] + + +func status_to_string() -> String: + match status: + Status.STATUS_POOR: + return "poor" + Status.STATUS_WEALTHY: + return "wealthy" + _: + assert(false, "tried to get string representation of invalid status") + return "stupid" + + +func size_to_string() -> String: + match size: + Size.SIZE_SMALL: + return "small" + Size.SIZE_MEDIUM: + return "medium" + Size.SIZE_LARGE: + return "large" + _: + assert(false, "tried to get string representation of invalid size") + return "microscopic" + + +func workshop_to_string(workshop: Workshop) -> String: + match workshop: + Workshop.WORKSHOP_NONE: + return "none" + Workshop.WORKSHOP_BLACKSMITH: + return "blacksmith" + _: + assert(false, "tried to get string representation of invalid workshop bit") + return "buggyworld" + + +func workshop_list(capitalize := true, add_commas := true) -> String: + var strings: PackedStringArray + + var current_bit: int = Workshop.WORKSHOP_NUM + while current_bit > Workshop.WORKSHOP_NONE: + if workshops & current_bit: + strings.push_back(workshop_to_string(current_bit)) + + current_bit >>= 1 + + if capitalize: + strings[0] = strings[0].to_pascal_case() + + if add_commas: + return ", ".join(strings) + else: + return " ".join(strings) diff --git a/gd/src/main.gd b/gd/src/main.gd new file mode 100644 index 0000000..d49f6a4 --- /dev/null +++ b/gd/src/main.gd @@ -0,0 +1,42 @@ +extends Node + + +# just to keep these resources loaded +# also makes them visible in the inspector while debugging +var _config: Config +var _game_state: GameState + +var _current_scene: Node +var _kept_scenes := {} + + +func _ready() -> void: + MainEvents.scene_change_requested.connect(_change_scene_to) + _current_scene = get_child(0) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("toggle_fullscreen"): + match DisplayServer.window_get_mode(): + DisplayServer.WINDOW_MODE_WINDOWED, DisplayServer.WINDOW_MODE_MAXIMIZED: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + DisplayServer.WINDOW_MODE_FULLSCREEN: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + + +func _change_scene_to(path: String, keep_state := false) -> void: + if keep_state: + _kept_scenes[_current_scene.scene_file_path] = _current_scene + remove_child(_current_scene) + _current_scene.process_mode = Node.PROCESS_MODE_DISABLED + else: + _current_scene.queue_free() + + if _kept_scenes.has(path): + _current_scene = _kept_scenes[path] + _current_scene.process_mode = Node.PROCESS_MODE_INHERIT + _kept_scenes.erase(path) + else: + _current_scene = load(path).instantiate() + + add_child(_current_scene) diff --git a/gd/src/main.tscn b/gd/src/main.tscn new file mode 100644 index 0000000..8760ff5 --- /dev/null +++ b/gd/src/main.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=3 format=3 uid="uid://b8lr3osatj5nf"] + +[ext_resource type="Script" path="res://src/main.gd" id="1_dhrc8"] +[ext_resource type="PackedScene" uid="uid://bjncpymja76aq" path="res://src/town_preview/town_preview.tscn" id="1_t1je0"] + +[node name="Main" type="Node"] +script = ExtResource("1_dhrc8") + +[node name="TownPreview" parent="." instance=ExtResource("1_t1je0")] diff --git a/gd/src/main_events.gd b/gd/src/main_events.gd new file mode 100644 index 0000000..83606d2 --- /dev/null +++ b/gd/src/main_events.gd @@ -0,0 +1,4 @@ +extends Node + + +signal scene_change_requested(path: String, keep_state: bool) diff --git a/gd/src/menus.tres b/gd/src/menus.tres new file mode 100644 index 0000000..e490b26 --- /dev/null +++ b/gd/src/menus.tres @@ -0,0 +1,202 @@ +[gd_resource type="Theme" load_steps=14 format=3 uid="uid://bdofl7w84udo"] + +[ext_resource type="FontFile" uid="uid://cj763w0tomglu" path="res://assets/fonts/yoster.ttf" id="1_fbdab"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mwwaj"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.55991, 0.360189, 0.00315228, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0h4mt"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(1, 1, 1, 0.75) +draw_center = false +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.79, 0.16, 1) +corner_radius_top_left = 9 +corner_radius_top_right = 9 +corner_radius_bottom_right = 9 +corner_radius_bottom_left = 9 +corner_detail = 5 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3khep"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.99, 0.646965, 0.00989999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_aoy2d"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.82, 0.53587, 0.00819999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3mp6b"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.65, 0.424775, 0.00649999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8v4pg"] +bg_color = Color(0.82, 0.31939, 0.00819999, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.52, 0.20254, 0.00519999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_b4lvu"] +bg_color = Color(0.82, 0.31939, 0.00819999, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.52, 0.20254, 0.00519999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y8jfj"] +content_margin_left = 8.0 +content_margin_top = 2.0 +content_margin_right = 8.0 +content_margin_bottom = 2.0 +bg_color = Color(0, 0, 0, 0.878431) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uaq2d"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.84, 0.4242, 0.00839999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ifoxb"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(1, 0.505, 0.00999999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mo7lx"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.74, 0.3737, 0.00739999, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 5 +anti_aliasing = false + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dtw8x"] +content_margin_left = 4.0 +content_margin_top = 0.0 +content_margin_right = 4.0 +content_margin_bottom = 0.0 +bg_color = Color(0.42, 0.182, 0, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 +corner_detail = 6 +anti_aliasing = false + +[resource] +Button/colors/font_color = Color(1, 1, 1, 1) +Button/colors/font_hover_pressed_color = Color(1, 1, 1, 1) +Button/font_sizes/font_size = 24 +Button/fonts/font = ExtResource("1_fbdab") +Button/styles/disabled = SubResource("StyleBoxFlat_mwwaj") +Button/styles/focus = SubResource("StyleBoxFlat_0h4mt") +Button/styles/hover = SubResource("StyleBoxFlat_3khep") +Button/styles/normal = SubResource("StyleBoxFlat_aoy2d") +Button/styles/pressed = SubResource("StyleBoxFlat_3mp6b") +Label/colors/font_color = Color(1, 1, 1, 1) +Label/font_sizes/font_size = 12 +Label/fonts/font = ExtResource("1_fbdab") +Panel/styles/panel = SubResource("StyleBoxFlat_8v4pg") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_b4lvu") +RichTextLabel/colors/default_color = Color(1, 1, 1, 1) +RichTextLabel/constants/line_separation = 0 +RichTextLabel/font_sizes/bold_font_size = 24 +RichTextLabel/font_sizes/bold_italics_font_size = 24 +RichTextLabel/font_sizes/italics_font_size = 24 +RichTextLabel/font_sizes/mono_font_size = 24 +RichTextLabel/font_sizes/normal_font_size = 24 +RichTextLabel/fonts/bold_font = ExtResource("1_fbdab") +RichTextLabel/fonts/bold_italics_font = ExtResource("1_fbdab") +RichTextLabel/fonts/italics_font = ExtResource("1_fbdab") +RichTextLabel/fonts/mono_font = ExtResource("1_fbdab") +RichTextLabel/fonts/normal_font = ExtResource("1_fbdab") +TooltipPanel/styles/panel = SubResource("StyleBoxFlat_y8jfj") +VScrollBar/styles/grabber = SubResource("StyleBoxFlat_uaq2d") +VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_ifoxb") +VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_mo7lx") +VScrollBar/styles/scroll = SubResource("StyleBoxFlat_dtw8x") +VScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_0h4mt") diff --git a/gd/src/overworld/npc_overworld.gd b/gd/src/overworld/npc_overworld.gd new file mode 100644 index 0000000..76b4759 --- /dev/null +++ b/gd/src/overworld/npc_overworld.gd @@ -0,0 +1,27 @@ +extends CharacterBody3D + + +const _SPEED = 5.0 + +@export var components: Components +@export var _npc_data: Script + + +func _physics_process(delta: float) -> void: + if not is_on_floor(): + velocity += get_gravity() * delta + + var dir := Vector2.ZERO + var direction := (transform.basis * Vector3(dir.x, 0, dir.y)).normalized() + if direction: + velocity.x = direction.x * _SPEED + velocity.z = direction.z * _SPEED + else: + velocity.x = move_toward(velocity.x, 0, _SPEED) + velocity.z = move_toward(velocity.z, 0, _SPEED) + + move_and_slide() + + +func _on_c_interactable_interacted(_source: Node) -> void: + get_parent().start_trade(_npc_data) diff --git a/gd/src/overworld/npc_overworld.tscn b/gd/src/overworld/npc_overworld.tscn new file mode 100644 index 0000000..f0bb14c --- /dev/null +++ b/gd/src/overworld/npc_overworld.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=6 format=3 uid="uid://dfwxneq7cu3su"] + +[ext_resource type="Script" path="res://src/overworld/npc_overworld.gd" id="1_glln8"] +[ext_resource type="Script" path="res://src/lib/components.gd" id="1_uslm8"] +[ext_resource type="Script" path="res://src/lib/c_interactable.gd" id="2_yding"] +[ext_resource type="Texture2D" uid="uid://kl1g25keou1k" path="res://icon.svg" id="4_xkp6b"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_f2rq4"] + +[node name="NpcOverworld" type="CharacterBody3D" node_paths=PackedStringArray("components")] +script = ExtResource("1_glln8") +components = NodePath("Components") + +[node name="Components" type="Node" parent="."] +script = ExtResource("1_uslm8") + +[node name="CInteractable" type="Node" parent="Components"] +script = ExtResource("2_yding") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("CapsuleShape3D_f2rq4") + +[node name="Sprite3D" type="Sprite3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.4, 0) +billboard = 1 +shaded = true +texture = ExtResource("4_xkp6b") + +[connection signal="interacted" from="Components/CInteractable" to="." method="_on_c_interactable_interacted"] diff --git a/gd/src/overworld/overworld.gd b/gd/src/overworld/overworld.gd new file mode 100644 index 0000000..e9ab9b8 --- /dev/null +++ b/gd/src/overworld/overworld.gd @@ -0,0 +1,34 @@ +extends Node3D + + +const _Player = preload("res://src/overworld/player.gd") + +@export var _interact_prompt_label: RichTextLabel +@export var _day_label: RichTextLabel +@export var _fade_rect: ColorRect +@export var _player: _Player + + +func _enter_tree() -> void: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + _fade_rect.show() + create_tween().tween_property(_fade_rect, "color:a", 0, 0.2) + + +func _process(_delta: float) -> void: + _interact_prompt_label.visible = is_instance_valid(_player.interact_target) + _day_label.text = "[right]Day %d[/right]" % GameState.fetch().day + + +func start_trade(npc_data: Script) -> void: + GameState.fetch().is_player_input_disabled = true + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + + var fade_tween := create_tween() + fade_tween.tween_property(_fade_rect, "color:a", 1, 0.2) + await fade_tween.finished + await get_tree().create_timer(0.1).timeout + + GameState.fetch().trade_npc_data = npc_data + MainEvents.scene_change_requested.emit("res://src/trade/trade.tscn", true) + GameState.fetch().is_player_input_disabled = false diff --git a/gd/src/overworld/overworld.tscn b/gd/src/overworld/overworld.tscn new file mode 100644 index 0000000..12f003d --- /dev/null +++ b/gd/src/overworld/overworld.tscn @@ -0,0 +1,73 @@ +[gd_scene load_steps=5 format=3 uid="uid://jwo1q2q8lsyp"] + +[ext_resource type="PackedScene" uid="uid://caydb1odthjag" path="res://src/overworld/player.tscn" id="1_rtats"] +[ext_resource type="Script" path="res://src/overworld/overworld.gd" id="1_wo1un"] +[ext_resource type="PackedScene" uid="uid://dfwxneq7cu3su" path="res://src/overworld/npc_overworld.tscn" id="2_st2f2"] +[ext_resource type="Script" path="res://src/trade/npcs/bloobert.gd" id="4_752ky"] + +[node name="Overworld" type="Node3D" node_paths=PackedStringArray("_interact_prompt_label", "_day_label", "_fade_rect", "_player")] +script = ExtResource("1_wo1un") +_interact_prompt_label = NodePath("UI/InteractPrompt") +_day_label = NodePath("UI/DayLabel") +_fade_rect = NodePath("UI/FadeRect") +_player = NodePath("Player") + +[node name="UI" type="CanvasLayer" parent="."] + +[node name="InteractPrompt" type="RichTextLabel" parent="UI"] +visible = false +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -172.0 +offset_right = -104.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "Press E to interact! Hahah!" +fit_content = true +autowrap_mode = 0 + +[node name="DayLabel" type="RichTextLabel" parent="UI"] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 104.0 +offset_top = -172.0 +offset_right = 312.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "[right]Day 0[/right]" +fit_content = true +autowrap_mode = 0 + +[node name="FadeRect" type="ColorRect" parent="UI"] +visible = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) + +[node name="Player" parent="." instance=ExtResource("1_rtats")] + +[node name="CSGBox3D" type="CSGBox3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, -1, -1) +use_collision = true +size = Vector3(31, 1, 45) + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.307905, 0.951417, 0, -0.951417, 0.307905, 0, 0, 0) + +[node name="NpcOverworld" parent="." instance=ExtResource("2_st2f2")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.99024, 0, -7.04422) +_npc_data = ExtResource("4_752ky") diff --git a/gd/src/overworld/player.gd b/gd/src/overworld/player.gd new file mode 100644 index 0000000..9c1b83f --- /dev/null +++ b/gd/src/overworld/player.gd @@ -0,0 +1,109 @@ +extends CharacterBody3D + + +const _BOB_FREQUENCY = 1.5 +const _BOB_AMPLITUDE = 0.1 + +@export var _pivot: Node3D +@export var _camera: Camera3D + +var interact_target: Node3D + +var _gravity := -30 +var _max_speed := 12 +var _mouse_sensitivity := 0.008 # radians/pixel +var _time_bob = 0.0 + +var _interact_pool: Array[Node3D] + + +func _ready() -> void: + pass + + +func _unhandled_input(event: InputEvent) -> void: + if GameState.fetch().is_player_input_disabled: + return + + if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + rotate_y(-event.relative.x * _mouse_sensitivity) + #pivot.rotate_x(-event.relative.y * mouse_sensitivity) + #pivot.rotation.x = clamp(pivot.rotation.x, -1.2, 1.2) + + if event.is_action_pressed("interact") and is_instance_valid(interact_target): + var comps := Components.get_from(interact_target) + var c_interactable := comps.get_component("CInteractable") as CInteractable + c_interactable.interact(self) + + +func _process(delta: float) -> void: + _apply_view_bobbing(delta) + _compute_interact_target() + + +func _physics_process(delta: float) -> void: + velocity.y += _gravity * delta + var desired_velocity := _get_movement_input() * _max_speed + + velocity.x = desired_velocity.x + velocity.z = desired_velocity.z + move_and_slide() + + +func _get_movement_input() -> Vector3: + if GameState.fetch().is_player_input_disabled: + return Vector3.ZERO + + var input_dir := Vector3() + + # desired move in camera direction + if Input.is_action_pressed("move_forward"): + input_dir += -global_transform.basis.z + if Input.is_action_pressed("move_backward"): + input_dir += global_transform.basis.z + if Input.is_action_pressed("strafe_left"): + input_dir += -global_transform.basis.x + if Input.is_action_pressed("strafe_right"): + input_dir += global_transform.basis.x + + return input_dir.normalized() + + +func _apply_view_bobbing(delta: float) -> void: + _time_bob += delta * velocity.length() * float(is_on_floor()) + _camera.position.y = sin(_time_bob * _BOB_FREQUENCY) * _BOB_AMPLITUDE + _camera.position.x = cos(_time_bob * (_BOB_FREQUENCY / 2)) * _BOB_AMPLITUDE + + +func _compute_interact_target() -> void: + interact_target = _compute_nearest(_interact_pool) + + +func _compute_nearest(things: Array[Node3D]) -> Node3D: + var lowest_distance := INF + var closest: Node3D + + for thing in things: + var distance := global_position.distance_squared_to(thing.global_position) + + if distance < lowest_distance: + closest = thing + lowest_distance = distance + + return closest + + +func _on_interact_area_body_entered(body: Node3D) -> void: + var comps := Components.get_from(body) + if comps == null: + return + + var c_interactable := comps.get_component("CInteractable") as CInteractable + if c_interactable == null: + return + + _interact_pool.push_back(body) + + +func _on_interact_area_body_exited(body: Node3D) -> void: + _interact_pool.erase(body) diff --git a/gd/src/overworld/player.tscn b/gd/src/overworld/player.tscn new file mode 100644 index 0000000..9466a8f --- /dev/null +++ b/gd/src/overworld/player.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=4 format=3 uid="uid://caydb1odthjag"] + +[ext_resource type="Script" path="res://src/overworld/player.gd" id="1_uppih"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cecbr"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_hhbuh"] +radius = 1.5 + +[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("_pivot", "_camera")] +script = ExtResource("1_uppih") +_pivot = NodePath("Pivot") +_camera = NodePath("Pivot/Camera3D") + +[node name="Pivot" type="Node3D" parent="."] + +[node name="Camera3D" type="Camera3D" parent="Pivot"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("CapsuleShape3D_cecbr") + +[node name="InteractArea" type="Area3D" parent="."] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractArea"] +shape = SubResource("SphereShape3D_hhbuh") + +[connection signal="body_entered" from="InteractArea" to="." method="_on_interact_area_body_entered"] +[connection signal="body_exited" from="InteractArea" to="." method="_on_interact_area_body_exited"] diff --git a/gd/src/town_preview/town_preview.gd b/gd/src/town_preview/town_preview.gd new file mode 100644 index 0000000..f41ee02 --- /dev/null +++ b/gd/src/town_preview/town_preview.gd @@ -0,0 +1,93 @@ +extends Node2D + + +@export var _town_label: RichTextLabel +@export var _items: HFlowContainer +@export var _towns: VBoxContainer +@export var _weight_label: RichTextLabel +@export var _description_box_label: RichTextLabel +@export var _fade_rect: ColorRect + + +func _ready() -> void: + _fade_rect.show() + create_tween().tween_property(_fade_rect, "color:a", 0, 0.2) + + for i in range(Config.START_ITEMS): + var item: Item = Config.ITEM_DB.pick_random() + GameState.fetch().player_inventory.add_item(item.duplicate()) + + _weight_label.text = ( + "%d/%d" % [ + GameState.fetch().player_inventory.total_weight(), + GameState.fetch().player_inventory.max_weight + ] + ) + + for item in GameState.fetch().player_inventory.items: + const ITEM_BOX_SCENE = preload("res://src/lib/item_box.tscn") + const ItemBox = preload("res://src/lib/item_box.gd") + + var new_node: ItemBox = ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, true) + + new_node.gui_input.connect(_on_item_gui_input.bind(item)) + new_node.focus_entered.connect(_display_item_info.bind(item)) + + _items.add_child(new_node) + + _set_town(Config.START_TOWN) + var towns_to_add: Array[Town] + towns_to_add.push_back(GameState.fetch().town) + towns_to_add.append_array(GameState.fetch().town.neighboring_towns) + for town in towns_to_add: + var new_button := Button.new() + new_button.text = town.name + new_button.pressed.connect(_on_town_pressed.bind(town)) + _towns.add_child(new_button) + + +func _display_item_info(item: Item) -> void: + var text := "" + + text += "[color=yellow]%s[/color]\n" % item.name + text += "[color=white]%s[/color]\n\n" % item.description + text += "[color=gray]%s[/color]\n" % item.category_list() + text += "[color=gray]Weighs %d[/color]" % item.weight + + if item.states != Item.State.STATE_NONE: + text += "\n\n[color=blue]%s[/color]" % item.state_list() + + _description_box_label.text = text + + +func _set_town(town: Town) -> void: + GameState.fetch().town = town + _town_label.text = "Going to \"%s\"" % GameState.fetch().town.name + + +func _on_item_gui_input(event: InputEvent, item: Item) -> void: + if event is InputEventMouseMotion: + _display_item_info(item) + + +func _on_town_pressed(town: Town) -> void: + var text := "" + text += "[color=yellow]%s[/color]\n" % town.name + text += "[color=white]%s %s town[/color]" % [ + town.size_to_string().to_pascal_case(), + town.status_to_string() + ] + if town.workshops != Town.Workshop.WORKSHOP_NONE: + text += "\n\n[color=gray]Has %s[/color]" % town.workshop_list(false) + _description_box_label.text = text + + _set_town(town) + + +func _on_go_forth_pressed() -> void: + var fade_tween := create_tween() + fade_tween.tween_property(_fade_rect, "color:a", 1, 0.2) + await fade_tween.finished + await get_tree().create_timer(0.1).timeout + MainEvents.scene_change_requested.emit("res://src/overworld/overworld.tscn") diff --git a/gd/src/town_preview/town_preview.tscn b/gd/src/town_preview/town_preview.tscn new file mode 100644 index 0000000..327972f --- /dev/null +++ b/gd/src/town_preview/town_preview.tscn @@ -0,0 +1,215 @@ +[gd_scene load_steps=4 format=3 uid="uid://bjncpymja76aq"] + +[ext_resource type="Script" path="res://src/town_preview/town_preview.gd" id="1_qmitc"] +[ext_resource type="Theme" uid="uid://bdofl7w84udo" path="res://src/menus.tres" id="2_2ie50"] +[ext_resource type="PackedScene" uid="uid://b6p1b5g3r77su" path="res://src/weird_background.tscn" id="2_8s4ba"] + +[node name="TownPreview" type="Node2D" node_paths=PackedStringArray("_town_label", "_items", "_towns", "_weight_label", "_description_box_label", "_fade_rect")] +script = ExtResource("1_qmitc") +_town_label = NodePath("CanvasLayer/Themed/TownLabel") +_items = NodePath("CanvasLayer/Themed/Things/ScrollContainer/HFlowContainer") +_towns = NodePath("CanvasLayer/Themed/TownSelect/ContentMargin/VBoxContainer") +_weight_label = NodePath("CanvasLayer/Themed/WeightLabel") +_description_box_label = NodePath("CanvasLayer/Themed/DescriptionBox/RichTextLabel") +_fade_rect = NodePath("CanvasLayer/FadeRect") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="WeirdBackground" parent="CanvasLayer" instance=ExtResource("2_8s4ba")] +offset_top = -76.0 +offset_bottom = 76.0 + +[node name="Themed" type="Control" parent="CanvasLayer"] +layout_mode = 3 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme = ExtResource("2_2ie50") + +[node name="TownLabel" type="RichTextLabel" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -172.0 +offset_right = 312.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Going to \"Nowhere\"" +fit_content = true + +[node name="WeightLabel" type="RichTextLabel" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -116.0 +offset_right = -88.0 +offset_bottom = -82.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "0/0" +fit_content = true + +[node name="GoFromHereLabel" type="Label" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 176.0 +offset_top = -92.0 +offset_right = 284.0 +offset_bottom = -69.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 0 +text = "Go From Here" + +[node name="Things" type="Panel" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -75.0 +offset_right = -93.0 +offset_bottom = 173.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/Themed/Things"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +vertical_scroll_mode = 2 + +[node name="HFlowContainer" type="HFlowContainer" parent="CanvasLayer/Themed/Things/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="DescriptionBox" type="Panel" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -87.5 +offset_top = -75.5 +offset_right = 120.5 +offset_bottom = 116.5 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +metadata/_edit_group_ = true + +[node name="RichTextLabel" type="RichTextLabel" parent="CanvasLayer/Themed/DescriptionBox"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/bold_italics_font_size = 12 +theme_override_font_sizes/italics_font_size = 12 +theme_override_font_sizes/mono_font_size = 12 +theme_override_font_sizes/normal_font_size = 12 +theme_override_font_sizes/bold_font_size = 12 +bbcode_enabled = true +fit_content = true + +[node name="GoForth" type="Button" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -88.0 +offset_top = 124.0 +offset_right = 120.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 8 +theme_override_font_sizes/font_size = 36 +text = "GO FORTH!" + +[node name="TownSelect" type="ScrollContainer" parent="CanvasLayer/Themed"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 128.0 +offset_top = -75.0 +offset_right = 312.0 +offset_bottom = 173.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +follow_focus = true +horizontal_scroll_mode = 0 +metadata/_edit_group_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/Themed/TownSelect"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/Themed/TownSelect/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 4 + +[node name="FadeRect" type="ColorRect" parent="CanvasLayer"] +visible = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) + +[connection signal="pressed" from="CanvasLayer/Themed/GoForth" to="." method="_on_go_forth_pressed"] diff --git a/gd/src/trade/npcs/bloobert.gd b/gd/src/trade/npcs/bloobert.gd new file mode 100644 index 0000000..2d816e1 --- /dev/null +++ b/gd/src/trade/npcs/bloobert.gd @@ -0,0 +1,35 @@ +extends NPC + + +func _init() -> void: + npc_name = "Bloobert" + preferred_categories = Item.Category.CATEGORY_FOOD + desired = [ + preload("res://data/items/olive.tres"), + ] + inventory = [ + preload("res://data/items/olive.tres").duplicate(), + preload("res://data/items/scented_hammer.tres").duplicate(), + ] + inventory[0].amount = 2 + + +func dialogue_beginning() -> DialogueResult: + if _items_have_any_categories(Item.Category.CATEGORY_FOOD): + return _response("I smell some vicious delicious [color=yellow]olives[/color] in that rickety backpack of yours.") + else: + return _response("None of your business, buddyboy!") + + +func dialogue_offer(_item: Item) -> DialogueResult: + if _items_have_any_categories(Item.Category.CATEGORY_FOOD): + return _response("I'll give you anything you want for them!") + else: + return _response("Eh...") + + +func dialogue_judge(items: Array[Item]) -> DialogueResult: + if items.any(func(item: Item) -> bool: return item.categories & Item.Category.CATEGORY_FOOD): + return _response("Ho-ho, yes!") + else: + return _response("You [shake rate=20.0 level=5]suck[/shake]. Gimme olives next time.") diff --git a/gd/src/trade/npcs/npc.gd b/gd/src/trade/npcs/npc.gd new file mode 100644 index 0000000..fb732c4 --- /dev/null +++ b/gd/src/trade/npcs/npc.gd @@ -0,0 +1,55 @@ +class_name NPC +extends RefCounted + + +var npc_name := "Nobody" +var patience := 3 + +var preferred_categories := 0 +var desired: Array[Item] +var inventory: Array[Item] + +var patience_remaining := patience + + +func dialogue_beginning() -> DialogueResult: + assert(false, "%s not implemented for %s" % [get_stack()[0].function, str(self)]) + return DialogueResult.new() + + +func dialogue_offer(_item: Item) -> DialogueResult: + assert(false, "%s not implemented for %s" % [get_stack()[0].function, str(self)]) + return DialogueResult.new() + + +func dialogue_judge(_item: Array[Item]) -> DialogueResult: + assert(false, "%s not implemented for %s" % [get_stack()[0].function, str(self)]) + return DialogueResult.new() + + +func _response(string: String, value := true) -> DialogueResult: + return DialogueResult.new(string, value) + + +func _items_have_any_categories(categories: int) -> bool: + return GameState.fetch().player_inventory.items.any( + func(item: Item) -> bool: + return item.categories & categories + ) + + +func _items_have_all_categories(categories: int) -> bool: + return GameState.fetch().player_inventory.items.all( + func(item: Item) -> bool: + return item.categories & categories + ) + + +class DialogueResult: + var string: String + var value: bool + + + func _init(p_string := "", p_value := true) -> void: + string = p_string + value = p_value diff --git a/gd/src/trade/trade.gd b/gd/src/trade/trade.gd new file mode 100644 index 0000000..93dbd32 --- /dev/null +++ b/gd/src/trade/trade.gd @@ -0,0 +1,345 @@ +extends Node2D + + +const _ITEM_BOX_SCENE = preload("res://src/lib/item_box.tscn") +const _ItemBox = preload("res://src/lib/item_box.gd") +const _TYPEWRITER_SEC_PER_CHAR = 0.015 +const _TYPEWRITER_SEC_PER_STOP = 0.3 + +@export var _fade_rect: ColorRect + +@export_group("Main Menu") +@export var _main_menu: Control +@export var _offer_container: HFlowContainer +@export var _desired_container: HFlowContainer +@export var _player_inventory_container: HFlowContainer +@export var _dialogue_name_label: RichTextLabel +@export var _dialogue_label: RichTextLabel + +@export_group("Offering Menu") +@export var _offering_menu: Control + +@export_subgroup("Dialogue") +@export var _offer_dialogue_name_label: RichTextLabel +@export var _offer_dialogue_label: RichTextLabel + +@export_subgroup("Player Inventory") +@export var _offer_player_inventory_label: RichTextLabel +@export var _offer_player_inventory_container: HFlowContainer + +@export_subgroup("Currently Offered") +@export var _offer_current_items: HFlowContainer + +@export_subgroup("Selected Item") +@export var _offer_selected_box: Control +@export var _offer_selected_num_spin_box: SpinBox + +@export_subgroup("Item To Add") +@export var _offer_box: Control +@export var _offer_box_slot: Control +@export var _offer_num_spin_box: SpinBox + +var _npc: NPC +var _typewriter_timer := 0.0 +var _typewriter_wait := 0.0 +var _typewriter_running := false + +var _offer_temp_inventory: Array[Item] +var _offer_current: Array[Item] +var _offer_selected: Item +var _offer_adding: Item +var _offer_adding_box: Control + + +func _ready() -> void: + _fade_rect.show() + create_tween().tween_property(_fade_rect, "color:a", 0, 0.2) + + _npc = GameState.fetch().trade_npc_data.new() + _dialogue_name_label.text = _npc.npc_name + _offer_dialogue_name_label.text = _npc.npc_name + _offer_num_spin_box.get_line_edit().context_menu_enabled = false + _offer_num_spin_box.get_line_edit().selecting_enabled = false + _offer_selected_num_spin_box.get_line_edit().context_menu_enabled = false + _offer_selected_num_spin_box.get_line_edit().selecting_enabled = false + + _update_all() + + _main_menu.show() + _offering_menu.hide() + _typewriter_start(_dialogue_label, _npc.dialogue_beginning().string) + + +func _process(delta: float) -> void: + if _typewriter_running: + _typewriter_timer += delta + + if _main_menu.visible: + _typewriter_label(_dialogue_label) + elif _offering_menu.visible: + _typewriter_label(_offer_dialogue_label) + + +func end_trade() -> void: + var fade_tween := create_tween() + fade_tween.tween_property(_fade_rect, "color", Color.BLACK, 0.2) + await fade_tween.finished + await get_tree().create_timer(0.1).timeout + MainEvents.scene_change_requested.emit("res://src/overworld/overworld.tscn") + + +func _update_offers() -> void: + var old_children := _offer_container.get_children() + for child in old_children: + child.queue_free() + + for item in _npc.inventory: + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, true) + new_node.gui_input.connect(_on_offer_gui_input.bind(item)) + _offer_container.add_child(new_node) + + +func _update_desired() -> void: + var old_children := _desired_container.get_children() + for child in old_children: + child.queue_free() + + for item in _npc.desired: + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, false) + _desired_container.add_child(new_node) + + +func _update_inventory() -> void: + var old_children := _player_inventory_container.get_children() + for child in old_children: + child.queue_free() + + for item in GameState.fetch().player_inventory.items: + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, true) + _player_inventory_container.add_child(new_node) + + +func _update_all() -> void: + _update_offers() + _update_desired() + _update_inventory() + + +func _offer_update_inventory() -> void: + var old_children := _offer_player_inventory_container.get_children() + for child in old_children: + child.queue_free() + + for item in _offer_temp_inventory: + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, true) + + new_node.gui_input.connect(_on_offer_inventory_gui_input.bind(item, new_node)) + + _offer_player_inventory_container.add_child(new_node) + + +func _offer_update_current() -> void: + var old_children := _offer_current_items.get_children() + for child in old_children: + child.queue_free() + + for item in _offer_current: + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(item, true) + _offer_current_items.add_child(new_node) + + +func _offer_update_selected() -> void: + var old_children := _offer_selected_box.get_children() + for child in old_children: + child.queue_free() + + # can't get more than what there is + _offer_selected_num_spin_box.max_value = _offer_selected.amount + + # only allow editing/focusing spin box if item is stackable + _offer_selected_num_spin_box.editable = _offer_selected.stackable + if _offer_selected.stackable: + _offer_selected_num_spin_box.focus_mode = Control.FOCUS_ALL + else: + _offer_selected_num_spin_box.focus_mode = Control.FOCUS_NONE + + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(_offer_selected, true) + _offer_selected_box.add_child(new_node) + new_node.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + + +func _offer_update_adding() -> void: + var old_children := _offer_box_slot.get_children() + for child in old_children: + child.queue_free() + + if not is_instance_valid(_offer_adding): + _offer_num_spin_box.max_value = 1 + _offer_num_spin_box.editable = false + _offer_num_spin_box.focus_mode = Control.FOCUS_NONE + return + + # can't add more than what you have + _offer_num_spin_box.max_value = _offer_adding.amount + + # only allow editing/focusing spin box if item is stackable + _offer_num_spin_box.editable = _offer_adding.stackable + if _offer_adding.stackable: + _offer_num_spin_box.focus_mode = Control.FOCUS_ALL + else: + _offer_num_spin_box.focus_mode = Control.FOCUS_NONE + + var new_node: _ItemBox = _ITEM_BOX_SCENE.instantiate() + new_node.update_from_item(_offer_adding, true) + _offer_box_slot.add_child(new_node) + new_node.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + + +func _offer_update_all() -> void: + _offer_update_inventory() + _offer_update_current() + _offer_update_selected() + _offer_update_adding() + + +func _typewriter_start(label: RichTextLabel, text: String) -> void: + label.text = text + label.visible_characters = 0 + _typewriter_running = true + _typewriter_timer = 0.0 + _typewriter_wait = _TYPEWRITER_SEC_PER_CHAR + + +func _typewriter_label(label: RichTextLabel) -> void: + # still running + if _typewriter_timer < _typewriter_wait: + return + + label.visible_characters += 1 + + # text is fully typed out + if label.visible_characters == label.text.length(): + _typewriter_running = false + + # if the character that was just revealed is punctuation, wait longer + match label.text[label.visible_characters - 1]: + "?", "!", ",", ".", ":", ";": + _typewriter_wait = _TYPEWRITER_SEC_PER_STOP + _: + _typewriter_wait = _TYPEWRITER_SEC_PER_CHAR + + _typewriter_timer = 0.0 + + +func _offer_go_back() -> void: + _offer_current.clear() + + _main_menu.show() + _offering_menu.hide() + _update_all() + + +func _sort_amount_ascending(a: Item, b: Item) -> bool: + return a.amount < b.amount + + +func _sort_amount_descending(a: Item, b: Item) -> bool: + return a.amount > b.amount + + +func _item_weight_sum(accum: int, item: Item) -> int: + return accum + item.weight + + +func _filter_same_items(item: Item, original: Item) -> bool: + return item.id == original.id + + +func _on_offer_gui_input(event: InputEvent, offer: Item) -> void: + if event is InputEventMouseButton: + var e := event as InputEventMouseButton + if e.button_index != MOUSE_BUTTON_LEFT or not e.is_released(): + return + else: + return + + _main_menu.hide() + _offering_menu.show() + + _offer_selected = offer + + # ask for everything by default + _offer_selected_num_spin_box.max_value = _offer_selected.amount + _offer_selected_num_spin_box.value = _offer_selected.amount + + _offer_temp_inventory = GameState.fetch().player_inventory.items.duplicate() + _offer_update_all() + _typewriter_start(_offer_dialogue_label, _npc.dialogue_offer(offer).string) + + +func _on_offer_inventory_gui_input(event: InputEvent, offer: Item, offer_box: Control) -> void: + if event is InputEventMouseButton: + var e := event as InputEventMouseButton + if e.is_released() and e.button_index == MOUSE_BUTTON_LEFT: + _offer_adding = offer + _offer_adding_box = offer_box + _offer_num_spin_box.max_value = _offer_adding.amount + _offer_num_spin_box.value = _offer_adding.amount + _offer_update_adding() + + +func _on_offer_accept_pressed() -> void: + # first make sure it would be possible to carry the selected item + # after giving up the offered items + var weight: int = _offer_temp_inventory.reduce(_item_weight_sum, 0) + if (weight + (_offer_selected.weight * _offer_selected.amount)) > GameState.fetch().player_inventory.max_weight: + var dialog = AcceptDialog.new() + dialog.dialog_text = "You wouldn't be able to carry all this!" + dialog.title = "" + dialog.close_requested.connect(dialog.queue_free) + get_tree().root.add_child(dialog) + dialog.popup_centered() + return + + var result := _npc.dialogue_judge(_offer_current) + if result.value == false: # deal rejected + _typewriter_start(_offer_dialogue_label, result.string) + else: # deal accepted + # you lose what you offered + GameState.fetch().player_inventory.items = _offer_temp_inventory + # and get what you wanted + var obtained: Item = _offer_selected.duplicate() + obtained.amount = _offer_selected_num_spin_box.value + GameState.fetch().player_inventory.add_item(obtained) + + _typewriter_start(_dialogue_label, result.string) + _offer_go_back() + + +func _on_offer_back_pressed() -> void: + _offer_go_back() + + +func _on_offer_add_pressed() -> void: + if not is_instance_valid(_offer_adding): + return + + var added: Item = _offer_adding.duplicate() + added.amount = _offer_num_spin_box.value + _offer_current.push_back(added) + _offer_update_current() + + if not _offer_adding.stackable or _offer_adding.amount <= _offer_num_spin_box.value: + _offer_temp_inventory.erase(_offer_adding) + else: + _offer_adding.amount -= _offer_num_spin_box.value + _offer_update_inventory() + + _offer_adding = null + _offer_update_adding() diff --git a/gd/src/trade/trade.tscn b/gd/src/trade/trade.tscn new file mode 100644 index 0000000..3442c04 --- /dev/null +++ b/gd/src/trade/trade.tscn @@ -0,0 +1,641 @@ +[gd_scene load_steps=4 format=3 uid="uid://1k1h15e4tiei"] + +[ext_resource type="Script" path="res://src/trade/trade.gd" id="1_x14ix"] +[ext_resource type="Theme" uid="uid://bdofl7w84udo" path="res://src/menus.tres" id="2_2cx3y"] +[ext_resource type="PackedScene" uid="uid://b6p1b5g3r77su" path="res://src/weird_background.tscn" id="2_fustq"] + +[node name="Trade" type="Node2D" node_paths=PackedStringArray("_fade_rect", "_main_menu", "_offer_container", "_desired_container", "_player_inventory_container", "_dialogue_name_label", "_dialogue_label", "_offering_menu", "_offer_dialogue_name_label", "_offer_dialogue_label", "_offer_player_inventory_label", "_offer_player_inventory_container", "_offer_current_items", "_offer_selected_box", "_offer_selected_num_spin_box", "_offer_box", "_offer_box_slot", "_offer_num_spin_box")] +script = ExtResource("1_x14ix") +_fade_rect = NodePath("CanvasLayer/FadeRect") +_main_menu = NodePath("CanvasLayer/MainMenu") +_offer_container = NodePath("CanvasLayer/MainMenu/Offers/ScrollContainer/ContentMargin/OfferContainer") +_desired_container = NodePath("CanvasLayer/MainMenu/Desired/ScrollContainer/ContentMargin/DesiredContainer") +_player_inventory_container = NodePath("CanvasLayer/MainMenu/PlayerInventory/ScrollContainer/ContentMargin/ItemContainer") +_dialogue_name_label = NodePath("CanvasLayer/MainMenu/DialogueNameLabel") +_dialogue_label = NodePath("CanvasLayer/MainMenu/DialogueBox/DialogueLabel") +_offering_menu = NodePath("CanvasLayer/OfferingMenu") +_offer_dialogue_name_label = NodePath("CanvasLayer/OfferingMenu/DialogueNameLabel") +_offer_dialogue_label = NodePath("CanvasLayer/OfferingMenu/DialogueBox/DialogueLabel") +_offer_player_inventory_label = NodePath("CanvasLayer/OfferingMenu/PlayerInventoryLabel") +_offer_player_inventory_container = NodePath("CanvasLayer/OfferingMenu/PlayerInventory/ScrollContainer/ContentMargin/ItemContainer") +_offer_current_items = NodePath("CanvasLayer/OfferingMenu/CurrentOffers/ScrollContainer/ContentMargin/ItemContainer") +_offer_selected_box = NodePath("CanvasLayer/OfferingMenu/SelectedBox") +_offer_selected_num_spin_box = NodePath("CanvasLayer/OfferingMenu/SelectedNumSpinBox") +_offer_box = NodePath("CanvasLayer/OfferingMenu/OfferBox") +_offer_box_slot = NodePath("CanvasLayer/OfferingMenu/OfferBox/OfferBoxSlot") +_offer_num_spin_box = NodePath("CanvasLayer/OfferingMenu/OfferBox/NumSpinBox") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="WeirdBackground" parent="CanvasLayer" instance=ExtResource("2_fustq")] +offset_top = -76.0 +offset_bottom = 76.0 + +[node name="MainMenu" type="Control" parent="CanvasLayer"] +visible = false +layout_mode = 3 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("2_2cx3y") + +[node name="OffersLabel" type="RichTextLabel" parent="CanvasLayer/MainMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -172.0 +offset_right = -144.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Offers" +autowrap_mode = 0 + +[node name="Offers" type="Panel" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -140.0 +offset_right = -136.0 +offset_bottom = -4.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/MainMenu/Offers"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_use_anchors_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/MainMenu/Offers/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="OfferContainer" type="HFlowContainer" parent="CanvasLayer/MainMenu/Offers/ScrollContainer/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="DesiredLabel" type="RichTextLabel" parent="CanvasLayer/MainMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -120.0 +offset_top = -172.0 +offset_right = 48.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Wants" +autowrap_mode = 0 + +[node name="Desired" type="Panel" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -120.0 +offset_top = -140.0 +offset_right = 64.0 +offset_bottom = -4.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/MainMenu/Desired"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_use_anchors_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/MainMenu/Desired/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="DesiredContainer" type="HFlowContainer" parent="CanvasLayer/MainMenu/Desired/ScrollContainer/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="PlayerInventoryLabel" type="RichTextLabel" parent="CanvasLayer/MainMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = 12.0 +offset_right = -144.0 +offset_bottom = 36.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Your Stuff" +autowrap_mode = 0 + +[node name="PlayerInventory" type="Panel" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = 44.0 +offset_right = 64.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/MainMenu/PlayerInventory"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_use_anchors_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/MainMenu/PlayerInventory/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="ItemContainer" type="HFlowContainer" parent="CanvasLayer/MainMenu/PlayerInventory/ScrollContainer/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="DialogueNameLabel" type="RichTextLabel" parent="CanvasLayer/MainMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 72.0 +offset_top = 28.0 +offset_right = 240.0 +offset_bottom = 44.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 12 +bbcode_enabled = true +text = "???" +autowrap_mode = 0 + +[node name="DialogueBox" type="Panel" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 72.0 +offset_top = 44.0 +offset_right = 312.0 +offset_bottom = 132.0 +grow_horizontal = 2 +grow_vertical = 2 +metadata/_edit_group_ = true + +[node name="DialogueLabel" type="RichTextLabel" parent="CanvasLayer/MainMenu/DialogueBox"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 12.0 +offset_top = 12.0 +offset_right = -12.0 +offset_bottom = -12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 12 +bbcode_enabled = true +visible_characters_behavior = 1 + +[node name="Cancel" type="Button" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 72.0 +offset_top = 140.0 +offset_right = 312.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "get out of here" + +[node name="OfferingMenu" type="Control" parent="CanvasLayer"] +layout_mode = 3 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("2_2cx3y") + +[node name="PlayerInventoryLabel" type="RichTextLabel" parent="CanvasLayer/OfferingMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 64.0 +offset_top = -172.0 +offset_right = 312.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Your Stuff" +autowrap_mode = 0 + +[node name="PlayerInventory" type="Panel" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 56.0 +offset_top = -140.0 +offset_right = 312.0 +offset_bottom = 44.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/OfferingMenu/PlayerInventory"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_use_anchors_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/OfferingMenu/PlayerInventory/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="ItemContainer" type="HFlowContainer" parent="CanvasLayer/OfferingMenu/PlayerInventory/ScrollContainer/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="DialogueNameLabel" type="RichTextLabel" parent="CanvasLayer/OfferingMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = 60.0 +offset_right = -56.0 +offset_bottom = 76.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 12 +bbcode_enabled = true +text = "???" +autowrap_mode = 0 + +[node name="DialogueBox" type="Panel" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = 76.0 +offset_right = -88.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +metadata/_edit_group_ = true + +[node name="DialogueLabel" type="RichTextLabel" parent="CanvasLayer/OfferingMenu/DialogueBox"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 12.0 +offset_top = 12.0 +offset_right = -12.0 +offset_bottom = -12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 12 +bbcode_enabled = true +visible_characters_behavior = 1 + +[node name="Accept" type="Button" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -80.0 +offset_top = 76.0 +offset_right = 48.0 +offset_bottom = 116.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "accept" + +[node name="Back" type="Button" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -80.0 +offset_top = 132.0 +offset_right = 48.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "back" + +[node name="CurrentOffersLabel" type="RichTextLabel" parent="CanvasLayer/OfferingMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -172.0 +offset_right = -136.0 +offset_bottom = -148.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "Offering" +autowrap_mode = 0 + +[node name="CurrentOffers" type="Panel" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -312.0 +offset_top = -140.0 +offset_right = -120.0 +offset_bottom = 44.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +metadata/_edit_group_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/OfferingMenu/CurrentOffers"] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_use_anchors_ = true + +[node name="ContentMargin" type="MarginContainer" parent="CanvasLayer/OfferingMenu/CurrentOffers/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="ItemContainer" type="HFlowContainer" parent="CanvasLayer/OfferingMenu/CurrentOffers/ScrollContainer/ContentMargin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="SelectedLabel" type="RichTextLabel" parent="CanvasLayer/OfferingMenu"] +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -96.0 +offset_top = -116.0 +offset_right = 32.0 +offset_bottom = -92.0 +grow_horizontal = 2 +grow_vertical = 2 +bbcode_enabled = true +text = "[center]Selected[/center]" +autowrap_mode = 0 + +[node name="SelectedBox" type="Control" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -64.0 +offset_top = -84.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SelectedNumSpinBox" type="SpinBox" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -80.0 +offset_top = -12.0 +offset_right = 8.0 +offset_bottom = 19.0 +grow_horizontal = 2 +grow_vertical = 2 +min_value = 1.0 +max_value = 1.0 +value = 1.0 +alignment = 1 +editable = false +update_on_text_changed = true +prefix = "x" + +[node name="OfferBox" type="Panel" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 96.0 +offset_top = 60.0 +offset_right = 208.0 +offset_bottom = 172.0 +grow_horizontal = 2 +grow_vertical = 2 +metadata/_edit_group_ = true + +[node name="OfferBoxSlot" type="Control" parent="CanvasLayer/OfferingMenu/OfferBox"] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_top = 38.0 +grow_horizontal = 2 + +[node name="NumSpinBox" type="SpinBox" parent="CanvasLayer/OfferingMenu/OfferBox"] +texture_filter = 1 +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_bottom = -8.0 +grow_horizontal = 2 +grow_vertical = 0 +focus_mode = 2 +min_value = 1.0 +value = 1.0 +rounded = true +alignment = 1 +editable = false +update_on_text_changed = true +prefix = "x" + +[node name="AddOffer" type="Button" parent="CanvasLayer/OfferingMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 216.0 +offset_top = 84.0 +offset_right = 280.0 +offset_bottom = 148.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "add" + +[node name="FadeRect" type="ColorRect" parent="CanvasLayer"] +visible = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) + +[connection signal="pressed" from="CanvasLayer/MainMenu/Cancel" to="." method="end_trade"] +[connection signal="pressed" from="CanvasLayer/OfferingMenu/Accept" to="." method="_on_offer_accept_pressed"] +[connection signal="pressed" from="CanvasLayer/OfferingMenu/Back" to="." method="_on_offer_back_pressed"] +[connection signal="pressed" from="CanvasLayer/OfferingMenu/AddOffer" to="." method="_on_offer_add_pressed"] diff --git a/gd/src/weird_background.gdshader b/gd/src/weird_background.gdshader new file mode 100644 index 0000000..a3209e8 --- /dev/null +++ b/gd/src/weird_background.gdshader @@ -0,0 +1,10 @@ +shader_type canvas_item; + +const float SCROLL_STEP = 0.1; + + +void vertex() { + float scroll = SCROLL_STEP * TIME; + UV.x += scroll; + UV.y -= scroll; +} diff --git a/gd/src/weird_background.tscn b/gd/src/weird_background.tscn new file mode 100644 index 0000000..b77a8d2 --- /dev/null +++ b/gd/src/weird_background.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=6 format=3 uid="uid://b6p1b5g3r77su"] + +[ext_resource type="Shader" path="res://src/weird_background.gdshader" id="1_cc2jl"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_7al8d"] +shader = ExtResource("1_cc2jl") + +[sub_resource type="Gradient" id="Gradient_qawev"] +colors = PackedColorArray(0.115517, 0.0145, 0.29, 1, 0.42, 0, 0.217, 1) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_sfyvt"] +noise_type = 2 +frequency = 0.05 +fractal_type = 0 +cellular_return_type = 0 +domain_warp_enabled = true +domain_warp_type = 2 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7v4mw"] +generate_mipmaps = false +seamless = true +color_ramp = SubResource("Gradient_qawev") +noise = SubResource("FastNoiseLite_sfyvt") + +[node name="WeirdBackground" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_bottom = 152.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Base" type="TextureRect" parent="."] +texture_filter = 1 +material = SubResource("ShaderMaterial_7al8d") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("NoiseTexture2D_7v4mw") +stretch_mode = 1