From 782e333922154bf7cb7dede9428a516dcd8cb657 Mon Sep 17 00:00:00 2001 From: ShyPiano Date: Sat, 13 Apr 2024 16:52:35 +0100 Subject: [PATCH] Added basic foundation for Virtual Pet game: Pet has needs, they decay over time, actions affect them. --- notes/idea.md | 30 +++++++++++++++ project.godot | 8 ++++ scenes/demon.tscn | 55 ++++++++++++++++++++++++++++ scenes/demon_action.tscn | 6 +++ scenes/demon_need.tscn | 6 +++ scenes/ui/hud.tscn | 79 ++++++++++++++++++++++++++++++++++++++++ scenes/world.tscn | 23 ++++++++++++ scripts/Demon.gd | 41 +++++++++++++++++++++ scripts/DemonAction.gd | 24 ++++++++++++ scripts/DemonNeed.gd | 26 +++++++++++++ scripts/World.gd | 26 +++++++++++++ scripts/ui/Hud.gd | 15 ++++++++ 12 files changed, 339 insertions(+) create mode 100644 notes/idea.md create mode 100644 scenes/demon.tscn create mode 100644 scenes/demon_action.tscn create mode 100644 scenes/demon_need.tscn create mode 100644 scenes/ui/hud.tscn create mode 100644 scenes/world.tscn create mode 100644 scripts/Demon.gd create mode 100644 scripts/DemonAction.gd create mode 100644 scripts/DemonNeed.gd create mode 100644 scripts/World.gd create mode 100644 scripts/ui/Hud.gd diff --git a/notes/idea.md b/notes/idea.md new file mode 100644 index 0000000..fe2881e --- /dev/null +++ b/notes/idea.md @@ -0,0 +1,30 @@ +# LUDUM DARE 55 +## Virtual Pet game +- take care of a little demon! + - Sims-like needs: + - Hunger + - Energy + - Confort + - Fun + - Bladder + - Hygiene + - no Social(?) (demons are lonely creatures) + - no Room(?) + - have them learn skills: + - Scaryness + - Possession + - Future vision + - Manipulation + - Barganing +- demon gets summoned from time to time + - each summoning has a list of requeriments + - certain skill levels + - failing (a certain amount of) summonings will lose you the game + - demon must be in a good mood to do a good job + - gain currency as a result of good pperformance + - better Barganing gets you more currency! + - player gets notified of imminent summoning sometime before they are summoned +- buy new stuff for your demon! + - successful summonings give you currency (souls) + - better objects allow faster skill building and need fulfilment + - some are decorative only? diff --git a/project.godot b/project.godot index 34d6918..546a0f5 100644 --- a/project.godot +++ b/project.godot @@ -11,9 +11,17 @@ config_version=5 [application] config/name="ludum-dare-55" +run/main_scene="res://scenes/world.tscn" config/features=PackedStringArray("4.2", "GL Compatibility") config/icon="res://icon.svg" +[display] + +window/size/viewport_width=720 +window/size/viewport_height=940 +window/size/resizable=false +window/handheld/orientation=5 + [rendering] renderer/rendering_method="gl_compatibility" diff --git a/scenes/demon.tscn b/scenes/demon.tscn new file mode 100644 index 0000000..2caabf5 --- /dev/null +++ b/scenes/demon.tscn @@ -0,0 +1,55 @@ +[gd_scene load_steps=5 format=3 uid="uid://cxr45r0ukiibw"] + +[ext_resource type="Script" path="res://scripts/Demon.gd" id="1_458ae"] +[ext_resource type="Texture2D" uid="uid://bl8vphjt2m81k" path="res://icon.svg" id="2_usy3u"] +[ext_resource type="PackedScene" uid="uid://cd5qy7i6kth1g" path="res://scenes/demon_need.tscn" id="3_23rv5"] +[ext_resource type="Script" path="res://scripts/DemonAction.gd" id="4_xwqmp"] + +[node name="Demon" type="Node2D"] +script = ExtResource("1_458ae") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("2_usy3u") + +[node name="Needs" type="Node" parent="."] + +[node name="Hunger" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Hunger" +decay = 4 + +[node name="Energy" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Energy" + +[node name="Bladder" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Bladder" +decay = 4 + +[node name="Hygiene" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Hygiene" + +[node name="Comfort" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Comfort" +decay = 6 + +[node name="Fun" parent="Needs" instance=ExtResource("3_23rv5")] +need_name = "Fun" +decay = 10 + +[node name="ActionTemplates" type="Node" parent="."] + +[node name="HaveMeal" type="Node" parent="ActionTemplates"] +script = ExtResource("4_xwqmp") +action_name = "Have a Meal" +needs_effect = { +"Bladder": -6, +"Comfort": 1, +"Hunger": 10 +} +action_duration = 5 + +[connection signal="need_update" from="Needs/Hunger" to="." method="_on_need_update"] +[connection signal="need_update" from="Needs/Energy" to="." method="_on_need_update"] +[connection signal="need_update" from="Needs/Bladder" to="." method="_on_need_update"] +[connection signal="need_update" from="Needs/Hygiene" to="." method="_on_need_update"] +[connection signal="need_update" from="Needs/Comfort" to="." method="_on_need_update"] +[connection signal="need_update" from="Needs/Fun" to="." method="_on_need_update"] diff --git a/scenes/demon_action.tscn b/scenes/demon_action.tscn new file mode 100644 index 0000000..41988a0 --- /dev/null +++ b/scenes/demon_action.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://do5kg1fbtty5g"] + +[ext_resource type="Script" path="res://scripts/DemonAction.gd" id="1_vo2i8"] + +[node name="DemonAction" type="Node"] +script = ExtResource("1_vo2i8") diff --git a/scenes/demon_need.tscn b/scenes/demon_need.tscn new file mode 100644 index 0000000..3117790 --- /dev/null +++ b/scenes/demon_need.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cd5qy7i6kth1g"] + +[ext_resource type="Script" path="res://scripts/DemonNeed.gd" id="1_wwgsx"] + +[node name="Need" type="Node"] +script = ExtResource("1_wwgsx") diff --git a/scenes/ui/hud.tscn b/scenes/ui/hud.tscn new file mode 100644 index 0000000..672aa10 --- /dev/null +++ b/scenes/ui/hud.tscn @@ -0,0 +1,79 @@ +[gd_scene load_steps=2 format=3 uid="uid://bh4d73stc2rt"] + +[ext_resource type="Script" path="res://scripts/ui/Hud.gd" id="1_1vslh"] + +[node name="HUD" type="CanvasLayer"] +script = ExtResource("1_1vslh") +need_bars = { +"Bladder": NodePath("NeedBarsBox/BladderNeedBar"), +"Comfort": NodePath("NeedBarsBox/ComfortNeedBar"), +"Energy": NodePath("NeedBarsBox/EnergyNeedBar"), +"Fun": NodePath("NeedBarsBox/FunNeedBar"), +"Hunger": NodePath("NeedBarsBox/HungerNeedBar"), +"Hygiene": NodePath("NeedBarsBox/HygieneNeedBar") +} + +[node name="PauseBtn" type="Button" parent="."] +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -64.0 +offset_bottom = 64.0 +grow_horizontal = 0 +toggle_mode = true +text = "Pause" + +[node name="NeedBarsBox" type="VBoxContainer" parent="."] +anchors_preset = -1 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_top = -92.0 +offset_bottom = -3.0 +grow_horizontal = 2 +grow_vertical = 0 +alignment = 1 + +[node name="HungerNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[node name="EnergyNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[node name="BladderNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[node name="HygieneNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[node name="ComfortNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[node name="FunNeedBar" type="ProgressBar" parent="NeedBarsBox"] +layout_mode = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 1.0 +value = 750.0 + +[connection signal="pressed" from="PauseBtn" to="." method="_on_pause_btn_pressed"] diff --git a/scenes/world.tscn b/scenes/world.tscn new file mode 100644 index 0000000..fb33357 --- /dev/null +++ b/scenes/world.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=4 format=3 uid="uid://cfoypma2ump0p"] + +[ext_resource type="Script" path="res://scripts/World.gd" id="1_1ap2o"] +[ext_resource type="PackedScene" uid="uid://cxr45r0ukiibw" path="res://scenes/demon.tscn" id="2_6t0hy"] +[ext_resource type="PackedScene" uid="uid://bh4d73stc2rt" path="res://scenes/ui/hud.tscn" id="3_oqr52"] + +[node name="World" type="Node" node_paths=PackedStringArray("demon", "ticker", "hud")] +script = ExtResource("1_1ap2o") +demon = NodePath("Demon") +ticker = NodePath("Ticker") +hud = NodePath("HUD") + +[node name="Demon" parent="." instance=ExtResource("2_6t0hy")] +position = Vector2(368, 384) + +[node name="Ticker" type="Timer" parent="."] +autostart = true + +[node name="HUD" parent="." instance=ExtResource("3_oqr52")] + +[connection signal="need_update" from="Demon" to="." method="_on_demon_need_update"] +[connection signal="timeout" from="Ticker" to="." method="_on_ticker_timeout"] +[connection signal="pause_pressed" from="HUD" to="." method="_on_pause_pressed"] diff --git a/scripts/Demon.gd b/scripts/Demon.gd new file mode 100644 index 0000000..1178c5f --- /dev/null +++ b/scripts/Demon.gd @@ -0,0 +1,41 @@ +class_name Demon +extends Node + +var curr_action: DemonAction = null : + set(action_template): + if action_template == null: + curr_action = null + return + curr_action = action_template.duplicate() + curr_action.action_end.connect(end_action) + +@export var needs: Dictionary = { + "Hunger": NodePath(^"Needs/Hunger"), + "Energy": NodePath(^"Needs/Energy"), + "Bladder": NodePath(^"Needs/Bladder"), + "Hygiene": NodePath(^"Needs/Hygiene"), + "Comfort": NodePath(^"Needs/Comfort"), + "Fun": NodePath(^"Needs/Fun") +} + +func _ready(): + for n in needs: + needs[n] = (get_node(needs[n]) as DemonNeed) + # TODO remove debug action: + curr_action = get_node(^"ActionTemplates/HaveMeal") + +func tick(): + if curr_action != null: + for n_name in curr_action.needs_effect: + needs[n_name].replenish(curr_action.needs_effect[n_name]) + curr_action.tick() + + for n_name in needs: + needs[n_name].tick() + +func end_action(): + curr_action = null + +signal need_update(need_name: String, old_val: int, new_val: int) +func _on_need_update(need_name, old_val, new_val): + need_update.emit(need_name, old_val, new_val) diff --git a/scripts/DemonAction.gd b/scripts/DemonAction.gd new file mode 100644 index 0000000..ed506f1 --- /dev/null +++ b/scripts/DemonAction.gd @@ -0,0 +1,24 @@ +class_name DemonAction +extends Node + +signal action_end + +@export var action_name: String +@export var needs_effect: Dictionary = { + "Hunger": 0, + "Energy": 0, + "Bladder": 0, + "Hygiene": 0, + "Comfort": 0, + "Fun": 0 +} +@export var action_duration: int = -1 + +func tick(): + if action_duration > 0: + action_duration -= 1 + if action_duration == 0: + end_action() + +func end_action(): + action_end.emit() diff --git a/scripts/DemonNeed.gd b/scripts/DemonNeed.gd new file mode 100644 index 0000000..1f84f2c --- /dev/null +++ b/scripts/DemonNeed.gd @@ -0,0 +1,26 @@ +class_name DemonNeed +extends Node + +signal need_catastrophy(need_name: String) +signal need_update(need_name: String, old_val: int, new_val: int) + +@export var need_name: String +@export var curr_val: int = 750 +const MAX_VAL: int = 1000 +const MIN_VAL:int = -1000 +@export var decay: int = 1 +var next_tick_delta: int = -decay + +func tick(): + var old_val: int = curr_val + set_value(curr_val + next_tick_delta) + if curr_val == MIN_VAL: + need_catastrophy.emit(need_name) + need_update.emit(need_name, old_val, curr_val) + next_tick_delta = -decay + +func replenish(val: int): + next_tick_delta = val + +func set_value(val: int): + curr_val = clamp(val, MIN_VAL, MAX_VAL) diff --git a/scripts/World.gd b/scripts/World.gd new file mode 100644 index 0000000..e2265da --- /dev/null +++ b/scripts/World.gd @@ -0,0 +1,26 @@ +extends Node + +@export var demon: Demon +@export var ticker: Timer +@export var hud: Hud + +# curr_time: number of minutes passed since in-game midnight +# start at 09:00 +const MINUTES_IN_DAY: int = 1440 +@export var day_start_time: int = 540 +var curr_time: int = day_start_time +var curr_day: int = 0 + +func _on_ticker_timeout(): + demon.tick() + curr_time = (curr_time + 1) % MINUTES_IN_DAY + if curr_time == 0: + curr_day += 1 + + +func _on_pause_pressed(): + ticker.paused = !ticker.paused + + +func _on_demon_need_update(need_name: String, old_val: int, new_val: int): + hud.update_need_bar(need_name, old_val, new_val) diff --git a/scripts/ui/Hud.gd b/scripts/ui/Hud.gd new file mode 100644 index 0000000..02c3276 --- /dev/null +++ b/scripts/ui/Hud.gd @@ -0,0 +1,15 @@ +class_name Hud +extends CanvasLayer + +signal pause_pressed + +@export var need_bars: Dictionary +func _ready(): + for n in need_bars: + need_bars[n] = (get_node(need_bars[n]) as ProgressBar) + +func _on_pause_btn_pressed(): + pause_pressed.emit() + +func update_need_bar(need_name: String, _old_val: int, new_val: int): + need_bars[need_name].value = new_val