initial commit

This commit is contained in:
wanp 2024-09-08 17:57:53 -03:00
commit c54bc5bf9b
46 changed files with 2838 additions and 0 deletions

15
gd/.gitignore vendored Normal file
View File

@ -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

Binary file not shown.

View File

@ -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={}

BIN
gd/assets/fonts/yoster.ttf Normal file

Binary file not shown.

View File

@ -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={}

BIN
gd/assets/music/mod27_2.ogg Normal file

Binary file not shown.

View File

@ -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

Binary file not shown.

View File

@ -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

BIN
gd/assets/music/sho.ogg Normal file

Binary file not shown.

View File

@ -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

20
gd/data/items/olive.tres Normal file
View File

@ -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

View File

@ -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

View File

@ -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]([])

View File

@ -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]([])

1
gd/icon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 994 B

37
gd/icon.svg.import Normal file
View File

@ -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

93
gd/project.godot Normal file
View File

@ -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"

20
gd/src/config.gd Normal file
View File

@ -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

24
gd/src/game_state.gd Normal file
View File

@ -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

View File

@ -0,0 +1,9 @@
class_name CInteractable
extends Node
signal interacted(source: Node)
func interact(source: Node) -> void:
interacted.emit(source)

41
gd/src/lib/components.gd Normal file
View File

@ -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()

139
gd/src/lib/item.gd Normal file
View File

@ -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)

32
gd/src/lib/item_box.gd Normal file
View File

@ -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()

68
gd/src/lib/item_box.tscn Normal file
View File

@ -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"]

View File

@ -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

View File

@ -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

83
gd/src/lib/town.gd Normal file
View File

@ -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)

42
gd/src/main.gd Normal file
View File

@ -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)

9
gd/src/main.tscn Normal file
View File

@ -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")]

4
gd/src/main_events.gd Normal file
View File

@ -0,0 +1,4 @@
extends Node
signal scene_change_requested(path: String, keep_state: bool)

202
gd/src/menus.tres Normal file
View File

@ -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")

View File

@ -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)

View File

@ -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"]

View File

@ -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

View File

@ -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")

109
gd/src/overworld/player.gd Normal file
View File

@ -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)

View File

@ -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"]

View File

@ -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")

View File

@ -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"]

View File

@ -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.")

55
gd/src/trade/npcs/npc.gd Normal file
View File

@ -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

345
gd/src/trade/trade.gd Normal file
View File

@ -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()

641
gd/src/trade/trade.tscn Normal file
View File

@ -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"]

View File

@ -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;
}

View File

@ -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