Compare commits

...

5 Commits

Author SHA1 Message Date
gamer147
e356078a9f Restructure tile size usage 2026-04-09 08:07:25 -04:00
gamer147
6b46d1c274 Appearance sets 2026-04-09 07:58:58 -04:00
gamer147
b807e9897d Refactored unit 2026-04-08 18:44:58 -04:00
gamer147
c192d48bc4 Reorganized files, started splitting up unit 2026-04-08 18:28:52 -04:00
gamer147
24134cfa33 Fixed doorway shadows 2026-04-08 12:46:17 -04:00
90 changed files with 1205 additions and 261 deletions

BIN
assets/sprites/CP002AB.BMP Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6smsdyydtiv4"
path="res://.godot/imported/CP002AB.BMP-83c77e61ea705a94bd90d268ad08d826.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/CP002AB.BMP"
dest_files=["res://.godot/imported/CP002AB.BMP-83c77e61ea705a94bd90d268ad08d826.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
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/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1

71
export_presets.cfg Normal file
View File

@@ -0,0 +1,71 @@
[preset.0]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path=""
patches=PackedStringArray()
patch_delta_encoding=false
patch_delta_compression_level_zstd=19
patch_delta_min_reduction=0.1
patch_delta_include_filters="*"
patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.0.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=false
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"

View File

@@ -1,56 +0,0 @@
class_name Unit extends Node2D
enum UnitState { ALIVE, DEAD }
#region Templates
@export var stat_template: UnitStats
@export var info_template: UnitInfo
@export var allegiance_template: UnitAllegiance
@export var tactics: Array[CombatTactic] = []
#endregion
var current_stats: UnitStats
var current_info: UnitInfo
var current_allegiance: UnitAllegiance
var state: UnitState = UnitState.ALIVE
signal unit_selected_changed(unit: Unit, selected: bool)
signal unit_allegiance_changed(unit: Unit, allegiance: UnitAllegiance)
signal unit_died(unit: Unit)
func _ready() -> void:
current_stats = stat_template.duplicate(true)
current_info = info_template.duplicate(true)
current_allegiance = allegiance_template.duplicate(true)
_append_builtin_tactics()
unit_allegiance_changed.emit(self, current_allegiance)
func _append_builtin_tactics() -> void:
var attack := AttackCombatTactic.new()
attack.tactic_name = "Attack"
attack.tactic_range = UnitMatchingCombatTacticRange.new()
tactics.append(attack)
var defend := DefendCombatTactic.new()
defend.tactic_name = "Defend"
defend.tactic_range = AnyCombatTacticRange.new()
tactics.append(defend)
func set_selected(selected: bool) -> void:
unit_selected_changed.emit(self, selected)
func is_alive() -> bool:
return state == UnitState.ALIVE
func take_damage(amount: int) -> void:
if state != UnitState.ALIVE:
return
current_stats.current_hp -= amount
if current_stats.current_hp <= 0:
current_stats.current_hp = 0
_die()
func _die() -> void:
state = UnitState.DEAD
unit_died.emit(self)
queue_free()

View File

@@ -1,14 +1,14 @@
[gd_scene format=3 uid="uid://dkhyh5ce4iuk3"]
[ext_resource type="Script" uid="uid://bks7uplgjjdg0" path="res://nodes/combat_map.gd" id="1_jyv1f"]
[ext_resource type="Script" uid="uid://c6701vy8h5rfx" path="res://resources/resource_definitions/dl_tileset.gd" id="2_8rn0j"]
[ext_resource type="Script" uid="uid://bks7uplgjjdg0" path="res://scripts/battle/map/combat_map.gd" id="1_jyv1f"]
[ext_resource type="Script" uid="uid://c6701vy8h5rfx" path="res://scripts/battle/map/dl_tileset.gd" id="2_8rn0j"]
[ext_resource type="Texture2D" uid="uid://sjsl8q7tkx8" path="res://assets/sprites/grid_highlight.png" id="3_vcj5e"]
[ext_resource type="Script" uid="uid://cxl38x2m6sj3w" path="res://scripts/battle/grid_overlay.gd" id="4_jelju"]
[ext_resource type="Script" uid="uid://cxl38x2m6sj3w" path="res://scripts/battle/map/grid_overlay.gd" id="4_jelju"]
[ext_resource type="Texture2D" uid="uid://b1ks72fiesfrm" path="res://assets/sprites/combat_map_ui.BMP" id="5_mycp7"]
[ext_resource type="Texture2D" uid="uid://65rmoynep5hy" path="res://assets/sprites/MP000A.BMP" id="6_muxvo"]
[ext_resource type="Script" uid="uid://c4f1vflwd81b8" path="res://nodes/wall_renderer.gd" id="7_wallr"]
[ext_resource type="Script" uid="uid://c4f1vflwd81b8" path="res://scripts/battle/map/wall_renderer.gd" id="7_wallr"]
[ext_resource type="Texture2D" uid="uid://b20mhn7ca5xyo" path="res://assets/sprites/aux_terrain.BMP" id="8_auxtr"]
[ext_resource type="Script" uid="uid://d1d1nbetdvynk" path="res://nodes/fog_renderer.gd" id="9_fogrn"]
[ext_resource type="Script" uid="uid://d1d1nbetdvynk" path="res://scripts/battle/map/fog_renderer.gd" id="9_fogrn"]
[sub_resource type="Resource" id="Resource_vcj5e"]
script = ExtResource("2_8rn0j")

View File

@@ -1,12 +1,12 @@
[gd_scene format=3]
[gd_scene format=3 uid="uid://dpcaa8x6xxup0"]
[ext_resource type="Script" path="res://nodes/debug_menu.gd" id="1_script"]
[ext_resource type="Script" uid="uid://c64yr8xvkb5cw" path="res://scripts/debug/debug_menu.gd" id="1_script"]
[node name="DebugMenu" type="CanvasLayer"]
[node name="DebugMenu" type="CanvasLayer" unique_id=240858900]
layer = 100
script = ExtResource("1_script")
[node name="Panel" type="PanelContainer" parent="."]
[node name="Panel" type="PanelContainer" parent="." unique_id=349886438]
unique_name_in_owner = true
anchors_preset = 15
anchor_right = 1.0
@@ -14,46 +14,45 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="Panel"]
[node name="MarginContainer" type="MarginContainer" parent="Panel" unique_id=322235564]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer" unique_id=1731008558]
layout_mode = 2
[node name="TitleLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
[node name="TitleLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=211532752]
layout_mode = 2
text = "Debug Menu"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer"]
[node name="HSeparator" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer" unique_id=1527486356]
layout_mode = 2
[node name="ScenesLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
[node name="ScenesLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=1071710223]
layout_mode = 2
text = "Scenes:"
[node name="SceneList" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
[node name="SceneList" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer" unique_id=1684653951]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator2" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer"]
[node name="HSeparator2" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer" unique_id=487142357]
layout_mode = 2
[node name="ConsoleLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
[node name="ConsoleLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=1165373860]
layout_mode = 2
text = "Console:"
[node name="CommandInput" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer"]
[node name="CommandInput" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer" unique_id=1402278573]
unique_name_in_owner = true
layout_mode = 2
placeholder_text = "Enter command or expression..."
[node name="ResultLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
[node name="ResultLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=881735021]
unique_name_in_owner = true
layout_mode = 2
text = ""
autowrap_mode = 3

View File

@@ -0,0 +1,50 @@
[gd_scene format=3 uid="uid://b6a7nlnf58mc4"]
[ext_resource type="Script" uid="uid://cmh4lphvboggy" path="res://scripts/battle/deployed_units/deployed_unit.gd" id="1_cq4v0"]
[ext_resource type="Texture2D" uid="uid://cw5su6lignryo" path="res://assets/sprites/flag.png" id="2_fhs1y"]
[ext_resource type="Shader" uid="uid://bd8ki8xwym5nc" path="res://shaders/chroma_key.gdshader" id="3_fhs1y"]
[sub_resource type="GDScript" id="GDScript_on614"]
resource_name = "UnitSelectorHandler"
script/source = "extends ColorRect
func _unit_selected_changed(_deployed: DeployedUnit, selected: bool) -> void:
visible = selected
"
[sub_resource type="GDScript" id="GDScript_fhs1y"]
resource_name = "AllegianceIndicatorManager"
script/source = "extends Sprite2D
func _on_unit_unit_allegiance_changed(_deployed: DeployedUnit, allegiance: UnitAllegiance) -> void:
self_modulate = allegiance.color
"
[sub_resource type="ShaderMaterial" id="ShaderMaterial_fhs1y"]
shader = ExtResource("3_fhs1y")
shader_parameter/key_color = Color(0, 1, 0, 1)
shader_parameter/threshold = 0.01
[node name="DeployedUnit" type="Node2D" unique_id=1893234933 groups=["deployed_units"]]
script = ExtResource("1_cq4v0")
metadata/_custom_type_script = "uid://cmh4lphvboggy"
[node name="SelectionIndicator" type="ColorRect" parent="." unique_id=1313394023]
visible = false
offset_right = 100.0
offset_bottom = 100.0
color = Color(1, 1, 0.3019608, 0.36078432)
script = SubResource("GDScript_on614")
[node name="AllegianceIndicator" type="Sprite2D" parent="." unique_id=1567439632]
z_index = 2
texture = ExtResource("2_fhs1y")
offset = Vector2(24, 24)
script = SubResource("GDScript_fhs1y")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1796991032]
material = SubResource("ShaderMaterial_fhs1y")
position = Vector2(50, 50)
[connection signal="unit_allegiance_changed" from="." to="AllegianceIndicator" method="_on_unit_unit_allegiance_changed"]
[connection signal="unit_selected_changed" from="." to="SelectionIndicator" method="_unit_selected_changed"]

View File

@@ -1,6 +1,6 @@
[gd_scene format=3 uid="uid://b6a7nlnf58mc4"]
[gd_scene format=3 uid="uid://dy0s7rfs4i64y"]
[ext_resource type="Script" uid="uid://c016mxgatcpse" path="res://nodes/unit.gd" id="1_cq4v0"]
[ext_resource type="Script" uid="uid://c016mxgatcpse" path="res://scripts/units/unit.gd" id="1_cq4v0"]
[ext_resource type="Texture2D" uid="uid://cw5su6lignryo" path="res://assets/sprites/flag.png" id="2_fhs1y"]
[ext_resource type="Shader" uid="uid://bd8ki8xwym5nc" path="res://shaders/chroma_key.gdshader" id="3_fhs1y"]
[ext_resource type="Texture2D" uid="uid://dyutp4m5d53gd" path="res://assets/sprites/CP002AA.BMP" id="3_on614"]
@@ -18,7 +18,7 @@ resource_name = "AllegianceIndicatorManager"
script/source = "extends Sprite2D
func _on_unit_unit_allegiance_changed(unit: Unit, allegiance: UnitAllegiance) -> void:
func _on_unit_unit_allegiance_changed(_unit: Unit, allegiance: UnitAllegiance) -> void:
self_modulate = allegiance.color
"
@@ -86,6 +86,3 @@ position = Vector2(50, 50)
sprite_frames = SubResource("SpriteFrames_7jqdg")
animation = &"idle"
autoplay = "idle"
[connection signal="unit_allegiance_changed" from="." to="AllegianceIndicator" method="_on_unit_unit_allegiance_changed"]
[connection signal="unit_selected_changed" from="." to="SelectionIndicator" method="_unit_selected_changed"]

View File

@@ -15,6 +15,10 @@ run/main_scene="res://scenes/game.tscn"
config/features=PackedStringArray("4.6", "Mobile")
config/icon="res://icon.svg"
[autoload]
BattleMapHelper="*res://scripts/autoloads/battle_map_helper.gd"
[display]
window/size/viewport_width=800

View File

@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="UnitAllegiance" format=3 uid="uid://cuc7kkknpsr1g"]
[ext_resource type="Script" uid="uid://bhglsexm8dtpj" path="res://resources/resource_definitions/unit_allegiance.gd" id="1_40cg2"]
[ext_resource type="Script" uid="uid://bhglsexm8dtpj" path="res://scripts/units/unit_allegiance.gd" id="1_40cg2"]
[resource]
script = ExtResource("1_40cg2")

View File

@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="UnitAllegiance" format=3 uid="uid://dufi2h00j5vrq"]
[ext_resource type="Script" uid="uid://bhglsexm8dtpj" path="res://resources/resource_definitions/unit_allegiance.gd" id="1_4mkdx"]
[ext_resource type="Script" uid="uid://bhglsexm8dtpj" path="res://scripts/units/unit_allegiance.gd" id="1_4mkdx"]
[resource]
script = ExtResource("1_4mkdx")

View File

@@ -1,10 +0,0 @@
class_name AttackCombatTactic extends CombatTactic
func get_offensive_stats(unit: Unit) -> Variant:
return {"atk": unit.current_stats.phys_atk, "hit": unit.current_stats.hit}
func get_relevant_defense(unit: Unit) -> int:
return unit.current_stats.phys_def
func deals_damage() -> bool:
return true

View File

@@ -1,10 +0,0 @@
class_name ConsoleCommand extends RefCounted
func get_command_name() -> String:
return ""
func get_help_text() -> String:
return ""
func run(args: Array, context: Dictionary) -> String:
return ""

View File

@@ -1,10 +0,0 @@
class_name DefendCombatTactic extends CombatTactic
func get_offensive_stats(unit: Unit) -> Variant:
return null
func get_relevant_defense(unit: Unit) -> int:
return unit.current_stats.phys_def
func deals_damage() -> bool:
return false

View File

@@ -0,0 +1,178 @@
[gd_resource type="Resource" script_class="UnitAppearanceSet" format=3 uid="uid://c18djmm6orf5y"]
[ext_resource type="Script" uid="uid://divxkbo321ql" path="res://scripts/units/unit_appearance_set.gd" id="1_am7go"]
[ext_resource type="Texture2D" uid="uid://b6smsdyydtiv4" path="res://assets/sprites/CP002AB.BMP" id="1_cdqv0"]
[ext_resource type="Texture2D" uid="uid://dyutp4m5d53gd" path="res://assets/sprites/CP002AA.BMP" id="2_3pgcd"]
[sub_resource type="AtlasTexture" id="AtlasTexture_4hgk0"]
atlas = ExtResource("1_cdqv0")
region = Rect2(0, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_scnul"]
atlas = ExtResource("1_cdqv0")
region = Rect2(40, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_ec51f"]
atlas = ExtResource("1_cdqv0")
region = Rect2(80, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_ix7mx"]
atlas = ExtResource("1_cdqv0")
region = Rect2(120, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_su6vo"]
atlas = ExtResource("2_3pgcd")
region = Rect2(0, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_6idlb"]
atlas = ExtResource("2_3pgcd")
region = Rect2(40, 0, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_ui8f1"]
atlas = ExtResource("2_3pgcd")
region = Rect2(0, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_wk1hp"]
atlas = ExtResource("2_3pgcd")
region = Rect2(40, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_p4e4i"]
atlas = ExtResource("1_cdqv0")
region = Rect2(0, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_knbtp"]
atlas = ExtResource("1_cdqv0")
region = Rect2(40, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_ni45v"]
atlas = ExtResource("1_cdqv0")
region = Rect2(80, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_a51r0"]
atlas = ExtResource("1_cdqv0")
region = Rect2(120, 50, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_pvxdl"]
atlas = ExtResource("1_cdqv0")
region = Rect2(0, 150, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_qle0x"]
atlas = ExtResource("1_cdqv0")
region = Rect2(40, 150, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_iork5"]
atlas = ExtResource("1_cdqv0")
region = Rect2(80, 150, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_hbqxy"]
atlas = ExtResource("1_cdqv0")
region = Rect2(120, 150, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_e4j68"]
atlas = ExtResource("1_cdqv0")
region = Rect2(0, 100, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_n1qde"]
atlas = ExtResource("1_cdqv0")
region = Rect2(40, 100, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_de2lv"]
atlas = ExtResource("1_cdqv0")
region = Rect2(80, 100, 40, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_1nls0"]
atlas = ExtResource("1_cdqv0")
region = Rect2(120, 100, 40, 50)
[sub_resource type="SpriteFrames" id="SpriteFrames_psufo"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_4hgk0")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_scnul")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_ec51f")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_ix7mx")
}],
"loop": true,
"name": &"down",
"speed": 8.0
}, {
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_su6vo")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_6idlb")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_ui8f1")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_wk1hp")
}],
"loop": true,
"name": &"idle",
"speed": 8.0
}, {
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_p4e4i")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_knbtp")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_ni45v")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_a51r0")
}],
"loop": true,
"name": &"left",
"speed": 8.0
}, {
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_pvxdl")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_qle0x")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_iork5")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_hbqxy")
}],
"loop": true,
"name": &"right",
"speed": 8.0
}, {
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_e4j68")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_n1qde")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_de2lv")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_1nls0")
}],
"loop": true,
"name": &"up",
"speed": 8.0
}]
[resource]
script = ExtResource("1_am7go")
deployed_sprite_sheet = SubResource("SpriteFrames_psufo")
metadata/_custom_type_script = "uid://divxkbo321ql"

View File

@@ -0,0 +1,33 @@
[gd_resource type="Resource" script_class="Unit" format=3 uid="uid://sqrlba15ncyr"]
[ext_resource type="Script" uid="uid://c016mxgatcpse" path="res://scripts/units/unit.gd" id="1_bqd3m"]
[ext_resource type="Script" uid="uid://divxkbo321ql" path="res://scripts/units/unit_appearance_set.gd" id="1_lko56"]
[ext_resource type="Script" uid="uid://b67rtbb5gixus" path="res://scripts/battle/combat_tactics/combat_tactic.gd" id="2_0tmvt"]
[ext_resource type="Resource" uid="uid://c18djmm6orf5y" path="res://resources/units/appearance_sets/lily_child_deployed.tres" id="2_f8ij3"]
[ext_resource type="Script" uid="uid://b402hsmbaj536" path="res://scripts/units/unit_appearance.gd" id="2_nxnrh"]
[ext_resource type="Script" uid="uid://d37ulss2k0bq5" path="res://scripts/units/unit_info.gd" id="3_f8ij3"]
[ext_resource type="Script" uid="uid://cydoey8a8nmb8" path="res://scripts/units/unit_stats.gd" id="5_rqhbp"]
[sub_resource type="Resource" id="Resource_ki4ax"]
script = ExtResource("2_nxnrh")
appearance_sets = Dictionary[String, ExtResource("1_lko56")]({
"default": ExtResource("2_f8ij3")
})
metadata/_custom_type_script = "uid://b402hsmbaj536"
[sub_resource type="Resource" id="Resource_q2jxx"]
script = ExtResource("3_f8ij3")
name = "Lily"
metadata/_custom_type_script = "uid://d37ulss2k0bq5"
[sub_resource type="Resource" id="Resource_nc6h6"]
script = ExtResource("5_rqhbp")
max_hp = 20
metadata/_custom_type_script = "uid://cydoey8a8nmb8"
[resource]
script = ExtResource("1_bqd3m")
stats = SubResource("Resource_nc6h6")
info = SubResource("Resource_q2jxx")
appearance = SubResource("Resource_ki4ax")
metadata/_custom_type_script = "uid://c016mxgatcpse"

View File

@@ -1,6 +1,6 @@
[gd_scene format=3 uid="uid://gfrxev22t0bc"]
[ext_resource type="Script" uid="uid://ifv6cww6fk6c" path="res://nodes/game.gd" id="1_script"]
[ext_resource type="Script" uid="uid://ifv6cww6fk6c" path="res://scripts/game.gd" id="1_script"]
[ext_resource type="PackedScene" path="res://prefabs/debug_menu.tscn" id="2_debug_menu"]
[node name="Game" type="Node" unique_id=906681388]

View File

@@ -4,6 +4,7 @@
[ext_resource type="Texture2D" uid="uid://c7e4jw4xcti0q" path="res://assets/sprites/castle_spritesheet.png" id="1_g7g4h"]
[ext_resource type="Shader" uid="uid://dakre5usldk6r" path="res://shaders/masked_palette_swap.gdshader" id="1_nd71p"]
[ext_resource type="Texture2D" uid="uid://b8td6sv5re6r8" path="res://assets/sprites/grey_castle_spritesheet_mask.bmp" id="2_7ddre"]
[ext_resource type="Texture2D" uid="uid://b20mhn7ca5xyo" path="res://assets/sprites/aux_terrain.BMP" id="5_qjeyg"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_qjeyg"]
texture = ExtResource("1_7ddre")
@@ -48,6 +49,10 @@ shader_parameter/chroma_threshold = 0.10000000475
atlas = ExtResource("1_g7g4h")
region = Rect2(0, 100, 100, 100)
[sub_resource type="AtlasTexture" id="AtlasTexture_3qnke"]
atlas = ExtResource("5_qjeyg")
region = Rect2(210, 0, 41, 32)
[node name="TestScene" type="Node2D" unique_id=1687841395]
[node name="TileMapLayer" type="TileMapLayer" parent="." unique_id=265586128]
@@ -58,3 +63,6 @@ tile_set = SubResource("TileSet_3qnke")
material = SubResource("ShaderMaterial_qjeyg")
position = Vector2(-150, -148)
texture = SubResource("AtlasTexture_j8ivh")
[node name="Sprite2D2" type="Sprite2D" parent="." unique_id=1517711877]
texture = SubResource("AtlasTexture_3qnke")

View File

@@ -1,10 +1,10 @@
[gd_scene format=3 uid="uid://wy7ur5r23ek3"]
[ext_resource type="Script" uid="uid://dnsqtsx4u2hx4" path="res://nodes/strategy_phase.gd" id="1_qs1ys"]
[ext_resource type="Script" uid="uid://dnsqtsx4u2hx4" path="res://scripts/battle/strategy_phase.gd" id="1_qs1ys"]
[ext_resource type="PackedScene" uid="uid://cy7r0udfcsqbn" path="res://prefabs/combat_ui.tscn" id="2_4s0rq"]
[ext_resource type="PackedScene" uid="uid://dkhyh5ce4iuk3" path="res://prefabs/combat_map.tscn" id="3_n1a8d"]
[ext_resource type="Script" uid="uid://dfojm3n0em4ef" path="res://nodes/player_controller.gd" id="4_208pr"]
[ext_resource type="Script" uid="uid://cf4ivrcbky0s3" path="res://nodes/combat_system.gd" id="5_n11my"]
[ext_resource type="Script" uid="uid://dfojm3n0em4ef" path="res://scripts/battle/player_controller.gd" id="4_208pr"]
[ext_resource type="Script" uid="uid://cf4ivrcbky0s3" path="res://scripts/battle/combat_engine/combat_system.gd" id="5_n11my"]
[ext_resource type="Script" uid="uid://csdcbi2gtwrly" path="res://scripts/battle/camera_controller.gd" id="6_m48os"]
[ext_resource type="AudioStream" uid="uid://dsikulned64qt" path="res://assets/music/combat_bgm_01.OGG" id="7_oih6t"]

View File

@@ -76,27 +76,21 @@ resource_name = "StartButton"
script/source = "extends TextureButton
const COMBAT_SCENE = preload(\"res://scenes/views/battle_view.tscn\")
const UNIT_SCENE = preload(\"res://prefabs/unit.tscn\")
const PLAYER_ALLEGIANCE = preload(\"res://resources/allegiance_types/player_allegiance.tres\")
const ENEMY_ALLEGIANCE = preload(\"res://resources/allegiance_types/enemy_allegiance.tres\")
const LILY_CHILD = preload(\"res://resources/units/lily_child.tres\")
func _pressed() -> void:
await get_tree().create_timer(0.2).timeout
var combat_instance := COMBAT_SCENE.instantiate()
var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\")
var player_unit: Unit = UNIT_SCENE.instantiate()
player_unit.stat_template = UnitStats.new(50)
player_unit.info_template = UnitInfo.new()
player_unit.info_template.name = \"Putit\"
player_unit.allegiance_template = PLAYER_ALLEGIANCE
var player_unit: Unit = LILY_CHILD.duplicate(true)
player_unit.allegiance = PLAYER_ALLEGIANCE
combat_map.deploy_unit(player_unit, Vector2i(3, 3))
var enemy_unit: Unit = UNIT_SCENE.instantiate()
enemy_unit.stat_template = UnitStats.new(50)
enemy_unit.info_template = UnitInfo.new()
enemy_unit.info_template.name = \"Putit\"
enemy_unit.allegiance_template = ENEMY_ALLEGIANCE
var enemy_unit: Unit = LILY_CHILD.duplicate(true)
enemy_unit.allegiance = ENEMY_ALLEGIANCE
combat_map.deploy_unit(enemy_unit, Vector2i(6, 3))
var tree := get_tree()

View File

@@ -0,0 +1,461 @@
[gd_scene format=3 uid="uid://dlbuo46n6q238"]
[ext_resource type="Theme" uid="uid://dx26d6py3n8xi" path="res://resources/main_ui_theme.tres" id="1_wmt4g"]
[ext_resource type="AudioStream" uid="uid://b7dgmblbcm0cj" path="res://assets/music/menu_theme.OGG" id="2_0dhhe"]
[ext_resource type="Texture2D" uid="uid://b47b6tt142b25" path="res://assets/sprites/main_menu.BMP" id="3_xgjk6"]
[ext_resource type="AudioStream" uid="uid://5ndo4w06umsa" path="res://assets/sounds/SE020.WAV" id="4_somrw"]
[ext_resource type="AudioStream" uid="uid://d1hacs4t5qni1" path="res://assets/sounds/SE015.WAV" id="5_ybnw1"]
[ext_resource type="Texture2D" uid="uid://8kr4vmvhu03p" path="res://assets/sprites/menu_selector_flame.BMP" id="6_5jfhr"]
[sub_resource type="AtlasTexture" id="AtlasTexture_wu84c"]
atlas = ExtResource("3_xgjk6")
region = Rect2(0, 0, 800, 400)
[sub_resource type="AtlasTexture" id="AtlasTexture_8ln24"]
atlas = ExtResource("3_xgjk6")
region = Rect2(0, 600, 800, 348)
[sub_resource type="AtlasTexture" id="AtlasTexture_a8gd2"]
atlas = ExtResource("3_xgjk6")
region = Rect2(800, 0, 745, 745)
[sub_resource type="AtlasTexture" id="AtlasTexture_bqqt6"]
atlas = ExtResource("3_xgjk6")
region = Rect2(-1, 995, 800, 43)
[sub_resource type="AtlasTexture" id="AtlasTexture_rtw2f"]
atlas = ExtResource("3_xgjk6")
region = Rect2(0, 950, 800, 45)
[sub_resource type="AtlasTexture" id="AtlasTexture_oa1go"]
atlas = ExtResource("3_xgjk6")
region = Rect2(800, 744, 515, 210)
[sub_resource type="GDScript" id="GDScript_hover"]
resource_name = "ButtonHoverSFX"
script/source = "extends VBoxContainer
@onready var hover_sfx: AudioStreamPlayer = $HoverSFX
@onready var left_indicator: Control = %LeftIndicator
@onready var right_indicator: Control = %RightIndicator
@onready var click_sfx: AudioStreamPlayer = $ClickSFX
func _ready() -> void:
for child in get_children():
if child is TextureButton:
child.mouse_entered.connect(_on_button_hovered.bind(child))
child.mouse_exited.connect(_on_button_unhovered)
child.pressed.connect(_on_button_clicked)
func _on_button_hovered(button: TextureButton) -> void:
hover_sfx.play()
var button_center := button.global_position + button.size / 2.0
left_indicator.global_position = Vector2(button.global_position.x, button_center.y)
right_indicator.global_position = Vector2(button.global_position.x + button.size.x, button_center.y)
left_indicator.visible = true
right_indicator.visible = true
func _on_button_unhovered() -> void:
left_indicator.visible = false
right_indicator.visible = false
func _on_button_clicked() -> void:
click_sfx.play()
"
[sub_resource type="AtlasTexture" id="AtlasTexture_tbmy8"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 0, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_jk1qb"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 300, 330, 50)
[sub_resource type="GDScript" id="GDScript_bqqt6"]
resource_name = "StartButton"
script/source = "extends TextureButton
const COMBAT_SCENE = preload(\"res://scenes/views/battle_view.tscn\")
const UNIT_SCENE = preload(\"res://prefabs/unit.tscn\")
const PLAYER_ALLEGIANCE = preload(\"res://resources/allegiance_types/player_allegiance.tres\")
const ENEMY_ALLEGIANCE = preload(\"res://resources/allegiance_types/enemy_allegiance.tres\")
func _pressed() -> void:
await get_tree().create_timer(0.2).timeout
var combat_instance := COMBAT_SCENE.instantiate()
var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\")
var player_unit: Unit = UNIT_SCENE.instantiate()
player_unit.stat_template = UnitStats.new()
player_unit.info_template = UnitInfo.new()
player_unit.info_template.name = \"Putit\"
player_unit.allegiance_template = PLAYER_ALLEGIANCE
combat_map.deploy_unit(player_unit, Vector2i(3, 3))
var enemy_unit: Unit = UNIT_SCENE.instantiate()
enemy_unit.stat_template = UnitStats.new()
enemy_unit.info_template = UnitInfo.new()
enemy_unit.info_template.name = \"Putit\"
enemy_unit.allegiance_template = ENEMY_ALLEGIANCE
combat_map.deploy_unit(enemy_unit, Vector2i(6, 3))
var tree := get_tree()
var root := tree.root
var current_scene := tree.current_scene
root.remove_child(current_scene)
current_scene.queue_free()
root.add_child(combat_instance)
tree.current_scene = combat_instance
"
[sub_resource type="AtlasTexture" id="AtlasTexture_5dd4i"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 60, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_lgwnu"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 360, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_flqon"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 120, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_rcqid"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 420, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_1ajci"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 180, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_7b55j"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 480, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_5pajh"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 240, 330, 50)
[sub_resource type="AtlasTexture" id="AtlasTexture_j7ex8"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1550, 540, 330, 50)
[sub_resource type="GDScript" id="GDScript_wu84c"]
resource_name = "ExitButton"
script/source = "extends TextureButton
func _pressed():
await get_tree().create_timer(0.2).timeout
get_tree().quit(0)
"
[sub_resource type="AtlasTexture" id="AtlasTexture_tcusk"]
atlas = ExtResource("3_xgjk6")
region = Rect2(1320, 746, 25, 22)
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_8ln24"]
blend_mode = 1
[sub_resource type="AtlasTexture" id="AtlasTexture_8egab"]
atlas = ExtResource("6_5jfhr")
region = Rect2(0, 0, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_gw5y6"]
atlas = ExtResource("6_5jfhr")
region = Rect2(140, 0, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_svtp6"]
atlas = ExtResource("6_5jfhr")
region = Rect2(280, 0, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_1dfpl"]
atlas = ExtResource("6_5jfhr")
region = Rect2(420, 0, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_qywvv"]
atlas = ExtResource("6_5jfhr")
region = Rect2(0, 140, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_3wgol"]
atlas = ExtResource("6_5jfhr")
region = Rect2(140, 140, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_1acrt"]
atlas = ExtResource("6_5jfhr")
region = Rect2(280, 140, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_vr8o3"]
atlas = ExtResource("6_5jfhr")
region = Rect2(420, 140, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_1a85y"]
atlas = ExtResource("6_5jfhr")
region = Rect2(0, 280, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_hl5e0"]
atlas = ExtResource("6_5jfhr")
region = Rect2(140, 280, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_engjn"]
atlas = ExtResource("6_5jfhr")
region = Rect2(280, 280, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_6h3lr"]
atlas = ExtResource("6_5jfhr")
region = Rect2(420, 280, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_dj67d"]
atlas = ExtResource("6_5jfhr")
region = Rect2(0, 420, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_6vcge"]
atlas = ExtResource("6_5jfhr")
region = Rect2(140, 420, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_ip0br"]
atlas = ExtResource("6_5jfhr")
region = Rect2(280, 420, 140, 140)
[sub_resource type="AtlasTexture" id="AtlasTexture_xyero"]
atlas = ExtResource("6_5jfhr")
region = Rect2(420, 420, 140, 140)
[sub_resource type="SpriteFrames" id="SpriteFrames_tcusk"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_8egab")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_gw5y6")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_svtp6")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_1dfpl")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_qywvv")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_3wgol")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_1acrt")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_vr8o3")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_1a85y")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_hl5e0")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_engjn")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_6h3lr")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_dj67d")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_6vcge")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_ip0br")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_xyero")
}],
"loop": true,
"name": &"default",
"speed": 10.0
}]
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_rtw2f"]
blend_mode = 1
[sub_resource type="AtlasTexture" id="AtlasTexture_hstxw"]
[node name="MainMenu" type="Control" unique_id=528000941]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_wmt4g")
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="." unique_id=1976575731]
stream = ExtResource("2_0dhhe")
autoplay = true
parameters/looping = true
[node name="Background" type="Control" parent="." unique_id=801579001]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Top" type="TextureRect" parent="Background" unique_id=2030397311]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 400.0
grow_horizontal = 2
texture = SubResource("AtlasTexture_wu84c")
[node name="Bottom" type="TextureRect" parent="Background" unique_id=736979824]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -348.0
grow_horizontal = 2
grow_vertical = 0
texture = SubResource("AtlasTexture_8ln24")
[node name="MagicCircle" type="TextureRect" parent="Background" unique_id=1610277203]
self_modulate = Color(1, 1, 1, 0.6156863)
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -72.5
offset_bottom = 72.5
grow_horizontal = 2
grow_vertical = 2
texture = SubResource("AtlasTexture_a8gd2")
[node name="BottomBorder" type="TextureRect" parent="Background" unique_id=2048064934]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -45.0
grow_horizontal = 2
grow_vertical = 0
texture = SubResource("AtlasTexture_bqqt6")
[node name="TopBorder" type="TextureRect" parent="Background" unique_id=812827884]
layout_mode = 0
offset_right = 800.0
offset_bottom = 45.0
texture = SubResource("AtlasTexture_rtw2f")
[node name="Logo" type="TextureRect" parent="Background" unique_id=815631332]
layout_mode = 1
anchors_preset = -1
anchor_left = 0.5
anchor_right = 0.5
offset_left = -257.5
offset_top = 50.0
offset_right = 257.5
offset_bottom = 210.0
grow_horizontal = 2
texture = SubResource("AtlasTexture_oa1go")
[node name="Buttons" type="VBoxContainer" parent="." unique_id=1869378860]
layout_mode = 1
anchors_preset = -1
anchor_left = 0.29375
anchor_top = 0.5566667
anchor_right = 0.70625
anchor_bottom = 1.0
offset_left = 1.5258789e-05
offset_bottom = -55.0
grow_horizontal = 2
grow_vertical = 0
alignment = 1
script = SubResource("GDScript_hover")
metadata/_edit_use_anchors_ = true
[node name="HoverSFX" type="AudioStreamPlayer" parent="Buttons" unique_id=256435189]
stream = ExtResource("4_somrw")
[node name="ClickSFX" type="AudioStreamPlayer" parent="Buttons" unique_id=2129807302]
stream = ExtResource("5_ybnw1")
[node name="StartButton" type="TextureButton" parent="Buttons" unique_id=973041905]
layout_mode = 2
size_flags_horizontal = 4
texture_normal = SubResource("AtlasTexture_tbmy8")
texture_hover = SubResource("AtlasTexture_jk1qb")
script = SubResource("GDScript_bqqt6")
[node name="LoadButton" type="TextureButton" parent="Buttons" unique_id=2075751086]
layout_mode = 2
size_flags_horizontal = 4
texture_normal = SubResource("AtlasTexture_5dd4i")
texture_hover = SubResource("AtlasTexture_lgwnu")
[node name="EushullyButton" type="TextureButton" parent="Buttons" unique_id=412756984]
layout_mode = 2
size_flags_horizontal = 4
texture_normal = SubResource("AtlasTexture_flqon")
texture_hover = SubResource("AtlasTexture_rcqid")
[node name="OptionsButton" type="TextureButton" parent="Buttons" unique_id=1002907774]
layout_mode = 2
size_flags_horizontal = 4
texture_normal = SubResource("AtlasTexture_1ajci")
texture_hover = SubResource("AtlasTexture_7b55j")
[node name="ExitButton" type="TextureButton" parent="Buttons" unique_id=286651369]
layout_mode = 2
size_flags_horizontal = 4
texture_normal = SubResource("AtlasTexture_5pajh")
texture_hover = SubResource("AtlasTexture_j7ex8")
script = SubResource("GDScript_wu84c")
[node name="LeftIndicator" type="Control" parent="." unique_id=100000001]
unique_name_in_owner = true
visible = false
anchors_preset = 0
offset_right = 50.0
offset_bottom = 50.0
[node name="Sprite2D" type="Sprite2D" parent="LeftIndicator" unique_id=1510731086]
position = Vector2(0, 15)
texture = SubResource("AtlasTexture_tcusk")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="LeftIndicator" unique_id=1906133697]
material = SubResource("CanvasItemMaterial_8ln24")
scale = Vector2(0.75, 0.75)
sprite_frames = SubResource("SpriteFrames_tcusk")
autoplay = "default"
frame = 12
frame_progress = 0.23836201
[node name="RightIndicator" type="Control" parent="." unique_id=100000002]
unique_name_in_owner = true
visible = false
anchors_preset = 0
offset_right = 50.0
offset_bottom = 50.0
[node name="Sprite2D" type="Sprite2D" parent="RightIndicator" unique_id=979863490]
position = Vector2(0, 15)
texture = SubResource("AtlasTexture_tcusk")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="RightIndicator" unique_id=1047794140]
material = SubResource("CanvasItemMaterial_rtw2f")
scale = Vector2(0.75, 0.75)
sprite_frames = SubResource("SpriteFrames_tcusk")
autoplay = "default"
frame = 12
frame_progress = 0.23836201
[node name="TextureRect" type="TextureRect" parent="." unique_id=968381019]
layout_mode = 0
offset_right = 40.0
offset_bottom = 40.0
texture = SubResource("AtlasTexture_hstxw")

View File

@@ -0,0 +1,17 @@
extends Node
var TILE_SIZE: float:
get:
return BattleMapConstants.TILE_SIZE
func snap_to_grid(pos: Vector2) -> Vector2:
return Vector2(floorf(pos.x / TILE_SIZE), floorf(pos.y / TILE_SIZE)) * TILE_SIZE
func world_to_coords(pos: Vector2) -> Vector2i:
return Vector2i(snap_to_grid(pos) / TILE_SIZE)
func coords_to_world(coords: Vector2i) -> Vector2:
return Vector2(coords) * TILE_SIZE

View File

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

View File

@@ -1,7 +1,7 @@
class_name CombatProposal extends Resource
class CombatantStats:
var unit: Unit
var deployed: DeployedUnit
var max_hp: int
var hp: int
var sp: int

View File

@@ -1,6 +1,6 @@
class_name CombatSystem extends Node
func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatProposal:
func create_proposal(attacker: DeployedUnit, defender: DeployedUnit, distance: int) -> CombatProposal:
var proposal := CombatProposal.new()
var atk_tactics := _filter_tactics(attacker, distance)
@@ -19,10 +19,10 @@ func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatPro
return proposal
func _filter_tactics(unit: Unit, distance: int) -> Array[CombatTactic]:
func _filter_tactics(deployed: DeployedUnit, distance: int) -> Array[CombatTactic]:
var valid: Array[CombatTactic] = []
for tactic in unit.tactics:
if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, unit):
for tactic in deployed.tactics:
if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, deployed.current_stats):
valid.append(tactic)
return valid
@@ -34,28 +34,30 @@ func _find_default_attack(tactics: Array[CombatTactic]) -> CombatTactic:
return tactics[0] if tactics.size() > 0 else null
func _snapshot(unit: Unit, opponent: Unit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats:
func _snapshot(deployed: DeployedUnit, opponent: DeployedUnit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats:
var current := deployed.current_stats
var opp_current := opponent.current_stats
var stats := CombatProposal.CombatantStats.new()
stats.unit = unit
stats.max_hp = unit.current_stats.max_hp
stats.hp = unit.current_stats.current_hp
stats.sp = unit.current_stats.current_sp
stats.spd = unit.current_stats.spd
stats.deployed = deployed
stats.max_hp = current.max_hp
stats.hp = current.current_hp
stats.sp = current.current_sp
stats.spd = current.spd
stats.available_tactics = available
stats.selected_tactic = selected
if selected and selected.deals_damage():
var offensive: Dictionary = selected.get_offensive_stats(unit)
var offensive: Dictionary = selected.get_offensive_stats(current)
stats.atk = offensive["atk"]
stats.hit = offensive["hit"] - opponent.current_stats.eva
stats.hit = offensive["hit"] - opp_current.eva
else:
stats.atk = 0
stats.hit = 0
if opponent_selected and opponent_selected.deals_damage():
stats.def = opponent_selected.get_relevant_defense(unit)
stats.def = opponent_selected.get_relevant_defense(current)
else:
stats.def = unit.current_stats.phys_def
stats.def = current.phys_def
return stats
@@ -74,29 +76,29 @@ func update_tactic(proposal: CombatProposal, is_attacker: bool, tactic: CombatTa
# Recalculate this side's offensive stats
if tactic and tactic.deals_damage():
var offensive: Dictionary = tactic.get_offensive_stats(self_stats.unit)
var offensive: Dictionary = tactic.get_offensive_stats(self_stats.deployed.current_stats)
self_stats.atk = offensive["atk"]
self_stats.hit = offensive["hit"] - opp_stats.unit.current_stats.eva
self_stats.hit = offensive["hit"] - opp_stats.deployed.current_stats.eva
else:
self_stats.atk = 0
self_stats.hit = 0
# Recalculate opponent's def based on this side's new tactic
if tactic and tactic.deals_damage():
opp_stats.def = tactic.get_relevant_defense(opp_stats.unit)
opp_stats.def = tactic.get_relevant_defense(opp_stats.deployed.current_stats)
else:
opp_stats.def = opp_stats.unit.current_stats.phys_def
opp_stats.def = opp_stats.deployed.current_stats.phys_def
func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[CombatTactic]) -> CombatTactic:
func select_ai_tactic(deployed: DeployedUnit, opponent: DeployedUnit, available_tactics: Array[CombatTactic]) -> CombatTactic:
var best_tactic: CombatTactic = null
var best_damage := -1
for tactic in available_tactics:
if not tactic.deals_damage():
continue
var offensive: Dictionary = tactic.get_offensive_stats(unit)
var defense: int = tactic.get_relevant_defense(opponent)
var offensive: Dictionary = tactic.get_offensive_stats(deployed.current_stats)
var defense: int = tactic.get_relevant_defense(opponent.current_stats)
var damage := maxi(offensive["atk"] - defense, 0)
if damage > best_damage:
best_damage = damage
@@ -114,10 +116,10 @@ func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[Comba
func apply_proposal(proposal: CombatProposal) -> void:
var atk_stats := proposal.attacker
var def_stats := proposal.defender
var atk_unit := atk_stats.unit
var def_unit := def_stats.unit
var atk_deployed := atk_stats.deployed
var def_deployed := def_stats.deployed
if not is_instance_valid(atk_unit) or not is_instance_valid(def_unit):
if not is_instance_valid(atk_deployed) or not is_instance_valid(def_deployed):
return
# Attacker strikes (if their tactic deals damage)
@@ -125,17 +127,17 @@ func apply_proposal(proposal: CombatProposal) -> void:
var atk_roll := randi_range(1, 100)
if atk_roll <= atk_stats.hit:
var damage := maxi(atk_stats.atk - def_stats.def, 0)
def_unit.take_damage(damage)
def_deployed.take_damage(damage)
# Counterattack if defender survives and their tactic deals damage
if is_instance_valid(def_unit) and def_unit.is_alive() \
and is_instance_valid(atk_unit) \
if is_instance_valid(def_deployed) and def_deployed.is_alive() \
and is_instance_valid(atk_deployed) \
and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage():
var def_roll := randi_range(1, 100)
if def_roll <= def_stats.hit:
var damage := maxi(def_stats.atk - atk_stats.def, 0)
atk_unit.take_damage(damage)
atk_deployed.take_damage(damage)
func _is_player_controlled(unit: Unit) -> bool:
return unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER
func _is_player_controlled(deployed: DeployedUnit) -> bool:
return deployed.unit.allegiance.type == UnitAllegiance.AllegianceType.PLAYER

View File

@@ -3,11 +3,11 @@ class_name CombatTactic extends Resource
@export var tactic_name: String = ""
@export var tactic_range: CombatTacticRange
func get_offensive_stats(unit: Unit) -> Variant:
func get_offensive_stats(_stats: DeployedUnitStats) -> Variant:
return null
func get_relevant_defense(unit: Unit) -> int:
return unit.current_stats.phys_def
func get_relevant_defense(stats: DeployedUnitStats) -> int:
return stats.phys_def
func deals_damage() -> bool:
return false

View File

@@ -1,5 +1,5 @@
# resources/resource_definitions/any_combat_tactic_range.gd
class_name AnyCombatTacticRange extends CombatTacticRange
func is_valid_range(distance: int, unit: Unit) -> bool:
func is_valid_range(_distance: int, _stats: DeployedUnitStats) -> bool:
return true

View File

@@ -1,5 +1,5 @@
# resources/resource_definitions/combat_tactic_range.gd
class_name CombatTacticRange extends Resource
func is_valid_range(distance: int, unit: Unit) -> bool:
func is_valid_range(_distance: int, _stats: DeployedUnitStats) -> bool:
return false

View File

@@ -3,5 +3,5 @@ class_name FixedCombatTacticRange extends CombatTacticRange
@export var tactic_range: int = 1
func is_valid_range(distance: int, unit: Unit) -> bool:
func is_valid_range(distance: int, _stats: DeployedUnitStats) -> bool:
return distance <= tactic_range

View File

@@ -1,5 +1,5 @@
# resources/resource_definitions/unit_matching_combat_tactic_range.gd
class_name UnitMatchingCombatTacticRange extends CombatTacticRange
func is_valid_range(distance: int, unit: Unit) -> bool:
return distance <= unit.current_stats.atk_range
func is_valid_range(distance: int, stats: DeployedUnitStats) -> bool:
return distance <= stats.atk_range

View File

@@ -0,0 +1,10 @@
class_name AttackCombatTactic extends CombatTactic
func get_offensive_stats(stats: DeployedUnitStats) -> Variant:
return {"atk": stats.phys_atk, "hit": stats.hit}
func get_relevant_defense(stats: DeployedUnitStats) -> int:
return stats.phys_def
func deals_damage() -> bool:
return true

View File

@@ -0,0 +1,10 @@
class_name DefendCombatTactic extends CombatTactic
func get_offensive_stats(_stats: DeployedUnitStats) -> Variant:
return null
func get_relevant_defense(stats: DeployedUnitStats) -> int:
return stats.phys_def
func deals_damage() -> bool:
return false

View File

@@ -26,7 +26,7 @@ signal fight_cancelled
@onready var atk_tactic_select: OptionButton = %AttackerTacticSelect
@onready var def_tactic_select: OptionButton = %DefenderTacticSelect
var _selected_unit: Unit
var _selected_unit: DeployedUnit
var _current_proposal: CombatProposal
var combat_system: CombatSystem
@@ -37,24 +37,24 @@ func _ready() -> void:
cancel_button.pressed.connect(_on_cancel_pressed)
atk_tactic_select.item_selected.connect(_on_atk_tactic_selected)
def_tactic_select.item_selected.connect(_on_def_tactic_selected)
for unit: Unit in get_tree().get_nodes_in_group("units"):
unit.unit_selected_changed.connect(_on_unit_selected_changed)
unit.unit_died.connect(_on_unit_died)
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
deployed.unit_selected_changed.connect(_on_unit_selected_changed)
deployed.unit_died.connect(_on_unit_died)
get_tree().node_added.connect(_on_node_added)
func _on_node_added(node: Node) -> void:
if node is Unit and node.is_in_group("units"):
if node is DeployedUnit and node.is_in_group("deployed_units"):
if not node.unit_selected_changed.is_connected(_on_unit_selected_changed):
node.unit_selected_changed.connect(_on_unit_selected_changed)
if not node.unit_died.is_connected(_on_unit_died):
node.unit_died.connect(_on_unit_died)
func _on_unit_died(unit: Unit) -> void:
if _selected_unit == unit:
func _on_unit_died(deployed: DeployedUnit) -> void:
if _selected_unit == deployed:
_selected_unit = null
unit_panel.visible = false
if _current_proposal:
if _current_proposal.attacker.unit == unit or _current_proposal.defender.unit == unit:
if _current_proposal.attacker.deployed == deployed or _current_proposal.defender.deployed == deployed:
_hide_proposal()
func _process(_delta: float) -> void:
@@ -68,12 +68,12 @@ func _unhandled_input(event: InputEvent) -> void:
fight_cancelled.emit()
get_viewport().set_input_as_handled()
func _on_unit_selected_changed(unit: Unit, selected: bool) -> void:
func _on_unit_selected_changed(deployed: DeployedUnit, selected: bool) -> void:
if selected:
_selected_unit = unit
name_label.text = unit.current_info.name
hp_bar.max_value = unit.current_stats.max_hp
hp_bar.value = unit.current_stats.current_hp
_selected_unit = deployed
name_label.text = deployed.unit.info.name
hp_bar.max_value = deployed.current_stats.max_hp
hp_bar.value = deployed.current_stats.current_hp
unit_panel.visible = true
else:
_selected_unit = null
@@ -112,21 +112,21 @@ func _populate_tactic_select(button: OptionButton, combatant: CombatProposal.Com
if tactic == combatant.selected_tactic:
selected_idx = i
button.selected = selected_idx
var is_player := combatant.unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER
var is_player := combatant.deployed.unit.allegiance.type == UnitAllegiance.AllegianceType.PLAYER
button.disabled = not is_player
func _refresh_stats() -> void:
var atk := _current_proposal.attacker
var def := _current_proposal.defender
atk_name_label.text = atk.unit.current_info.name
atk_name_label.text = atk.deployed.unit.info.name
atk_hp_bar.max_value = atk.max_hp
atk_hp_bar.value = atk.hp
atk_atk_label.text = "ATK: %d" % atk.atk
atk_def_label.text = "DEF: %d" % atk.def
atk_hit_label.text = "HIT: %d%%" % atk.hit
atk_spd_label.text = "SPD: %d" % atk.spd
def_name_label.text = def.unit.current_info.name
def_name_label.text = def.deployed.unit.info.name
def_hp_bar.max_value = def.max_hp
def_hp_bar.value = def.hp
def_atk_label.text = "ATK: %d" % def.atk

View File

@@ -0,0 +1,81 @@
class_name DeployedUnit extends Node2D
enum UnitState { ALIVE, DEAD }
@export var unit: Unit
var current_stats: DeployedUnitStats
var tactics: Array[CombatTactic] = []
var state: UnitState = UnitState.ALIVE
var _sprite: AnimatedSprite2D
var _previous_position: Vector2
signal unit_selected_changed(deployed: DeployedUnit, selected: bool)
signal unit_allegiance_changed(deployed: DeployedUnit, allegiance: UnitAllegiance)
signal unit_died(deployed: DeployedUnit)
func _ready() -> void:
current_stats = DeployedUnitStats.from_unit_stats(unit.stats)
tactics = unit.tactics.duplicate()
_append_builtin_tactics()
unit_allegiance_changed.emit(self, unit.allegiance)
_previous_position = position
_setup_appearance()
func _setup_appearance() -> void:
_sprite = get_node_or_null("AnimatedSprite2D") as AnimatedSprite2D
if not _sprite:
return
var sprite_frames: SpriteFrames = unit.appearance.deployed_sprite_sheet if unit.appearance else null
if not sprite_frames:
return
_sprite.sprite_frames = sprite_frames
_sprite.play("idle")
func _physics_process(_delta: float) -> void:
if not _sprite or not _sprite.sprite_frames:
return
var delta_pos := position - _previous_position
_previous_position = position
if delta_pos.length_squared() < 0.01:
if _sprite.animation != &"idle":
_sprite.play("idle")
return
var anim: StringName
if absf(delta_pos.x) >= absf(delta_pos.y):
anim = &"right" if delta_pos.x > 0 else &"left"
else:
anim = &"down" if delta_pos.y > 0 else &"up"
if _sprite.animation != anim:
_sprite.play(anim)
func _append_builtin_tactics() -> void:
var attack := AttackCombatTactic.new()
attack.tactic_name = "Attack"
attack.tactic_range = UnitMatchingCombatTacticRange.new()
tactics.append(attack)
var defend := DefendCombatTactic.new()
defend.tactic_name = "Defend"
defend.tactic_range = AnyCombatTacticRange.new()
tactics.append(defend)
func set_selected(selected: bool) -> void:
unit_selected_changed.emit(self, selected)
func is_alive() -> bool:
return state == UnitState.ALIVE
func take_damage(amount: int) -> void:
if state != UnitState.ALIVE:
return
current_stats.current_hp -= amount
if current_stats.current_hp <= 0:
current_stats.current_hp = 0
_die()
func _die() -> void:
state = UnitState.DEAD
unit_died.emit(self)
queue_free()

View File

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

View File

@@ -0,0 +1,43 @@
class_name DeployedUnitStats extends Resource
@export var unit_stats: UnitStats
@export var current_hp: int
@export var current_sp: int
@export var current_fs: int
# Passthrough accessors. Future buff/debuff layers can override these
# without mutating the underlying UnitStats template.
var max_hp: int:
get: return unit_stats.max_hp
var max_sp: int:
get: return unit_stats.max_sp
var max_fs: int:
get: return unit_stats.max_fs
var phys_atk: int:
get: return unit_stats.phys_atk
var phys_def: int:
get: return unit_stats.phys_def
var magic_atk: int:
get: return unit_stats.magic_atk
var magic_def: int:
get: return unit_stats.magic_def
var hit: int:
get: return unit_stats.hit
var atk_range: int:
get: return unit_stats.atk_range
var spd: int:
get: return unit_stats.spd
var eva: int:
get: return unit_stats.eva
var lck: int:
get: return unit_stats.lck
var mov: int:
get: return unit_stats.mov
static func from_unit_stats(source: UnitStats) -> DeployedUnitStats:
var stats := DeployedUnitStats.new()
stats.unit_stats = source
stats.current_hp = source.max_hp
stats.current_sp = source.max_sp
stats.current_fs = source.max_fs
return stats

View File

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

View File

@@ -8,7 +8,7 @@ extends Node2D
@onready var wall_renderer: WallRenderer = %WallRenderer
@onready var fog_renderer: FogRenderer = %FogRenderer
const TILE_SIZE := 100.0
const DEPLOYED_UNIT_SCENE = preload("res://prefabs/deployed_unit.tscn")
const SOURCE_ID: int = 0
var _pending_layout: String
@@ -19,21 +19,12 @@ func _ready() -> void:
if _pending_layout:
_apply_layout(_pending_layout)
for entry in _pending_units:
_apply_deploy(entry.unit, entry.coords)
_apply_deploy(entry.deployed, entry.coords)
_pending_units.clear()
if map_layout:
apply_layout(map_layout)
func snap_to_grid(pos: Vector2) -> Vector2:
return Vector2(floorf(pos.x / TILE_SIZE), floorf(pos.y / TILE_SIZE)) * TILE_SIZE
func world_to_coords(pos: Vector2) -> Vector2i:
return Vector2i(snap_to_grid(pos) / TILE_SIZE)
func coords_to_world(coords: Vector2i) -> Vector2:
return Vector2(coords) * TILE_SIZE
func draw_wall(coords: Vector2i) -> void:
draw_custom(coords, tile_set.wall_tile_coords)
@@ -51,10 +42,12 @@ func load_map(layout: String) -> void:
func deploy_unit(unit: Unit, coords: Vector2i) -> void:
var deployed: DeployedUnit = DEPLOYED_UNIT_SCENE.instantiate()
deployed.unit = unit
if is_node_ready():
_apply_deploy(unit, coords)
_apply_deploy(deployed, coords)
else:
_pending_units.append({unit = unit, coords = coords})
_pending_units.append({deployed = deployed, coords = coords})
func _apply_layout(layout: String) -> void:
@@ -69,14 +62,14 @@ func _apply_layout(layout: String) -> void:
draw_floor(coords)
func _apply_deploy(unit: Unit, coords: Vector2i) -> void:
unit.position = coords_to_world(coords)
add_child(unit)
func _apply_deploy(deployed: DeployedUnit, coords: Vector2i) -> void:
deployed.position = BattleMapHelper.coords_to_world(coords)
add_child(deployed)
func remove_unit(unit: Unit) -> void:
if unit.get_parent() == self:
remove_child(unit)
func remove_unit(deployed: DeployedUnit) -> void:
if deployed.get_parent() == self:
remove_child(deployed)
func target_tile(coords: Vector2i) -> void:
@@ -116,7 +109,7 @@ func draw_fog() -> void:
func get_map_rect() -> Rect2:
if not map_layout:
return Rect2()
return Rect2(Vector2.ZERO, Vector2(map_layout.size) * TILE_SIZE)
return Rect2(Vector2.ZERO, Vector2(map_layout.size) * BattleMapHelper.TILE_SIZE)
func load_from_layout() -> void:

View File

@@ -4,7 +4,6 @@ extends Node2D
## Renders a fog/cave texture over every tile inside the map's bounding rect
## that is not part of any room. Future: drive visibility from map state.
const TILE_SIZE := 100.0
## Fog tile region in aux_terrain.BMP
const FOG_RECT := Rect2(53, 53, 100, 100)
@@ -23,14 +22,14 @@ func draw_fog_for_layout(map_layout: MapLayout) -> void:
var tile := Vector2i(x, y)
if map_layout.is_tile_valid(tile):
continue
_fog_tiles.append(Vector2(tile) * TILE_SIZE)
_fog_tiles.append(Vector2(tile) * BattleMapHelper.TILE_SIZE)
queue_redraw()
func _draw() -> void:
if not atlas_texture:
return
var dest_size := Vector2(TILE_SIZE, TILE_SIZE)
var dest_size := Vector2(BattleMapHelper.TILE_SIZE, BattleMapHelper.TILE_SIZE)
for pos in _fog_tiles:
draw_texture_rect_region(
atlas_texture,

View File

@@ -4,8 +4,6 @@ extends Node2D
## Renders wall textures by sampling segments from the aux_terrain texture atlas
## and compositing them onto tile edges. Each edge is made of two half-segments.
const TILE_SIZE := 100.0
## Source atlas rects (x, y, w, h) from aux_terrain.BMP
## Each edge has two half-segments that together span the full tile edge.
@@ -33,16 +31,18 @@ const INNER_CORNER_LOWER_RIGHT_RECT := Rect2(156, 156, 50, 50)
# -- Openings (drawn on top of wall segments at doorway edges) --
## Vertical opening: tiles separated on y-axis (north-south doorway through a horizontal wall)
const VERTICAL_OPENING_RECT := Rect2(206, 43, 31, 29)
const VERTICAL_OPENING_RECT := Rect2(206, 36, 36, 42)
## Horizontal opening: tiles separated on x-axis (east-west doorway through a vertical wall)
const HORIZONTAL_OPENING_RECT := Rect2(217, 0, 28, 31)
const HORIZONTAL_OPENING_RECT := Rect2(210, 0, 41, 32)
## Wall thickness in game pixels (how far the border extends into the tile)
const WALL_THICKNESS := 20.0
## Inner corner piece size in game pixels (quarter of a tile)
const CORNER_SIZE := 50.0
## Half the tile edge length
const HALF_EDGE := TILE_SIZE / 2.0
var HALF_EDGE: float:
get:
return BattleMapHelper.TILE_SIZE / 2.0
@export var atlas_texture: Texture2D
@@ -114,7 +114,7 @@ func _direction_to_edge(dir: Vector2i) -> StringName:
func _build_tile_walls(tile: Vector2i, edges: Array) -> void:
var tile_origin := Vector2(tile) * TILE_SIZE
var tile_origin := Vector2(tile) * BattleMapHelper.TILE_SIZE
for edge in edges:
_build_edge_segments(tile_origin, edge)
@@ -147,8 +147,8 @@ func _build_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
seg_b_rect = RIGHT_LOWER_RECT
seg_a_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_b_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_a_offset = Vector2(TILE_SIZE - WALL_THICKNESS, 0)
seg_b_offset = Vector2(TILE_SIZE - WALL_THICKNESS, HALF_EDGE)
seg_a_offset = Vector2(BattleMapHelper.TILE_SIZE - WALL_THICKNESS, 0)
seg_b_offset = Vector2(BattleMapHelper.TILE_SIZE - WALL_THICKNESS, HALF_EDGE)
&"top":
seg_a_rect = TOP_LEFT_RECT
seg_b_rect = TOP_RIGHT_RECT
@@ -161,8 +161,8 @@ func _build_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
seg_b_rect = BOTTOM_RIGHT_RECT
seg_a_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_b_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_a_offset = Vector2(0, TILE_SIZE - WALL_THICKNESS)
seg_b_offset = Vector2(HALF_EDGE, TILE_SIZE - WALL_THICKNESS)
seg_a_offset = Vector2(0, BattleMapHelper.TILE_SIZE - WALL_THICKNESS)
seg_b_offset = Vector2(HALF_EDGE, BattleMapHelper.TILE_SIZE - WALL_THICKNESS)
_queue_segment(tile_origin + seg_a_offset, seg_a_size, seg_a_rect)
_queue_segment(tile_origin + seg_b_offset, seg_b_size, seg_b_rect)
@@ -187,8 +187,8 @@ func _build_opening_sprites(map_layout: MapLayout) -> void:
tile_b = swap
diff = -diff
var origin_a := Vector2(tile_a) * TILE_SIZE
var origin_b := Vector2(tile_b) * TILE_SIZE
var origin_a := Vector2(tile_a) * BattleMapHelper.TILE_SIZE
var origin_b := Vector2(tile_b) * BattleMapHelper.TILE_SIZE
if diff == Vector2i.DOWN:
_queue_vertical_opening(origin_a, origin_b)
@@ -203,10 +203,10 @@ func _queue_vertical_opening(origin_upper: Vector2, origin_lower: Vector2) -> vo
var h_total: float = src.size.y
var h_upper: float = floorf(h_total / 2.0) # 14
var h_lower: float = h_total - h_upper # 15
var x_offset := (TILE_SIZE - w) / 2.0
var x_offset := (BattleMapHelper.TILE_SIZE - w) / 2.0
var src_upper := Rect2(src.position, Vector2(w, h_upper))
var src_lower := Rect2(src.position + Vector2(0, h_upper), Vector2(w, h_lower))
_queue_segment(origin_upper + Vector2(x_offset, TILE_SIZE - h_upper), Vector2(w, h_upper), src_upper)
_queue_segment(origin_upper + Vector2(x_offset, BattleMapHelper.TILE_SIZE - h_upper), Vector2(w, h_upper), src_upper)
_queue_segment(origin_lower + Vector2(x_offset, 0), Vector2(w, h_lower), src_lower)
@@ -217,10 +217,10 @@ func _queue_horizontal_opening(origin_left: Vector2, origin_right: Vector2) -> v
var h: float = src.size.y
var w_left: float = floorf(w_total / 2.0) # 14
var w_right: float = w_total - w_left # 14
var y_offset := (TILE_SIZE - h) / 2.0
var y_offset := (BattleMapHelper.TILE_SIZE - h) / 2.0
var src_left := Rect2(src.position, Vector2(w_left, h))
var src_right := Rect2(src.position + Vector2(w_left, 0), Vector2(w_right, h))
_queue_segment(origin_left + Vector2(TILE_SIZE - w_left, y_offset), Vector2(w_left, h), src_left)
_queue_segment(origin_left + Vector2(BattleMapHelper.TILE_SIZE - w_left, y_offset), Vector2(w_left, h), src_left)
_queue_segment(origin_right + Vector2(0, y_offset), Vector2(w_right, h), src_right)
@@ -248,19 +248,19 @@ func _build_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -
)
if has_top and has_right:
_queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, 0),
tile_origin + Vector2(BattleMapHelper.TILE_SIZE - CORNER_SIZE, 0),
corner_size,
INNER_CORNER_UPPER_RIGHT_RECT
)
if has_bottom and has_left:
_queue_segment(
tile_origin + Vector2(0, TILE_SIZE - CORNER_SIZE),
tile_origin + Vector2(0, BattleMapHelper.TILE_SIZE - CORNER_SIZE),
corner_size,
INNER_CORNER_LOWER_LEFT_RECT
)
if has_bottom and has_right:
_queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, TILE_SIZE - CORNER_SIZE),
tile_origin + Vector2(BattleMapHelper.TILE_SIZE - CORNER_SIZE, BattleMapHelper.TILE_SIZE - CORNER_SIZE),
corner_size,
INNER_CORNER_LOWER_RIGHT_RECT
)

View File

@@ -4,13 +4,13 @@ const SPEED = 192.0
@export var dl_map: CombatMap
signal combat_requested(attacker: Unit, defender: Unit)
signal combat_requested(attacker: DeployedUnit, defender: DeployedUnit)
signal mouse_grid_changed(coords: Vector2i)
signal camera_drag(delta: Vector2)
var input_disabled := false
var _selected_unit: Unit = null
var _selected_unit: DeployedUnit = null
var _target_pos: Vector2
var _goal_pos: Vector2
var _moving := false
@@ -24,19 +24,19 @@ const DRAG_THRESHOLD := 8.0
func _ready() -> void:
for unit: Unit in get_tree().get_nodes_in_group("units"):
unit.unit_died.connect(_on_unit_died)
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
deployed.unit_died.connect(_on_unit_died)
get_tree().node_added.connect(_on_node_added)
func _on_node_added(node: Node) -> void:
if node is Unit and node.is_in_group("units"):
if node is DeployedUnit and node.is_in_group("deployed_units"):
if not node.unit_died.is_connected(_on_unit_died):
node.unit_died.connect(_on_unit_died)
func _on_unit_died(unit: Unit) -> void:
if _selected_unit == unit:
func _on_unit_died(deployed: DeployedUnit) -> void:
if _selected_unit == deployed:
_selected_unit = null
_moving = false
@@ -45,7 +45,7 @@ func _process(_delta: float) -> void:
if input_disabled:
return
var mouse_pos := get_viewport().get_canvas_transform().affine_inverse() * get_viewport().get_mouse_position()
var coords := dl_map.world_to_coords(mouse_pos)
var coords := BattleMapHelper.world_to_coords(mouse_pos)
if coords != _current_grid_coords:
_current_grid_coords = coords
mouse_grid_changed.emit(coords)
@@ -116,9 +116,9 @@ func _physics_process(delta: float) -> void:
else:
dir = Vector2(0, signf(diff.y))
var next_pos := _selected_unit.position + dir * dl_map.TILE_SIZE
var grid_coords := dl_map.world_to_coords(next_pos)
var current_coords := dl_map.world_to_coords(_selected_unit.position)
var next_pos := _selected_unit.position + dir * BattleMapHelper.TILE_SIZE
var grid_coords := BattleMapHelper.world_to_coords(next_pos)
var current_coords := BattleMapHelper.world_to_coords(_selected_unit.position)
if not dl_map.is_tile_passable(current_coords, grid_coords):
_goal_pos = _selected_unit.position
return
@@ -138,30 +138,30 @@ func _handle_left_click(screen_pos: Vector2) -> void:
_select_unit(clicked_unit)
get_viewport().set_input_as_handled()
elif _selected_unit:
var snapped_pos := dl_map.snap_to_grid(world_pos)
var grid_coords := dl_map.world_to_coords(world_pos)
var snapped_pos := BattleMapHelper.snap_to_grid(world_pos)
var grid_coords := BattleMapHelper.world_to_coords(world_pos)
if not dl_map.is_tile_valid(grid_coords):
return
_goal_pos = snapped_pos
get_viewport().set_input_as_handled()
func _select_unit(unit: Unit) -> void:
func _select_unit(deployed: DeployedUnit) -> void:
if _selected_unit:
_selected_unit.set_selected(false)
_selected_unit = unit
_selected_unit = deployed
_selected_unit.set_selected(true)
_goal_pos = _selected_unit.position
_target_pos = _selected_unit.position
_moving = false
func _get_unit_at(world_pos: Vector2) -> Unit:
var snapped := dl_map.snap_to_grid(world_pos)
for unit: Unit in get_tree().get_nodes_in_group("units"):
if not unit.is_alive():
func _get_unit_at(world_pos: Vector2) -> DeployedUnit:
var snapped_coords := BattleMapHelper.snap_to_grid(world_pos)
for deployed: DeployedUnit in get_tree().get_nodes_in_group("deployed_units"):
if not deployed.is_alive():
continue
var unit_snapped := dl_map.snap_to_grid(unit.global_position)
if unit_snapped == snapped:
return unit
var unit_snapped := BattleMapHelper.snap_to_grid(deployed.global_position)
if unit_snapped == snapped_coords:
return deployed
return null

View File

@@ -45,9 +45,9 @@ func _ready() -> void:
func _on_mouse_grid_changed(coords: Vector2i) -> void:
combat_map.target_tile(coords)
func _on_combat_requested(attacker: Unit, defender: Unit) -> void:
var atk_coords := combat_map.world_to_coords(attacker.position)
var def_coords := combat_map.world_to_coords(defender.position)
func _on_combat_requested(attacker: DeployedUnit, defender: DeployedUnit) -> void:
var atk_coords := BattleMapHelper.world_to_coords(attacker.position)
var def_coords := BattleMapHelper.world_to_coords(defender.position)
var distance := absi(atk_coords.x - def_coords.x) + absi(atk_coords.y - def_coords.y)
var proposal := combat_system.create_proposal(attacker, defender, distance)
_set_input_disabled(true)

View File

@@ -0,0 +1,3 @@
class_name BattleMapConstants extends Object
const TILE_SIZE: float = 100.0

View File

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

View File

@@ -0,0 +1,7 @@
@abstract class_name ConsoleCommand extends RefCounted
@abstract func get_command_name() -> String
@abstract func get_help_text() -> String
@abstract func run(args: Array, context: Dictionary) -> String

View File

@@ -6,7 +6,7 @@ func get_command_name() -> String:
func get_help_text() -> String:
return "Lists all available commands"
func run(args: Array, context: Dictionary) -> String:
func run(_args: Array, context: Dictionary) -> String:
var commands: Array = context["commands"]
var lines: PackedStringArray = []
for command: ConsoleCommand in commands:

View File

@@ -6,7 +6,7 @@ func get_command_name() -> String:
func get_help_text() -> String:
return "Lists available scenes for swapping"
func run(args: Array, context: Dictionary) -> String:
func run(_args: Array, context: Dictionary) -> String:
var registry: Array = context["scene_registry"]
var lines: PackedStringArray = []
for entry: Dictionary in registry:

View File

@@ -11,7 +11,6 @@ var scene_registry: Array = [
]
var commands: Array[ConsoleCommand] = []
var _result_tween: Tween
@onready var panel: PanelContainer = %Panel
@onready var scene_list: VBoxContainer = %SceneList
@@ -48,7 +47,7 @@ func swap_scene(entry: Dictionary) -> void:
_apply_setup(entry["setup"], instance)
close_requested.emit()
func _apply_setup(setup_key: String, scene_instance: Node) -> void:
func _apply_setup(setup_key: String, _scene_instance: Node) -> void:
match setup_key:
_:
push_warning("Unknown setup key: %s" % setup_key)

7
scripts/units/unit.gd Normal file
View File

@@ -0,0 +1,7 @@
class_name Unit extends Resource
@export var stats: UnitStats
@export var info: UnitInfo
@export var allegiance: UnitAllegiance
@export var tactics: Array[CombatTactic] = []
@export var appearance: UnitAppearance

View File

@@ -0,0 +1,19 @@
class_name UnitAppearance extends Resource
@export var default_appearance_key: String = "default"
@export var current_appearance_key: String
@export var appearance_sets: Dictionary[String, UnitAppearanceSet]
var deployed_sprite_sheet: SpriteFrames:
get:
var key: String = current_appearance_key
if(key == null or key == ""):
key = default_appearance_key
if(!appearance_sets.has(key)):
return null
var appearance_set: UnitAppearanceSet = appearance_sets[key]
var sprite_value = appearance_set.deployed_sprite_sheet
if(sprite_value == null):
sprite_value = appearance_sets[default_appearance_key].deployed_sprite_sheet
return sprite_value

View File

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

View File

@@ -0,0 +1,3 @@
class_name UnitAppearanceSet extends Resource
@export var deployed_sprite_sheet: SpriteFrames

View File

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

View File

@@ -1,9 +1,8 @@
class_name UnitStats extends Resource
@export var max_hp: int = 10
@export var current_hp: int
@export var max_sp: int = 10
@export var current_sp: int = 10
@export var max_fs: int = 10
@export var phys_atk: int = 10
@export var phys_def: int = 5
@export var magic_atk: int = 0
@@ -13,7 +12,4 @@ class_name UnitStats extends Resource
@export var spd: int = 1
@export var eva: int = 1
@export var lck: int = 1
func _init(max_hp: int = 10) -> void:
self.max_hp = max_hp
current_hp = max_hp
@export var mov: int = 3