Plant spawn

This commit is contained in:
Rendo 2025-08-03 18:34:54 +05:00
commit 941912d7f1
25 changed files with 281 additions and 40 deletions

View file

@ -17,7 +17,7 @@ warnings/check_angle_interpolation_type_conflicting=false
config/name="Liberation of the Neighbourville"
config/version="0.3.0"
run/main_scene="uid://boxt0hfowqe66"
run/main_scene="uid://bh1yiiv1fi0b8"
config/features=PackedStringArray("4.4", "Forward Plus")
config/icon="res://icon.png"
config/windows_native_icon="res://icon.ico"
@ -44,6 +44,12 @@ enabled=PackedStringArray()
import/blender/enabled=false
[global_group]
Plants="Mostly player's means of defence"
Zombies="Agressors of player"
Projectile="Something that has ben shot"
[gui]
theme/custom_font="res://assets/fonts/pico12.ttf"

View file

@ -1,7 +1,8 @@
[gd_resource type="Resource" script_class="SeedpacketResource" load_steps=3 format=3 uid="uid://d1nwlatkrtkpe"]
[gd_resource type="Resource" script_class="SeedpacketResource" load_steps=4 format=3 uid="uid://d1nwlatkrtkpe"]
[ext_resource type="Script" uid="uid://dtjdfji87kybn" path="res://scripts/resources/entity_resource.gd" id="1_giqbo"]
[ext_resource type="Texture2D" uid="uid://d4btl7vqi4v0q" path="res://assets/sprites/plants/aloe.tres" id="1_qou1a"]
[ext_resource type="PackedScene" uid="uid://dba4s3ke32u0l" path="res://scenes/plants/aloe.tscn" id="2_6p2kr"]
[resource]
script = ExtResource("1_giqbo")
@ -9,5 +10,6 @@ preview = ExtResource("1_qou1a")
cost = 75.0
recharge_time = 5.0
initial_recharge_percent = 1.0
scene = ExtResource("2_6p2kr")
order = 0
metadata/_custom_type_script = "uid://dtjdfji87kybn"

View file

@ -1,34 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://boxt0hfowqe66"]
[ext_resource type="Script" uid="uid://crlumefuo1biu" path="res://scripts/gui/plant_pick/seedpacket_generator.gd" id="1_celhi"]
[ext_resource type="Script" uid="uid://co3yto3q7mnm7" path="res://scripts/level/level_data.gd" id="2_pdbkq"]
[ext_resource type="Script" uid="uid://cbrhnb4tp4pem" path="res://scripts/gui/hotbar.gd" id="3_qyndx"]
[node name="PlantsPickTest" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="GridContainer" type="GridContainer" parent="."]
layout_mode = 0
anchor_left = 0.27
anchor_top = 0.48
anchor_right = 0.74
anchor_bottom = 0.94
columns = 3
script = ExtResource("1_celhi")
metadata/_edit_use_anchors_ = true
[node name="LevelData" type="Node" parent="."]
script = ExtResource("2_pdbkq")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 0
offset_left = 69.0
offset_top = 12.0
offset_right = 479.0
offset_bottom = 75.0
script = ExtResource("3_qyndx")

View file

@ -137,5 +137,6 @@ grow_vertical = 2
texture = SubResource("AtlasTexture_gtmhg")
[node name="RechargeTimer" type="Timer" parent="."]
one_shot = true
[connection signal="pressed" from="TextureButton" to="." method="on_pressed"]

View file

@ -0,0 +1,71 @@
[gd_scene load_steps=9 format=3 uid="uid://bh1yiiv1fi0b8"]
[ext_resource type="Texture2D" uid="uid://b0tb2hjum40aw" path="res://assets/sprites/background_summer.png" id="1_3bchh"]
[ext_resource type="Script" uid="uid://cj8mt02far5np" path="res://scripts/level/field_rect_offsetter.gd" id="1_4dbyi"]
[ext_resource type="Script" uid="uid://cyttjwv888cb4" path="res://scripts/level/layered_entity_container.gd" id="1_ia5ss"]
[ext_resource type="Script" uid="uid://co3yto3q7mnm7" path="res://scripts/level/level_data.gd" id="1_ssqyj"]
[ext_resource type="Script" uid="uid://sjnxv5q4yk2" path="res://scripts/level/seedpacket_placer.gd" id="4_5r585"]
[ext_resource type="Script" uid="uid://crlumefuo1biu" path="res://scripts/gui/plant_pick/seedpacket_generator.gd" id="6_2y6d3"]
[ext_resource type="Script" uid="uid://cbrhnb4tp4pem" path="res://scripts/gui/plant_pick/hotbar.gd" id="7_e4b3h"]
[ext_resource type="Script" uid="uid://60lwfjb634kd" path="res://scripts/gui/plant_pick/go_button.gd" id="8_e4b3h"]
[node name="StandardLevel" type="Node"]
[node name="Data" type="Node" parent="."]
script = ExtResource("1_ssqyj")
metadata/_custom_type_script = "uid://co3yto3q7mnm7"
[node name="LayeredEntityContainer" type="Node" parent="."]
script = ExtResource("1_ia5ss")
[node name="Game" type="Node2D" parent="."]
[node name="Field Start" type="Marker2D" parent="Game"]
position = Vector2(305, 76)
script = ExtResource("1_4dbyi")
metadata/_edit_lock_ = true
[node name="SeedpacketPlacer" type="CanvasGroup" parent="Game" node_paths=PackedStringArray("entity_container")]
script = ExtResource("4_5r585")
entity_container = NodePath("../../LayeredEntityContainer")
[node name="Plants" type="Node2D" parent="Game"]
unique_name_in_owner = true
[node name="Background" type="CanvasLayer" parent="Game"]
layer = -10
follow_viewport_enabled = true
[node name="BackgroundSummer" type="Sprite2D" parent="Game/Background"]
texture = ExtResource("1_3bchh")
centered = false
metadata/_edit_lock_ = true
[node name="Camera2D" type="Camera2D" parent="Game"]
position = Vector2(517, 206)
[node name="User Interface" type="CanvasLayer" parent="."]
layer = 5
metadata/_edit_lock_ = true
[node name="GridContainer" type="GridContainer" parent="User Interface"]
offset_left = -1.0
offset_top = 216.0
offset_right = 257.0
offset_bottom = 401.0
columns = 5
script = ExtResource("6_2y6d3")
[node name="Hotbar" type="HBoxContainer" parent="User Interface"]
offset_left = -1.0
offset_top = -2.0
offset_right = 365.0
offset_bottom = 65.0
script = ExtResource("7_e4b3h")
[node name="Button" type="Button" parent="User Interface"]
offset_top = 178.0
offset_right = 124.0
offset_bottom = 210.0
text = "next state"
script = ExtResource("8_e4b3h")

15
scenes/plants/aloe.tscn Normal file
View file

@ -0,0 +1,15 @@
[gd_scene load_steps=3 format=3 uid="uid://dba4s3ke32u0l"]
[ext_resource type="Texture2D" uid="uid://d4btl7vqi4v0q" path="res://assets/sprites/plants/aloe.tres" id="1_38l32"]
[ext_resource type="Script" uid="uid://bwdvaov8sse4k" path="res://scripts/entities/entity.gd" id="1_yhqjp"]
[node name="Aloe" type="Node2D" groups=["Plants"]]
[node name="Entity" type="Node" parent="."]
script = ExtResource("1_yhqjp")
max_hp = 30.0
layer = &"base"
metadata/_custom_type_script = "uid://bwdvaov8sse4k"
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("1_38l32")

View file

@ -20,6 +20,11 @@ signal entity_killed(context : Entity.KilledContext)
## Called for every entity that gets damage
signal entity_hp_changed(context : Entity.HPChangedContext)
## Called for every entity that has layer set that enters game
signal layer_entity_created(entity : Entity)
## Called for every entity that has layer set that exits game
signal layer_entity_killed(entity : Entity.KilledContext)
#endregion
#region Seedpacket manipulation
@ -27,6 +32,9 @@ signal entity_hp_changed(context : Entity.HPChangedContext)
## Called when player selects SeedpacketResource
signal packet_selected(resource : SeedpacketResource)
## Called when player deselects (usually loses focus) SeedpacketResource
signal packet_deselected(resource : SeedpacketResource)
## Called when selected packets are updated
signal hotbar_packets_update(selected : Array[SeedpacketResource])
#endregion

View file

@ -0,0 +1,4 @@
extends Node
var seedpacket_count : int
var owned_plants : Array[SeedpacketResource]

View file

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

View file

@ -5,6 +5,8 @@ class_name Entity
## Maximum health points of an entity. Any heal cannot exceed this value
@export var max_hp : float
## Optional spawn layer for grid interactions
@export var layer : StringName = ""
## Current amount of health points of an entity. Cannot be below 0 or [code]max_hp[/code]
var hp : float = max_hp
@ -67,6 +69,10 @@ func kill(source : Entity):
context.target = self
killed.emit(context)
LevelEventBus.entity_killed.emit(context)
if not layer.is_empty():
LevelEventBus.layer_entity_killed.emit(context)
deconstruct()

View file

@ -0,0 +1,5 @@
extends Button
func _pressed() -> void:
LevelEventBus.state_advance_requested.emit()

View file

@ -0,0 +1 @@
uid://60lwfjb634kd

View file

@ -29,4 +29,4 @@ func on_level_state_changed(state : LevelData.LevelStates):
func on_sun_count_updated(to : float):
enough_sun = to >= seedpacket.held_resource.cost
seedpacket.update_contents()

View file

@ -16,6 +16,9 @@ class_name Seedpacket
var held_resource : SeedpacketResource
var handler : SeedpacketHandler
func _ready() -> void:
focus_exited.connect(on_focus_exited)
func _process(_delta: float) -> void:
button.disabled = recharge_timer.time_left > 0 or handler.is_avaiable() == false
@ -45,3 +48,6 @@ func on_packet_placed(packet : SeedpacketResource):
func disconnect_placement():
LevelEventBus.packet_placed.disconnect(on_packet_placed)
focus_exited.disconnect(disconnect_placement)
func on_focus_exited():
LevelEventBus.packet_deselected.emit(held_resource)

View file

@ -0,0 +1,6 @@
extends Marker2D
func _ready() -> void:
FieldParams.field_rect.position = global_position
queue_free()

View file

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

View file

@ -0,0 +1,37 @@
extends Node
class_name LayeredEntityContainer
var entities := {
"under" : [],
"base" : [],
"above" : []
}
func _ready() -> void:
LevelEventBus.layer_entity_created.connect(on_entity_spawned)
LevelEventBus.layer_entity_killed.connect(on_entity_killed)
for layer in entities.keys():
entities[layer].resize(FieldParams.COLUMNS * FieldParams.ROWS)
## Checks if tile is occupied. [br]
## If position or layer are invalid, returns true
func is_occupied(position : int, layer : StringName) -> bool:
if position >= FieldParams.COLUMNS * FieldParams.ROWS or entities.has(layer) == false: return true
return not entities[layer][position] == null
func on_entity_spawned(entity : Entity) -> void:
if entities.has(entity.layer) == false: return
var index = FieldParams.indexify(entity.get_parent().global_position)
if index >= FieldParams.COLUMNS * FieldParams.ROWS or is_occupied(index,entity.layer): return
entities[entity.layer][index] = entity
func on_entity_killed(context : Entity.KilledContext) -> void:
var entity = context.target
var index = FieldParams.indexify(entity.get_parent().global_position)
if is_occupied(index,entity.layer) == false: return
entities[entity.layer][index] = null

View file

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

View file

@ -3,12 +3,35 @@ extends Node
class_name LevelData
static var state : LevelStates = LevelStates.NotInGame
static var sun_count : float
static var sun_count : float = 100
var hotbar_seedpackets : Array[SeedpacketResource]
func _ready() -> void:
LevelEventBus.packet_selected.connect(on_seedpacket_clicked)
set_state(LevelStates.PlantPick)
LevelEventBus.state_advance_requested.connect(advance_state)
LevelEventBus.packet_placed.connect(on_packet_placed)
func _exit_tree() -> void:
set_state(LevelStates.NotInGame)
func advance_state() -> void:
set_state(state + 1)
func set_state(to : LevelStates):
state = to
match state:
LevelStates.PlantPick:
LevelEventBus.packet_selected.connect(on_seedpacket_clicked)
LevelStates.Pregame:
LevelEventBus.packet_selected.disconnect(on_seedpacket_clicked)
LevelStates.Game:
pass
LevelStates.Postgame:
pass
LevelStates.NotInGame:
pass
LevelEventBus.state_changed.emit(state)
func on_seedpacket_clicked(seedpacket : SeedpacketResource):
if hotbar_seedpackets.has(seedpacket):
@ -17,6 +40,11 @@ func on_seedpacket_clicked(seedpacket : SeedpacketResource):
hotbar_seedpackets.append(seedpacket)
LevelEventBus.hotbar_packets_update.emit(hotbar_seedpackets)
func on_packet_placed(seedpacket : SeedpacketResource):
print(seedpacket)
sun_count -= seedpacket.cost
LevelEventBus.sun_count_updated.emit(sun_count)
## Possible states of level
enum LevelStates {
## The game is during plant pick stage
@ -27,6 +55,6 @@ enum LevelStates {
Game,
## Game ended
Postgame,
## Not in a level,
## Not in a level
NotInGame
}

View file

@ -0,0 +1,51 @@
extends CanvasGroup
@export var entity_container : LayeredEntityContainer
var can_plant : bool = false
var held_packet : SeedpacketResource = null
func _ready() -> void:
LevelEventBus.state_changed.connect(on_level_state_changed)
LevelEventBus.packet_deselected.connect(on_seedpacket_deselected)
func _process(_delta: float) -> void:
var mouse_pos = get_global_mouse_position()
var indexified_mouse = FieldParams.indexify(mouse_pos - FieldParams.field_rect.position)
if FieldParams.field_rect.has_point(mouse_pos) and entity_container.is_occupied(indexified_mouse,"base") == false:
global_position = FieldParams.deindexify(indexified_mouse) + FieldParams.TILE / 2.0 + FieldParams.field_rect.position
can_plant = true
else:
global_position = mouse_pos
can_plant = false
func _input(event: InputEvent) -> void:
if held_packet != null and event.is_action_pressed("cancel_action"):
stop_placement()
if held_packet != null and can_plant and event.is_action_pressed("primary_action"):
for child in get_children():
child.reparent(get_tree().current_scene.get_node("%Plants"))
LevelEventBus.packet_placed.emit(held_packet)
held_packet = null
visible = false
func on_seedpacket_selected(packet : SeedpacketResource) -> void:
add_child(packet.scene.instantiate())
held_packet = packet
visible = true
func on_seedpacket_deselected(packet : SeedpacketResource) -> void:
if packet == held_packet:
stop_placement()
func on_level_state_changed(state : LevelData.LevelStates) -> void:
if state == LevelData.LevelStates.Game:
LevelEventBus.state_changed.disconnect(on_level_state_changed)
LevelEventBus.packet_selected.connect(on_seedpacket_selected)
func stop_placement():
for child in get_children():
child.queue_free()
held_packet = null
visible = false

View file

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

View file

@ -0,0 +1,23 @@
extends Node
## Utility class for field parameters and connected functions
class_name FieldParams
## Tile size
const TILE : Vector2 = Vector2(50,60)
## Rows count of field
const ROWS : int = 5
## Columns count of field
const COLUMNS : int = 9
## Field rectangle. Origin is set in-game
static var field_rect : Rect2 = Rect2(0,0,COLUMNS*TILE.x,ROWS*TILE.y)
## Converts vector position into int index. Hash somewhat
static func indexify(position : Vector2) -> int:
var tiled_position = (position/TILE).floor()
return int(tiled_position.x) + int(tiled_position.y * COLUMNS)
static func deindexify(index : int) -> Vector2:
return Vector2(index % COLUMNS * TILE.x, index / COLUMNS * TILE.y)

View file

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