Compare commits
24 Commits
344efee7b4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2891fb6248 | ||
|
|
31a787a7f0 | ||
|
|
f332771153 | ||
|
|
2d74e15006 | ||
|
|
6d9d08d78c | ||
|
|
834d6a3a83 | ||
|
|
676c82c4e5 | ||
|
|
e2d23bec48 | ||
|
|
b01d8c6648 | ||
|
|
f6ac31b52e | ||
|
|
9b1d6e8c8f | ||
|
|
664c9694de | ||
|
|
e356078a9f | ||
|
|
6b46d1c274 | ||
|
|
b807e9897d | ||
|
|
c192d48bc4 | ||
|
|
24134cfa33 | ||
|
|
767df71975 | ||
|
|
92a0bb1d58 | ||
|
|
e42a98fece | ||
|
|
0882908e4c | ||
|
|
880d4ecc77 | ||
|
|
39d2222546 | ||
|
|
97909235ff |
BIN
assets/sprites/CP002AB.BMP
Normal file
|
After Width: | Height: | Size: 32 KiB |
@@ -2,16 +2,16 @@
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dbcuqclg3xhrk"
|
||||
path="res://.godot/imported/aux_terrain_tileset.bmp-cfff3a0c5b37bcf01b239cc3e0e49ae5.ctex"
|
||||
uid="uid://b6smsdyydtiv4"
|
||||
path="res://.godot/imported/CP002AB.BMP-83c77e61ea705a94bd90d268ad08d826.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/aux_terrain_tileset.bmp"
|
||||
dest_files=["res://.godot/imported/aux_terrain_tileset.bmp-cfff3a0c5b37bcf01b239cc3e0e49ae5.ctex"]
|
||||
source_file="res://assets/sprites/CP002AB.BMP"
|
||||
dest_files=["res://.godot/imported/CP002AB.BMP-83c77e61ea705a94bd90d268ad08d826.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 732 KiB |
BIN
assets/ui/SO008A.BMP
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
40
assets/ui/SO008A.BMP.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cau61m1755dkn"
|
||||
path="res://.godot/imported/SO008A.BMP-dedc5f46caf0f66b50eed190cef6a3a9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/ui/SO008A.BMP"
|
||||
dest_files=["res://.godot/imported/SO008A.BMP-dedc5f46caf0f66b50eed190cef6a3a9.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/ui/SO008B.BMP
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
40
assets/ui/SO008B.BMP.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cavpqnd0qqoou"
|
||||
path="res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/ui/SO008B.BMP"
|
||||
dest_files=["res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.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/ui/unit_faces.BMP
Normal file
|
After Width: | Height: | Size: 173 KiB |
40
assets/ui/unit_faces.BMP.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c7coajdu61crq"
|
||||
path="res://.godot/imported/unit_faces.BMP-4eb539ec6753a0109183a7c01fbbbf92.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/ui/unit_faces.BMP"
|
||||
dest_files=["res://.godot/imported/unit_faces.BMP-4eb539ec6753a0109183a7c01fbbbf92.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
|
||||
278
docs/review/2026-04-07-architecture-review.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# Architecture & Code Quality Review — Dungeon Lords
|
||||
|
||||
Reviewed: 2026-04-07
|
||||
Previous review: `architecture_review.md` (2026-04-02)
|
||||
|
||||
This pass re-checks the 15 issues from the prior review and surfaces new findings introduced by recent work (fog of war, camera fixes, map sizing, corners, room/wall renderer).
|
||||
|
||||
---
|
||||
|
||||
## Status of Prior Review (2026-04-02)
|
||||
|
||||
| # | Issue | Status | Notes |
|
||||
|---|-------|--------|-------|
|
||||
| 1 | No allegiance check on combat | **Open** | `nodes/player_controller.gd:135` |
|
||||
| 2 | No pathfinding | **Partially mitigated** | `is_tile_passable()` now blocks the next step (`player_controller.gd:122`), but units still walk around L-walls one axis at a time and have no movement budget |
|
||||
| 3 | Stale combat proposal | **Open** | `apply_proposal()` still has no `is_instance_valid()` guards (`combat_system.gd:129-148`) |
|
||||
| 4 | `queue_free()` dangling refs | **Open** | `nodes/unit.gd` still queue_frees immediately on death |
|
||||
| 5 | Duplicated unit-tracking | **Open** | `player_controller.gd:26-35` and `scripts/battle/combat_ui.gd:40-50` |
|
||||
| 6 | Inline scripts in unit.tscn | **Open** | Two `SubResource` GDScript blocks remain in `prefabs/unit.tscn` |
|
||||
| 7 | Two combat code paths | **Open** | `combat_system.gd:114` `process_combat()` is dead (no callers in code, only docs/plans) |
|
||||
| 8 | O(n) unit lookup | **Open** | `player_controller.gd:159-167` |
|
||||
| 9 | No turn / movement system | **Open** | `spd` still unused |
|
||||
| 10 | `atk_range` not enforced | **Open** | Tactic-range is checked when *building* a proposal (`combat_system.gd:25`), but `_handle_left_click` emits `combat_requested` without any pre-check |
|
||||
| 11 | tile_highlight always processing | **Resolved** | `tile_highlight.gd` is gone; replaced by `scripts/battle/grid_overlay.gd` which only updates on `target_tile()` calls |
|
||||
| 12 | Unused GridOverlay | **Resolved** | Now wired through `StrategyPhase._on_mouse_grid_changed → CombatMap.target_tile()` (`combat_map.gd:85`) |
|
||||
| 13 | Jolt 3D physics configured | **Open** | Still in `project.godot` |
|
||||
| 14 | `current_hp` default fragile | **Resolved-ish** | `UnitStats._init` sets it; works but `@export` still has no default |
|
||||
| 15 | `UnitInfo.name` shadows `Object.name` | **Open** | `resources/resource_definitions/unit_info.gd:3` |
|
||||
|
||||
**Tally:** 11 still open, 1 partially mitigated, 3 resolved.
|
||||
|
||||
---
|
||||
|
||||
## High Priority
|
||||
|
||||
### A. `apply_proposal` does not validate unit references — **Resolved 2026-04-07**
|
||||
|
||||
`apply_proposal` now early-returns if either unit reference is invalid, and re-checks both before the counterattack branch.
|
||||
|
||||
**File:** `nodes/combat_system.gd:129-148`
|
||||
|
||||
`process_combat()` was given an `is_instance_valid()` guard at the top, but the canonical path used by the UI — `apply_proposal()` — calls `def_unit.take_damage(...)` and `atk_unit.take_damage(...)` directly without checking either reference. Today this is harmless because `Unit._die()` only happens through this same code path, but the moment a second damage source exists (poison, AoE, traps), a unit can be freed mid-proposal and `apply_proposal` will hit a freed reference.
|
||||
|
||||
**Fix:** Guard both unit accesses with `is_instance_valid()`. Better, snapshot the unit references into the proposal at creation time and treat them as nullable from then on.
|
||||
|
||||
---
|
||||
|
||||
### B. No allegiance check on combat targeting
|
||||
|
||||
**File:** `nodes/player_controller.gd:135`
|
||||
|
||||
```gdscript
|
||||
if _selected_unit and clicked_unit != _selected_unit \
|
||||
and _selected_unit.is_alive() and clicked_unit.is_alive():
|
||||
combat_requested.emit(_selected_unit, clicked_unit)
|
||||
```
|
||||
|
||||
A player unit can attack a friendly unit. Carried over unchanged from the prior review.
|
||||
|
||||
**Fix:** Compare `clicked_unit.current_allegiance.type` against the selected unit, ideally in `StrategyPhase` so `PlayerController` stays allegiance-agnostic.
|
||||
|
||||
---
|
||||
|
||||
### C. `Unit.queue_free()` on death leaves dangling references in proposals
|
||||
|
||||
**File:** `nodes/unit.gd` (`_die`)
|
||||
|
||||
When a unit dies it emits `unit_died` and immediately calls `queue_free()`. `CombatUI._on_unit_died` (`scripts/battle/combat_ui.gd:52-58`) compares the dying unit against `_current_proposal.attacker.unit` — that comparison happens *this* frame, so it's fine, but `CombatProposal` still holds the same reference and will be invalid for any code that runs after the deferred free.
|
||||
|
||||
**Fix:** Either delay the `queue_free()` until after combat resolution finishes (hide-then-free), or null out unit references in any active proposal as part of the death handler.
|
||||
|
||||
---
|
||||
|
||||
### D. `attack_range` is never enforced before requesting combat
|
||||
|
||||
**Files:** `nodes/player_controller.gd:135`, `nodes/combat_system.gd:22-27`
|
||||
|
||||
Tactic-range filtering happens *inside* `create_proposal`, so a click that targets an unreachable enemy will produce a proposal with no valid tactics — and from there the behavior depends on `_find_default_attack` returning `null` and `_snapshot` happily writing zeros. The user-visible bug is that you can click any enemy on the map and a proposal opens regardless of distance.
|
||||
|
||||
**Fix:** Check distance against the attacker's max tactic range (or `current_stats.atk_range`) inside `_handle_left_click` before emitting `combat_requested`. Reject the click otherwise.
|
||||
|
||||
---
|
||||
|
||||
## Medium Priority
|
||||
|
||||
### E. Pathfinding still missing; movement is greedy axis-stepping
|
||||
|
||||
**File:** `nodes/player_controller.gd:96-127`
|
||||
|
||||
`is_tile_passable` now blocks the next single step, which is an improvement, but the mover is still a greedy "pick the larger axis" loop with no global plan. Going around an L-shaped wall succeeds; going around a U-shaped wall doesn't. There's also still no movement budget — a unit can walk to the far corner of the map with a single click.
|
||||
|
||||
**Fix:** Compute the full path with BFS/A* over `MapLayout.is_passable` at click time, store it as a list of tiles, and step along it. Add a per-turn movement budget once a turn system exists.
|
||||
|
||||
---
|
||||
|
||||
### F. Duplicated unit-tracking boilerplate
|
||||
|
||||
**Files:** `nodes/player_controller.gd:26-35`, `scripts/battle/combat_ui.gd:40-50`
|
||||
|
||||
Both systems independently iterate `get_nodes_in_group("units")`, hook `node_added`, and guard against duplicate connections. Any third system that cares about units will copy this again.
|
||||
|
||||
**Fix:** Promote a small `UnitRegistry` autoload that emits `unit_registered(unit)` / `unit_deregistered(unit)`. This ties into finding G below.
|
||||
|
||||
---
|
||||
|
||||
### G. `CombatMap.remove_unit` doesn't disconnect signals
|
||||
|
||||
**File:** `nodes/combat_map.gd:80-82`
|
||||
|
||||
`remove_unit` only calls `remove_child`. PlayerController and CombatUI both connected to `unit_died` (and `unit_selected_changed` for PlayerController via the AllegianceIndicator path). If a unit is removed and re-added — or destroyed without going through `_die` — the connections leak. Combined with finding F, an event-bus / registry would solve both.
|
||||
|
||||
---
|
||||
|
||||
### H. Two combat entry points; `process_combat` is dead — **Resolved 2026-04-07**
|
||||
|
||||
`process_combat()` deleted from `combat_system.gd`. Only `create_proposal` → `apply_proposal` remains.
|
||||
|
||||
**File:** `nodes/combat_system.gd:114-127`
|
||||
|
||||
`process_combat()` has no callers in `nodes/` or `scripts/`; the only references are in plan/spec docs under `docs/superpowers/`. It mirrors the proposal-application path and is liable to drift.
|
||||
|
||||
**Fix:** Delete `process_combat()` and let any future AI/auto-combat go through `create_proposal` → `apply_proposal` directly.
|
||||
|
||||
---
|
||||
|
||||
### I. O(n) unit lookup on every click
|
||||
|
||||
**File:** `nodes/player_controller.gd:159-167`
|
||||
|
||||
`_get_unit_at` snaps every unit in the group to the grid and compares. Fine for tens of units, slow for hundreds.
|
||||
|
||||
**Fix:** Maintain a `Dictionary[Vector2i, Unit]` in `CombatMap` that updates on deploy/move/death. Lookups become O(1) and you get tile-occupancy collision detection for free.
|
||||
|
||||
---
|
||||
|
||||
### J. Sprite spam in `FogRenderer` and `WallRenderer` — **Resolved 2026-04-07**
|
||||
|
||||
Both renderers now override `_draw()` and use `draw_texture_rect_region()` directly. Zero child sprites; layout changes call `queue_redraw()` instead of thrashing the scene tree. Findings **O** (redundant `match` arms) and **P** (undocumented `FOG_RECT`) were folded into the same change.
|
||||
|
||||
**Files:** `nodes/fog_renderer.gd:13-43`, `nodes/wall_renderer.gd:44-176`
|
||||
|
||||
Both renderers create one `Sprite2D` per fog tile / per wall half-segment / per inner corner, with no pooling, and `_clear()` `queue_free`s every child on the next layout change. For a modest 30×30 map this is already in the high hundreds of nodes; for larger maps it scales linearly with `width × height` plus wall surface area, and every map reload thrashes the scene tree.
|
||||
|
||||
**Fix:** Override `_draw()` and use `draw_texture_rect_region()` for both renderers. You get the same visual result with one draw call per region instead of N nodes, and `queue_redraw()` on layout change replaces the whole `_clear` + recreate cycle.
|
||||
|
||||
---
|
||||
|
||||
### K. `MapLayout` requires `initialize()` but exposes no guard
|
||||
|
||||
**Files:** `nodes/combat_map.gd:89-101`, `resources/resource_definitions/map_layout.gd`
|
||||
|
||||
`apply_layout()` is the only place that calls `map_layout.initialize()`. Anything that constructs a `MapLayout` and calls `is_passable()` / `is_tile_valid()` without going through `apply_layout` will silently see empty internal state.
|
||||
|
||||
**Fix:** Either lazy-initialize inside `is_passable` / `is_tile_valid`, or assert that `initialize()` has been called.
|
||||
|
||||
---
|
||||
|
||||
### L. `target_tile` highlight updates while combat UI is open — **Not a bug (2026-04-07)**
|
||||
|
||||
On re-reading `player_controller.gd:44-46`, `_process` already early-returns when `input_disabled` is true. Original finding was based on a misread; no code change needed.
|
||||
|
||||
**File:** `nodes/player_controller.gd:44-52`
|
||||
|
||||
`input_disabled` short-circuits `_unhandled_input` but `_process` still runs and emits `mouse_grid_changed`, so the cursor highlight keeps tracking the mouse during the combat proposal panel. May be intentional, but looks like a bug — the player just told the game "I'm interacting with the modal", not "keep showing me where my next click would land on the map."
|
||||
|
||||
**Fix:** Bail out of `_process` (or at least suppress the emit) when `input_disabled` is true.
|
||||
|
||||
---
|
||||
|
||||
### M. Inline scripts in `prefabs/unit.tscn`
|
||||
|
||||
The AllegianceIndicator and selection-highlight scripts are still embedded as `SubResource` GDScript blocks in the scene file. Invisible to grep, painful to refactor.
|
||||
|
||||
**Fix:** Extract to `scripts/units/allegiance_indicator.gd` and `scripts/units/unit_selection_highlight.gd`.
|
||||
|
||||
---
|
||||
|
||||
## Low Priority
|
||||
|
||||
### N. `WallRenderer._draw_outer_corners` is a stub
|
||||
|
||||
**File:** `nodes/wall_renderer.gd:114-115, 179-183`
|
||||
|
||||
The function is called but no-ops. The TODO is fine for now, but the matching atlas constants for outer corners aren't defined either, so when you implement it you'll need to add both. Worth a tracker entry.
|
||||
|
||||
---
|
||||
|
||||
### O. `WallRenderer._draw_tile_walls` match arms are identical — **Resolved 2026-04-07**
|
||||
|
||||
Collapsed to a single `for edge in edges: _build_edge_segments(...)` loop as part of finding J's refactor.
|
||||
|
||||
**File:** `nodes/wall_renderer.gd:103-112`
|
||||
|
||||
```gdscript
|
||||
match edge:
|
||||
&"left": _draw_edge_segments(tile_origin, edge)
|
||||
&"right": _draw_edge_segments(tile_origin, edge)
|
||||
&"top": _draw_edge_segments(tile_origin, edge)
|
||||
&"bottom":_draw_edge_segments(tile_origin, edge)
|
||||
```
|
||||
|
||||
The whole `match` collapses to `for edge in edges: _draw_edge_segments(tile_origin, edge)`.
|
||||
|
||||
---
|
||||
|
||||
### P. `FogRenderer.FOG_RECT` is `(53, 53, 100, 100)` — tile-aligned magic constant
|
||||
|
||||
**File:** `nodes/fog_renderer.gd:8`
|
||||
|
||||
Hardcoded atlas region with no comment about why those coordinates. Same kind of magic numbers that the wall renderer uses, but at least the wall constants have section comments. Add a one-line comment naming the source asset.
|
||||
|
||||
---
|
||||
|
||||
### Q. `Jolt Physics` configured for a 2D project
|
||||
|
||||
**File:** `project.godot`
|
||||
|
||||
Carried over from prior review. `3d/physics_engine="Jolt Physics"` is harmless but unused — the game has no 3D nodes. Removing it removes a "huh, why?" for any new contributor.
|
||||
|
||||
---
|
||||
|
||||
### R. `UnitInfo.name` still shadows `Object.name`
|
||||
|
||||
**File:** `resources/resource_definitions/unit_info.gd:3`
|
||||
|
||||
Rename to `display_name` or `unit_name` to disambiguate from `Node.name` / `Object.name`.
|
||||
|
||||
---
|
||||
|
||||
### S. `CombatMap.is_wall` and `is_tile_valid` have legacy fallbacks — **Resolved 2026-04-07**
|
||||
|
||||
Fallback branches removed from `is_tile_passable` and `is_tile_valid`; both now `assert(map_layout != null)`. The now-dead `is_wall()` helper was deleted.
|
||||
|
||||
**File:** `nodes/combat_map.gd:97-108`
|
||||
|
||||
Both methods have a "no room system" fallback path. Now that every map goes through `MapLayout`, the fallback is dead — but it's also the kind of dead code that quietly hides bugs (a missing `map_layout` won't crash, it'll silently fall through to wall-tile checks). Either delete the fallbacks and assert, or document the contract.
|
||||
|
||||
---
|
||||
|
||||
## Architectural Notes
|
||||
|
||||
### Renderer pattern is the right shape, wrong implementation
|
||||
`WallRenderer` and `FogRenderer` correctly isolate "draw the map's decoration layer" from `CombatMap`. The split is good. The execution — instantiating Sprite2D children — is the part to revisit. A `_draw()`-based implementation would let both classes keep the same public API (`draw_walls_for_layout(MapLayout)` / `draw_fog_for_layout(MapLayout)`) and their `_clear()` calls would become `queue_redraw()`.
|
||||
|
||||
### Combat data flow is solid
|
||||
`PlayerController → StrategyPhase → CombatSystem ← CombatUI` is still clean. The remaining issues in this area (A, B, C, D) are all edge cases at the boundaries, not architectural problems.
|
||||
|
||||
### The missing piece is still turn/movement
|
||||
Findings 2 and 9 from the prior review are the largest gameplay gap. Until that exists, "combat" is "pick two units anywhere on the map and click." Worth scoping a small turn manager (`turn_started(unit)`, `turn_ended(unit)`, `action_points_remaining`) before adding more combat features on top of the current freeform model.
|
||||
|
||||
### No tests
|
||||
`CombatSystem` is pure logic that takes resources and returns a proposal — it's the easiest thing in the codebase to test, and the proposal/apply path is the most fragile. A few GdUnit4 tests on combat math would catch most of A/B/C/D as regressions.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| # | Finding | Priority | Effort |
|
||||
|---|---------|----------|--------|
|
||||
| A | `apply_proposal` no `is_instance_valid` | ~~High~~ | **Done** |
|
||||
| B | No allegiance check | High | Small |
|
||||
| C | `queue_free` dangling refs | High | Small |
|
||||
| D | `atk_range` not enforced pre-request | High | Small |
|
||||
| E | Greedy axis movement, no pathfinding | Medium | Medium |
|
||||
| F | Duplicated unit-tracking | Medium | Medium |
|
||||
| G | `remove_unit` leaks signal connections | Medium | Small |
|
||||
| H | Dead `process_combat` | ~~Medium~~ | **Done** |
|
||||
| I | O(n) unit lookup | Medium | Small |
|
||||
| J | Sprite-per-tile renderers | ~~Medium~~ | **Done** |
|
||||
| K | `MapLayout.initialize` not guarded | Medium | Small |
|
||||
| L | Highlight updates while modal open | ~~Medium~~ | **Not a bug** |
|
||||
| M | Inline scripts in `unit.tscn` | Medium | Small |
|
||||
| N | `_draw_outer_corners` stub | Low | Medium |
|
||||
| O | Redundant `match` in wall renderer | ~~Low~~ | **Done** |
|
||||
| P | Magic `FOG_RECT` constant | Low | Trivial |
|
||||
| Q | Jolt 3D in 2D project | Low | Trivial |
|
||||
| R | `UnitInfo.name` shadows | Low | Trivial |
|
||||
| S | Dead fallback paths in `CombatMap` | ~~Low~~ | **Done** |
|
||||
@@ -1,6 +1,15 @@
|
||||
* Reogranize files
|
||||
* Singletons named 'XXXServer'
|
||||
* Dialogue scene command system (ShowText, ShowSprite, MoveSprite, PlaySound, ChangeBackground, etc)
|
||||
* Need to figure out more complicated systems like choices, script eval, conditionals, jumping, etc. Probably end up wanting a DialogueEditor or just making them gdscripts and being done with it
|
||||
* Finish Dialogue Scene (fast forward, auto, history functionality, etc)
|
||||
* Setup room system (everything is unpassable, carve out rooms, walls automatic, specify connections between rooms on tiles)
|
||||
* Basic map editor (test map data will be harder to craft the more we add)
|
||||
* Finish main menu
|
||||
* Game Start screen
|
||||
* Load Data Screen
|
||||
* Eushully room (CG viewer, scene viewer (after dialogue system), unit data screen (needs a unit registry))
|
||||
* Options
|
||||
* Battle View
|
||||
* Fog of war
|
||||
* Basic map editor (test map data will be harder to craft the more we add)
|
||||
* Start plugging in the Himegari UI
|
||||
* Unit panel needs fixed width number boxes, difference between digits causes UI jumps now
|
||||
* Dialog boxes
|
||||
71
export_presets.cfg
Normal 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}'"
|
||||
@@ -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()
|
||||
@@ -1,218 +0,0 @@
|
||||
class_name WallRenderer
|
||||
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.
|
||||
|
||||
# -- Left edge --
|
||||
const LEFT_UPPER_RECT := Rect2(0, 103, 20, 50)
|
||||
const LEFT_LOWER_RECT := Rect2(0, 53, 20, 50)
|
||||
|
||||
# -- Right edge --
|
||||
const RIGHT_UPPER_RECT := Rect2(186, 103, 20, 50)
|
||||
const RIGHT_LOWER_RECT := Rect2(186, 53, 20, 50)
|
||||
|
||||
# -- Top edge --
|
||||
const TOP_LEFT_RECT := Rect2(103, 0, 50, 20)
|
||||
const TOP_RIGHT_RECT := Rect2(53, 0, 50, 20)
|
||||
|
||||
# -- Bottom edge --
|
||||
const BOTTOM_LEFT_RECT := Rect2(103, 186, 50, 20)
|
||||
const BOTTOM_RIGHT_RECT := Rect2(53, 186, 50, 20)
|
||||
|
||||
# -- Inner corners (drawn where two perpendicular wall edges meet) --
|
||||
const INNER_CORNER_UPPER_LEFT_RECT := Rect2(0, 0, 50, 50)
|
||||
const INNER_CORNER_UPPER_RIGHT_RECT := Rect2(156, 0, 50, 50)
|
||||
const INNER_CORNER_LOWER_LEFT_RECT := Rect2(0, 156, 50, 50)
|
||||
const INNER_CORNER_LOWER_RIGHT_RECT := Rect2(156, 156, 50, 50)
|
||||
|
||||
## 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
|
||||
|
||||
@export var atlas_texture: Texture2D
|
||||
|
||||
|
||||
func draw_walls_for_layout(map_layout: MapLayout) -> void:
|
||||
_clear_walls()
|
||||
if not map_layout or not atlas_texture:
|
||||
return
|
||||
|
||||
var tile_edges := _collect_tile_edges(map_layout)
|
||||
for tile in tile_edges:
|
||||
var edges: Array = tile_edges[tile]
|
||||
_draw_tile_walls(tile, edges)
|
||||
|
||||
|
||||
func _clear_walls() -> void:
|
||||
for child in get_children():
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func _collect_tile_edges(map_layout: MapLayout) -> Dictionary:
|
||||
## Returns {Vector2i: Array[StringName]} mapping each tile to its wall edge directions.
|
||||
var tile_edges: Dictionary = {}
|
||||
var walls := map_layout.get_walls()
|
||||
|
||||
for wall in walls:
|
||||
var tile_a: Vector2i = wall[0]
|
||||
var tile_b: Vector2i = wall[1]
|
||||
var diff: Vector2i = tile_b - tile_a
|
||||
|
||||
# Determine which edge of tile_a faces the wall
|
||||
var edge_a := _direction_to_edge(diff)
|
||||
if edge_a != &"":
|
||||
if not tile_edges.has(tile_a):
|
||||
tile_edges[tile_a] = []
|
||||
tile_edges[tile_a].append(edge_a)
|
||||
|
||||
# Determine which edge of tile_b faces the wall (opposite direction)
|
||||
var edge_b := _direction_to_edge(-diff)
|
||||
if edge_b != &"" and map_layout.is_tile_valid(tile_b):
|
||||
if not tile_edges.has(tile_b):
|
||||
tile_edges[tile_b] = []
|
||||
tile_edges[tile_b].append(edge_b)
|
||||
|
||||
return tile_edges
|
||||
|
||||
|
||||
func _direction_to_edge(dir: Vector2i) -> StringName:
|
||||
match dir:
|
||||
Vector2i.RIGHT:
|
||||
return &"right"
|
||||
Vector2i.LEFT:
|
||||
return &"left"
|
||||
Vector2i.UP:
|
||||
return &"top"
|
||||
Vector2i.DOWN:
|
||||
return &"bottom"
|
||||
return &""
|
||||
|
||||
|
||||
func _draw_tile_walls(tile: Vector2i, edges: Array) -> void:
|
||||
var tile_origin := Vector2(tile) * TILE_SIZE
|
||||
|
||||
for edge in edges:
|
||||
match edge:
|
||||
&"left":
|
||||
_draw_edge_segments(tile_origin, edge)
|
||||
&"right":
|
||||
_draw_edge_segments(tile_origin, edge)
|
||||
&"top":
|
||||
_draw_edge_segments(tile_origin, edge)
|
||||
&"bottom":
|
||||
_draw_edge_segments(tile_origin, edge)
|
||||
|
||||
# TODO: Outer corner segments
|
||||
_draw_outer_corners(tile, tile_origin, edges)
|
||||
|
||||
# TODO: Inner corner segments (for non-rectangular room support)
|
||||
_draw_inner_corners(tile, tile_origin, edges)
|
||||
|
||||
|
||||
func _draw_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
|
||||
var seg_a_rect: Rect2
|
||||
var seg_b_rect: Rect2
|
||||
var seg_a_offset: Vector2
|
||||
var seg_b_offset: Vector2
|
||||
var seg_a_size: Vector2
|
||||
var seg_b_size: Vector2
|
||||
|
||||
match edge:
|
||||
&"left":
|
||||
seg_a_rect = LEFT_UPPER_RECT
|
||||
seg_b_rect = LEFT_LOWER_RECT
|
||||
seg_a_size = Vector2(WALL_THICKNESS, HALF_EDGE)
|
||||
seg_b_size = Vector2(WALL_THICKNESS, HALF_EDGE)
|
||||
seg_a_offset = Vector2(0, 0)
|
||||
seg_b_offset = Vector2(0, HALF_EDGE)
|
||||
&"right":
|
||||
seg_a_rect = RIGHT_UPPER_RECT
|
||||
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)
|
||||
&"top":
|
||||
seg_a_rect = TOP_LEFT_RECT
|
||||
seg_b_rect = TOP_RIGHT_RECT
|
||||
seg_a_size = Vector2(HALF_EDGE, WALL_THICKNESS)
|
||||
seg_b_size = Vector2(HALF_EDGE, WALL_THICKNESS)
|
||||
seg_a_offset = Vector2(0, 0)
|
||||
seg_b_offset = Vector2(HALF_EDGE, 0)
|
||||
&"bottom":
|
||||
seg_a_rect = BOTTOM_LEFT_RECT
|
||||
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)
|
||||
|
||||
_create_segment_sprite(tile_origin + seg_a_offset, seg_a_size, seg_a_rect)
|
||||
_create_segment_sprite(tile_origin + seg_b_offset, seg_b_size, seg_b_rect)
|
||||
|
||||
|
||||
func _create_segment_sprite(pos: Vector2, target_size: Vector2, source_rect: Rect2) -> void:
|
||||
var atlas := AtlasTexture.new()
|
||||
atlas.atlas = atlas_texture
|
||||
atlas.region = source_rect
|
||||
|
||||
var sprite := Sprite2D.new()
|
||||
sprite.texture = atlas
|
||||
sprite.centered = false
|
||||
sprite.position = pos
|
||||
sprite.scale = Vector2(
|
||||
target_size.x / source_rect.size.x,
|
||||
target_size.y / source_rect.size.y
|
||||
)
|
||||
add_child(sprite)
|
||||
|
||||
|
||||
func _draw_outer_corners(_tile: Vector2i, _tile_origin: Vector2, _edges: Array) -> void:
|
||||
# TODO: Implement outer corner segments
|
||||
# Check pairs of adjacent edges (e.g., "top" + "left" → top-left outer corner)
|
||||
# and draw the corner piece from the atlas.
|
||||
pass
|
||||
|
||||
|
||||
func _draw_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -> void:
|
||||
## Draws decorative corner pieces wherever two perpendicular wall edges meet
|
||||
## on the same tile (e.g., a top wall + left wall produces an upper-left corner).
|
||||
var has_top := edges.has(&"top")
|
||||
var has_bottom := edges.has(&"bottom")
|
||||
var has_left := edges.has(&"left")
|
||||
var has_right := edges.has(&"right")
|
||||
var corner_size := Vector2(CORNER_SIZE, CORNER_SIZE)
|
||||
|
||||
if has_top and has_left:
|
||||
_create_segment_sprite(
|
||||
tile_origin + Vector2(0, 0),
|
||||
corner_size,
|
||||
INNER_CORNER_UPPER_LEFT_RECT
|
||||
)
|
||||
if has_top and has_right:
|
||||
_create_segment_sprite(
|
||||
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, 0),
|
||||
corner_size,
|
||||
INNER_CORNER_UPPER_RIGHT_RECT
|
||||
)
|
||||
if has_bottom and has_left:
|
||||
_create_segment_sprite(
|
||||
tile_origin + Vector2(0, TILE_SIZE - CORNER_SIZE),
|
||||
corner_size,
|
||||
INNER_CORNER_LOWER_LEFT_RECT
|
||||
)
|
||||
if has_bottom and has_right:
|
||||
_create_segment_sprite(
|
||||
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, TILE_SIZE - CORNER_SIZE),
|
||||
corner_size,
|
||||
INNER_CORNER_LOWER_RIGHT_RECT
|
||||
)
|
||||
21
prefabs/chip_bar.tscn
Normal file
@@ -0,0 +1,21 @@
|
||||
[gd_scene format=3 uid="uid://8edgswcwdiwu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cvmmsm13nyr62" path="res://scripts/ui/chip_bar.gd" id="1_3whrn"]
|
||||
|
||||
[node name="ChipBar" type="Control" unique_id=379110810]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_3whrn")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=185867767]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 0
|
||||
@@ -1,13 +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" 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://scripts/battle/map/fog_renderer.gd" id="9_fogrn"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_vcj5e"]
|
||||
script = ExtResource("2_8rn0j")
|
||||
@@ -86,7 +87,12 @@ offset = Vector2(50, 50)
|
||||
unique_name_in_owner = true
|
||||
tile_set = SubResource("TileSet_e2u25")
|
||||
|
||||
[node name="WallRenderer" type="Node2D" parent="."]
|
||||
[node name="FogRenderer" type="Node2D" parent="." unique_id=641669860]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("9_fogrn")
|
||||
atlas_texture = ExtResource("8_auxtr")
|
||||
|
||||
[node name="WallRenderer" type="Node2D" parent="." unique_id=1008825128]
|
||||
unique_name_in_owner = true
|
||||
z_index = 1
|
||||
script = ExtResource("7_wallr")
|
||||
|
||||
@@ -2,47 +2,155 @@
|
||||
|
||||
[ext_resource type="Theme" uid="uid://dx26d6py3n8xi" path="res://resources/main_ui_theme.tres" id="1_2ro41"]
|
||||
[ext_resource type="Script" uid="uid://w2wh6gtv3u2l" path="res://scripts/battle/combat_ui.gd" id="2_ui_script"]
|
||||
[ext_resource type="Texture2D" uid="uid://cavpqnd0qqoou" path="res://assets/ui/SO008B.BMP" id="3_hadma"]
|
||||
[ext_resource type="Texture2D" uid="uid://cau61m1755dkn" path="res://assets/ui/SO008A.BMP" id="3_tfn3h"]
|
||||
[ext_resource type="PackedScene" uid="uid://bc5a7tb0my6n5" path="res://prefabs/stylized_number_display.tscn" id="5_55shj"]
|
||||
[ext_resource type="PackedScene" uid="uid://8edgswcwdiwu" path="res://prefabs/chip_bar.tscn" id="6_gqe5k"]
|
||||
|
||||
[node name="CombatUI" type="CanvasLayer" unique_id=1093388037]
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_hadma"]
|
||||
atlas = ExtResource("3_tfn3h")
|
||||
region = Rect2(0, 0, 800, 600)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_55shj"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(393, 280, 168, 77)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_gqe5k"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(141, 0, 513, 169)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_hr2yf"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(393, 169, 263, 111)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_3wejr"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(614, 711, 137, 26)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_uh1k2"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(614, 737, 55, 20)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_yayqj"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(547, 839, 270, 36)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_eskga"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(249, 272, 3, 14)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_14b7f"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(246, 272, 3, 14)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_manhy"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(1055, 151, 28, 19)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_ox7qj"]
|
||||
atlas = ExtResource("3_hadma")
|
||||
region = Rect2(547, 824, 130, 15)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_gqe5k"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_yhw6j"]
|
||||
atlas = SubResource("CompressedTexture2D_gqe5k")
|
||||
region = Rect2(261, 272, 3, 9)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_icxrh"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_kdblo"]
|
||||
atlas = SubResource("CompressedTexture2D_icxrh")
|
||||
region = Rect2(255, 272, 3, 9)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_ptl6p"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_vj7wc"]
|
||||
atlas = SubResource("CompressedTexture2D_ptl6p")
|
||||
region = Rect2(1056, 172, 27, 15)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5b4vf"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_kd55s"]
|
||||
atlas = SubResource("CompressedTexture2D_5b4vf")
|
||||
region = Rect2(261, 272, 3, 9)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_4lqfr"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_alhr0"]
|
||||
atlas = SubResource("CompressedTexture2D_4lqfr")
|
||||
region = Rect2(258, 272, 3, 9)
|
||||
|
||||
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_eajv6"]
|
||||
load_path = "res://.godot/imported/SO008B.BMP-57a2d92123e6b32a5ec6367855ebadf6.ctex"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_r8i3r"]
|
||||
atlas = SubResource("CompressedTexture2D_eajv6")
|
||||
region = Rect2(1056, 190, 26, 16)
|
||||
|
||||
[node name="BattleViewUI" 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
|
||||
[node name="UIBase" type="Control" parent="." unique_id=7839209]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 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")
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UnitPanel" unique_id=899996785]
|
||||
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="Overlay" type="Control" parent="UIBase" unique_id=114439631]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UnitPanel/MarginContainer" unique_id=333573880]
|
||||
layout_mode = 2
|
||||
[node name="Background" type="TextureRect" parent="UIBase/Overlay" unique_id=1726665864]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_hadma")
|
||||
|
||||
[node name="NameLabel" type="Label" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000002]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Unit"
|
||||
[node name="HoverInfo" type="TextureRect" parent="UIBase/Overlay" unique_id=1180275113]
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
texture = SubResource("AtlasTexture_55shj")
|
||||
|
||||
[node name="HPBar" type="ProgressBar" parent="UnitPanel/MarginContainer/VBoxContainer" unique_id=2000000003]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
value = 100.0
|
||||
show_percentage = false
|
||||
[node name="Bars" type="TextureRect" parent="UIBase/Overlay" unique_id=780801800]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -513.0
|
||||
offset_bottom = 169.0
|
||||
grow_horizontal = 0
|
||||
texture = SubResource("AtlasTexture_gqe5k")
|
||||
|
||||
[node name="BackgroundTint" type="ColorRect" parent="." unique_id=253893594]
|
||||
[node name="TextureRect" type="TextureRect" parent="UIBase/Overlay" unique_id=1469571203]
|
||||
layout_mode = 0
|
||||
offset_left = 536.0
|
||||
offset_top = 432.0
|
||||
offset_right = 799.0
|
||||
offset_bottom = 543.0
|
||||
texture = SubResource("AtlasTexture_hr2yf")
|
||||
|
||||
[node name="BackgroundTint" type="ColorRect" parent="UIBase" unique_id=253893594]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
@@ -50,121 +158,278 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0, 0, 0, 0.4)
|
||||
|
||||
[node name="CombatProposalPanel" type="PanelContainer" parent="." unique_id=2088533653]
|
||||
[node name="CombatProposalPanel" type="PanelContainer" parent="UIBase" unique_id=2088533653]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -88.0
|
||||
offset_top = -103.5
|
||||
offset_right = 88.0
|
||||
offset_bottom = 103.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_2ro41")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="CombatProposalPanel" unique_id=924522188]
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UIBase/CombatProposalPanel" unique_id=924522188]
|
||||
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="CombatProposalPanel/MarginContainer" unique_id=666671196]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UIBase/CombatProposalPanel/MarginContainer" unique_id=666671196]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=1520669125]
|
||||
[node name="TitleLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=1520669125]
|
||||
layout_mode = 2
|
||||
text = "COMBAT FORECAST"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="StatsContainer" type="HBoxContainer" parent="CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=1101898616]
|
||||
[node name="StatsContainer" type="HBoxContainer" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=1101898616]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
|
||||
[node name="AttackerStats" type="VBoxContainer" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer" unique_id=1193603706]
|
||||
[node name="AttackerStats" type="VBoxContainer" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer" unique_id=1193603706]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AttackerNameLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1574861154]
|
||||
[node name="AttackerNameLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1574861154]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Attacker"
|
||||
|
||||
[node name="AttackerTacticSelect" type="OptionButton" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1466363800]
|
||||
[node name="AttackerTacticSelect" type="OptionButton" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1466363800]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AttackerHPBar" type="ProgressBar" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=241886156]
|
||||
[node name="AttackerHPBar" type="ProgressBar" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=241886156]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
show_percentage = false
|
||||
|
||||
[node name="AttackerATKLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1656041171]
|
||||
[node name="AttackerATKLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1656041171]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ATK: 0"
|
||||
|
||||
[node name="AttackerDEFLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=2145851939]
|
||||
[node name="AttackerDEFLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=2145851939]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "DEF: 0"
|
||||
|
||||
[node name="AttackerHITLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1234331828]
|
||||
[node name="AttackerHITLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1234331828]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "HIT: 0%"
|
||||
|
||||
[node name="AttackerSPDLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1461624142]
|
||||
[node name="AttackerSPDLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/AttackerStats" unique_id=1461624142]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "SPD: 0"
|
||||
|
||||
[node name="DefenderStats" type="VBoxContainer" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer" unique_id=1747482540]
|
||||
[node name="DefenderStats" type="VBoxContainer" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer" unique_id=1747482540]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DefenderNameLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=365769500]
|
||||
[node name="DefenderNameLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=365769500]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Defender"
|
||||
|
||||
[node name="DefenderTacticSelect" type="OptionButton" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1226652499]
|
||||
[node name="DefenderTacticSelect" type="OptionButton" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1226652499]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DefenderHPBar" type="ProgressBar" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1878516243]
|
||||
[node name="DefenderHPBar" type="ProgressBar" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1878516243]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
show_percentage = false
|
||||
|
||||
[node name="DefenderATKLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1571527922]
|
||||
[node name="DefenderATKLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1571527922]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "ATK: 0"
|
||||
|
||||
[node name="DefenderDEFLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1755454849]
|
||||
[node name="DefenderDEFLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=1755454849]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "DEF: 0"
|
||||
|
||||
[node name="DefenderHITLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=717216786]
|
||||
[node name="DefenderHITLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=717216786]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "HIT: 0%"
|
||||
|
||||
[node name="DefenderSPDLabel" type="Label" parent="CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=132072292]
|
||||
[node name="DefenderSPDLabel" type="Label" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/StatsContainer/DefenderStats" unique_id=132072292]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "SPD: 0"
|
||||
|
||||
[node name="ButtonContainer" type="HBoxContainer" parent="CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=2145647565]
|
||||
[node name="ButtonContainer" type="HBoxContainer" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer" unique_id=2145647565]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="FightButton" type="Button" parent="CombatProposalPanel/MarginContainer/VBoxContainer/ButtonContainer" unique_id=2109262268]
|
||||
[node name="FightButton" type="Button" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/ButtonContainer" unique_id=2109262268]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Fight"
|
||||
|
||||
[node name="CancelButton" type="Button" parent="CombatProposalPanel/MarginContainer/VBoxContainer/ButtonContainer" unique_id=238295206]
|
||||
[node name="CancelButton" type="Button" parent="UIBase/CombatProposalPanel/MarginContainer/VBoxContainer/ButtonContainer" unique_id=238295206]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
|
||||
[node name="UnitPanel" type="Control" parent="UIBase" unique_id=1823763147]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -265.0
|
||||
offset_top = -194.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="UIBase/UnitPanel" unique_id=691752297]
|
||||
layout_mode = 0
|
||||
offset_left = 128.0
|
||||
offset_right = 265.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 8
|
||||
texture = SubResource("AtlasTexture_3wejr")
|
||||
|
||||
[node name="UnitLevel" type="HBoxContainer" parent="UIBase/UnitPanel/TextureRect" unique_id=828992814]
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -63.0
|
||||
offset_top = -20.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="Spacer" type="Control" parent="UIBase/UnitPanel/TextureRect/UnitLevel" unique_id=1068603879]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="TextureRect" parent="UIBase/UnitPanel/TextureRect/UnitLevel" unique_id=1299333906]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
texture = SubResource("AtlasTexture_uh1k2")
|
||||
|
||||
[node name="LevelNumber" parent="UIBase/UnitPanel/TextureRect/UnitLevel" unique_id=702997768 instance=ExtResource("5_55shj")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
sprite_sheet = SubResource("AtlasTexture_yayqj")
|
||||
number_sprite_width = 27
|
||||
number_sprite_height = 39
|
||||
value = 12
|
||||
|
||||
[node name="UnitHealth" type="HBoxContainer" parent="UIBase/UnitPanel" unique_id=53239936]
|
||||
layout_mode = 0
|
||||
offset_left = -127.0
|
||||
offset_top = 103.0
|
||||
offset_right = 253.0
|
||||
offset_bottom = 131.0
|
||||
alignment = 2
|
||||
|
||||
[node name="HealthChipBar" parent="UIBase/UnitPanel/UnitHealth" unique_id=379110810 instance=ExtResource("6_gqe5k")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
max_chips_per_row = 100
|
||||
empty_chip_texture = SubResource("AtlasTexture_eskga")
|
||||
filled_chip_texture = SubResource("AtlasTexture_14b7f")
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="UIBase/UnitPanel/UnitHealth" unique_id=668812970]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
texture = SubResource("AtlasTexture_manhy")
|
||||
|
||||
[node name="HealthNumber" parent="UIBase/UnitPanel/UnitHealth" unique_id=1442983008 instance=ExtResource("5_55shj")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
sprite_sheet = SubResource("AtlasTexture_ox7qj")
|
||||
number_sprite_width = 13
|
||||
number_sprite_height = 15
|
||||
|
||||
[node name="UnitSP" type="HBoxContainer" parent="UIBase/UnitPanel" unique_id=514403074]
|
||||
layout_mode = 0
|
||||
offset_left = 1.0
|
||||
offset_top = 133.0
|
||||
offset_right = 249.0
|
||||
offset_bottom = 151.0
|
||||
|
||||
[node name="SPChipBar" parent="UIBase/UnitPanel/UnitSP" unique_id=374103132 instance=ExtResource("6_gqe5k")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
max_chips_per_row = 100
|
||||
empty_chip_texture = SubResource("AtlasTexture_yhw6j")
|
||||
filled_chip_texture = SubResource("AtlasTexture_kdblo")
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="UIBase/UnitPanel/UnitSP" unique_id=805168370]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
texture = SubResource("AtlasTexture_vj7wc")
|
||||
|
||||
[node name="SPNumber" parent="UIBase/UnitPanel/UnitSP" unique_id=442319509 instance=ExtResource("5_55shj")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
sprite_sheet = SubResource("AtlasTexture_ox7qj")
|
||||
number_sprite_width = 13
|
||||
number_sprite_height = 15
|
||||
|
||||
[node name="UnitFS" type="HBoxContainer" parent="UIBase/UnitPanel" unique_id=1209088756]
|
||||
layout_mode = 0
|
||||
offset_left = 1.0
|
||||
offset_top = 160.0
|
||||
offset_right = 249.0
|
||||
offset_bottom = 178.0
|
||||
|
||||
[node name="FSChipBar" parent="UIBase/UnitPanel/UnitFS" unique_id=1380843979 instance=ExtResource("6_gqe5k")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
max_chips_per_row = 100
|
||||
empty_chip_texture = SubResource("AtlasTexture_kd55s")
|
||||
filled_chip_texture = SubResource("AtlasTexture_alhr0")
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="UIBase/UnitPanel/UnitFS" unique_id=1340673242]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
texture = SubResource("AtlasTexture_r8i3r")
|
||||
|
||||
[node name="FSNumber" parent="UIBase/UnitPanel/UnitFS" unique_id=1558008542 instance=ExtResource("5_55shj")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
sprite_sheet = SubResource("AtlasTexture_ox7qj")
|
||||
number_sprite_width = 13
|
||||
number_sprite_height = 15
|
||||
|
||||
[node name="UnitName" type="RichTextLabel" parent="UIBase/UnitPanel" unique_id=1945794101]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_left = 69.0
|
||||
offset_top = 51.0
|
||||
offset_right = 258.0
|
||||
offset_bottom = 66.0
|
||||
theme = ExtResource("1_2ro41")
|
||||
theme_override_font_sizes/normal_font_size = 16
|
||||
theme_override_font_sizes/bold_font_size = 16
|
||||
bbcode_enabled = true
|
||||
scroll_active = false
|
||||
text_direction = 2
|
||||
|
||||
52
prefabs/contiguous_bar.tscn
Normal file
@@ -0,0 +1,52 @@
|
||||
[gd_scene format=3 uid="uid://fw4ug70qd8nm"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cavpqnd0qqoou" path="res://assets/ui/SO008B.BMP" id="1_nj0tn"]
|
||||
[ext_resource type="Script" uid="uid://cb32ywwuyi706" path="res://scripts/ui/contiguous_bar.gd" id="2_cbar"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_vkyrt"]
|
||||
atlas = ExtResource("1_nj0tn")
|
||||
region = Rect2(680, 557, 52, 7)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_3kyon"]
|
||||
atlas = ExtResource("1_nj0tn")
|
||||
region = Rect2(680, 564, 50, 5)
|
||||
|
||||
[node name="ContiguousBar" type="Control" unique_id=1119297666]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("2_cbar")
|
||||
|
||||
[node name="EmptyBar" type="NinePatchRect" parent="." unique_id=303722124]
|
||||
layout_mode = 1
|
||||
anchors_preset = 14
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
offset_bottom = 7.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_vkyrt")
|
||||
region_rect = Rect2(0, 0, 52, 7)
|
||||
patch_margin_left = 1
|
||||
patch_margin_top = 1
|
||||
patch_margin_right = 1
|
||||
patch_margin_bottom = 1
|
||||
|
||||
[node name="Fill" type="NinePatchRect" parent="EmptyBar" unique_id=1864372620]
|
||||
layout_mode = 1
|
||||
anchors_preset = 11
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -1.0
|
||||
offset_top = 1.0
|
||||
offset_right = -1.0
|
||||
offset_bottom = -1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_3kyon")
|
||||
region_rect = Rect2(0, 0, 50, 5)
|
||||
@@ -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
|
||||
|
||||
51
prefabs/deployed_unit.tscn
Normal file
@@ -0,0 +1,51 @@
|
||||
[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"]
|
||||
186
prefabs/deployment_slot.tscn
Normal file
@@ -0,0 +1,186 @@
|
||||
[gd_scene format=3 uid="uid://csu4xocsj71td"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://cavpqnd0qqoou" path="res://assets/ui/SO008B.BMP" id="1_vc35g"]
|
||||
[ext_resource type="Shader" uid="uid://bd8ki8xwym5nc" path="res://shaders/chroma_key.gdshader" id="2_mwfff"]
|
||||
[ext_resource type="Texture2D" uid="uid://c7coajdu61crq" path="res://assets/ui/unit_faces.BMP" id="3_q3r6y"]
|
||||
[ext_resource type="Script" uid="uid://cb32ywwuyi706" path="res://scripts/ui/contiguous_bar.gd" id="4_byj02"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_07lbo"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(680, 522, 60, 35)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_scfqt"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(680, 605, 50, 17)
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_a8mo2"]
|
||||
shader = ExtResource("2_mwfff")
|
||||
shader_parameter/key_color = Color(0, 1, 0, 1)
|
||||
shader_parameter/threshold = 0.01
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_ajsjv"]
|
||||
atlas = ExtResource("3_q3r6y")
|
||||
region = Rect2(40, 0, 40, 40)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_kqrkf"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(708, 587, 28, 17)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_1c3gn"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(264, 272, 52, 7)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_d4kxr"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(264, 279, 50, 5)
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_vxtih"]
|
||||
blend_mode = 1
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_og56w"]
|
||||
atlas = ExtResource("1_vc35g")
|
||||
region = Rect2(740, 522, 45, 35)
|
||||
|
||||
[node name="DeploymentSlot" type="Control" unique_id=258677476]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Base" type="TextureRect" parent="." unique_id=1397025328]
|
||||
self_modulate = Color(0.85881317, 0.8389489, 0.60661924, 1)
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -30.0
|
||||
offset_top = -17.5
|
||||
offset_right = 30.0
|
||||
offset_bottom = 17.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_07lbo")
|
||||
|
||||
[node name="Contents" type="Control" parent="Base" unique_id=1351926154]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Empty" type="Control" parent="Base/Contents" unique_id=1459198631]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Label" type="TextureRect" parent="Base/Contents/Empty" unique_id=460221998]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -25.0
|
||||
offset_top = -8.5
|
||||
offset_right = 25.0
|
||||
offset_bottom = 8.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_scfqt")
|
||||
|
||||
[node name="Deployed" type="Control" parent="Base/Contents" unique_id=1502290289]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="UnitSprite" type="TextureRect" parent="Base/Contents/Deployed" unique_id=1617524406]
|
||||
material = SubResource("ShaderMaterial_a8mo2")
|
||||
layout_mode = 1
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -45.0
|
||||
offset_right = 40.0
|
||||
offset_bottom = -5.0
|
||||
grow_vertical = 0
|
||||
texture = SubResource("AtlasTexture_ajsjv")
|
||||
|
||||
[node name="ReadyLabel" type="TextureRect" parent="Base/Contents/Deployed" unique_id=1608591044]
|
||||
layout_mode = 1
|
||||
anchors_preset = 6
|
||||
anchor_left = 1.0
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -30.0
|
||||
offset_top = -14.0
|
||||
offset_right = -2.0
|
||||
offset_bottom = 3.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_kqrkf")
|
||||
|
||||
[node name="EmptyBar" type="NinePatchRect" parent="Base/Contents/Deployed" unique_id=855067973]
|
||||
layout_mode = 1
|
||||
anchors_preset = 14
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 2.0
|
||||
offset_top = 7.0
|
||||
offset_right = -2.0
|
||||
offset_bottom = 14.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_1c3gn")
|
||||
region_rect = Rect2(0, 0, 52, 7)
|
||||
patch_margin_left = 1
|
||||
patch_margin_top = 1
|
||||
patch_margin_right = 1
|
||||
patch_margin_bottom = 1
|
||||
script = ExtResource("4_byj02")
|
||||
value = 50
|
||||
max_value = 50
|
||||
|
||||
[node name="Fill" type="NinePatchRect" parent="Base/Contents/Deployed/EmptyBar" unique_id=318061429]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 1.0
|
||||
offset_top = 1.0
|
||||
offset_right = -1.0
|
||||
offset_bottom = -1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_d4kxr")
|
||||
region_rect = Rect2(0, 0, 50, 5)
|
||||
|
||||
[node name="Hover" type="TextureRect" parent="." unique_id=1517469291]
|
||||
visible = false
|
||||
material = SubResource("CanvasItemMaterial_vxtih")
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -22.5
|
||||
offset_top = -17.5
|
||||
offset_right = 22.5
|
||||
offset_bottom = 17.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("AtlasTexture_og56w")
|
||||
30
prefabs/stylized_number_display.tscn
Normal file
@@ -0,0 +1,30 @@
|
||||
[gd_scene format=3 uid="uid://bc5a7tb0my6n5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://biud4ob4h0rrs" path="res://scripts/ui/stylized_number_display.gd" id="1_yn0fd"]
|
||||
[ext_resource type="Texture2D" uid="uid://cavpqnd0qqoou" path="res://assets/ui/SO008B.BMP" id="2_2wvj5"]
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_b1oqg"]
|
||||
atlas = ExtResource("2_2wvj5")
|
||||
region = Rect2(546, 839, 272, 39)
|
||||
|
||||
[node name="StylizedNumberDisplay" type="Control" unique_id=702997768]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_yn0fd")
|
||||
sprite_sheet = SubResource("AtlasTexture_b1oqg")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=2089310026]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 2
|
||||
alignment = 1
|
||||
@@ -1,23 +1,31 @@
|
||||
[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"]
|
||||
|
||||
[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
|
||||
"
|
||||
|
||||
[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:
|
||||
func _on_unit_unit_allegiance_changed(_unit: Unit, 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
|
||||
shader_parameter/threshold = 0.010000000475
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_fhs1y"]
|
||||
atlas = ExtResource("3_on614")
|
||||
@@ -55,18 +63,17 @@ animations = [{
|
||||
"speed": 5.0
|
||||
}]
|
||||
|
||||
[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="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")
|
||||
@@ -79,13 +86,3 @@ position = Vector2(50, 50)
|
||||
sprite_frames = SubResource("SpriteFrames_7jqdg")
|
||||
animation = &"idle"
|
||||
autoplay = "idle"
|
||||
|
||||
[node name="ColorRect" 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")
|
||||
|
||||
[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"]
|
||||
|
||||
@@ -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
|
||||
@@ -30,10 +34,11 @@ debug_toggle={
|
||||
|
||||
[physics]
|
||||
|
||||
3d/physics_engine="Jolt Physics"
|
||||
3d/physics_engine="Dummy"
|
||||
|
||||
[rendering]
|
||||
|
||||
textures/canvas_textures/default_texture_filter=0
|
||||
rendering_device/driver.windows="d3d12"
|
||||
renderer/rendering_method="mobile"
|
||||
environment/defaults/default_clear_color=Color(0, 0, 0, 1)
|
||||
|
||||
BIN
reference_images/Screenshot 2026-04-07 075123.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
40
reference_images/Screenshot 2026-04-07 075123.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d4fwfh0cyj3bd"
|
||||
path="res://.godot/imported/Screenshot 2026-04-07 075123.png-98f6527da5d69103c0f83722dd6ba790.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://reference_images/Screenshot 2026-04-07 075123.png"
|
||||
dest_files=["res://.godot/imported/Screenshot 2026-04-07 075123.png-98f6527da5d69103c0f83722dd6ba790.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
reference_images/chip.png
Normal file
|
After Width: | Height: | Size: 463 KiB |
40
reference_images/chip.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhtnqu000slm2"
|
||||
path="res://.godot/imported/chip.png-5735088daa8f24a5994c25e597283774.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://reference_images/chip.png"
|
||||
dest_files=["res://.godot/imported/chip.png-5735088daa8f24a5994c25e597283774.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
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -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 ""
|
||||
@@ -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
|
||||
178
resources/units/appearance_sets/lily_child_deployed.tres
Normal 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": 5.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": 5.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": 5.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": 5.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": 5.0
|
||||
}]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_am7go")
|
||||
deployed_sprite_sheet = SubResource("SpriteFrames_psufo")
|
||||
metadata/_custom_type_script = "uid://divxkbo321ql"
|
||||
33
resources/units/lily_child.tres
Normal 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 = 156
|
||||
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"
|
||||
@@ -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]
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
[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"]
|
||||
[ext_resource type="Script" uid="uid://csdcbi2gtwrly" path="res://scripts/battle/camera_controller.gd" id="6_wtsjf"]
|
||||
[ext_resource type="PackedScene" uid="uid://bc5a7tb0my6n5" path="res://prefabs/stylized_number_display.tscn" id="7_rnaij"]
|
||||
[ext_resource type="PackedScene" uid="uid://8edgswcwdiwu" path="res://prefabs/chip_bar.tscn" id="8_h3xc6"]
|
||||
[ext_resource type="Texture2D" uid="uid://cavpqnd0qqoou" path="res://assets/ui/SO008B.BMP" id="9_s36qc"]
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_qjeyg"]
|
||||
texture = ExtResource("1_7ddre")
|
||||
@@ -48,6 +53,18 @@ 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)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_6qxox"]
|
||||
atlas = ExtResource("9_s36qc")
|
||||
region = Rect2(249, 272, 3, 14)
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_gthg3"]
|
||||
atlas = ExtResource("9_s36qc")
|
||||
region = Rect2(246, 272, 3, 14)
|
||||
|
||||
[node name="TestScene" type="Node2D" unique_id=1687841395]
|
||||
|
||||
[node name="TileMapLayer" type="TileMapLayer" parent="." unique_id=265586128]
|
||||
@@ -58,3 +75,43 @@ 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")
|
||||
|
||||
[node name="CameraController" type="Camera2D" parent="." unique_id=1277373781]
|
||||
script = ExtResource("6_wtsjf")
|
||||
metadata/_custom_type_script = "uid://csdcbi2gtwrly"
|
||||
|
||||
[node name="Control" type="Control" parent="." unique_id=794632273]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="StylizedNumberDisplay" parent="Control" unique_id=702997768 instance=ExtResource("7_rnaij")]
|
||||
layout_mode = 0
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_right = 50.0
|
||||
offset_bottom = 50.0
|
||||
grow_horizontal = 1
|
||||
grow_vertical = 1
|
||||
number_sprite_width = 27
|
||||
number_sprite_height = 36
|
||||
value = 1234567890
|
||||
|
||||
[node name="ChipBar" parent="Control" unique_id=379110810 instance=ExtResource("8_h3xc6")]
|
||||
layout_mode = 1
|
||||
offset_left = -87.0
|
||||
offset_top = -101.0
|
||||
offset_right = -37.0
|
||||
offset_bottom = -1.0
|
||||
value = 5
|
||||
max_value = 5
|
||||
max_chips_per_row = 10
|
||||
empty_chip_texture = SubResource("AtlasTexture_6qxox")
|
||||
filled_chip_texture = SubResource("AtlasTexture_gthg3")
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -76,28 +76,23 @@ 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
|
||||
combat_map.deploy_unit(player_unit, Vector2i(2, 2))
|
||||
var player_unit: Unit = LILY_CHILD.duplicate(true)
|
||||
player_unit.allegiance = PLAYER_ALLEGIANCE
|
||||
player_unit.stats.level = 68
|
||||
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
|
||||
combat_map.deploy_unit(enemy_unit, Vector2i(2, 1))
|
||||
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()
|
||||
var root := tree.root
|
||||
|
||||
461
scenes/views/main_menu_view.tscn111937213334.tmp
Normal 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")
|
||||
17
scripts/autoloads/battle_map_helper.gd
Normal 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
|
||||
1
scripts/autoloads/battle_map_helper.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bt28d2xnvfjmf
|
||||
@@ -2,7 +2,13 @@ class_name CameraController extends Camera2D
|
||||
|
||||
|
||||
func apply_drag(delta: Vector2) -> void:
|
||||
position += delta / zoom
|
||||
var half_view: Vector2 = get_viewport_rect().size * 0.5 / zoom
|
||||
var min_pos := Vector2(limit_left, limit_top) + half_view
|
||||
var max_pos := Vector2(limit_right, limit_bottom) - half_view
|
||||
# Guard against maps smaller than the viewport (min > max)
|
||||
max_pos.x = max(min_pos.x, max_pos.x)
|
||||
max_pos.y = max(min_pos.y, max_pos.y)
|
||||
position = (position + delta / zoom).clamp(min_pos, max_pos)
|
||||
|
||||
|
||||
func set_map_bounds(rect: Rect2) -> void:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -111,41 +113,31 @@ func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[Comba
|
||||
return best_tactic
|
||||
|
||||
|
||||
func process_combat(attacker: Unit, defender: Unit, distance: int) -> void:
|
||||
if not attacker.is_alive() or not defender.is_alive():
|
||||
return
|
||||
var proposal := create_proposal(attacker, defender, distance)
|
||||
var atk_name := attacker.current_info.name
|
||||
var def_name := defender.current_info.name
|
||||
print("=== Combat: %s vs %s ===" % [atk_name, def_name])
|
||||
print(" %s — HP:%d ATK:%d DEF:%d HIT:%d" % [atk_name, proposal.attacker.hp, proposal.attacker.atk, proposal.attacker.def, proposal.attacker.hit])
|
||||
print(" %s — HP:%d ATK:%d DEF:%d HIT:%d" % [def_name, proposal.defender.hp, proposal.defender.atk, proposal.defender.def, proposal.defender.hit])
|
||||
apply_proposal(proposal)
|
||||
var atk_hp := attacker.current_stats.current_hp if is_instance_valid(attacker) else 0
|
||||
var def_hp := defender.current_stats.current_hp if is_instance_valid(defender) else 0
|
||||
print(" Result: %s HP=%d, %s HP=%d" % [atk_name, atk_hp, def_name, def_hp])
|
||||
|
||||
|
||||
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_deployed) or not is_instance_valid(def_deployed):
|
||||
return
|
||||
|
||||
# Attacker strikes (if their tactic deals damage)
|
||||
if atk_stats.selected_tactic and atk_stats.selected_tactic.deals_damage():
|
||||
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 def_unit.is_alive() and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage():
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -3,9 +3,15 @@ class_name CombatUI extends CanvasLayer
|
||||
signal fight_confirmed(proposal: CombatProposal)
|
||||
signal fight_cancelled
|
||||
|
||||
@onready var unit_panel: PanelContainer = %UnitPanel
|
||||
@onready var name_label: Label = %NameLabel
|
||||
@onready var hp_bar: ProgressBar = %HPBar
|
||||
@onready var unit_panel: Control = %UnitPanel
|
||||
@onready var unit_name_label: RichTextLabel = %UnitName
|
||||
@onready var level_number: StylizedNumberDisplay = %LevelNumber
|
||||
@onready var health_chip_bar: ChipBar = %HealthChipBar
|
||||
@onready var health_number: StylizedNumberDisplay = %HealthNumber
|
||||
@onready var sp_chip_bar: ChipBar = %SPChipBar
|
||||
@onready var sp_number: StylizedNumberDisplay = %SPNumber
|
||||
@onready var fs_chip_bar: ChipBar = %FSChipBar
|
||||
@onready var fs_number: StylizedNumberDisplay = %FSNumber
|
||||
|
||||
@onready var background_tint: ColorRect = %BackgroundTint
|
||||
@onready var proposal_panel: PanelContainer = %CombatProposalPanel
|
||||
@@ -26,7 +32,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,30 +43,29 @@ 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:
|
||||
if _selected_unit and is_instance_valid(_selected_unit):
|
||||
hp_bar.max_value = _selected_unit.current_stats.max_hp
|
||||
hp_bar.value = _selected_unit.current_stats.current_hp
|
||||
_refresh_unit_panel()
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if proposal_panel.visible and event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
@@ -68,17 +73,29 @@ 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
|
||||
_refresh_unit_panel()
|
||||
unit_panel.visible = true
|
||||
else:
|
||||
_selected_unit = null
|
||||
unit_panel.visible = false
|
||||
|
||||
func _refresh_unit_panel() -> void:
|
||||
var stats := _selected_unit.current_stats
|
||||
unit_name_label.text = "[b]%s[/b]" % _selected_unit.unit.info.name
|
||||
level_number.value = stats.level
|
||||
health_chip_bar.max_value = stats.max_hp
|
||||
health_chip_bar.value = stats.current_hp
|
||||
health_number.value = stats.current_hp
|
||||
sp_chip_bar.max_value = stats.max_sp
|
||||
sp_chip_bar.value = stats.current_sp
|
||||
sp_number.value = stats.current_sp
|
||||
fs_chip_bar.max_value = stats.max_fs
|
||||
fs_chip_bar.value = stats.current_fs
|
||||
fs_number.value = stats.current_fs
|
||||
|
||||
func show_proposal(proposal: CombatProposal) -> void:
|
||||
_current_proposal = proposal
|
||||
_populate_tactic_select(atk_tactic_select, proposal.attacker)
|
||||
@@ -112,21 +129,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
|
||||
|
||||
81
scripts/battle/deployed_units/deployed_unit.gd
Normal 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()
|
||||
1
scripts/battle/deployed_units/deployed_unit.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cmh4lphvboggy
|
||||
45
scripts/battle/deployed_units/deployed_unit_stats.gd
Normal file
@@ -0,0 +1,45 @@
|
||||
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
|
||||
var level: int:
|
||||
get: return unit_stats.level
|
||||
|
||||
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
|
||||
1
scripts/battle/deployed_units/deployed_unit_stats.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b3jekvxwi8sxi
|
||||
@@ -6,8 +6,9 @@ extends Node2D
|
||||
@onready var tile_map: TileMapLayer = %TerrainLayer
|
||||
@onready var highlight_map: GridOverlay = %OverlayLayer
|
||||
@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
|
||||
@@ -18,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)
|
||||
|
||||
@@ -50,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:
|
||||
@@ -68,17 +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 is_wall(coords: Vector2i) -> bool:
|
||||
return tile_map.get_cell_atlas_coords(coords) == tile_set.wall_tile_coords
|
||||
|
||||
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:
|
||||
@@ -90,20 +81,17 @@ func apply_layout(layout: MapLayout) -> void:
|
||||
map_layout.initialize()
|
||||
load_from_layout()
|
||||
draw_room_walls()
|
||||
draw_fog()
|
||||
|
||||
|
||||
func is_tile_passable(from: Vector2i, to: Vector2i) -> bool:
|
||||
if map_layout:
|
||||
return map_layout.is_passable(from, to)
|
||||
# Fallback: no room system, use legacy wall check
|
||||
return not is_wall(to)
|
||||
assert(map_layout != null, "CombatMap.is_tile_passable called before map_layout was set")
|
||||
return map_layout.is_passable(from, to)
|
||||
|
||||
|
||||
func is_tile_valid(coords: Vector2i) -> bool:
|
||||
if map_layout:
|
||||
return map_layout.is_tile_valid(coords)
|
||||
# Fallback: no room system, any non-wall tile is valid
|
||||
return not is_wall(coords)
|
||||
assert(map_layout != null, "CombatMap.is_tile_valid called before map_layout was set")
|
||||
return map_layout.is_tile_valid(coords)
|
||||
|
||||
|
||||
func draw_room_walls() -> void:
|
||||
@@ -112,10 +100,16 @@ func draw_room_walls() -> void:
|
||||
wall_renderer.draw_walls_for_layout(map_layout)
|
||||
|
||||
|
||||
func draw_fog() -> void:
|
||||
if not map_layout:
|
||||
return
|
||||
fog_renderer.draw_fog_for_layout(map_layout)
|
||||
|
||||
|
||||
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:
|
||||
38
scripts/battle/map/fog_renderer.gd
Normal file
@@ -0,0 +1,38 @@
|
||||
class_name FogRenderer
|
||||
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.
|
||||
|
||||
## Fog tile region in aux_terrain.BMP
|
||||
const FOG_RECT := Rect2(53, 53, 100, 100)
|
||||
|
||||
@export var atlas_texture: Texture2D
|
||||
|
||||
var _fog_tiles: PackedVector2Array = []
|
||||
|
||||
|
||||
func draw_fog_for_layout(map_layout: MapLayout) -> void:
|
||||
_fog_tiles.clear()
|
||||
if not map_layout or not atlas_texture:
|
||||
queue_redraw()
|
||||
return
|
||||
for y in map_layout.size.y:
|
||||
for x in map_layout.size.x:
|
||||
var tile := Vector2i(x, y)
|
||||
if map_layout.is_tile_valid(tile):
|
||||
continue
|
||||
_fog_tiles.append(Vector2(tile) * BattleMapHelper.TILE_SIZE)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not atlas_texture:
|
||||
return
|
||||
var dest_size := Vector2(BattleMapHelper.TILE_SIZE, BattleMapHelper.TILE_SIZE)
|
||||
for pos in _fog_tiles:
|
||||
draw_texture_rect_region(
|
||||
atlas_texture,
|
||||
Rect2(pos, dest_size),
|
||||
FOG_RECT,
|
||||
)
|
||||
1
scripts/battle/map/fog_renderer.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1d1nbetdvynk
|
||||
@@ -48,6 +48,14 @@ func is_passable(from: Vector2i, to: Vector2i) -> bool:
|
||||
return _opening_set.has(_edge_key(from, to))
|
||||
|
||||
|
||||
func get_openings() -> Array:
|
||||
## Returns an array of [Vector2i, Vector2i] pairs representing opening edges.
|
||||
var result: Array = []
|
||||
for i in range(0, openings.size(), 2):
|
||||
result.append([openings[i], openings[i + 1]])
|
||||
return result
|
||||
|
||||
|
||||
func get_walls() -> Array:
|
||||
## Returns an array of [Vector2i, Vector2i] pairs representing wall edges.
|
||||
## A wall exists where a room tile borders void or a different room (without an opening).
|
||||
266
scripts/battle/map/wall_renderer.gd
Normal file
@@ -0,0 +1,266 @@
|
||||
class_name WallRenderer
|
||||
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.
|
||||
|
||||
## Source atlas rects (x, y, w, h) from aux_terrain.BMP
|
||||
## Each edge has two half-segments that together span the full tile edge.
|
||||
|
||||
# -- Left edge --
|
||||
const LEFT_UPPER_RECT := Rect2(0, 103, 20, 50)
|
||||
const LEFT_LOWER_RECT := Rect2(0, 53, 20, 50)
|
||||
|
||||
# -- Right edge --
|
||||
const RIGHT_UPPER_RECT := Rect2(186, 103, 20, 50)
|
||||
const RIGHT_LOWER_RECT := Rect2(186, 53, 20, 50)
|
||||
|
||||
# -- Top edge --
|
||||
const TOP_LEFT_RECT := Rect2(103, 0, 50, 20)
|
||||
const TOP_RIGHT_RECT := Rect2(53, 0, 50, 20)
|
||||
|
||||
# -- Bottom edge --
|
||||
const BOTTOM_LEFT_RECT := Rect2(103, 186, 50, 20)
|
||||
const BOTTOM_RIGHT_RECT := Rect2(53, 186, 50, 20)
|
||||
|
||||
# -- Inner corners (drawn where two perpendicular wall edges meet) --
|
||||
const INNER_CORNER_UPPER_LEFT_RECT := Rect2(0, 0, 50, 50)
|
||||
const INNER_CORNER_UPPER_RIGHT_RECT := Rect2(156, 0, 50, 50)
|
||||
const INNER_CORNER_LOWER_LEFT_RECT := Rect2(0, 156, 50, 50)
|
||||
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, 36, 36, 42)
|
||||
## Horizontal opening: tiles separated on x-axis (east-west doorway through a vertical wall)
|
||||
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
|
||||
var HALF_EDGE: float:
|
||||
get:
|
||||
return BattleMapHelper.TILE_SIZE / 2.0
|
||||
|
||||
@export var atlas_texture: Texture2D
|
||||
|
||||
# Each entry is [dest_rect: Rect2, source_rect: Rect2]
|
||||
var _segments: Array = []
|
||||
|
||||
|
||||
func draw_walls_for_layout(map_layout: MapLayout) -> void:
|
||||
_segments.clear()
|
||||
if not map_layout or not atlas_texture:
|
||||
queue_redraw()
|
||||
return
|
||||
|
||||
var tile_edges := _collect_tile_edges(map_layout)
|
||||
for tile in tile_edges:
|
||||
var edges: Array = tile_edges[tile]
|
||||
_build_tile_walls(tile, edges)
|
||||
_build_opening_sprites(map_layout)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not atlas_texture:
|
||||
return
|
||||
for seg in _segments:
|
||||
draw_texture_rect_region(atlas_texture, seg[0], seg[1])
|
||||
|
||||
|
||||
func _collect_tile_edges(map_layout: MapLayout) -> Dictionary:
|
||||
## Returns {Vector2i: Array[StringName]} mapping each tile to its wall edge directions.
|
||||
## Includes both true walls and opening edges, so wall sprites are drawn underneath openings.
|
||||
var tile_edges: Dictionary = {}
|
||||
for edge_pair in map_layout.get_walls():
|
||||
_add_edge_pair(tile_edges, edge_pair, map_layout)
|
||||
for edge_pair in map_layout.get_openings():
|
||||
_add_edge_pair(tile_edges, edge_pair, map_layout)
|
||||
return tile_edges
|
||||
|
||||
|
||||
func _add_edge_pair(tile_edges: Dictionary, edge_pair: Array, map_layout: MapLayout) -> void:
|
||||
var tile_a: Vector2i = edge_pair[0]
|
||||
var tile_b: Vector2i = edge_pair[1]
|
||||
var diff: Vector2i = tile_b - tile_a
|
||||
|
||||
var edge_a := _direction_to_edge(diff)
|
||||
if edge_a != &"":
|
||||
if not tile_edges.has(tile_a):
|
||||
tile_edges[tile_a] = []
|
||||
tile_edges[tile_a].append(edge_a)
|
||||
|
||||
var edge_b := _direction_to_edge(-diff)
|
||||
if edge_b != &"" and map_layout.is_tile_valid(tile_b):
|
||||
if not tile_edges.has(tile_b):
|
||||
tile_edges[tile_b] = []
|
||||
tile_edges[tile_b].append(edge_b)
|
||||
|
||||
|
||||
func _direction_to_edge(dir: Vector2i) -> StringName:
|
||||
match dir:
|
||||
Vector2i.RIGHT:
|
||||
return &"right"
|
||||
Vector2i.LEFT:
|
||||
return &"left"
|
||||
Vector2i.UP:
|
||||
return &"top"
|
||||
Vector2i.DOWN:
|
||||
return &"bottom"
|
||||
return &""
|
||||
|
||||
|
||||
func _build_tile_walls(tile: Vector2i, edges: Array) -> void:
|
||||
var tile_origin := Vector2(tile) * BattleMapHelper.TILE_SIZE
|
||||
|
||||
for edge in edges:
|
||||
_build_edge_segments(tile_origin, edge)
|
||||
|
||||
# TODO: Outer corner segments
|
||||
_build_outer_corners(tile, tile_origin, edges)
|
||||
|
||||
# Inner corner segments (for non-rectangular room support)
|
||||
_build_inner_corners(tile, tile_origin, edges)
|
||||
|
||||
|
||||
func _build_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
|
||||
var seg_a_rect: Rect2
|
||||
var seg_b_rect: Rect2
|
||||
var seg_a_offset: Vector2
|
||||
var seg_b_offset: Vector2
|
||||
var seg_a_size: Vector2
|
||||
var seg_b_size: Vector2
|
||||
|
||||
match edge:
|
||||
&"left":
|
||||
seg_a_rect = LEFT_UPPER_RECT
|
||||
seg_b_rect = LEFT_LOWER_RECT
|
||||
seg_a_size = Vector2(WALL_THICKNESS, HALF_EDGE)
|
||||
seg_b_size = Vector2(WALL_THICKNESS, HALF_EDGE)
|
||||
seg_a_offset = Vector2(0, 0)
|
||||
seg_b_offset = Vector2(0, HALF_EDGE)
|
||||
&"right":
|
||||
seg_a_rect = RIGHT_UPPER_RECT
|
||||
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(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
|
||||
seg_a_size = Vector2(HALF_EDGE, WALL_THICKNESS)
|
||||
seg_b_size = Vector2(HALF_EDGE, WALL_THICKNESS)
|
||||
seg_a_offset = Vector2(0, 0)
|
||||
seg_b_offset = Vector2(HALF_EDGE, 0)
|
||||
&"bottom":
|
||||
seg_a_rect = BOTTOM_LEFT_RECT
|
||||
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, 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)
|
||||
|
||||
|
||||
func _queue_segment(pos: Vector2, target_size: Vector2, source_rect: Rect2) -> void:
|
||||
_segments.append([Rect2(pos, target_size), source_rect])
|
||||
|
||||
|
||||
func _build_opening_sprites(map_layout: MapLayout) -> void:
|
||||
## Composites opening (doorway) sprites on top of the wall segments at opening edges.
|
||||
## Each opening is split in half across the shared edge, half drawn on each tile.
|
||||
for opening in map_layout.get_openings():
|
||||
var tile_a: Vector2i = opening[0]
|
||||
var tile_b: Vector2i = opening[1]
|
||||
var diff: Vector2i = tile_b - tile_a
|
||||
|
||||
# Normalize so the pair is ordered along the positive axis (tile_a < tile_b).
|
||||
if diff == Vector2i.LEFT or diff == Vector2i.UP:
|
||||
var swap := tile_a
|
||||
tile_a = tile_b
|
||||
tile_b = swap
|
||||
diff = -diff
|
||||
|
||||
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)
|
||||
elif diff == Vector2i.RIGHT:
|
||||
_queue_horizontal_opening(origin_a, origin_b)
|
||||
|
||||
|
||||
func _queue_vertical_opening(origin_upper: Vector2, origin_lower: Vector2) -> void:
|
||||
# Vertical opening: tiles vertically adjacent; horizontal wall edge between them.
|
||||
var src := VERTICAL_OPENING_RECT
|
||||
var w: float = src.size.x
|
||||
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 := (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, 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)
|
||||
|
||||
|
||||
func _queue_horizontal_opening(origin_left: Vector2, origin_right: Vector2) -> void:
|
||||
# Horizontal opening: tiles horizontally adjacent; vertical wall edge between them.
|
||||
var src := HORIZONTAL_OPENING_RECT
|
||||
var w_total: float = src.size.x
|
||||
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 := (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(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)
|
||||
|
||||
|
||||
func _build_outer_corners(_tile: Vector2i, _tile_origin: Vector2, _edges: Array) -> void:
|
||||
# TODO: Implement outer corner segments
|
||||
# Check pairs of adjacent edges (e.g., "top" + "left" → top-left outer corner)
|
||||
# and draw the corner piece from the atlas.
|
||||
pass
|
||||
|
||||
|
||||
func _build_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -> void:
|
||||
## Draws decorative corner pieces wherever two perpendicular wall edges meet
|
||||
## on the same tile (e.g., a top wall + left wall produces an upper-left corner).
|
||||
var has_top := edges.has(&"top")
|
||||
var has_bottom := edges.has(&"bottom")
|
||||
var has_left := edges.has(&"left")
|
||||
var has_right := edges.has(&"right")
|
||||
var corner_size := Vector2(CORNER_SIZE, CORNER_SIZE)
|
||||
|
||||
if has_top and has_left:
|
||||
_queue_segment(
|
||||
tile_origin + Vector2(0, 0),
|
||||
corner_size,
|
||||
INNER_CORNER_UPPER_LEFT_RECT
|
||||
)
|
||||
if has_top and has_right:
|
||||
_queue_segment(
|
||||
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, BattleMapHelper.TILE_SIZE - CORNER_SIZE),
|
||||
corner_size,
|
||||
INNER_CORNER_LOWER_LEFT_RECT
|
||||
)
|
||||
if has_bottom and has_right:
|
||||
_queue_segment(
|
||||
tile_origin + Vector2(BattleMapHelper.TILE_SIZE - CORNER_SIZE, BattleMapHelper.TILE_SIZE - CORNER_SIZE),
|
||||
corner_size,
|
||||
INNER_CORNER_LOWER_RIGHT_RECT
|
||||
)
|
||||
@@ -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
|
||||
@@ -11,24 +11,24 @@ func _ready() -> void:
|
||||
var room_a := Room.new()
|
||||
room_a.id = 0
|
||||
room_a.tiles = [
|
||||
Vector2i(0, 0), Vector2i(1, 0), Vector2i(2, 0),
|
||||
Vector2i(0, 1), Vector2i(1, 1), Vector2i(2, 1),
|
||||
Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2),
|
||||
Vector2i(2, 2), Vector2i(3, 2), Vector2i(4, 2),
|
||||
Vector2i(2, 3), Vector2i(3, 3), Vector2i(4, 3),
|
||||
Vector2i(2, 4), Vector2i(3, 4), Vector2i(4, 4),
|
||||
]
|
||||
|
||||
var room_b := Room.new()
|
||||
room_b.id = 1
|
||||
room_b.tiles = [
|
||||
Vector2i(3, 0), Vector2i(4, 0), Vector2i(5, 0),
|
||||
Vector2i(3, 1), Vector2i(4, 1), Vector2i(5, 1),
|
||||
Vector2i(3, 2), Vector2i(4, 2), Vector2i(5, 2),
|
||||
Vector2i(5, 2), Vector2i(6, 2), Vector2i(7, 2),
|
||||
Vector2i(5, 3), Vector2i(6, 3), Vector2i(7, 3),
|
||||
Vector2i(5, 4), Vector2i(6, 4), Vector2i(7, 4),
|
||||
]
|
||||
|
||||
var layout := MapLayout.new()
|
||||
layout.rooms = [room_a, room_b]
|
||||
# Opening between (2,1) in room_a and (3,1) in room_b
|
||||
layout.openings = [Vector2i(2, 1), Vector2i(3, 1)]
|
||||
layout.size = Vector2i(6, 3)
|
||||
# Opening between (4,3) in room_a and (5,3) in room_b
|
||||
layout.openings = [Vector2i(4, 3), Vector2i(5, 3)]
|
||||
layout.size = Vector2i(10, 7)
|
||||
|
||||
combat_map.apply_layout(layout)
|
||||
camera.set_map_bounds(combat_map.get_map_rect())
|
||||
@@ -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)
|
||||
3
scripts/constants/battle_map_constants.gd
Normal file
@@ -0,0 +1,3 @@
|
||||
class_name BattleMapConstants extends Object
|
||||
|
||||
const TILE_SIZE: float = 100.0
|
||||
1
scripts/constants/battle_map_constants.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://pv4l5upgp0vu
|
||||
7
scripts/debug/console_command.gd
Normal 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
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -8,10 +8,10 @@ var scene_registry: Array = [
|
||||
{ "name": "Battle Test", "path": "res://scenes/views/battle_view.tscn" },
|
||||
{ "name": "Main Menu", "path": "res://scenes/views/main_menu_view.tscn" },
|
||||
{ "name": "Dialogue Test", "path": "res://scenes/dialogue_scene.tscn" },
|
||||
{ "name": "Test Scene", "path": "res://scenes/test_scene.tscn" },
|
||||
]
|
||||
|
||||
var commands: Array[ConsoleCommand] = []
|
||||
var _result_tween: Tween
|
||||
|
||||
@onready var panel: PanelContainer = %Panel
|
||||
@onready var scene_list: VBoxContainer = %SceneList
|
||||
@@ -48,7 +48,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)
|
||||
68
scripts/ui/chip_bar.gd
Normal file
@@ -0,0 +1,68 @@
|
||||
class_name ChipBar extends Control
|
||||
|
||||
# The value to represent in filled chips. In the future chips may be colored differently above certain thresholds to stop drawing too many
|
||||
@export var value: int:
|
||||
set(v):
|
||||
value = v
|
||||
if is_node_ready():
|
||||
_refresh()
|
||||
|
||||
# The total number of chips. If value is less than this, those chips should be empty.
|
||||
@export var max_value: int:
|
||||
set(v):
|
||||
max_value = v
|
||||
if is_node_ready():
|
||||
_refresh()
|
||||
|
||||
# Configures the size of the hflow based on the chip texture size.
|
||||
@export var max_chips_per_row: int
|
||||
|
||||
# The texture for an empty chip
|
||||
@export var empty_chip_texture: Texture2D
|
||||
|
||||
# The texture for a filled chip
|
||||
@export var filled_chip_texture: Texture2D
|
||||
|
||||
@onready var _container: VBoxContainer = $VBoxContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
for child in _container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
if max_chips_per_row <= 0 or not filled_chip_texture:
|
||||
return
|
||||
|
||||
var chip_width := filled_chip_texture.get_width()
|
||||
var effective_per_row := max_chips_per_row
|
||||
if chip_width > 0 and _container.size.x > 0:
|
||||
effective_per_row = mini(effective_per_row, int(_container.size.x / chip_width))
|
||||
|
||||
var filled_remaining := value
|
||||
var chips_laid_out := 0
|
||||
while chips_laid_out < max_value:
|
||||
var row := HBoxContainer.new()
|
||||
row.alignment = BoxContainer.ALIGNMENT_END
|
||||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_theme_constant_override("separation", 0)
|
||||
|
||||
var chips_in_row := mini(effective_per_row, max_value - chips_laid_out)
|
||||
var filled_in_row := mini(filled_remaining, chips_in_row)
|
||||
var empty_in_row := chips_in_row - filled_in_row
|
||||
|
||||
for i in empty_in_row:
|
||||
var tex_rect := TextureRect.new()
|
||||
tex_rect.texture = empty_chip_texture
|
||||
row.add_child(tex_rect)
|
||||
for i in filled_in_row:
|
||||
var tex_rect := TextureRect.new()
|
||||
tex_rect.texture = filled_chip_texture
|
||||
row.add_child(tex_rect)
|
||||
|
||||
filled_remaining -= filled_in_row
|
||||
chips_laid_out += chips_in_row
|
||||
_container.add_child(row)
|
||||
1
scripts/ui/chip_bar.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cvmmsm13nyr62
|
||||