Added simple goblonoid creature

master
hheik 2025-09-12 16:35:30 +03:00
parent 56c993e856
commit e5b39e4bf5
14 changed files with 112 additions and 31 deletions

View File

@ -0,0 +1,10 @@
@icon("res://icons/turn_action.svg")
extends ActionDecider
@export var mover: Mover
func handle_decide_action():
var directions = [Vector2i.UP, Vector2i.DOWN, Vector2i.LEFT, Vector2i.RIGHT]
if try_perform(MoveAction.new(mover, directions.pick_random())):
return
try_perform(SkipAction.new())

View File

@ -0,0 +1 @@
uid://hbrne8wt101n

View File

@ -0,0 +1,28 @@
[gd_scene load_steps=4 format=3 uid="uid://dgiy7a67o1c6i"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_4ufx4"]
[ext_resource type="Script" uid="uid://swwas6oe75sc" path="res://nodes/mover_sprite_flipper.gd" id="2_d3j4p"]
[ext_resource type="Script" uid="uid://hbrne8wt101n" path="res://nodes/creature/ai_wanderer.gd" id="3_4ufx4"]
[node name="Goblonoid" type="GridPosition"]
[node name="Sprite2D" type="Sprite2D" parent="." node_paths=PackedStringArray("mover")]
texture = ExtResource("1_4ufx4")
hframes = 8
vframes = 8
frame = 8
script = ExtResource("2_d3j4p")
mover = NodePath("../Mover")
[node name="TurnActor" type="TurnActor" parent="."]
[node name="ActionDecider" type="Node" parent="." node_paths=PackedStringArray("mover", "actor")]
script = ExtResource("3_4ufx4")
mover = NodePath("../Mover")
actor = NodePath("../TurnActor")
metadata/_custom_type_script = "uid://okxdlbfuvb1b"
[node name="Mover" type="Mover" parent="."]
movement_speed = 100.0
[node name="Gatherer" type="Gatherer" parent="."]

View File

@ -28,9 +28,8 @@ zoom = Vector2(2, 2)
[node name="TurnActor" type="TurnActor" parent="."] [node name="TurnActor" type="TurnActor" parent="."]
[node name="ActionDecider" type="Node2D" parent="." node_paths=PackedStringArray("grid", "mover", "actor")] [node name="ActionDecider" type="Node2D" parent="." node_paths=PackedStringArray("mover", "actor")]
script = ExtResource("2_rdx4y") script = ExtResource("2_rdx4y")
grid = NodePath("..")
mover = NodePath("../Mover") mover = NodePath("../Mover")
actor = NodePath("../TurnActor") actor = NodePath("../TurnActor")
metadata/_custom_type_script = "uid://okxdlbfuvb1b" metadata/_custom_type_script = "uid://okxdlbfuvb1b"

View File

@ -1,7 +1,6 @@
@icon("res://icons/turn_action.svg") @icon("res://icons/turn_action.svg")
extends ActionDecider extends ActionDecider
@export var grid: GridPosition
@export var mover: Mover @export var mover: Mover
func poll_movement_dir(): func poll_movement_dir():

View File

@ -10,7 +10,7 @@ var manager: GameManager
func _ready(): func _ready():
manager = GameManager.from_node(self) manager = GameManager.from_node(self)
manager.time().time_advanced.connect(_on_time_advance) manager.time().time_of_day_changed.connect(_on_time_of_day_changed)
player.entity_inventory.inventory.changed.connect(_on_player_inventory_changed) player.entity_inventory.inventory.changed.connect(_on_player_inventory_changed)
update_inventory_ui() update_inventory_ui()
update_clock_hand(manager.time().day_progress()) update_clock_hand(manager.time().day_progress())
@ -21,11 +21,8 @@ func _on_player_inventory_changed():
func update_inventory_ui(): func update_inventory_ui():
inventory_ui.update(player.get_inventory()) inventory_ui.update(player.get_inventory())
func _on_time_advance(_now: int, _advanced_by: int): func _on_time_of_day_changed(_time_of_day: int):
update_clock_hand(manager.time().day_progress()) update_clock_hand(manager.time().day_progress())
func update_clock_hand(progress: float): func update_clock_hand(progress: float):
clock_hand.rotation = progress * PI * 2.0 clock_hand.rotation = progress * PI * 2.0
func on_day_end():
print("Day over!")

View File

@ -22,7 +22,7 @@ tile_map_data = PackedByteArray("AAD8//z/AQAAAAQAAAD8//3/AQAAAAQAAAD8//7/AQAAAAQ
tile_set = ExtResource("1_m1b5j") tile_set = ExtResource("1_m1b5j")
[node name="Foreground" type="TileMapLayer" parent="World"] [node name="Foreground" type="TileMapLayer" parent="World"]
tile_map_data = PackedByteArray("AAD/////AQAAAAMAAAD8//3/AQABAAMAAAD9//3/AQABAAMAAAD9//7/AQABAAMAAAD9/wAAAQABAAMAAAD9/wEAAQABAAMAAAD8/wEAAQABAAMAAAAEAPv/AQACAAMAAAAGAP3/AQACAAMAAAAHAP7/AQACAAMAAAAIAP7/AQACAAMAAAAJAP7/AQACAAMAAAAGAPn/AQACAAMAAAAGAPr/AQACAAMAAAAGAPv/AQACAAMAAAAHAPv/AQACAAMAAAAIAPv/AQACAAMAAAAIAPz/AQACAAMAAAAJAPz/AQACAAMAAAAKAPz/AQACAAMAAAAKAPv/AQACAAMAAAAJAPv/AQACAAMAAAAIAPr/AQACAAMAAAAHAPr/AQACAAMAAAAHAPn/AQACAAMAAAD3//v/AQABAAMAAAD3//z/AQABAAMAAAD3//3/AQABAAMAAAD3//7/AQABAAMAAAD3////AQABAAMAAAD3/wAAAQABAAMAAAD3/wEAAQABAAMAAAD3/wIAAQABAAMAAAD4//v/AQABAAMAAAD4//z/AQABAAMAAAD4//3/AQABAAMAAAD4//7/AQABAAMAAAD4////AQABAAMAAAD4/wAAAQABAAMAAAD4/wEAAQABAAMAAAD4/wIAAQABAAMAAAD3/wMAAQABAAMAAAD3/wQAAQABAAMAAAD3/wUAAQABAAMAAAD4/wMAAQABAAMAAAD4/wQAAQABAAMAAAD4/wUAAQABAAMAAAD5/wUAAQABAAMAAAD6/wUAAQABAAMAAAD7/wUAAQABAAMAAAD8/wUAAQABAAMAAAD9/wUAAQABAAMAAAD+/wUAAQABAAMAAAD//wUAAQABAAMAAAAAAAUAAQABAAMAAAABAAUAAQABAAMAAAACAAUAAQABAAMAAAADAAUAAQABAAMAAAAEAAUAAQABAAMAAAAFAAUAAQABAAMAAAAGAAUAAQABAAMAAAAHAAUAAQABAAMAAAAIAAUAAQABAAMAAAAJAAUAAQABAAMAAAD5//v/AQABAAMAAAD6//v/AQABAAMAAAD7//v/AQABAAMAAAD8//v/AQABAAMAAAD9//v/AQABAAMAAAD+//v/AQABAAMAAAD///v/AQABAAMAAAAAAPv/AQABAAMAAAABAPv/AQABAAMAAAACAPv/AQABAAMAAAADAPv/AQABAAMAAAAJAP//AQABAAMAAAAJAAAAAQABAAMAAAAJAAEAAQABAAMAAAAJAAIAAQABAAMAAAAJAAMAAQABAAMAAAAJAAQAAQABAAMAAAD7/wEAAQABAAMAAAD7//3/AQABAAMAAAD6//3/AQABAAMAAAD6/wEAAQABAAMAAAD6//7/AQABAAMAAAD6/wAAAQABAAMAAAD6/wQAAAAAAAAAAgD5/wQAAAAAAAAAAgD5/wMAAAAAAAAAAgAEAAEAAAAAAAAAAQADAAEAAAAAAAAAAQADAAIAAAAAAAAAAQADAAMAAAAAAAAAAQACAAMAAAAAAAAAAQA=") tile_map_data = PackedByteArray("AAD/////AQAAAAMAAAD8//3/AQABAAMAAAD9//3/AQABAAMAAAD9//7/AQABAAMAAAD9/wAAAQABAAMAAAD9/wEAAQABAAMAAAD8/wEAAQABAAMAAAAEAPv/AQACAAMAAAAGAP3/AQACAAMAAAAHAP7/AQACAAMAAAAIAP7/AQACAAMAAAAJAP7/AQACAAMAAAAGAPn/AQACAAMAAAAGAPr/AQACAAMAAAAGAPv/AQACAAMAAAAHAPv/AQACAAMAAAAIAPv/AQACAAMAAAAIAPz/AQACAAMAAAAJAPz/AQACAAMAAAAKAPz/AQACAAMAAAAKAPv/AQACAAMAAAAJAPv/AQACAAMAAAAIAPr/AQACAAMAAAAHAPr/AQACAAMAAAAHAPn/AQACAAMAAAD3//v/AQABAAMAAAD3//z/AQABAAMAAAD3//3/AQABAAMAAAD3//7/AQABAAMAAAD3////AQABAAMAAAD3/wAAAQABAAMAAAD3/wEAAQABAAMAAAD3/wIAAQABAAMAAAD4//v/AQABAAMAAAD4//z/AQABAAMAAAD4//3/AQABAAMAAAD4//7/AQABAAMAAAD4////AQABAAMAAAD4/wAAAQABAAMAAAD4/wEAAQABAAMAAAD4/wIAAQABAAMAAAD3/wMAAQABAAMAAAD3/wQAAQABAAMAAAD3/wUAAQABAAMAAAD4/wMAAQABAAMAAAD4/wQAAQABAAMAAAD4/wUAAQABAAMAAAD5/wUAAQABAAMAAAD6/wUAAQABAAMAAAD7/wUAAQABAAMAAAD8/wUAAQABAAMAAAD9/wUAAQABAAMAAAD+/wUAAQABAAMAAAD//wUAAQABAAMAAAAAAAUAAQABAAMAAAABAAUAAQABAAMAAAACAAUAAQABAAMAAAADAAUAAQABAAMAAAAEAAUAAQABAAMAAAAFAAUAAQABAAMAAAAGAAUAAQABAAMAAAAHAAUAAQABAAMAAAAIAAUAAQABAAMAAAAJAAUAAQABAAMAAAD5//v/AQABAAMAAAD6//v/AQABAAMAAAD7//v/AQABAAMAAAD8//v/AQABAAMAAAD9//v/AQABAAMAAAD+//v/AQABAAMAAAD///v/AQABAAMAAAAAAPv/AQABAAMAAAABAPv/AQABAAMAAAACAPv/AQABAAMAAAADAPv/AQABAAMAAAAJAP//AQABAAMAAAAJAAAAAQABAAMAAAAJAAEAAQABAAMAAAAJAAIAAQABAAMAAAAJAAMAAQABAAMAAAAJAAQAAQABAAMAAAD7/wEAAQABAAMAAAD7//3/AQABAAMAAAD6//3/AQABAAMAAAD6/wEAAQABAAMAAAD6//7/AQABAAMAAAD6/wAAAQABAAMAAAD6/wQAAAAAAAAAAgD5/wQAAAAAAAAAAgD5/wMAAAAAAAAAAgAEAAEAAAAAAAAAAQADAAEAAAAAAAAAAQADAAIAAAAAAAAAAQADAAMAAAAAAAAAAQACAAMAAAAAAAAAAQAEAAMAAAAAAAAAAQAFAAMAAAAAAAAAAQAFAAIAAAAAAAAAAQAFAAEAAAAAAAAAAQAEAAIAAAAAAAAAAwD7//7/AAAAAAAAAwA=")
tile_set = ExtResource("2_u2ss0") tile_set = ExtResource("2_u2ss0")
[node name="Player" parent="World" instance=ExtResource("3_u2ss0")] [node name="Player" parent="World" instance=ExtResource("3_u2ss0")]

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,13 +1,15 @@
[gd_resource type="TileSet" load_steps=6 format=3 uid="uid://bxwohuw2p43k1"] [gd_resource type="TileSet" load_steps=7 format=3 uid="uid://bxwohuw2p43k1"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_oe62d"] [ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_oe62d"]
[ext_resource type="PackedScene" uid="uid://8xuvjmyjkpeo" path="res://prefabs/tiles/blueberry.tscn" id="1_rc83b"] [ext_resource type="PackedScene" uid="uid://8xuvjmyjkpeo" path="res://prefabs/tiles/blueberry.tscn" id="1_rc83b"]
[ext_resource type="PackedScene" uid="uid://l72f05nek0y4" path="res://prefabs/tiles/cowberry.tscn" id="2_w3v7n"] [ext_resource type="PackedScene" uid="uid://l72f05nek0y4" path="res://prefabs/tiles/cowberry.tscn" id="2_w3v7n"]
[ext_resource type="PackedScene" uid="uid://dgiy7a67o1c6i" path="res://prefabs/creatures/goblonoid.tscn" id="3_nsg88"]
[sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_w3v7n"] [sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_w3v7n"]
resource_name = "Entities" resource_name = "Entities"
scenes/1/scene = ExtResource("1_rc83b") scenes/1/scene = ExtResource("1_rc83b")
scenes/2/scene = ExtResource("2_w3v7n") scenes/2/scene = ExtResource("2_w3v7n")
scenes/3/scene = ExtResource("3_nsg88")
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_rc83b"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_rc83b"]
texture = ExtResource("1_oe62d") texture = ExtResource("1_oe62d")

View File

@ -53,6 +53,10 @@ pub struct TimeManager {
#[init(val = 40)] #[init(val = 40)]
day_length: i32, day_length: i32,
#[var(get, set = set_time_of_day)]
#[init(val = -1)]
day: i32,
#[var(get)] #[var(get)]
time_of_day: i32, time_of_day: i32,
@ -65,21 +69,37 @@ impl INode for TimeManager {}
#[godot_api] #[godot_api]
impl TimeManager { impl TimeManager {
#[func] #[func]
pub fn reset_time(&mut self) { pub fn set_time_of_day(&mut self, value: i32) {
self.time_of_day = 0; self.time_of_day = value;
self.signals().time_resetted().emit(); self.signals().time_of_day_changed().emit(value);
}
#[func]
pub fn set_day(&mut self, value: i32) {
self.day = value;
self.signals().day_changed().emit(value);
}
#[func]
pub fn start_new_day(&mut self) {
self.set_time_of_day(0);
self.set_day(self.day + 1);
GameManager::get(self.to_gd()).propagate_call(calls::ON_DAY_START);
} }
#[func] #[func]
pub fn advance_time(&mut self, amount: i32) { pub fn advance_time(&mut self, amount: i32) {
let new_time = self.time_of_day + amount; self.set_time_of_day(self.time_of_day + amount);
self.time_of_day = new_time; if self.is_day_over() {
self.signals().time_advanced().emit(new_time, amount);
if self.time_of_day >= self.day_length {
GameManager::get(self.to_gd()).propagate_call(calls::ON_DAY_END); GameManager::get(self.to_gd()).propagate_call(calls::ON_DAY_END);
} }
} }
#[func]
pub fn is_day_over(&self) -> bool {
self.time_of_day >= self.day_length
}
#[func] #[func]
pub fn day_progress(&self) -> f32 { pub fn day_progress(&self) -> f32 {
self.time_of_day as f32 / self.day_length as f32 self.time_of_day as f32 / self.day_length as f32
@ -91,8 +111,8 @@ impl TimeManager {
} }
#[signal] #[signal]
fn time_resetted(); fn day_changed(day: i32);
#[signal] #[signal]
fn time_advanced(now: i32, advanced_by: i32); fn time_of_day_changed(time_of_day: i32);
} }

View File

@ -1,4 +1,4 @@
use godot::{classes::*, prelude::*}; use godot::{classes::*, obj::WithBaseField, prelude::*};
use crate::prelude::*; use crate::prelude::*;
@ -46,6 +46,20 @@ pub struct Gatherable {
base: Base<Node>, base: Base<Node>,
} }
#[godot_api]
impl INode for Gatherable {
fn ready(&mut self) {
GameManager::get(self.to_gd())
.bind()
.turn()
.signals()
.action_taken()
.connect_other(self, |this, actor, action| {
this.on_action_taken(actor, action)
});
}
}
#[godot_api] #[godot_api]
impl Gatherable { impl Gatherable {
#[func] #[func]
@ -63,14 +77,10 @@ impl Gatherable {
} }
#[func] #[func]
fn on_turn_end(&mut self, actor: Option<Gd<TurnActor>>, _action: GString) { fn on_action_taken(&mut self, actor: Gd<TurnActor>, _action: GString) {
if self.get_is_picked() { if self.get_is_picked() {
return; return;
} }
let actor = match actor {
Some(actor) => actor,
None => return,
};
let actor_grid = match find_in_parents::<GridPosition, TurnActor>(actor.clone()) { let actor_grid = match find_in_parents::<GridPosition, TurnActor>(actor.clone()) {
Some(grid) => grid, Some(grid) => grid,
None => return, None => return,

View File

@ -70,9 +70,15 @@ impl TurnManager {
.signals() .signals()
.turn_ended() .turn_ended()
.builder() .builder()
.flags(ConnectFlags::ONE_SHOT) // `ConnectFlags::Deferred` prevents double-borrow errors.
// The double-borrow occurs when there are 2 actors, and the first one
// immediately skips their turn.
//
// This should be investigated further.
.flags(ConnectFlags::ONE_SHOT | ConnectFlags::DEFERRED)
.connect_other_mut(self, Self::on_actor_turn_end); .connect_other_mut(self, Self::on_actor_turn_end);
actor.bind_mut().start_turn(); actor.bind_mut().start_turn();
GameManager::get(self.to_gd()).propagate_call(calls::ON_TURN_START);
} else { } else {
self.unregister(&actor); self.unregister(&actor);
self.start_next_turn(); self.start_next_turn();
@ -83,12 +89,14 @@ impl TurnManager {
} }
fn on_actor_turn_end(&mut self, action: GString) { fn on_actor_turn_end(&mut self, action: GString) {
GameManager::get(self.to_gd()) GameManager::get(self.to_gd()).propagate_call(calls::ON_TURN_END);
.propagate_call_ex(calls::ON_TURN_END) let actor = self.current_actor.clone().unwrap();
.args(&varray![self.current_actor, action]) self.signals().action_taken().emit(&actor, &action);
.done();
self.start_next_turn(); self.start_next_turn();
} }
#[signal]
pub fn action_taken(actor: Gd<TurnActor>, action: GString);
} }
#[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)] #[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)]

View File

@ -1,10 +1,17 @@
use godot::prelude::*; use godot::prelude::*;
/// Contains hard-coded method names for `propagate_call`.
///
/// This project uses the convention, that `propagate_call` should never pass arguments, and if
/// arguments are needed, signals should be used instead. `propagate_call` should be called from
/// the `GameManager` node.
pub mod calls { pub mod calls {
pub const ON_DAY_START: &str = "on_day_start";
pub const ON_DAY_END: &str = "on_day_end";
pub const ON_ROUND_START: &str = "on_round_start"; pub const ON_ROUND_START: &str = "on_round_start";
pub const ON_ROUND_END: &str = "on_round_end"; pub const ON_ROUND_END: &str = "on_round_end";
pub const ON_TURN_START: &str = "on_turn_start";
pub const ON_TURN_END: &str = "on_turn_end"; pub const ON_TURN_END: &str = "on_turn_end";
pub const ON_DAY_END: &str = "on_day_end";
} }
pub fn find_in_parents<T, U>(from: Gd<U>) -> Option<Gd<T>> pub fn find_in_parents<T, U>(from: Gd<U>) -> Option<Gd<T>>