Add early campaign battlefield backgrounds
This commit is contained in:
@@ -114,7 +114,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- Victory and defeat result overlay.
|
- Victory and defeat result overlay.
|
||||||
- Dialogue portrait slot with officer default image paths, optional per-line overrides, and speaker-initial fallback.
|
- Dialogue portrait slot with officer default image paths, optional per-line overrides, and speaker-initial fallback.
|
||||||
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
||||||
- Initial AI-generated battlefield, transparent class unit, transparent generic-enemy unit, and transparent item icon assets live under `art/backgrounds`, `art/units`, and `art/items`.
|
- AI-generated scenario battlefield backgrounds, transparent class units, transparent generic-enemy units, and transparent item icon assets live under `art/backgrounds`, `art/units`, and `art/items`.
|
||||||
- Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected.
|
- Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected.
|
||||||
- Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects.
|
- Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects.
|
||||||
- Dialogue lines can place the portrait on the left or right side of an expanded visual-novel-style dialogue panel with progress and previous-line controls.
|
- Dialogue lines can place the portrait on the left or right side of an expanded visual-novel-style dialogue panel with progress and previous-line controls.
|
||||||
|
|||||||
BIN
art/backgrounds/guandu_defensive_camps.png
Normal file
BIN
art/backgrounds/guandu_defensive_camps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
art/backgrounds/sishui_gate_pass.png
Normal file
BIN
art/backgrounds/sishui_gate_pass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
BIN
art/backgrounds/wan_castle_escape.png
Normal file
BIN
art/backgrounds/wan_castle_escape.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
BIN
art/backgrounds/xiapi_flooded_siege.png
Normal file
BIN
art/backgrounds/xiapi_flooded_siege.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
@@ -46,6 +46,7 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"width": 12,
|
"width": 12,
|
||||||
"height": 9,
|
"height": 9,
|
||||||
|
"background": "res://art/backgrounds/sishui_gate_pass.png",
|
||||||
"terrain": [
|
"terrain": [
|
||||||
"GGGGGRRGGGGG",
|
"GGGGGRRGGGGG",
|
||||||
"GGGFFRRGHHGG",
|
"GGGFFRRGHHGG",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"width": 15,
|
"width": 15,
|
||||||
"height": 10,
|
"height": 10,
|
||||||
|
"background": "res://art/backgrounds/wan_castle_escape.png",
|
||||||
"terrain": [
|
"terrain": [
|
||||||
"GGGGGHHGGGFFFFG",
|
"GGGGGHHGGGFFFFG",
|
||||||
"GGFFGHHGGGFFFFG",
|
"GGFFGHHGGGFFFFG",
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"width": 15,
|
"width": 15,
|
||||||
"height": 10,
|
"height": 10,
|
||||||
|
"background": "res://art/backgrounds/xiapi_flooded_siege.png",
|
||||||
"terrain": [
|
"terrain": [
|
||||||
"GGGGGHHGGGWWGGG",
|
"GGGGGHHGGGWWGGG",
|
||||||
"GGGFFHHGGGWWGGG",
|
"GGGFFHHGGGWWGGG",
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
"map": {
|
"map": {
|
||||||
"width": 17,
|
"width": 17,
|
||||||
"height": 11,
|
"height": 11,
|
||||||
|
"background": "res://art/backgrounds/guandu_defensive_camps.png",
|
||||||
"terrain": [
|
"terrain": [
|
||||||
"GGGGGHHGGGGHHGGGG",
|
"GGGGGHHGGGGHHGGGG",
|
||||||
"GGFFGHHGGFFHHGFFG",
|
"GGFFGHHGGFFHHGFFG",
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
|
|
||||||
## Milestone 5: Presentation
|
## Milestone 5: Presentation
|
||||||
|
|
||||||
- Portrait and art pipeline. Officer definitions can provide default portrait paths, classes can provide board `sprite` paths, items can provide transparent `icon` cutout paths, scenarios can provide map `background` paths, and the first generated officer portraits, unit sprites, item icons, and battlefield background are in `art/`.
|
- Portrait and art pipeline. Officer definitions can provide default portrait paths, classes can provide board `sprite` paths, items can provide transparent `icon` cutout paths, scenarios can provide map `background` paths, and generated officer portraits, unit sprites, item icons, plus scenario battlefield backgrounds are in `art/`.
|
||||||
- Unit sprites and animations. Board movement now has a short slide animation, attacks request class-specific motion profiles for infantry, archers, cavalry, command units, and heavy warrior/bandit units, and board units show class sprite tokens, team rings, class badges, low-HP warning rings, and small status pips for active support, debuff, poison, seal, snare, and disarm effects, with HUD summaries showing remaining phases.
|
- Unit sprites and animations. Board movement now has a short slide animation, attacks request class-specific motion profiles for infantry, archers, cavalry, command units, and heavy warrior/bandit units, and board units show class sprite tokens, team rings, class badges, low-HP warning rings, and small status pips for active support, debuff, poison, seal, snare, and disarm effects, with HUD summaries showing remaining phases.
|
||||||
- Battlefield presentation. Maps can draw a high-resolution background under translucent terrain overlays, with terrain-specific detail patterns, connected road segments, shoreline/castle/forest/hill edge blends, and generic-enemy token marks.
|
- Battlefield presentation. Maps can draw a high-resolution background under translucent terrain overlays, with terrain-specific detail patterns, connected road segments, shoreline/castle/forest/hill edge blends, and generic-enemy token marks.
|
||||||
- Battle HUD presentation. Selected and hovered units now summarize battlefield role, movement type, current terrain move cost, defense/avoid bonuses, core stats, status effects, and gear so class identity reads alongside the visual tokens.
|
- Battle HUD presentation. Selected and hovered units now summarize battlefield role, movement type, current terrain move cost, defense/avoid bonuses, core stats, status effects, and gear so class identity reads alongside the visual tokens.
|
||||||
|
|||||||
@@ -3,7 +3,39 @@ extends SceneTree
|
|||||||
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
||||||
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
||||||
|
|
||||||
const WUCHAO_BACKGROUND_PATH := "res://art/backgrounds/wuchao_night_granary.png"
|
const SCENARIO_BACKGROUNDS := [
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/001_yellow_turbans.json",
|
||||||
|
"background_path": "res://art/backgrounds/battlefield_frontier.png",
|
||||||
|
"label": "001 opening field background",
|
||||||
|
"allow_default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/002_sishui_gate.json",
|
||||||
|
"background_path": "res://art/backgrounds/sishui_gate_pass.png",
|
||||||
|
"label": "002 Sishui Gate background"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/008_wan_castle_escape.json",
|
||||||
|
"background_path": "res://art/backgrounds/wan_castle_escape.png",
|
||||||
|
"label": "008 Wan Castle background"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/009_xiapi_siege.json",
|
||||||
|
"background_path": "res://art/backgrounds/xiapi_flooded_siege.png",
|
||||||
|
"label": "009 Xiapi flooded siege background"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/012_guandu_showdown.json",
|
||||||
|
"background_path": "res://art/backgrounds/guandu_defensive_camps.png",
|
||||||
|
"label": "012 Guandu defensive camps background"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_path": "res://data/scenarios/013_wuchao_raid.json",
|
||||||
|
"background_path": "res://art/backgrounds/wuchao_night_granary.png",
|
||||||
|
"label": "013 Wuchao night granary background"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
@@ -47,7 +79,7 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
|
|||||||
failures.append("001 shop should expose merchant lines")
|
failures.append("001 shop should expose merchant lines")
|
||||||
|
|
||||||
_check_image_path(failures, state.get_map_background_path(), "001 map background")
|
_check_image_path(failures, state.get_map_background_path(), "001 map background")
|
||||||
_check_wuchao_background_data(failures)
|
_check_scenario_backgrounds_data(failures)
|
||||||
for unit_id in ["cao_cao", "xiahou_dun", "yellow_turban_1", "yellow_turban_2", "yellow_turban_3"]:
|
for unit_id in ["cao_cao", "xiahou_dun", "yellow_turban_1", "yellow_turban_2", "yellow_turban_3"]:
|
||||||
var unit := state.get_unit(unit_id)
|
var unit := state.get_unit(unit_id)
|
||||||
if unit.is_empty():
|
if unit.is_empty():
|
||||||
@@ -88,16 +120,21 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
|
|||||||
_check_alpha_cutout_path(failures, icon_path, "item %s icon cutout" % item_id)
|
_check_alpha_cutout_path(failures, icon_path, "item %s icon cutout" % item_id)
|
||||||
|
|
||||||
|
|
||||||
func _check_wuchao_background_data(failures: Array[String]) -> void:
|
func _check_scenario_backgrounds_data(failures: Array[String]) -> void:
|
||||||
var state = BattleStateScript.new()
|
for expectation in SCENARIO_BACKGROUNDS:
|
||||||
if not state.load_battle("res://data/scenarios/013_wuchao_raid.json"):
|
var entry: Dictionary = expectation
|
||||||
failures.append("could not load Wuchao raid for background")
|
var scenario_path := str(entry.get("scenario_path", ""))
|
||||||
return
|
var expected_path := str(entry.get("background_path", ""))
|
||||||
var background_path := state.get_map_background_path()
|
var label := str(entry.get("label", scenario_path))
|
||||||
if background_path != WUCHAO_BACKGROUND_PATH:
|
var state = BattleStateScript.new()
|
||||||
failures.append("013 Wuchao should use its night granary background: %s" % background_path)
|
if not state.load_battle(scenario_path):
|
||||||
return
|
failures.append("could not load scenario for background: %s" % label)
|
||||||
_check_image_path(failures, background_path, "013 Wuchao map background")
|
continue
|
||||||
|
var background_path: String = state.get_map_background_path()
|
||||||
|
if background_path != expected_path:
|
||||||
|
failures.append("%s should use %s, got %s" % [label, expected_path, background_path])
|
||||||
|
continue
|
||||||
|
_check_image_path(failures, background_path, label)
|
||||||
|
|
||||||
|
|
||||||
func _check_scene_texture_loading(failures: Array[String]) -> void:
|
func _check_scene_texture_loading(failures: Array[String]) -> void:
|
||||||
@@ -136,16 +173,29 @@ func _check_scene_texture_loading(failures: Array[String]) -> void:
|
|||||||
failures.append("camp conversation button text should preview supply effect")
|
failures.append("camp conversation button text should preview supply effect")
|
||||||
scene.free()
|
scene.free()
|
||||||
|
|
||||||
var wuchao_scene = BattleSceneScript.new()
|
_check_scenario_background_texture_loading(failures)
|
||||||
if not wuchao_scene.state.load_battle("res://data/scenarios/013_wuchao_raid.json"):
|
|
||||||
failures.append("could not load Wuchao scene state")
|
|
||||||
wuchao_scene.free()
|
func _check_scenario_background_texture_loading(failures: Array[String]) -> void:
|
||||||
return
|
for expectation in SCENARIO_BACKGROUNDS:
|
||||||
if wuchao_scene.state.get_map_background_path() == BattleSceneScript.DEFAULT_BATTLE_BACKGROUND_PATH:
|
var entry: Dictionary = expectation
|
||||||
failures.append("Wuchao scene should not rely on the default battlefield background")
|
var scenario_path := str(entry.get("scenario_path", ""))
|
||||||
if wuchao_scene._current_battle_background_texture() == null:
|
var expected_path := str(entry.get("background_path", ""))
|
||||||
failures.append("battle scene should load Wuchao night granary background texture")
|
var label := str(entry.get("label", scenario_path))
|
||||||
wuchao_scene.free()
|
var scene = BattleSceneScript.new()
|
||||||
|
if not scene.state.load_battle(scenario_path):
|
||||||
|
failures.append("could not load scene state for background: %s" % label)
|
||||||
|
scene.free()
|
||||||
|
continue
|
||||||
|
var actual_path: String = scene.state.get_map_background_path()
|
||||||
|
if actual_path != expected_path:
|
||||||
|
failures.append("%s scene loaded %s instead of %s" % [label, actual_path, expected_path])
|
||||||
|
var allow_default := bool(entry.get("allow_default", false))
|
||||||
|
if not allow_default and actual_path == BattleSceneScript.DEFAULT_BATTLE_BACKGROUND_PATH:
|
||||||
|
failures.append("%s should not rely on the default battlefield background" % label)
|
||||||
|
if scene._current_battle_background_texture() == null:
|
||||||
|
failures.append("battle scene should load texture for %s" % label)
|
||||||
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
func _check_hud_focus_text(failures: Array[String]) -> void:
|
func _check_hud_focus_text(failures: Array[String]) -> void:
|
||||||
|
|||||||
Reference in New Issue
Block a user