Initial commit
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
BIN
assets/editor/unit_node.bmp
Normal file
|
After Width: | Height: | Size: 822 B |
40
assets/editor/unit_node.bmp.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kg0de6r1lsj0"
|
||||
path="res://.godot/imported/unit_node.bmp-78f7ae8f67af3742e2be442acdff72e7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/editor/unit_node.bmp"
|
||||
dest_files=["res://.godot/imported/unit_node.bmp-78f7ae8f67af3742e2be442acdff72e7.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
|
||||
BIN
assets/editor/unit_node.png
Normal file
|
After Width: | Height: | Size: 232 B |
40
assets/editor/unit_node.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dv8p38jv35ejg"
|
||||
path="res://.godot/imported/unit_node.png-9b23e77b8d09018da177358cfa433365.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/editor/unit_node.png"
|
||||
dest_files=["res://.godot/imported/unit_node.png-9b23e77b8d09018da177358cfa433365.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
|
||||
BIN
assets/fonts/Minecraft.ttf
Normal file
36
assets/fonts/Minecraft.ttf.import
Normal file
@@ -0,0 +1,36 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://eqhm6gv5p05t"
|
||||
path="res://.godot/imported/Minecraft.ttf-e1845dfc080db589cd29d952a380f38e.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/fonts/Minecraft.ttf"
|
||||
dest_files=["res://.godot/imported/Minecraft.ttf-e1845dfc080db589cd29d952a380f38e.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
modulate_color_glyphs=false
|
||||
hinting=1
|
||||
subpixel_positioning=4
|
||||
keep_rounding_remainders=true
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
assets/music/combat_bgm_01.OGG
Normal file
19
assets/music/combat_bgm_01.OGG.import
Normal file
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://dsikulned64qt"
|
||||
path="res://.godot/imported/combat_bgm_01.OGG-6c9a537751cc7bec8580130b48af399c.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music/combat_bgm_01.OGG"
|
||||
dest_files=["res://.godot/imported/combat_bgm_01.OGG-6c9a537751cc7bec8580130b48af399c.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=true
|
||||
loop_offset=0.0
|
||||
bpm=0.0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
BIN
assets/music/menu_theme.OGG
Normal file
19
assets/music/menu_theme.OGG.import
Normal file
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://b7dgmblbcm0cj"
|
||||
path="res://.godot/imported/menu_theme.OGG-70a417a11ba25010cea8e5ee015ba9aa.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music/menu_theme.OGG"
|
||||
dest_files=["res://.godot/imported/menu_theme.OGG-70a417a11ba25010cea8e5ee015ba9aa.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=true
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
BIN
assets/sprites/castle_spritesheet.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
40
assets/sprites/castle_spritesheet.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c7e4jw4xcti0q"
|
||||
path="res://.godot/imported/castle_spritesheet.png-6ff90c6355eaddab50addab2e87b1f2c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/castle_spritesheet.png"
|
||||
dest_files=["res://.godot/imported/castle_spritesheet.png-6ff90c6355eaddab50addab2e87b1f2c.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
|
||||
BIN
assets/sprites/character.bmp
Normal file
|
After Width: | Height: | Size: 5.4 MiB |
40
assets/sprites/character.bmp.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c2se5wyly6gr6"
|
||||
path="res://.godot/imported/character.bmp-cf65b7adb0d5844b4deb09628dc23b0b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/character.bmp"
|
||||
dest_files=["res://.godot/imported/character.bmp-cf65b7adb0d5844b4deb09628dc23b0b.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
|
||||
BIN
assets/sprites/flag.png
Normal file
|
After Width: | Height: | Size: 182 B |
40
assets/sprites/flag.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cw5su6lignryo"
|
||||
path="res://.godot/imported/flag.png-b8d0210a4bba4a3dc7cd10170df0a728.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/flag.png"
|
||||
dest_files=["res://.godot/imported/flag.png-b8d0210a4bba4a3dc7cd10170df0a728.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
|
||||
BIN
assets/sprites/grey_castle_spritesheet_mask.bmp
Normal file
|
After Width: | Height: | Size: 59 KiB |
40
assets/sprites/grey_castle_spritesheet_mask.bmp.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b8td6sv5re6r8"
|
||||
path="res://.godot/imported/grey_castle_spritesheet_mask.bmp-194d929eb1771eae4ca4719fb40521c6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/grey_castle_spritesheet_mask.bmp"
|
||||
dest_files=["res://.godot/imported/grey_castle_spritesheet_mask.bmp-194d929eb1771eae4ca4719fb40521c6.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
|
||||
BIN
assets/sprites/grid_highlight.png
Normal file
|
After Width: | Height: | Size: 291 B |
40
assets/sprites/grid_highlight.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://sjsl8q7tkx8"
|
||||
path="res://.godot/imported/grid_highlight.png-bbd56dceab9bd3e965927c9144c4c052.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/grid_highlight.png"
|
||||
dest_files=["res://.godot/imported/grid_highlight.png-bbd56dceab9bd3e965927c9144c4c052.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
|
||||
BIN
assets/sprites/map1.bmp
Normal file
|
After Width: | Height: | Size: 5.4 MiB |
40
assets/sprites/map1.bmp.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://udsusbp3o76m"
|
||||
path="res://.godot/imported/map1.bmp-9e2f42fc155126c194d55961a68898fc.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/map1.bmp"
|
||||
dest_files=["res://.godot/imported/map1.bmp-9e2f42fc155126c194d55961a68898fc.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
|
||||
BIN
assets/sprites/selector.png
Normal file
|
After Width: | Height: | Size: 137 B |
40
assets/sprites/selector.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://h7nfrjxagqmc"
|
||||
path="res://.godot/imported/selector.png-d0018707617902c3e886c6fbf4977608.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/selector.png"
|
||||
dest_files=["res://.godot/imported/selector.png-d0018707617902c3e886c6fbf4977608.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
|
||||
77
code_review_findings.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Code Review Findings
|
||||
|
||||
## Fixed
|
||||
|
||||
### 1. Camera and PlayerController input conflict
|
||||
**Files:** `scripts/camera_controller.gd`, `nodes/player_controller.gd`
|
||||
|
||||
Both scripts used `_unhandled_input` with left mouse button. The camera used left click for dragging and never called `set_input_as_handled()`, so every left click also triggered unit selection/movement. Fixed with a drag threshold and click-on-release pattern.
|
||||
|
||||
---
|
||||
|
||||
## Outstanding
|
||||
|
||||
### 2. Unit lookup is fragile
|
||||
**File:** `nodes/player_controller.gd:75`
|
||||
|
||||
`_get_unit_at()` iterates `get_parent().get_children()` to find units. This couples the controller to a specific scene tree layout and will degrade as the scene grows.
|
||||
|
||||
**Fix:** Use a group (`"units"`) or a dedicated unit registry so lookup doesn't depend on parent structure.
|
||||
|
||||
### 3. No pathfinding — movement can skip walls diagonally
|
||||
**File:** `nodes/player_controller.gd:45-60`
|
||||
|
||||
Step-by-step movement picks one axis per frame. If the goal is diagonally past an L-shaped wall, the unit can path around it depending on approach angle since only the next immediate tile is checked.
|
||||
|
||||
**Fix:** Implement BFS/A* on the grid, or at minimum validate the full path before starting movement.
|
||||
|
||||
### ~~4. CombatMap extends CanvasItem instead of Node2D~~ (fixed)
|
||||
**File:** `nodes/combat_map.gd:2`
|
||||
|
||||
Changed to extend `Node2D`.
|
||||
|
||||
### ~~5. Inconsistent naming convention~~ (fixed)
|
||||
**File:** `nodes/combat_map.gd:16`
|
||||
|
||||
Renamed `tileCoords` to `tile_coords`.
|
||||
|
||||
### ~~6. unit_template is a string path, not a PackedScene~~ (fixed)
|
||||
**File:** `scripts/test_map_generator.gd:7`
|
||||
|
||||
Changed to `@export var unit_template: PackedScene` and removed the `load()` call. **Note:** The scene must be assigned in the Godot inspector on the TestMapGenerator node.
|
||||
|
||||
### ~~7. No current_hp on units~~ (fixed)
|
||||
**File:** `nodes/unit.gd`
|
||||
|
||||
Added `current_hp` variable, initialized to `max_hp` in `_ready()`.
|
||||
|
||||
### ~~8. Debug print left in production code~~ (fixed)
|
||||
**File:** `scripts/test_map_generator.gd`
|
||||
|
||||
Removed the debug `print()` call.
|
||||
|
||||
### 9. tile_highlight.gd runs every frame unconditionally
|
||||
**File:** `scripts/tile_highlight.gd:12-17`
|
||||
|
||||
`_process` updates position and alpha every frame even when the highlight is hidden. Could skip processing when invisible via `set_process(false)` on hide.
|
||||
|
||||
### 10. GridOverlay is wired but unused
|
||||
**Files:** `scripts/grid_overlay.gd`, `nodes/combat_map.gd:6`
|
||||
|
||||
`highlight_map` reference exists but is never called from any game logic. Dead code at the moment.
|
||||
|
||||
---
|
||||
|
||||
## Priority
|
||||
|
||||
| Priority | Issue |
|
||||
|----------|-------|
|
||||
| ~~High~~ | ~~#1 Input conflict~~ (fixed) |
|
||||
| High | #3 Wall-skipping movement |
|
||||
| Medium | #2 Fragile unit lookup |
|
||||
| ~~Medium~~ | ~~#6 String path instead of PackedScene~~ (fixed) |
|
||||
| ~~Low~~ | ~~#4 CanvasItem base class~~ (fixed) |
|
||||
| ~~Low~~ | ~~#5 Inconsistent naming~~ (fixed) |
|
||||
| ~~Low~~ | ~~#7 No current_hp~~ (fixed) |
|
||||
| ~~Low~~ | ~~#8 Debug print~~ (fixed) |
|
||||
| Low | #9, #10 |
|
||||
205
docs/superpowers/plans/2026-04-01-combat-ui-unit-panel.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Combat UI Unit Panel Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace the exit-button CombatUI with a bottom-left panel that shows the selected unit's name and HP.
|
||||
|
||||
**Architecture:** CombatUI listens to the `unit_selected_changed` signal from units in the `"units"` group. On selection, it populates a name label and HP progress bar and shows the panel. On deselection, it hides the panel.
|
||||
|
||||
**Tech Stack:** Godot 4.6, GDScript, `.tscn` scene files
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|------|--------|----------------|
|
||||
| `prefabs/unit.tscn` | Modify | Add Unit node to `"units"` group |
|
||||
| `scripts/combat_ui.gd` | Create | Script that connects to unit signals, shows/hides panel, populates data |
|
||||
| `prefabs/combat_ui.tscn` | Modify | Remove exit button, add UnitPanel with NameLabel and HPBar, attach new script |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add Unit to the "units" group
|
||||
|
||||
**Files:**
|
||||
- Modify: `prefabs/unit.tscn`
|
||||
|
||||
- [ ] **Step 1: Add the group to the Unit scene**
|
||||
|
||||
In `prefabs/unit.tscn`, add a `groups` property to the root `Unit` node so every instantiated unit is automatically in the `"units"` group. Add this line to the root `[node name="Unit" ...]` block:
|
||||
|
||||
```
|
||||
groups=["units"]
|
||||
```
|
||||
|
||||
The full node line becomes:
|
||||
```
|
||||
[node name="Unit" type="Node2D" unique_id=1893234933 groups=["units"]]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify in editor**
|
||||
|
||||
Open `scenes/combat_test.tscn` in Godot, run the scene (F5), and use the Remote scene tree to confirm the spawned unit node appears under the `"units"` group.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add prefabs/unit.tscn
|
||||
git commit -m "feat: add Unit to 'units' group for discovery"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create combat_ui.gd script
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/combat_ui.gd`
|
||||
|
||||
- [ ] **Step 1: Write the script**
|
||||
|
||||
Create `scripts/combat_ui.gd` with the following content:
|
||||
|
||||
```gdscript
|
||||
class_name CombatUI extends CanvasLayer
|
||||
|
||||
@onready var unit_panel: PanelContainer = %UnitPanel
|
||||
@onready var name_label: Label = %NameLabel
|
||||
@onready var hp_bar: ProgressBar = %HPBar
|
||||
|
||||
func _ready() -> void:
|
||||
unit_panel.visible = false
|
||||
for unit: Unit in get_tree().get_nodes_in_group("units"):
|
||||
unit.unit_selected_changed.connect(_on_unit_selected_changed)
|
||||
|
||||
func _on_unit_selected_changed(unit: Unit, selected: bool) -> void:
|
||||
if selected:
|
||||
name_label.text = unit.current_info.name
|
||||
hp_bar.max_value = unit.current_stats.max_hp
|
||||
hp_bar.value = unit.current_stats.current_hp
|
||||
unit_panel.visible = true
|
||||
else:
|
||||
unit_panel.visible = false
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add scripts/combat_ui.gd
|
||||
git commit -m "feat: add CombatUI script for unit info panel"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Rebuild combat_ui.tscn
|
||||
|
||||
**Files:**
|
||||
- Modify: `prefabs/combat_ui.tscn`
|
||||
|
||||
- [ ] **Step 1: Replace the scene file contents**
|
||||
|
||||
Replace the entire contents of `prefabs/combat_ui.tscn` with:
|
||||
|
||||
```
|
||||
[gd_scene format=3 uid="uid://cy7r0udfcsqbn"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dx26d6py3n8xi" path="res://resources/main_ui_theme.tres" id="1_2ro41"]
|
||||
[ext_resource type="Script" path="res://scripts/combat_ui.gd" id="2_ui_script"]
|
||||
|
||||
[node name="CombatUI" type="CanvasLayer" unique_id=1093388037]
|
||||
script = ExtResource("2_ui_script")
|
||||
|
||||
[node name="UnitPanel" type="PanelContainer" parent="." unique_id=2000000001]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = -78.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = -8.0
|
||||
grow_vertical = 0
|
||||
theme = ExtResource("1_2ro41")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UnitPanel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UnitPanel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="NameLabel" type="Label" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000002]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Unit"
|
||||
|
||||
[node name="HPBar" type="ProgressBar" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000003]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
max_value = 100.0
|
||||
value = 100.0
|
||||
show_percentage = false
|
||||
```
|
||||
|
||||
Key changes from the old file:
|
||||
- Removed the exit button, its inline GDScript, the VBoxContainer, and the `[connection]` block.
|
||||
- Added the `combat_ui.gd` script as an ext_resource and attached it to the root node.
|
||||
- Added `UnitPanel` (PanelContainer) anchored to bottom-left with 8px margin from edges, 200px wide, ~70px tall.
|
||||
- `NameLabel` and `HPBar` are marked with `unique_name_in_owner = true` so the script can reference them via `%NameLabel` and `%HPBar`.
|
||||
- `UnitPanel` is also unique-named for `%UnitPanel` access.
|
||||
- `show_percentage = false` on HPBar since we just want the bar visual.
|
||||
|
||||
- [ ] **Step 2: Open in Godot and verify scene tree**
|
||||
|
||||
Open `prefabs/combat_ui.tscn` in the Godot editor. Confirm:
|
||||
1. No errors in the Scene dock.
|
||||
2. The node tree shows: `CombatUI > UnitPanel > MarginContainer > VBoxContainer > NameLabel + HPBar`.
|
||||
3. The UnitPanel is positioned at the bottom-left of the viewport.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add prefabs/combat_ui.tscn
|
||||
git commit -m "feat: replace exit button with unit info panel in CombatUI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Integration test — verify end-to-end behavior
|
||||
|
||||
**Files:**
|
||||
- None (manual verification)
|
||||
|
||||
- [ ] **Step 1: Run the combat test scene**
|
||||
|
||||
Run `scenes/combat_test.tscn` (F5 or F6 from the scene). The test map generator spawns a unit named "Putit" with 50 HP.
|
||||
|
||||
- [ ] **Step 2: Verify panel is hidden on start**
|
||||
|
||||
Confirm the bottom-left corner is empty — no panel visible.
|
||||
|
||||
- [ ] **Step 3: Click the unit to select it**
|
||||
|
||||
Click the "Putit" unit on the map. Confirm:
|
||||
1. The panel appears in the bottom-left corner.
|
||||
2. The name label shows "Putit".
|
||||
3. The HP bar is full (50/50).
|
||||
|
||||
- [ ] **Step 4: Click an empty tile to deselect**
|
||||
|
||||
Click an empty floor tile (this moves the unit, but the unit stays selected). Currently, clicking a different unit would deselect the first — since there's only one test unit, verify that selecting the same unit again keeps the panel visible and data correct.
|
||||
|
||||
- [ ] **Step 5: Verify exit button is gone**
|
||||
|
||||
Confirm there is no "Exit" button anywhere in the UI.
|
||||
|
||||
- [ ] **Step 6: Commit (if any tweaks were needed)**
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "fix: adjustments from integration testing"
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
# Combat UI: Unit Info Panel
|
||||
|
||||
## Overview
|
||||
|
||||
Replace the current exit-button-only CombatUI with a unit info panel that displays the selected unit's name and HP. The panel appears in the bottom-left corner when a unit is selected and hides when deselected.
|
||||
|
||||
## Scene Structure
|
||||
|
||||
```
|
||||
CombatUI (CanvasLayer) — script: scripts/combat_ui.gd
|
||||
└─ UnitPanel (PanelContainer) — anchored bottom-left, hidden by default
|
||||
└─ MarginContainer
|
||||
└─ VBoxContainer
|
||||
├─ NameLabel (Label) — displays unit name
|
||||
└─ HPBar (ProgressBar) — displays current_hp / max_hp
|
||||
```
|
||||
|
||||
- The existing exit button and its VBoxContainer are removed entirely.
|
||||
- The CombatUI CanvasLayer is preserved; its theme reference to `main_ui_theme.tres` stays.
|
||||
|
||||
## Layout
|
||||
|
||||
- UnitPanel is anchored to the bottom-left of the viewport.
|
||||
- Margin of 8px from the left and bottom edges.
|
||||
- Panel width: ~200px (enough for name and HP bar).
|
||||
- Height: determined by contents.
|
||||
|
||||
## Behavior
|
||||
|
||||
### Unit Discovery
|
||||
|
||||
CombatUI finds units by looking for nodes in the `"units"` group. Units must be added to this group — this is done in `unit.tscn` (scene-level group assignment) or in `unit.gd`'s `_ready()`.
|
||||
|
||||
### Signal Connection
|
||||
|
||||
On `_ready()`, CombatUI:
|
||||
1. Calls `get_tree().get_nodes_in_group("units")` to find all current units.
|
||||
2. Connects each unit's `unit_selected_changed(unit: Unit, selected: bool)` signal to a handler.
|
||||
|
||||
### Selection Handling
|
||||
|
||||
When `unit_selected_changed` is received:
|
||||
- If `selected == true`: populate NameLabel with `unit.current_info.name`, set HPBar's `max_value` to `unit.current_stats.max_hp` and `value` to `unit.current_stats.current_hp`, show UnitPanel.
|
||||
- If `selected == false`: hide UnitPanel.
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- If no units exist in the group at ready time, the panel simply stays hidden.
|
||||
- If a unit is removed from the scene (e.g., defeated), the signal disconnects naturally with the node. The panel hides if that unit was selected.
|
||||
|
||||
## Files Changed
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `prefabs/combat_ui.tscn` | Remove exit button/VBoxContainer. Add UnitPanel with NameLabel and HPBar. Attach new script. |
|
||||
| `scripts/combat_ui.gd` | New script: connects to unit signals, shows/hides panel, populates data. |
|
||||
| `prefabs/unit.tscn` | Add the node to the `"units"` group. |
|
||||
|
||||
## Styling
|
||||
|
||||
Uses the existing `main_ui_theme.tres` applied at the CombatUI or UnitPanel level. No new theme resources needed.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Full stat display (ATK, DEF, etc.) — future expansion.
|
||||
- Animations (fade/slide) — instant show/hide only.
|
||||
- Pause menu / exit button — separate future feature.
|
||||
- HP bar color theming — uses default ProgressBar style from theme.
|
||||
1
icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
||||
|
After Width: | Height: | Size: 995 B |
43
icon.svg.import
Normal file
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cpthotfnx5l30"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
39
nodes/combat_map.gd
Normal file
@@ -0,0 +1,39 @@
|
||||
class_name CombatMap
|
||||
extends Node2D
|
||||
|
||||
signal tile_hovered(coords: Vector2i)
|
||||
|
||||
@export var tile_set: DLTileset
|
||||
@onready var tile_map: TileMapLayer = %TerrainLayer
|
||||
@onready var highlight_map: GridOverlay = %OverlayLayer
|
||||
|
||||
const TILE_SIZE := 48.0
|
||||
const SOURCE_ID: int = 0
|
||||
|
||||
|
||||
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)
|
||||
|
||||
func draw_floor(coords: Vector2i) -> void:
|
||||
draw_custom(coords, tile_set.floor_tile_coords)
|
||||
|
||||
func draw_custom(coords: Vector2i, tile_coords: Vector2i) -> void:
|
||||
tile_map.set_cell(coords, SOURCE_ID, tile_coords)
|
||||
|
||||
func is_wall(coords: Vector2i) -> bool:
|
||||
return tile_map.get_cell_atlas_coords(coords) == tile_set.wall_tile_coords
|
||||
|
||||
func target_tile(coords: Vector2i) -> void:
|
||||
highlight_map.target_tile(coords)
|
||||
tile_hovered.emit(coords)
|
||||
1
nodes/combat_map.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bks7uplgjjdg0
|
||||
80
nodes/player_controller.gd
Normal file
@@ -0,0 +1,80 @@
|
||||
class_name PlayerController extends Node
|
||||
|
||||
const SPEED = 192.0
|
||||
|
||||
@export var dl_map: CombatMap
|
||||
|
||||
var _selected_unit: Unit = null
|
||||
var _target_pos: Vector2
|
||||
var _goal_pos: Vector2
|
||||
var _moving := false
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and not event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
var world_pos: Vector2 = get_viewport().get_canvas_transform().affine_inverse() * event.position
|
||||
var clicked_unit := _get_unit_at(world_pos)
|
||||
|
||||
if clicked_unit:
|
||||
_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)
|
||||
if dl_map.is_wall(grid_coords):
|
||||
return
|
||||
_goal_pos = snapped_pos
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not _selected_unit:
|
||||
return
|
||||
|
||||
if _moving:
|
||||
var remaining := _target_pos - _selected_unit.position
|
||||
var step := SPEED * delta
|
||||
|
||||
if remaining.length() <= step:
|
||||
_selected_unit.position = _target_pos
|
||||
_moving = false
|
||||
else:
|
||||
_selected_unit.position += remaining.normalized() * step
|
||||
return
|
||||
|
||||
if _selected_unit.position != _goal_pos:
|
||||
var diff := _goal_pos - _selected_unit.position
|
||||
var dir: Vector2
|
||||
if absf(diff.x) >= absf(diff.y):
|
||||
dir = Vector2(signf(diff.x), 0)
|
||||
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)
|
||||
if dl_map.is_wall(grid_coords):
|
||||
_goal_pos = _selected_unit.position
|
||||
return
|
||||
|
||||
_target_pos = next_pos
|
||||
_moving = true
|
||||
|
||||
|
||||
func _select_unit(unit: Unit) -> void:
|
||||
if _selected_unit:
|
||||
_selected_unit.set_selected(false)
|
||||
_selected_unit = unit
|
||||
_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 child in get_parent().get_children():
|
||||
if child is Unit:
|
||||
var unit_snapped := dl_map.snap_to_grid(child.global_position)
|
||||
if unit_snapped == snapped:
|
||||
return child
|
||||
return null
|
||||
1
nodes/player_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dfojm3n0em4ef
|
||||
23
nodes/unit.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
class_name Unit extends Node2D
|
||||
|
||||
#region Templates
|
||||
@export var stat_template: UnitStats
|
||||
@export var info_template: UnitInfo
|
||||
@export var allegiance_template: UnitAllegiance
|
||||
#endregion
|
||||
|
||||
var current_stats: UnitStats
|
||||
var current_info: UnitInfo
|
||||
var current_allegiance: UnitAllegiance
|
||||
|
||||
signal unit_selected_changed(unit: Unit, selected: bool)
|
||||
signal unit_allegiance_changed(unit: Unit, allegiance: UnitAllegiance)
|
||||
|
||||
func _ready() -> void:
|
||||
current_stats = stat_template.duplicate(true)
|
||||
current_info = info_template.duplicate(true)
|
||||
current_allegiance = allegiance_template.duplicate(true)
|
||||
unit_allegiance_changed.emit(self, current_allegiance)
|
||||
|
||||
func set_selected(selected: bool) -> void:
|
||||
unit_selected_changed.emit(self, selected)
|
||||
1
nodes/unit.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c016mxgatcpse
|
||||
54
prefabs/combat_map.tscn
Normal file
@@ -0,0 +1,54 @@
|
||||
[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="TileSet" uid="uid://dm5wl6d4xkstu" path="res://resources/combat_tileset.tres" id="3_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/grid_overlay.gd" id="4_jelju"]
|
||||
[ext_resource type="Texture2D" uid="uid://h7nfrjxagqmc" path="res://assets/sprites/selector.png" id="5_muxvo"]
|
||||
[ext_resource type="Script" uid="uid://b31eyqov8w7bm" path="res://scripts/tile_highlight.gd" id="7_tileh"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_vcj5e"]
|
||||
script = ExtResource("2_8rn0j")
|
||||
wall_tile_coords = Vector2i(2, 15)
|
||||
metadata/_custom_type_script = "uid://c6701vy8h5rfx"
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_jelju"]
|
||||
texture = ExtResource("3_vcj5e")
|
||||
texture_region_size = Vector2i(48, 48)
|
||||
0:0/animation_columns = 6
|
||||
0:0/animation_frame_0/duration = 1.0
|
||||
0:0/animation_frame_1/duration = 1.0
|
||||
0:0/animation_frame_2/duration = 1.0
|
||||
0:0/animation_frame_3/duration = 1.0
|
||||
0:0/animation_frame_4/duration = 1.0
|
||||
0:0/animation_frame_5/duration = 1.0
|
||||
0:0/0 = 0
|
||||
|
||||
[sub_resource type="TileSet" id="TileSet_muxvo"]
|
||||
tile_size = Vector2i(48, 48)
|
||||
sources/0 = SubResource("TileSetAtlasSource_jelju")
|
||||
|
||||
[node name="CombatMap" type="Node2D" unique_id=546780706]
|
||||
script = ExtResource("1_jyv1f")
|
||||
tile_set = SubResource("Resource_vcj5e")
|
||||
|
||||
[node name="OverlayLayer" type="TileMapLayer" parent="." unique_id=1335660296]
|
||||
unique_name_in_owner = true
|
||||
tile_set = SubResource("TileSet_muxvo")
|
||||
script = ExtResource("4_jelju")
|
||||
|
||||
[node name="TargetingIndicator" type="Sprite2D" parent="OverlayLayer" unique_id=1836328987]
|
||||
visible = false
|
||||
z_index = 3
|
||||
texture = ExtResource("5_muxvo")
|
||||
offset = Vector2(24, 24)
|
||||
|
||||
[node name="TerrainLayer" type="TileMapLayer" parent="." unique_id=1201875024]
|
||||
unique_name_in_owner = true
|
||||
tile_set = ExtResource("3_8rn0j")
|
||||
|
||||
[node name="TileHighlight" type="ColorRect" parent="." unique_id=211433569]
|
||||
script = ExtResource("7_tileh")
|
||||
|
||||
[connection signal="tile_hovered" from="TileHighlight" to="." method="target_tile"]
|
||||
42
prefabs/combat_ui.tscn
Normal file
@@ -0,0 +1,42 @@
|
||||
[gd_scene format=3 uid="uid://cy7r0udfcsqbn"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dx26d6py3n8xi" path="res://resources/main_ui_theme.tres" id="1_2ro41"]
|
||||
[ext_resource type="Script" path="res://scripts/combat_ui.gd" id="2_ui_script"]
|
||||
|
||||
[node name="CombatUI" type="CanvasLayer" unique_id=1093388037]
|
||||
script = ExtResource("2_ui_script")
|
||||
|
||||
[node name="UnitPanel" type="PanelContainer" parent="." unique_id=2000000001]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = -78.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = -8.0
|
||||
grow_vertical = 0
|
||||
theme = ExtResource("1_2ro41")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UnitPanel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UnitPanel/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="NameLabel" type="Label" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000002]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Unit"
|
||||
|
||||
[node name="HPBar" type="ProgressBar" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000003]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
max_value = 100.0
|
||||
value = 100.0
|
||||
show_percentage = false
|
||||
57
prefabs/unit.tscn
Normal file
@@ -0,0 +1,57 @@
|
||||
[gd_scene format=3 uid="uid://b6a7nlnf58mc4"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c016mxgatcpse" path="res://nodes/unit.gd" id="1_cq4v0"]
|
||||
[ext_resource type="Texture2D" uid="uid://cw5su6lignryo" path="res://assets/sprites/flag.png" id="2_fhs1y"]
|
||||
[ext_resource type="Texture2D" uid="uid://c2se5wyly6gr6" path="res://assets/sprites/character.bmp" id="2_on614"]
|
||||
[ext_resource type="Shader" uid="uid://bd8ki8xwym5nc" path="res://shaders/chroma_key.gdshader" id="3_fhs1y"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_fhs1y"]
|
||||
resource_name = "AllegianceIndicatorManager"
|
||||
script/source = "extends Sprite2D
|
||||
|
||||
|
||||
func _on_unit_unit_allegiance_changed(unit: Unit, allegiance: UnitAllegiance) -> void:
|
||||
self_modulate = allegiance.color
|
||||
"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4j20j"]
|
||||
shader = ExtResource("3_fhs1y")
|
||||
shader_parameter/threshold = 0.01
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_on614"]
|
||||
atlas = ExtResource("2_on614")
|
||||
region = Rect2(144, 240, 48, 48)
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_on614"]
|
||||
resource_name = "UnitSelectorHandler"
|
||||
script/source = "extends ColorRect
|
||||
|
||||
func _unit_selected_changed(_unit: Unit, selected: bool) -> void:
|
||||
visible = selected
|
||||
"
|
||||
|
||||
[node name="Unit" type="Node2D" unique_id=1893234933 groups=["units"]]
|
||||
script = ExtResource("1_cq4v0")
|
||||
metadata/_custom_type_script = "uid://c016mxgatcpse"
|
||||
|
||||
[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="UnitSprite" type="Sprite2D" parent="." unique_id=350615297]
|
||||
z_index = 1
|
||||
material = SubResource("ShaderMaterial_4j20j")
|
||||
texture = SubResource("AtlasTexture_on614")
|
||||
offset = Vector2(24, 24)
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="." unique_id=1313394023]
|
||||
visible = false
|
||||
offset_right = 48.0
|
||||
offset_bottom = 48.0
|
||||
color = Color(1, 1, 0.3019608, 0.36078432)
|
||||
script = SubResource("GDScript_on614")
|
||||
|
||||
[connection signal="unit_allegiance_changed" from="." to="AllegianceIndicator" method="_on_unit_unit_allegiance_changed"]
|
||||
[connection signal="unit_selected_changed" from="." to="ColorRect" method="_unit_selected_changed"]
|
||||
26
project.godot
Normal file
@@ -0,0 +1,26 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Dungeon Lords"
|
||||
run/main_scene="uid://dlbuo46n6q238"
|
||||
config/features=PackedStringArray("4.6", "Mobile")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[physics]
|
||||
|
||||
3d/physics_engine="Jolt Physics"
|
||||
|
||||
[rendering]
|
||||
|
||||
textures/canvas_textures/default_texture_filter=0
|
||||
rendering_device/driver.windows="d3d12"
|
||||
renderer/rendering_method="mobile"
|
||||
9
resources/allegiance_types/enemy_allegiance.tres
Normal file
@@ -0,0 +1,9 @@
|
||||
[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"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_40cg2")
|
||||
type = 1
|
||||
color = Color(0, 0, 1, 1)
|
||||
metadata/_custom_type_script = "uid://bhglsexm8dtpj"
|
||||
8
resources/allegiance_types/player_allegiance.tres
Normal file
@@ -0,0 +1,8 @@
|
||||
[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"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_4mkdx")
|
||||
color = Color(0.8288101, 2.7193873e-06, 5.7756904e-07, 1)
|
||||
metadata/_custom_type_script = "uid://bhglsexm8dtpj"
|
||||
836
resources/combat_tileset.tres
Normal file
@@ -0,0 +1,836 @@
|
||||
[gd_resource type="TileSet" format=3 uid="uid://dm5wl6d4xkstu"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://udsusbp3o76m" path="res://assets/sprites/map1.bmp" id="1_v1d02"]
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_5lppa"]
|
||||
texture = ExtResource("1_v1d02")
|
||||
texture_region_size = Vector2i(48, 48)
|
||||
0:0/0 = 0
|
||||
1:0/0 = 0
|
||||
2:0/0 = 0
|
||||
3:0/0 = 0
|
||||
4:0/0 = 0
|
||||
5:0/0 = 0
|
||||
6:0/0 = 0
|
||||
7:0/0 = 0
|
||||
8:0/0 = 0
|
||||
9:0/0 = 0
|
||||
10:0/0 = 0
|
||||
11:0/0 = 0
|
||||
12:0/0 = 0
|
||||
13:0/0 = 0
|
||||
14:0/0 = 0
|
||||
15:0/0 = 0
|
||||
16:0/0 = 0
|
||||
17:0/0 = 0
|
||||
18:0/0 = 0
|
||||
19:0/0 = 0
|
||||
20:0/0 = 0
|
||||
21:0/0 = 0
|
||||
22:0/0 = 0
|
||||
23:0/0 = 0
|
||||
24:0/0 = 0
|
||||
25:0/0 = 0
|
||||
26:0/0 = 0
|
||||
27:0/0 = 0
|
||||
28:0/0 = 0
|
||||
29:0/0 = 0
|
||||
30:0/0 = 0
|
||||
31:0/0 = 0
|
||||
32:0/0 = 0
|
||||
0:1/0 = 0
|
||||
1:1/0 = 0
|
||||
2:1/0 = 0
|
||||
3:1/0 = 0
|
||||
4:1/0 = 0
|
||||
5:1/0 = 0
|
||||
6:1/0 = 0
|
||||
7:1/0 = 0
|
||||
8:1/0 = 0
|
||||
9:1/0 = 0
|
||||
10:1/0 = 0
|
||||
11:1/0 = 0
|
||||
12:1/0 = 0
|
||||
13:1/0 = 0
|
||||
14:1/0 = 0
|
||||
15:1/0 = 0
|
||||
16:1/0 = 0
|
||||
17:1/0 = 0
|
||||
18:1/0 = 0
|
||||
19:1/0 = 0
|
||||
20:1/0 = 0
|
||||
21:1/0 = 0
|
||||
22:1/0 = 0
|
||||
23:1/0 = 0
|
||||
24:1/0 = 0
|
||||
25:1/0 = 0
|
||||
26:1/0 = 0
|
||||
27:1/0 = 0
|
||||
28:1/0 = 0
|
||||
29:1/0 = 0
|
||||
30:1/0 = 0
|
||||
31:1/0 = 0
|
||||
32:1/0 = 0
|
||||
0:2/0 = 0
|
||||
1:2/0 = 0
|
||||
2:2/0 = 0
|
||||
3:2/0 = 0
|
||||
4:2/0 = 0
|
||||
5:2/0 = 0
|
||||
6:2/0 = 0
|
||||
7:2/0 = 0
|
||||
8:2/0 = 0
|
||||
9:2/0 = 0
|
||||
10:2/0 = 0
|
||||
11:2/0 = 0
|
||||
12:2/0 = 0
|
||||
13:2/0 = 0
|
||||
14:2/0 = 0
|
||||
15:2/0 = 0
|
||||
16:2/0 = 0
|
||||
17:2/0 = 0
|
||||
18:2/0 = 0
|
||||
19:2/0 = 0
|
||||
20:2/0 = 0
|
||||
21:2/0 = 0
|
||||
22:2/0 = 0
|
||||
23:2/0 = 0
|
||||
24:2/0 = 0
|
||||
25:2/0 = 0
|
||||
26:2/0 = 0
|
||||
27:2/0 = 0
|
||||
28:2/0 = 0
|
||||
29:2/0 = 0
|
||||
30:2/0 = 0
|
||||
31:2/0 = 0
|
||||
32:2/0 = 0
|
||||
0:3/0 = 0
|
||||
1:3/0 = 0
|
||||
2:3/0 = 0
|
||||
3:3/0 = 0
|
||||
4:3/0 = 0
|
||||
5:3/0 = 0
|
||||
6:3/0 = 0
|
||||
7:3/0 = 0
|
||||
8:3/0 = 0
|
||||
9:3/0 = 0
|
||||
10:3/0 = 0
|
||||
11:3/0 = 0
|
||||
12:3/0 = 0
|
||||
13:3/0 = 0
|
||||
14:3/0 = 0
|
||||
15:3/0 = 0
|
||||
16:3/0 = 0
|
||||
17:3/0 = 0
|
||||
18:3/0 = 0
|
||||
19:3/0 = 0
|
||||
20:3/0 = 0
|
||||
21:3/0 = 0
|
||||
22:3/0 = 0
|
||||
23:3/0 = 0
|
||||
24:3/0 = 0
|
||||
25:3/0 = 0
|
||||
26:3/0 = 0
|
||||
27:3/0 = 0
|
||||
28:3/0 = 0
|
||||
29:3/0 = 0
|
||||
30:3/0 = 0
|
||||
31:3/0 = 0
|
||||
32:3/0 = 0
|
||||
0:4/0 = 0
|
||||
1:4/0 = 0
|
||||
2:4/0 = 0
|
||||
3:4/0 = 0
|
||||
4:4/0 = 0
|
||||
5:4/0 = 0
|
||||
6:4/0 = 0
|
||||
7:4/0 = 0
|
||||
8:4/0 = 0
|
||||
9:4/0 = 0
|
||||
10:4/0 = 0
|
||||
11:4/0 = 0
|
||||
12:4/0 = 0
|
||||
13:4/0 = 0
|
||||
14:4/0 = 0
|
||||
15:4/0 = 0
|
||||
16:4/0 = 0
|
||||
17:4/0 = 0
|
||||
18:4/0 = 0
|
||||
19:4/0 = 0
|
||||
20:4/0 = 0
|
||||
21:4/0 = 0
|
||||
22:4/0 = 0
|
||||
23:4/0 = 0
|
||||
24:4/0 = 0
|
||||
25:4/0 = 0
|
||||
26:4/0 = 0
|
||||
27:4/0 = 0
|
||||
28:4/0 = 0
|
||||
29:4/0 = 0
|
||||
30:4/0 = 0
|
||||
31:4/0 = 0
|
||||
32:4/0 = 0
|
||||
0:5/0 = 0
|
||||
1:5/0 = 0
|
||||
2:5/0 = 0
|
||||
3:5/0 = 0
|
||||
4:5/0 = 0
|
||||
5:5/0 = 0
|
||||
6:5/0 = 0
|
||||
7:5/0 = 0
|
||||
8:5/0 = 0
|
||||
9:5/0 = 0
|
||||
10:5/0 = 0
|
||||
11:5/0 = 0
|
||||
12:5/0 = 0
|
||||
13:5/0 = 0
|
||||
14:5/0 = 0
|
||||
15:5/0 = 0
|
||||
16:5/0 = 0
|
||||
17:5/0 = 0
|
||||
18:5/0 = 0
|
||||
19:5/0 = 0
|
||||
20:5/0 = 0
|
||||
21:5/0 = 0
|
||||
22:5/0 = 0
|
||||
23:5/0 = 0
|
||||
24:5/0 = 0
|
||||
25:5/0 = 0
|
||||
26:5/0 = 0
|
||||
27:5/0 = 0
|
||||
28:5/0 = 0
|
||||
29:5/0 = 0
|
||||
30:5/0 = 0
|
||||
31:5/0 = 0
|
||||
32:5/0 = 0
|
||||
0:6/0 = 0
|
||||
1:6/0 = 0
|
||||
2:6/0 = 0
|
||||
3:6/0 = 0
|
||||
4:6/0 = 0
|
||||
5:6/0 = 0
|
||||
6:6/0 = 0
|
||||
7:6/0 = 0
|
||||
8:6/0 = 0
|
||||
9:6/0 = 0
|
||||
10:6/0 = 0
|
||||
11:6/0 = 0
|
||||
12:6/0 = 0
|
||||
13:6/0 = 0
|
||||
14:6/0 = 0
|
||||
15:6/0 = 0
|
||||
16:6/0 = 0
|
||||
17:6/0 = 0
|
||||
18:6/0 = 0
|
||||
19:6/0 = 0
|
||||
20:6/0 = 0
|
||||
21:6/0 = 0
|
||||
22:6/0 = 0
|
||||
23:6/0 = 0
|
||||
24:6/0 = 0
|
||||
25:6/0 = 0
|
||||
26:6/0 = 0
|
||||
27:6/0 = 0
|
||||
28:6/0 = 0
|
||||
29:6/0 = 0
|
||||
30:6/0 = 0
|
||||
31:6/0 = 0
|
||||
32:6/0 = 0
|
||||
0:7/0 = 0
|
||||
1:7/0 = 0
|
||||
2:7/0 = 0
|
||||
3:7/0 = 0
|
||||
4:7/0 = 0
|
||||
5:7/0 = 0
|
||||
6:7/0 = 0
|
||||
7:7/0 = 0
|
||||
8:7/0 = 0
|
||||
9:7/0 = 0
|
||||
10:7/0 = 0
|
||||
11:7/0 = 0
|
||||
12:7/0 = 0
|
||||
13:7/0 = 0
|
||||
14:7/0 = 0
|
||||
15:7/0 = 0
|
||||
16:7/0 = 0
|
||||
17:7/0 = 0
|
||||
18:7/0 = 0
|
||||
19:7/0 = 0
|
||||
20:7/0 = 0
|
||||
21:7/0 = 0
|
||||
22:7/0 = 0
|
||||
23:7/0 = 0
|
||||
24:7/0 = 0
|
||||
25:7/0 = 0
|
||||
26:7/0 = 0
|
||||
27:7/0 = 0
|
||||
28:7/0 = 0
|
||||
29:7/0 = 0
|
||||
30:7/0 = 0
|
||||
31:7/0 = 0
|
||||
32:7/0 = 0
|
||||
0:8/0 = 0
|
||||
1:8/0 = 0
|
||||
2:8/0 = 0
|
||||
3:8/0 = 0
|
||||
4:8/0 = 0
|
||||
5:8/0 = 0
|
||||
6:8/0 = 0
|
||||
7:8/0 = 0
|
||||
8:8/0 = 0
|
||||
9:8/0 = 0
|
||||
10:8/0 = 0
|
||||
11:8/0 = 0
|
||||
12:8/0 = 0
|
||||
13:8/0 = 0
|
||||
14:8/0 = 0
|
||||
15:8/0 = 0
|
||||
16:8/0 = 0
|
||||
17:8/0 = 0
|
||||
18:8/0 = 0
|
||||
19:8/0 = 0
|
||||
20:8/0 = 0
|
||||
21:8/0 = 0
|
||||
22:8/0 = 0
|
||||
23:8/0 = 0
|
||||
24:8/0 = 0
|
||||
25:8/0 = 0
|
||||
26:8/0 = 0
|
||||
27:8/0 = 0
|
||||
28:8/0 = 0
|
||||
29:8/0 = 0
|
||||
30:8/0 = 0
|
||||
31:8/0 = 0
|
||||
32:8/0 = 0
|
||||
0:9/0 = 0
|
||||
1:9/0 = 0
|
||||
2:9/0 = 0
|
||||
3:9/0 = 0
|
||||
4:9/0 = 0
|
||||
5:9/0 = 0
|
||||
6:9/0 = 0
|
||||
7:9/0 = 0
|
||||
8:9/0 = 0
|
||||
9:9/0 = 0
|
||||
10:9/0 = 0
|
||||
11:9/0 = 0
|
||||
12:9/0 = 0
|
||||
13:9/0 = 0
|
||||
14:9/0 = 0
|
||||
15:9/0 = 0
|
||||
16:9/0 = 0
|
||||
17:9/0 = 0
|
||||
18:9/0 = 0
|
||||
19:9/0 = 0
|
||||
20:9/0 = 0
|
||||
21:9/0 = 0
|
||||
22:9/0 = 0
|
||||
23:9/0 = 0
|
||||
24:9/0 = 0
|
||||
25:9/0 = 0
|
||||
26:9/0 = 0
|
||||
27:9/0 = 0
|
||||
28:9/0 = 0
|
||||
29:9/0 = 0
|
||||
30:9/0 = 0
|
||||
31:9/0 = 0
|
||||
32:9/0 = 0
|
||||
0:10/0 = 0
|
||||
1:10/0 = 0
|
||||
2:10/0 = 0
|
||||
3:10/0 = 0
|
||||
4:10/0 = 0
|
||||
5:10/0 = 0
|
||||
6:10/0 = 0
|
||||
7:10/0 = 0
|
||||
8:10/0 = 0
|
||||
9:10/0 = 0
|
||||
10:10/0 = 0
|
||||
11:10/0 = 0
|
||||
12:10/0 = 0
|
||||
13:10/0 = 0
|
||||
14:10/0 = 0
|
||||
15:10/0 = 0
|
||||
16:10/0 = 0
|
||||
17:10/0 = 0
|
||||
18:10/0 = 0
|
||||
19:10/0 = 0
|
||||
20:10/0 = 0
|
||||
21:10/0 = 0
|
||||
22:10/0 = 0
|
||||
23:10/0 = 0
|
||||
24:10/0 = 0
|
||||
25:10/0 = 0
|
||||
26:10/0 = 0
|
||||
27:10/0 = 0
|
||||
28:10/0 = 0
|
||||
29:10/0 = 0
|
||||
30:10/0 = 0
|
||||
31:10/0 = 0
|
||||
32:10/0 = 0
|
||||
0:11/0 = 0
|
||||
1:11/0 = 0
|
||||
2:11/0 = 0
|
||||
3:11/0 = 0
|
||||
4:11/0 = 0
|
||||
5:11/0 = 0
|
||||
6:11/0 = 0
|
||||
7:11/0 = 0
|
||||
8:11/0 = 0
|
||||
9:11/0 = 0
|
||||
10:11/0 = 0
|
||||
11:11/0 = 0
|
||||
12:11/0 = 0
|
||||
13:11/0 = 0
|
||||
14:11/0 = 0
|
||||
15:11/0 = 0
|
||||
16:11/0 = 0
|
||||
17:11/0 = 0
|
||||
18:11/0 = 0
|
||||
19:11/0 = 0
|
||||
20:11/0 = 0
|
||||
21:11/0 = 0
|
||||
22:11/0 = 0
|
||||
23:11/0 = 0
|
||||
24:11/0 = 0
|
||||
25:11/0 = 0
|
||||
26:11/0 = 0
|
||||
27:11/0 = 0
|
||||
28:11/0 = 0
|
||||
29:11/0 = 0
|
||||
30:11/0 = 0
|
||||
31:11/0 = 0
|
||||
32:11/0 = 0
|
||||
0:12/0 = 0
|
||||
1:12/0 = 0
|
||||
2:12/0 = 0
|
||||
3:12/0 = 0
|
||||
4:12/0 = 0
|
||||
5:12/0 = 0
|
||||
6:12/0 = 0
|
||||
7:12/0 = 0
|
||||
8:12/0 = 0
|
||||
9:12/0 = 0
|
||||
10:12/0 = 0
|
||||
11:12/0 = 0
|
||||
12:12/0 = 0
|
||||
13:12/0 = 0
|
||||
14:12/0 = 0
|
||||
15:12/0 = 0
|
||||
16:12/0 = 0
|
||||
17:12/0 = 0
|
||||
18:12/0 = 0
|
||||
19:12/0 = 0
|
||||
20:12/0 = 0
|
||||
21:12/0 = 0
|
||||
22:12/0 = 0
|
||||
23:12/0 = 0
|
||||
24:12/0 = 0
|
||||
25:12/0 = 0
|
||||
26:12/0 = 0
|
||||
27:12/0 = 0
|
||||
28:12/0 = 0
|
||||
29:12/0 = 0
|
||||
30:12/0 = 0
|
||||
31:12/0 = 0
|
||||
32:12/0 = 0
|
||||
0:13/0 = 0
|
||||
1:13/0 = 0
|
||||
2:13/0 = 0
|
||||
3:13/0 = 0
|
||||
4:13/0 = 0
|
||||
5:13/0 = 0
|
||||
6:13/0 = 0
|
||||
7:13/0 = 0
|
||||
8:13/0 = 0
|
||||
9:13/0 = 0
|
||||
10:13/0 = 0
|
||||
11:13/0 = 0
|
||||
12:13/0 = 0
|
||||
13:13/0 = 0
|
||||
14:13/0 = 0
|
||||
15:13/0 = 0
|
||||
16:13/0 = 0
|
||||
17:13/0 = 0
|
||||
18:13/0 = 0
|
||||
19:13/0 = 0
|
||||
20:13/0 = 0
|
||||
21:13/0 = 0
|
||||
22:13/0 = 0
|
||||
23:13/0 = 0
|
||||
24:13/0 = 0
|
||||
25:13/0 = 0
|
||||
26:13/0 = 0
|
||||
27:13/0 = 0
|
||||
28:13/0 = 0
|
||||
29:13/0 = 0
|
||||
30:13/0 = 0
|
||||
31:13/0 = 0
|
||||
32:13/0 = 0
|
||||
0:14/0 = 0
|
||||
1:14/0 = 0
|
||||
2:14/0 = 0
|
||||
3:14/0 = 0
|
||||
4:14/0 = 0
|
||||
5:14/0 = 0
|
||||
6:14/0 = 0
|
||||
7:14/0 = 0
|
||||
8:14/0 = 0
|
||||
9:14/0 = 0
|
||||
10:14/0 = 0
|
||||
11:14/0 = 0
|
||||
12:14/0 = 0
|
||||
13:14/0 = 0
|
||||
14:14/0 = 0
|
||||
15:14/0 = 0
|
||||
16:14/0 = 0
|
||||
17:14/0 = 0
|
||||
18:14/0 = 0
|
||||
19:14/0 = 0
|
||||
20:14/0 = 0
|
||||
21:14/0 = 0
|
||||
22:14/0 = 0
|
||||
23:14/0 = 0
|
||||
24:14/0 = 0
|
||||
25:14/0 = 0
|
||||
26:14/0 = 0
|
||||
27:14/0 = 0
|
||||
28:14/0 = 0
|
||||
29:14/0 = 0
|
||||
30:14/0 = 0
|
||||
31:14/0 = 0
|
||||
32:14/0 = 0
|
||||
0:15/0 = 0
|
||||
1:15/0 = 0
|
||||
2:15/0 = 0
|
||||
3:15/0 = 0
|
||||
4:15/0 = 0
|
||||
5:15/0 = 0
|
||||
6:15/0 = 0
|
||||
7:15/0 = 0
|
||||
8:15/0 = 0
|
||||
9:15/0 = 0
|
||||
10:15/0 = 0
|
||||
11:15/0 = 0
|
||||
12:15/0 = 0
|
||||
13:15/0 = 0
|
||||
14:15/0 = 0
|
||||
15:15/0 = 0
|
||||
16:15/0 = 0
|
||||
17:15/0 = 0
|
||||
18:15/0 = 0
|
||||
19:15/0 = 0
|
||||
20:15/0 = 0
|
||||
21:15/0 = 0
|
||||
22:15/0 = 0
|
||||
23:15/0 = 0
|
||||
24:15/0 = 0
|
||||
25:15/0 = 0
|
||||
26:15/0 = 0
|
||||
27:15/0 = 0
|
||||
28:15/0 = 0
|
||||
29:15/0 = 0
|
||||
30:15/0 = 0
|
||||
31:15/0 = 0
|
||||
32:15/0 = 0
|
||||
0:16/0 = 0
|
||||
1:16/0 = 0
|
||||
2:16/0 = 0
|
||||
3:16/0 = 0
|
||||
4:16/0 = 0
|
||||
5:16/0 = 0
|
||||
6:16/0 = 0
|
||||
7:16/0 = 0
|
||||
8:16/0 = 0
|
||||
9:16/0 = 0
|
||||
10:16/0 = 0
|
||||
11:16/0 = 0
|
||||
12:16/0 = 0
|
||||
13:16/0 = 0
|
||||
14:16/0 = 0
|
||||
15:16/0 = 0
|
||||
16:16/0 = 0
|
||||
17:16/0 = 0
|
||||
18:16/0 = 0
|
||||
19:16/0 = 0
|
||||
20:16/0 = 0
|
||||
21:16/0 = 0
|
||||
22:16/0 = 0
|
||||
23:16/0 = 0
|
||||
24:16/0 = 0
|
||||
25:16/0 = 0
|
||||
26:16/0 = 0
|
||||
27:16/0 = 0
|
||||
28:16/0 = 0
|
||||
29:16/0 = 0
|
||||
30:16/0 = 0
|
||||
31:16/0 = 0
|
||||
32:16/0 = 0
|
||||
0:17/0 = 0
|
||||
1:17/0 = 0
|
||||
2:17/0 = 0
|
||||
3:17/0 = 0
|
||||
4:17/0 = 0
|
||||
5:17/0 = 0
|
||||
6:17/0 = 0
|
||||
7:17/0 = 0
|
||||
8:17/0 = 0
|
||||
9:17/0 = 0
|
||||
10:17/0 = 0
|
||||
11:17/0 = 0
|
||||
12:17/0 = 0
|
||||
13:17/0 = 0
|
||||
14:17/0 = 0
|
||||
15:17/0 = 0
|
||||
16:17/0 = 0
|
||||
17:17/0 = 0
|
||||
18:17/0 = 0
|
||||
19:17/0 = 0
|
||||
20:17/0 = 0
|
||||
21:17/0 = 0
|
||||
22:17/0 = 0
|
||||
23:17/0 = 0
|
||||
24:17/0 = 0
|
||||
25:17/0 = 0
|
||||
26:17/0 = 0
|
||||
27:17/0 = 0
|
||||
28:17/0 = 0
|
||||
29:17/0 = 0
|
||||
30:17/0 = 0
|
||||
31:17/0 = 0
|
||||
32:17/0 = 0
|
||||
0:18/0 = 0
|
||||
1:18/0 = 0
|
||||
2:18/0 = 0
|
||||
3:18/0 = 0
|
||||
4:18/0 = 0
|
||||
5:18/0 = 0
|
||||
6:18/0 = 0
|
||||
7:18/0 = 0
|
||||
8:18/0 = 0
|
||||
9:18/0 = 0
|
||||
10:18/0 = 0
|
||||
11:18/0 = 0
|
||||
12:18/0 = 0
|
||||
13:18/0 = 0
|
||||
14:18/0 = 0
|
||||
15:18/0 = 0
|
||||
16:18/0 = 0
|
||||
17:18/0 = 0
|
||||
18:18/0 = 0
|
||||
19:18/0 = 0
|
||||
20:18/0 = 0
|
||||
21:18/0 = 0
|
||||
22:18/0 = 0
|
||||
23:18/0 = 0
|
||||
24:18/0 = 0
|
||||
25:18/0 = 0
|
||||
26:18/0 = 0
|
||||
27:18/0 = 0
|
||||
28:18/0 = 0
|
||||
29:18/0 = 0
|
||||
30:18/0 = 0
|
||||
31:18/0 = 0
|
||||
32:18/0 = 0
|
||||
0:19/0 = 0
|
||||
1:19/0 = 0
|
||||
2:19/0 = 0
|
||||
3:19/0 = 0
|
||||
4:19/0 = 0
|
||||
5:19/0 = 0
|
||||
6:19/0 = 0
|
||||
7:19/0 = 0
|
||||
8:19/0 = 0
|
||||
9:19/0 = 0
|
||||
10:19/0 = 0
|
||||
11:19/0 = 0
|
||||
12:19/0 = 0
|
||||
13:19/0 = 0
|
||||
14:19/0 = 0
|
||||
15:19/0 = 0
|
||||
16:19/0 = 0
|
||||
17:19/0 = 0
|
||||
18:19/0 = 0
|
||||
19:19/0 = 0
|
||||
20:19/0 = 0
|
||||
21:19/0 = 0
|
||||
22:19/0 = 0
|
||||
23:19/0 = 0
|
||||
24:19/0 = 0
|
||||
25:19/0 = 0
|
||||
26:19/0 = 0
|
||||
27:19/0 = 0
|
||||
28:19/0 = 0
|
||||
29:19/0 = 0
|
||||
30:19/0 = 0
|
||||
31:19/0 = 0
|
||||
32:19/0 = 0
|
||||
0:20/0 = 0
|
||||
1:20/0 = 0
|
||||
2:20/0 = 0
|
||||
3:20/0 = 0
|
||||
4:20/0 = 0
|
||||
5:20/0 = 0
|
||||
6:20/0 = 0
|
||||
7:20/0 = 0
|
||||
8:20/0 = 0
|
||||
9:20/0 = 0
|
||||
10:20/0 = 0
|
||||
11:20/0 = 0
|
||||
12:20/0 = 0
|
||||
13:20/0 = 0
|
||||
14:20/0 = 0
|
||||
15:20/0 = 0
|
||||
16:20/0 = 0
|
||||
17:20/0 = 0
|
||||
18:20/0 = 0
|
||||
19:20/0 = 0
|
||||
20:20/0 = 0
|
||||
21:20/0 = 0
|
||||
22:20/0 = 0
|
||||
23:20/0 = 0
|
||||
24:20/0 = 0
|
||||
25:20/0 = 0
|
||||
26:20/0 = 0
|
||||
27:20/0 = 0
|
||||
28:20/0 = 0
|
||||
29:20/0 = 0
|
||||
30:20/0 = 0
|
||||
31:20/0 = 0
|
||||
32:20/0 = 0
|
||||
0:21/0 = 0
|
||||
1:21/0 = 0
|
||||
2:21/0 = 0
|
||||
3:21/0 = 0
|
||||
4:21/0 = 0
|
||||
5:21/0 = 0
|
||||
6:21/0 = 0
|
||||
7:21/0 = 0
|
||||
8:21/0 = 0
|
||||
9:21/0 = 0
|
||||
10:21/0 = 0
|
||||
11:21/0 = 0
|
||||
12:21/0 = 0
|
||||
13:21/0 = 0
|
||||
14:21/0 = 0
|
||||
15:21/0 = 0
|
||||
16:21/0 = 0
|
||||
17:21/0 = 0
|
||||
18:21/0 = 0
|
||||
19:21/0 = 0
|
||||
20:21/0 = 0
|
||||
21:21/0 = 0
|
||||
22:21/0 = 0
|
||||
23:21/0 = 0
|
||||
24:21/0 = 0
|
||||
25:21/0 = 0
|
||||
26:21/0 = 0
|
||||
27:21/0 = 0
|
||||
28:21/0 = 0
|
||||
29:21/0 = 0
|
||||
30:21/0 = 0
|
||||
31:21/0 = 0
|
||||
32:21/0 = 0
|
||||
0:22/0 = 0
|
||||
1:22/0 = 0
|
||||
2:22/0 = 0
|
||||
3:22/0 = 0
|
||||
4:22/0 = 0
|
||||
5:22/0 = 0
|
||||
6:22/0 = 0
|
||||
7:22/0 = 0
|
||||
8:22/0 = 0
|
||||
9:22/0 = 0
|
||||
10:22/0 = 0
|
||||
11:22/0 = 0
|
||||
12:22/0 = 0
|
||||
13:22/0 = 0
|
||||
14:22/0 = 0
|
||||
15:22/0 = 0
|
||||
16:22/0 = 0
|
||||
17:22/0 = 0
|
||||
18:22/0 = 0
|
||||
19:22/0 = 0
|
||||
20:22/0 = 0
|
||||
21:22/0 = 0
|
||||
22:22/0 = 0
|
||||
23:22/0 = 0
|
||||
24:22/0 = 0
|
||||
25:22/0 = 0
|
||||
26:22/0 = 0
|
||||
27:22/0 = 0
|
||||
28:22/0 = 0
|
||||
29:22/0 = 0
|
||||
30:22/0 = 0
|
||||
31:22/0 = 0
|
||||
32:22/0 = 0
|
||||
0:23/0 = 0
|
||||
1:23/0 = 0
|
||||
2:23/0 = 0
|
||||
3:23/0 = 0
|
||||
4:23/0 = 0
|
||||
5:23/0 = 0
|
||||
6:23/0 = 0
|
||||
7:23/0 = 0
|
||||
8:23/0 = 0
|
||||
9:23/0 = 0
|
||||
10:23/0 = 0
|
||||
11:23/0 = 0
|
||||
12:23/0 = 0
|
||||
13:23/0 = 0
|
||||
14:23/0 = 0
|
||||
15:23/0 = 0
|
||||
16:23/0 = 0
|
||||
17:23/0 = 0
|
||||
18:23/0 = 0
|
||||
19:23/0 = 0
|
||||
20:23/0 = 0
|
||||
21:23/0 = 0
|
||||
22:23/0 = 0
|
||||
23:23/0 = 0
|
||||
24:23/0 = 0
|
||||
25:23/0 = 0
|
||||
26:23/0 = 0
|
||||
27:23/0 = 0
|
||||
28:23/0 = 0
|
||||
29:23/0 = 0
|
||||
30:23/0 = 0
|
||||
31:23/0 = 0
|
||||
32:23/0 = 0
|
||||
0:24/0 = 0
|
||||
1:24/0 = 0
|
||||
2:24/0 = 0
|
||||
3:24/0 = 0
|
||||
4:24/0 = 0
|
||||
5:24/0 = 0
|
||||
6:24/0 = 0
|
||||
7:24/0 = 0
|
||||
8:24/0 = 0
|
||||
9:24/0 = 0
|
||||
10:24/0 = 0
|
||||
11:24/0 = 0
|
||||
12:24/0 = 0
|
||||
13:24/0 = 0
|
||||
14:24/0 = 0
|
||||
15:24/0 = 0
|
||||
16:24/0 = 0
|
||||
17:24/0 = 0
|
||||
18:24/0 = 0
|
||||
19:24/0 = 0
|
||||
20:24/0 = 0
|
||||
21:24/0 = 0
|
||||
22:24/0 = 0
|
||||
23:24/0 = 0
|
||||
24:24/0 = 0
|
||||
25:24/0 = 0
|
||||
26:24/0 = 0
|
||||
27:24/0 = 0
|
||||
28:24/0 = 0
|
||||
29:24/0 = 0
|
||||
30:24/0 = 0
|
||||
31:24/0 = 0
|
||||
32:24/0 = 0
|
||||
|
||||
[resource]
|
||||
tile_size = Vector2i(48, 48)
|
||||
sources/0 = SubResource("TileSetAtlasSource_5lppa")
|
||||
6
resources/main_ui_theme.tres
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_resource type="Theme" format=3 uid="uid://dx26d6py3n8xi"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://eqhm6gv5p05t" path="res://assets/fonts/Minecraft.ttf" id="1_nmd6r"]
|
||||
|
||||
[resource]
|
||||
default_font = ExtResource("1_nmd6r")
|
||||
4
resources/resource_definitions/dl_tileset.gd
Normal file
@@ -0,0 +1,4 @@
|
||||
class_name DLTileset extends Resource
|
||||
|
||||
@export var floor_tile_coords: Vector2i
|
||||
@export var wall_tile_coords: Vector2i
|
||||
1
resources/resource_definitions/dl_tileset.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c6701vy8h5rfx
|
||||
12
resources/resource_definitions/unit_allegiance.gd
Normal file
@@ -0,0 +1,12 @@
|
||||
class_name UnitAllegiance extends Resource
|
||||
|
||||
enum AllegianceType {
|
||||
PLAYER,
|
||||
ENEMY,
|
||||
PLAYER_ALLY,
|
||||
ENEMY_ALLY,
|
||||
UNAFFILIATED
|
||||
}
|
||||
|
||||
@export var type: AllegianceType
|
||||
@export var color: Color
|
||||
1
resources/resource_definitions/unit_allegiance.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bhglsexm8dtpj
|
||||
3
resources/resource_definitions/unit_info.gd
Normal file
@@ -0,0 +1,3 @@
|
||||
class_name UnitInfo extends Resource
|
||||
|
||||
@export var name: String = "Unit"
|
||||
1
resources/resource_definitions/unit_info.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d37ulss2k0bq5
|
||||
17
resources/resource_definitions/unit_stats.gd
Normal file
@@ -0,0 +1,17 @@
|
||||
class_name UnitStats extends Resource
|
||||
|
||||
@export var max_hp: int = 10
|
||||
@export var current_hp: int
|
||||
@export var phys_atk: int = 1
|
||||
@export var phys_def: int = 1
|
||||
@export var magic_atk: int = 0
|
||||
@export var magic_def: int = 0
|
||||
@export var hit: int = 85
|
||||
@export var atk_range: int = 1
|
||||
@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
|
||||
1
resources/resource_definitions/unit_stats.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cydoey8a8nmb8
|
||||
36
scenes/combat_test.tscn
Normal file
@@ -0,0 +1,36 @@
|
||||
[gd_scene format=3 uid="uid://wy7ur5r23ek3"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://cy7r0udfcsqbn" path="res://prefabs/combat_ui.tscn" id="1_5jbmu"]
|
||||
[ext_resource type="PackedScene" uid="uid://dkhyh5ce4iuk3" path="res://prefabs/combat_map.tscn" id="1_7abyo"]
|
||||
[ext_resource type="Script" uid="uid://c8xb86ty7rduf" path="res://scripts/test_map_generator.gd" id="2_ekcfv"]
|
||||
[ext_resource type="Script" uid="uid://csdcbi2gtwrly" path="res://scripts/camera_controller.gd" id="3_cam"]
|
||||
[ext_resource type="Script" uid="uid://dfojm3n0em4ef" path="res://nodes/player_controller.gd" id="4_s5ga2"]
|
||||
[ext_resource type="AudioStream" uid="uid://dsikulned64qt" path="res://assets/music/combat_bgm_01.OGG" id="6_0yobm"]
|
||||
[ext_resource type="PackedScene" uid="uid://b6a7nlnf58mc4" path="res://prefabs/unit.tscn" id="6_rfoto"]
|
||||
[ext_resource type="Resource" uid="uid://dufi2h00j5vrq" path="res://resources/allegiance_types/player_allegiance.tres" id="7_0wg56"]
|
||||
[ext_resource type="Resource" uid="uid://cuc7kkknpsr1g" path="res://resources/allegiance_types/enemy_allegiance.tres" id="8_w105o"]
|
||||
|
||||
[node name="CombatTest" type="Node2D" unique_id=855645983]
|
||||
|
||||
[node name="CombatUI" parent="." unique_id=329168107 instance=ExtResource("1_5jbmu")]
|
||||
|
||||
[node name="CombatMap" parent="." unique_id=546780706 instance=ExtResource("1_7abyo")]
|
||||
|
||||
[node name="PlayerController" type="Node" parent="." unique_id=774568109 node_paths=PackedStringArray("dl_map")]
|
||||
script = ExtResource("4_s5ga2")
|
||||
dl_map = NodePath("../CombatMap")
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="." unique_id=1739569732]
|
||||
zoom = Vector2(1.5, 1.5)
|
||||
script = ExtResource("3_cam")
|
||||
|
||||
[node name="TestMapGenerator" type="Node" parent="." unique_id=833658301 node_paths=PackedStringArray("dl_map")]
|
||||
script = ExtResource("2_ekcfv")
|
||||
dl_map = NodePath("../CombatMap")
|
||||
unit_template = ExtResource("6_rfoto")
|
||||
player_allegiance = ExtResource("7_0wg56")
|
||||
enemy_allegiance = ExtResource("8_w105o")
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="." unique_id=1057500234]
|
||||
stream = ExtResource("6_0yobm")
|
||||
autoplay = true
|
||||
59
scenes/main_menu.tscn
Normal file
@@ -0,0 +1,59 @@
|
||||
[gd_scene format=3 uid="uid://dlbuo46n6q238"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dx26d6py3n8xi" path="res://resources/main_ui_theme.tres" id="1_ekxnf"]
|
||||
[ext_resource type="AudioStream" uid="uid://b7dgmblbcm0cj" path="res://assets/music/menu_theme.OGG" id="1_yqeox"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_bqqt6"]
|
||||
resource_name = "StartButton"
|
||||
script/source = "extends Button
|
||||
|
||||
func _pressed() -> void:
|
||||
get_parent().queue_free()
|
||||
get_tree().change_scene_to_file(\"res://scenes/combat_test.tscn\")
|
||||
"
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_ekxnf"]
|
||||
resource_name = "ExitButton"
|
||||
script/source = "extends Button
|
||||
|
||||
func _pressed() -> void:
|
||||
get_tree().quit(0)
|
||||
"
|
||||
|
||||
[node name="Menu" 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_ekxnf")
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="." unique_id=1976575731]
|
||||
stream = ExtResource("1_yqeox")
|
||||
autoplay = true
|
||||
|
||||
[node name="Buttons" type="VBoxContainer" parent="." unique_id=1869378860]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="StartButton" type="Button" parent="Buttons" unique_id=734401781]
|
||||
layout_mode = 2
|
||||
text = "Start"
|
||||
script = SubResource("GDScript_bqqt6")
|
||||
|
||||
[node name="Options" type="Button" parent="Buttons" unique_id=1037030570]
|
||||
layout_mode = 2
|
||||
text = "Options"
|
||||
|
||||
[node name="ExitButton" type="Button" parent="Buttons" unique_id=1205095617]
|
||||
layout_mode = 2
|
||||
text = "Exit"
|
||||
script = SubResource("GDScript_ekxnf")
|
||||
|
||||
[connection signal="pressed" from="Buttons/Options" to="Buttons/Options" method="_on_pressed"]
|
||||
20
scenes/test_scene.tscn
Normal file
@@ -0,0 +1,20 @@
|
||||
[gd_scene format=3 uid="uid://b7hhdysqqmx4y"]
|
||||
|
||||
[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"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_qjeyg"]
|
||||
shader = ExtResource("1_nd71p")
|
||||
shader_parameter/flag_mask = ExtResource("2_7ddre")
|
||||
shader_parameter/team_color = Color(0.84830123, 0.29993045, 0.292207, 1)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_j8ivh"]
|
||||
atlas = ExtResource("1_g7g4h")
|
||||
region = Rect2(0, 100, 100, 100)
|
||||
|
||||
[node name="TestScene" type="Node2D" unique_id=1687841395]
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1997336331]
|
||||
material = SubResource("ShaderMaterial_qjeyg")
|
||||
texture = SubResource("AtlasTexture_j8ivh")
|
||||
42
scripts/camera_controller.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
class_name CameraController extends Camera2D
|
||||
|
||||
const DRAG_THRESHOLD := 8.0
|
||||
|
||||
var _dragging := false
|
||||
var _left_pending := false
|
||||
var _drag_start := Vector2.ZERO
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
match event.button_index:
|
||||
MOUSE_BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
_left_pending = true
|
||||
_drag_start = event.position
|
||||
else:
|
||||
_left_pending = false
|
||||
if _dragging:
|
||||
_dragging = false
|
||||
Input.set_default_cursor_shape(Input.CURSOR_ARROW)
|
||||
get_viewport().set_input_as_handled()
|
||||
MOUSE_BUTTON_MIDDLE:
|
||||
if event.pressed:
|
||||
_dragging = true
|
||||
_drag_start = event.position
|
||||
Input.set_default_cursor_shape(Input.CURSOR_DRAG)
|
||||
else:
|
||||
_dragging = false
|
||||
Input.set_default_cursor_shape(Input.CURSOR_ARROW)
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event is InputEventMouseMotion:
|
||||
if _left_pending and not _dragging:
|
||||
if event.position.distance_to(_drag_start) >= DRAG_THRESHOLD:
|
||||
_dragging = true
|
||||
_left_pending = false
|
||||
Input.set_default_cursor_shape(Input.CURSOR_DRAG)
|
||||
if _dragging:
|
||||
var delta: Vector2 = _drag_start - event.position
|
||||
_drag_start = event.position
|
||||
position += delta / zoom
|
||||
get_viewport().set_input_as_handled()
|
||||
1
scripts/camera_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://csdcbi2gtwrly
|
||||
33
scripts/combat_ui.gd
Normal file
@@ -0,0 +1,33 @@
|
||||
class_name CombatUI extends CanvasLayer
|
||||
|
||||
@onready var unit_panel: PanelContainer = %UnitPanel
|
||||
@onready var name_label: Label = %NameLabel
|
||||
@onready var hp_bar: ProgressBar = %HPBar
|
||||
|
||||
var _selected_unit: Unit
|
||||
|
||||
func _ready() -> void:
|
||||
unit_panel.visible = false
|
||||
for unit: Unit in get_tree().get_nodes_in_group("units"):
|
||||
unit.unit_selected_changed.connect(_on_unit_selected_changed)
|
||||
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"):
|
||||
node.unit_selected_changed.connect(_on_unit_selected_changed)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if _selected_unit:
|
||||
hp_bar.max_value = _selected_unit.current_stats.max_hp
|
||||
hp_bar.value = _selected_unit.current_stats.current_hp
|
||||
|
||||
func _on_unit_selected_changed(unit: Unit, 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
|
||||
unit_panel.visible = true
|
||||
else:
|
||||
_selected_unit = null
|
||||
unit_panel.visible = false
|
||||
1
scripts/combat_ui.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://w2wh6gtv3u2l
|
||||
16
scripts/grid_overlay.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
class_name GridOverlay extends TileMapLayer
|
||||
|
||||
const SOURCE_ID = 0
|
||||
const HIGHLIGHT_SPRITE_ID = Vector2i(0,0)
|
||||
|
||||
@onready var targeting_selector: Sprite2D = $TargetingIndicator
|
||||
|
||||
func highlight_tile(coords: Vector2i) -> void:
|
||||
set_cell(coords, SOURCE_ID, HIGHLIGHT_SPRITE_ID)
|
||||
|
||||
func clear_tile(coords: Vector2i) -> void:
|
||||
set_cell(coords)
|
||||
|
||||
func target_tile(coords: Vector2i) -> void:
|
||||
targeting_selector.position = coords * tile_set.tile_size
|
||||
targeting_selector.visible = true
|
||||
1
scripts/grid_overlay.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cxl38x2m6sj3w
|
||||
38
scripts/test_map_generator.gd
Normal file
@@ -0,0 +1,38 @@
|
||||
extends Node
|
||||
|
||||
const GRID_SIZE := 5
|
||||
|
||||
@export var dl_map: CombatMap
|
||||
@export var unit_template: PackedScene
|
||||
@export var player_allegiance: UnitAllegiance
|
||||
@export var enemy_allegiance: UnitAllegiance
|
||||
|
||||
func _ready() -> void:
|
||||
for x in GRID_SIZE:
|
||||
for y in GRID_SIZE:
|
||||
var pos := Vector2i(x, y)
|
||||
var is_edge := x == 0 or x == GRID_SIZE - 1 or y == 0 or y == GRID_SIZE - 1
|
||||
if is_edge:
|
||||
dl_map.draw_wall(pos)
|
||||
else:
|
||||
dl_map.draw_floor(pos)
|
||||
|
||||
# Create a putit at the center belonging to the player
|
||||
var center := Vector2i(GRID_SIZE / 2, GRID_SIZE / 2)
|
||||
var unit: Unit = unit_template.instantiate()
|
||||
unit.stat_template = UnitStats.new(50)
|
||||
unit.info_template = UnitInfo.new()
|
||||
unit.allegiance_template = player_allegiance
|
||||
unit.info_template.name = "Putit"
|
||||
unit.position = dl_map.coords_to_world(center)
|
||||
get_parent().add_child.call_deferred(unit)
|
||||
|
||||
# Create a putit at one above the center belonging to the enemy
|
||||
var center_enemy := Vector2i(GRID_SIZE / 2, (GRID_SIZE / 2) - 1)
|
||||
var enemy_unit: Unit = unit_template.instantiate()
|
||||
enemy_unit.stat_template = UnitStats.new(50)
|
||||
enemy_unit.info_template = UnitInfo.new()
|
||||
enemy_unit.allegiance_template = enemy_allegiance
|
||||
enemy_unit.info_template.name = "Putit"
|
||||
enemy_unit.position = dl_map.coords_to_world(center_enemy)
|
||||
get_parent().add_child.call_deferred(enemy_unit)
|
||||
1
scripts/test_map_generator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c8xb86ty7rduf
|
||||
37
scripts/tile_highlight.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
extends ColorRect
|
||||
|
||||
signal tile_hovered(coords: Vector2i)
|
||||
|
||||
@export var tile_size: float = 48.0
|
||||
|
||||
var _time: float = 0.0
|
||||
var _previous_coords := Vector2i(INF, INF)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
size = Vector2(tile_size, tile_size)
|
||||
color = Color(1.0, 1.0, 1.0, 0.25)
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_time += delta
|
||||
color.a = 0.25 + 0.1 * sin(_time * 4.0)
|
||||
var mouse_pos := get_global_mouse_position()
|
||||
var snapped_pos := _snap_to_grid(mouse_pos)
|
||||
global_position = snapped_pos
|
||||
var coords := Vector2i(snapped_pos / tile_size)
|
||||
if coords != _previous_coords:
|
||||
_previous_coords = coords
|
||||
tile_hovered.emit(coords)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_WM_MOUSE_EXIT:
|
||||
hide()
|
||||
elif what == NOTIFICATION_WM_MOUSE_ENTER:
|
||||
show()
|
||||
|
||||
|
||||
func _snap_to_grid(pos: Vector2) -> Vector2:
|
||||
return Vector2(floorf(pos.x / tile_size), floorf(pos.y / tile_size)) * tile_size
|
||||
1
scripts/tile_highlight.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b31eyqov8w7bm
|
||||
12
shaders/chroma_key.gdshader
Normal file
@@ -0,0 +1,12 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform float threshold : hint_range(0.0, 1.0) = 0.01;
|
||||
|
||||
void fragment() {
|
||||
vec4 tex_color = texture(TEXTURE, UV);
|
||||
float brightness = max(tex_color.r, max(tex_color.g, tex_color.b));
|
||||
if (brightness < threshold) {
|
||||
discard;
|
||||
}
|
||||
COLOR = tex_color;
|
||||
}
|
||||
1
shaders/chroma_key.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bd8ki8xwym5nc
|
||||
16
shaders/masked_palette_swap.gdshader
Normal file
@@ -0,0 +1,16 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D flag_mask : source_color, hint_default_black;
|
||||
uniform vec4 team_color : source_color = vec4(1.0, 0.0, 0.0, 1.0); // red by default
|
||||
|
||||
void fragment() {
|
||||
vec4 base = texture(TEXTURE, UV);
|
||||
float mask = texture(flag_mask, UV).r; // 1.0 on flag pixels, 0.0 on castle
|
||||
|
||||
// Use the grey luminance as a brightness multiplier on the team color
|
||||
float brightness = base.r; // grey means R == G == B
|
||||
vec4 tinted = vec4(team_color.rgb * brightness * 2.0, base.a);
|
||||
|
||||
// Blend: castle pixels stay grey, flag pixels get tinted
|
||||
COLOR = mix(base, tinted, mask);
|
||||
}
|
||||
1
shaders/masked_palette_swap.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dakre5usldk6r
|
||||