@@ -62,6 +62,37 @@ const THIRD_BATTLE_PHASE_DURATIONS := {
" EQ-061 " : [ 1.10 , 0.70 , 0.90 , 0.90 , 0.90 , 0.90 , 1.20 ] ,
" recruit " : [ 1.10 , 0.80 , 0.90 , 0.80 , 0.80 , 1.10 , 1.50 ] ,
}
const FOURTH_BATTLE_PHASE_DURATIONS := {
" R6-01:guarded " : [ 1.10 , 0.80 , 1.00 , 0.90 , 1.00 , 1.10 , 1.50 ] ,
" R6-01:tempo " : [ 1.00 , 0.70 , 0.80 , 0.80 , 0.80 , 0.90 , 1.40 ] ,
" R6-01:full_line " : [ 1.00 , 0.80 , 0.90 , 0.90 , 0.90 , 1.00 , 1.50 ] ,
" R6-02:guarded " : [ 1.20 , 1.00 , 1.10 , 1.00 , 1.10 , 1.10 , 1.50 ] ,
" R6-02:tempo " : [ 1.00 , 0.60 , 0.80 , 0.70 , 0.70 , 0.90 , 1.30 ] ,
" R6-02:full_line " : [ 1.00 , 0.70 , 0.90 , 0.80 , 0.80 , 1.00 , 1.60 ] ,
" R6-03:guarded " : [ 1.10 , 0.90 , 1.00 , 1.00 , 1.00 , 1.20 , 1.60 ] ,
" R6-03:tempo " : [ 1.00 , 0.70 , 0.90 , 0.80 , 0.80 , 1.00 , 1.50 ] ,
" R6-03:full_line " : [ 1.00 , 0.80 , 0.90 , 0.90 , 0.90 , 1.00 , 1.50 ] ,
}
const ROUTES := {
" R6-01 " : {
" name_zh " : " 风墙长堤 " , " name_en " : " Windwall Causeway " ,
" pressure_zh " : " 队长正面粉碎 " , " pressure_en " : " Captain front crush " ,
" exposure_zh " : " 低暴露 " , " exposure_en " : " Low exposure " ,
" reward " : 4 ,
} ,
" R6-02 " : {
" name_zh " : " 钟晶疾道 " , " name_en " : " Bellglass Run " ,
" pressure_zh " : " 敌方速度爆发 " , " pressure_en " : " Enemy tempo burst " ,
" exposure_zh " : " 中暴露 " , " exposure_en " : " Medium exposure " ,
" reward " : 7 ,
} ,
" R6-03 " : {
" name_zh " : " 双辉裂谷 " , " name_en " : " Twinflare Ravine " ,
" pressure_zh " : " 分裂后排打击 " , " pressure_en " : " Split rear strike " ,
" exposure_zh " : " 高暴露 " , " exposure_en " : " High exposure " ,
" reward " : 10 ,
} ,
}
const FIRST_VICTORY_COIN := 20
const RETAIN_ITEM_COST := 12
const RECRUIT_REAR_COST := 14
@@ -118,7 +149,7 @@ const CANDIDATES := {
} ,
}
enum AppPage { BOOT , TITLE , YARD , BATTLE , VICTORY , ADAPTATION , SECOND_BATTLE , SECOND_RESULT , GUILD_RETURN , THIRD_BATTLE , SESSION_RESULT }
enum AppPage { BOOT , TITLE , YARD , BATTLE , VICTORY , ADAPTATION , SECOND_BATTLE , SECOND_RESULT , GUILD_RETURN , THIRD_BATTLE , SESSION_RESULT , ROUTE_BOARD , ROUTE_REVIEW , FOURTH_BATTLE , SECOND_CYCLE_RESULT }
const COLOR_INK := Color ( " #10182c " )
const COLOR_INDIGO := Color ( " #172745 " )
@@ -172,6 +203,12 @@ var retained_item := ""
var added_rear := " "
var third_battle_elapsed := 0.0
var third_result_time := 0.0
var rear_hp := 100
var route_choice := " "
var route_committed := false
var route_reward_committed := false
var fourth_battle_elapsed := 0.0
var fourth_result_time := 0.0
var animation_library : Dictionary = { }
var victory_clock := 0.0
var active_battle_feedback : Dictionary = { }
@@ -290,6 +327,8 @@ func animation_texture(character: String, state: String, elapsed: float) -> Text
func front_animation_state ( ) - > String :
if page == AppPage . FOURTH_BATTLE and selected_build_profile ( ) == " tempo " and route_choice == " R6-02 " and battle_phase == 1 :
return " basic_attack " if front_choice == " Hearthguard " else " evade_counter "
if page == AppPage . THIRD_BATTLE and retained_item == " EQ-061 " and battle_phase == 1 :
return " basic_attack " if front_choice == " Hearthguard " else " evade_counter "
if page == AppPage . SECOND_BATTLE and adaptation_choice == " EQ-061 " and battle_phase == 1 :
@@ -305,6 +344,8 @@ func front_animation_state() -> String:
func rear_animation_state ( ) - > String :
if page == AppPage . FOURTH_BATTLE and battle_phase == 3 :
return " draw_release " if rear_choice == " Farstring " else " lantern_heal "
if page == AppPage . THIRD_BATTLE and battle_phase == 3 :
return " draw_release " if rear_choice == " Farstring " else " lantern_heal "
match battle_phase :
@@ -316,7 +357,7 @@ func rear_animation_state() -> String:
func added_rear_animation_state ( ) - > String :
if page == AppPage . THIRD_BATTLE and battle_phase == 4 :
if page in [ AppPage . THIRD_BATTLE , AppPage . FOURTH_BATTLE ] and battle_phase == 4 :
return " draw_release " if added_rear == " Farstring " else " lantern_heal "
if battle_phase == 0 :
return " advance "
@@ -324,6 +365,20 @@ func added_rear_animation_state() -> String:
func enemy_animation_state ( ) - > String :
if page == AppPage . FOURTH_BATTLE :
if selected_build_profile ( ) == " tempo " and route_choice == " R6-02 " and battle_phase == 1 :
return " hit "
match battle_phase :
0 :
return " advance "
1 :
return " telegraph "
2 :
return " attack "
3 , 4 , 5 :
return " hit "
6 :
return " death "
if page == AppPage . THIRD_BATTLE and retained_item == " EQ-061 " and battle_phase == 1 :
return " hit "
if page == AppPage . THIRD_BATTLE :
@@ -377,7 +432,7 @@ func render() -> void:
projectile = null
for child in display_root . get_children ( ) :
child . free ( )
var background_texture : Texture2D = FIELD_BACKGROUND if page in [ AppPage . BATTLE , AppPage . VICTORY , AppPage . ADAPTATION , AppPage . SECOND_BATTLE , AppPage . SECOND_RESULT , AppPage . THIRD_BATTLE , AppPage . SESSION_RESULT ] else YARD_BACKGROUND
var background_texture : Texture2D = FIELD_BACKGROUND if page in [ AppPage . BATTLE , AppPage . VICTORY , AppPage . ADAPTATION , AppPage . SECOND_BATTLE , AppPage . SECOND_RESULT , AppPage . THIRD_BATTLE , AppPage . SESSION_RESULT , AppPage . ROUTE_BOARD , AppPage . ROUTE_REVIEW , AppPage . FOURTH_BATTLE , AppPage . SECOND_CYCLE_RESULT ] else YARD_BACKGROUND
add_background ( background_texture )
if portrait_layout ( ) :
render_portrait_notice ( )
@@ -405,6 +460,14 @@ func render() -> void:
render_battle ( )
AppPage . SESSION_RESULT :
render_session_result ( )
AppPage . ROUTE_BOARD :
render_route_board ( )
AppPage . ROUTE_REVIEW :
render_route_review ( )
AppPage . FOURTH_BATTLE :
render_battle ( )
AppPage . SECOND_CYCLE_RESULT :
render_second_cycle_result ( )
if restart_confirmation :
render_restart_confirmation ( )
@@ -613,7 +676,8 @@ func render_battle() -> void:
add_full_wash ( Color ( 0.015 , 0.025 , 0.055 , 0.1 ) )
var second_cycle := page == AppPage . SECOND_BATTLE
var third_cycle := page == AppPage . THIRD_BATTLE
var battle_title := tr_text ( " 第三次远征 · 裂隙队长 " , " Third Expedition · Rift Captain " ) if third_cycle else ( tr_text ( " 第二次远征 · 老兵截道 " , " Second Expedition · Veteran Ambush " ) if second_cycle else tr_text ( " 第一次远征 · 风环遗迹 " , " First Expedition · Windring Ruins " ) )
var fourth_cycle := page == AppPage . FOURTH_BATTLE
var battle_title := tr_text ( " 第四次远征 · %s " % route_name ( route_choice ) , " Fourth Expedition · %s " % route_name ( route_choice ) ) if fourth_cycle else ( tr_text ( " 第三次远征 · 裂隙队长 " , " Third Expedition · Rift Captain " ) if third_cycle else ( tr_text ( " 第二次远征 · 老兵截道 " , " Second Expedition · Veteran Ambush " ) if second_cycle else tr_text ( " 第一次远征 · 风环遗迹 " , " First Expedition · Windring Ruins " ) ) )
render_hud ( battle_title )
var world_bottom := size . y - 58.0
var actor_height : float = clampf ( size . y * 0.27 , 92.0 , 170.0 )
@@ -625,12 +689,14 @@ func render_battle() -> void:
enemy_base_position = Vector2 ( size . x * ( 0.56 if contact_reached else 0.77 ) , world_bottom - actor_height * 0.84 )
front_sprite = actor_at ( animation_texture ( front_choice , front_animation_state ( ) , battle_clock ) , front_base_position , Vector2 ( actor_width , actor_height ) , " FrontActor " )
rear_sprite = actor_at ( animation_texture ( rear_choice , rear_animation_state ( ) , battle_clock ) , rear_base_position , Vector2 ( actor_width * 0.9 , actor_height * 0.9 ) , " RearActor " )
if third_cycle and not added_rear . is_empty ( ) :
if ( third_cycle or fourth_cycle ) and not added_rear . is_empty ( ) :
added_rear_sprite = actor_at ( animation_texture ( added_rear , added_rear_animation_state ( ) , battle_clock ) , added_rear_base_position , Vector2 ( actor_width * 0.86 , actor_height * 0.86 ) , " AddedRearActor " )
enemy_sprite = actor_at ( animation_texture ( " Riftblade " , enemy_animation_state ( ) , battle_clock ) , enemy_base_position , Vector2 ( actor_width * 0.82 , actor_height * 0.82 ) , " EnemyActor " )
add_health_bar ( " FrontHealth " , Vector2 ( 22 , 72 ) , Vector2 ( size . x * 0.29 , 14 ) , front_hp , 100 , candidate_name ( front_choice ) )
add_health_bar ( " EnemyHealth " , Vector2 ( size . x * 0.69 - 22 , 72 ) , Vector2 ( size . x * 0.29 , 14 ) , enemy_hp , 130 if third_cycle else 100 , tr_text ( " 裂隙队长 " , " Rift Captain " ) if third_cycle else tr_text ( " 裂影刃手 " , " Riftblade " ) )
add_health_bar ( " EnemyHealth " , Vector2 ( size . x * 0.69 - 22 , 72 ) , Vector2 ( size . x * 0.29 , 14 ) , enemy_hp , 130 if third_cycle else ( 120 if fourth_cycle else 100 ) , tr_text ( " 路线守卫 " , " Route Warden " ) if fourth_cycle else ( tr_text ( " 裂隙队长 " , " Rift Captain " ) if third_cycle else tr_text ( " 裂影刃手 " , " Riftblade " ) ) )
if fourth_cycle and route_choice == " R6-03 " :
add_health_bar ( " RearHealth " , Vector2 ( 22 , 101 ) , Vector2 ( size . x * 0.20 , 12 ) , rear_hp , 100 , candidate_name ( rear_choice ) )
var event := battle_event_text ( )
var ribbon := PanelContainer . new ( )
ribbon . name = " BattleEvent "
@@ -639,15 +705,15 @@ func render_battle() -> void:
ribbon . add_theme_stylebox_override ( " panel " , lacquer_style ( Color ( 0.04 , 0.07 , 0.13 , 0.94 ) , COLOR_GOLD , 2 ) )
ribbon . add_child ( centered_label ( event , 12 , COLOR_WHITE ) )
display_root . add_child ( ribbon )
if second_cycle or ( third_cycle and not retained_item . is_empty ( ) ) :
var visible_item := retained_item if third_cycle else adaptation_choice
if second_cycle or ( ( third_cycle or fourth_cycle ) and not retained_item . is_empty ( ) ) :
var visible_item := retained_item if ( third_cycle or fourth_cycle ) else adaptation_choice
var loan := centered_label ( ( " ▣ " if visible_item == " EQ-002 " else " ◷ " ) + adaptation_name ( visible_item ) , 11 , COLOR_GOLD_LIGHT )
loan . name = " ActiveAdaptation "
loan . position = Vector2 ( size . x * 0.02 , 116 )
loan . size = Vector2 ( size . x * 0.23 , 48 )
loan . add_theme_stylebox_override ( " normal " , lacquer_style ( Color ( 0.03 , 0.055 , 0.10 , 0.92 ) , COLOR_TEAL , 2 ) )
display_root . add_child ( loan )
elif third_cycle :
elif third_cycle or fourth_cycle :
var company := centered_label ( tr_text ( " ◆ 三人小队 \n 弓手 + 治疗 " , " ◆ THREE-PERSON LINE \n Archer + Healer " ) , 11 , COLOR_GOLD_LIGHT )
company . name = " ActiveCompany "
company . position = Vector2 ( size . x * 0.02 , 116 )
@@ -655,7 +721,10 @@ func render_battle() -> void:
company . add_theme_stylebox_override ( " normal " , lacquer_style ( Color ( 0.03 , 0.055 , 0.10 , 0.92 ) , COLOR_TEAL , 2 ) )
display_root . add_child ( company )
if third_cycle and battle_phase == 1 and retained_item != " EQ-061 " :
if fourth_cycle and battle_phase == 1 :
var cue_x := size . x * ( 0.20 if route_choice == " R6-03 " else 0.61 )
add_signal_label ( " ! " , Vector2 ( cue_x , size . y * 0.31 ) , COLOR_CRIMSON )
elif third_cycle and battle_phase == 1 and retained_item != " EQ-061 " :
add_signal_label ( " ! " , Vector2 ( size . x * 0.61 , size . y * 0.31 ) , COLOR_CRIMSON )
elif battle_phase == 1 and not third_cycle and ( not second_cycle or adaptation_choice == " EQ-002 " ) :
add_signal_label ( " ! " , Vector2 ( size . x * 0.61 , size . y * 0.31 ) , COLOR_CRIMSON )
@@ -663,7 +732,7 @@ func render_battle() -> void:
add_signal_label ( " ! " , Vector2 ( size . x * 0.61 , size . y * 0.31 ) , COLOR_CRIMSON )
elif battle_phase == 3 and rear_choice == " Farstring " :
projectile = add_signal_label ( " ➤ " , Vector2 ( size . x * 0.48 , size . y * 0.50 ) , COLOR_GOLD_LIGHT )
elif third_cycle and battle_phase == 4 and added_rear == " Farstring " :
elif ( third_cycle or fourth_cycle ) and battle_phase == 4 and added_rear == " Farstring " :
projectile = add_signal_label ( " ➤ " , Vector2 ( size . x * 0.45 , size . y * 0.52 ) , COLOR_GOLD_LIGHT )
render_battle_feedback ( actor_width , actor_height )
update_battle_feedback_visual ( )
@@ -879,10 +948,152 @@ func render_session_result() -> void:
var facts := centered_label ( tr_text ( " 金币 %d | 小队 %d 人 \n 前排生命 %d / 100 | 用时 %.2f 秒 " % [ coin , company_size , front_hp , third_result_time ] , " Coin %d | Company %d \n Front HP %d / 100 | Time %.2f s " % [ coin , company_size , front_hp , third_result_time ] ) , 12 , Color ( " #6d5523 " ) )
facts . name = " SessionResultFacts "
box . add_child ( facts )
box . add_child ( game_button ( " ReplayExperienceButton " , tr_text ( " 用另一条路线重新开始 " , " Restart with another route" ) , request_restart , true ) )
box . add_child ( game_button ( " OpenRouteBoardButton " , tr_text ( " 查看下一轮三条路线 " , " Compare the next three routes " ) , open_route_board , true ) )
box . add_child ( game_button ( " SessionResultHomeButton " , tr_text ( " 回到主页 " , " Home " ) , go_home , false ) )
func render_route_board ( ) - > void :
add_full_wash ( Color ( 0.015 , 0.03 , 0.07 , 0.25 ) )
render_hud ( tr_text ( " 第二轮 · 选择下一条路线 " , " Second Cycle · Choose the next route " ) )
var actor_height : float = clampf ( size . y * 0.22 , 82.0 , 138.0 )
actor_at ( animation_texture ( front_choice , " idle " , victory_clock ) , Vector2 ( 20 , size . y - actor_height - 24 ) , Vector2 ( actor_height , actor_height ) , " RouteFront " )
actor_at ( animation_texture ( rear_choice , " idle " , victory_clock ) , Vector2 ( 100 , size . y - actor_height * 0.90 - 24 ) , Vector2 ( actor_height * 0.90 , actor_height * 0.90 ) , " RouteRear " )
if not added_rear . is_empty ( ) :
actor_at ( animation_texture ( added_rear , " idle " , victory_clock ) , Vector2 ( 176 , size . y - actor_height * 0.84 - 24 ) , Vector2 ( actor_height * 0.84 , actor_height * 0.84 ) , " RouteAddedRear " )
var build_panel := PanelContainer . new ( )
build_panel . name = " RouteBuildSummary "
build_panel . position = Vector2 ( 16 , 72 )
build_panel . size = Vector2 ( size . x * 0.30 , 86 )
build_panel . add_theme_stylebox_override ( " panel " , lacquer_style ( Color ( 0.03 , 0.055 , 0.10 , 0.94 ) , COLOR_GOLD , 2 ) )
display_root . add_child ( build_panel )
var build_copy := VBoxContainer . new ( )
build_panel . add_child ( build_copy )
build_copy . add_child ( game_label ( tr_text ( " 上一轮留下的构筑 " , " BUILD INHERITED FROM LAST CYCLE " ) , 9 , COLOR_TEAL ) )
build_copy . add_child ( game_label ( build_summary_text ( ) , 12 , COLOR_WHITE ) )
var board := PanelContainer . new ( )
board . name = " RouteMap "
board . position = Vector2 ( size . x * 0.32 , 72 )
board . size = Vector2 ( size . x * 0.66 , size . y - 88 )
board . add_theme_stylebox_override ( " panel " , lacquer_style ( Color ( 0.025 , 0.05 , 0.10 , 0.82 ) , COLOR_GOLD , 2 ) )
display_root . add_child ( board )
var shell := VBoxContainer . new ( )
shell . add_theme_constant_override ( " separation " , 3 )
board . add_child ( shell )
shell . add_child ( game_label ( tr_text ( " 三条路线都能前进,但当前构筑的应对不同 " , " All routes advance, but current coverage differs " ) , 15 , COLOR_WHITE ) )
shell . add_child ( game_label ( route_board_guidance ( ) , 9 , COLOR_TEAL ) )
var route_map := Control . new ( )
route_map . name = " RouteNodeMap "
route_map . custom_minimum_size = Vector2 ( 0 , 142 )
route_map . size_flags_vertical = Control . SIZE_EXPAND_FILL
shell . add_child ( route_map )
var map_width : float = board . size . x - 22.0
var node_size := Vector2 ( 150 , 108 )
var node_positions := [
Vector2 ( 6 , 24 ) ,
Vector2 ( ( map_width - node_size . x ) * 0.5 , 6 ) ,
Vector2 ( map_width - node_size . x - 6 , 24 ) ,
]
var route_path := Line2D . new ( )
route_path . name = " RoutePath "
route_path . width = 7.0
route_path . default_color = Color ( 0.69 , 0.52 , 0.24 , 0.82 )
route_path . joint_mode = Line2D . LINE_JOINT_ROUND
route_path . begin_cap_mode = Line2D . LINE_CAP_ROUND
route_path . end_cap_mode = Line2D . LINE_CAP_ROUND
for route_position in node_positions :
route_path . add_point ( route_position + node_size * 0.5 )
route_map . add_child ( route_path )
for index in range ( 3 ) :
var id : String = [ " R6-01 " , " R6-02 " , " R6-03 " ] [ index ]
var node := route_button ( id )
node . position = node_positions [ index ]
node . size = node_size
route_map . add_child ( node )
var review := game_button ( " ReviewRouteButton " , tr_text ( " 查看所选路线并确认 " , " Review selected route " ) , open_route_review , true )
review . disabled = route_choice . is_empty ( )
review . custom_minimum_size . y = 48
shell . add_child ( review )
func route_button ( id : String ) - > Button :
var selected := route_choice == id
var button := Button . new ( )
button . name = " Route_ %s " % id . replace ( " - " , " _ " )
button . text = " ◆ %s ◆ \n %s · %s \n %s \n %s " % [ route_name ( id ) , route_exposure ( id ) , tr_text ( " + %d 金币 " % route_reward ( id ) , " + %d Coin " % route_reward ( id ) ) , route_pressure ( id ) , route_coverage_label ( id ) ]
button . focus_mode = Control . FOCUS_ALL
button . custom_minimum_size = Vector2 ( 150 , 108 )
button . add_theme_font_size_override ( " font_size " , scaled ( 9 ) )
button . add_theme_color_override ( " font_color " , COLOR_GOLD_LIGHT if selected else COLOR_WHITE )
button . add_theme_stylebox_override ( " normal " , route_node_style ( selected , false , coverage_color ( id ) ) )
button . add_theme_stylebox_override ( " hover " , route_node_style ( selected , true , coverage_color ( id ) ) )
button . add_theme_stylebox_override ( " pressed " , route_node_style ( true , true , coverage_color ( id ) ) )
button . add_theme_stylebox_override ( " focus " , route_node_style ( true , true , coverage_color ( id ) ) )
button . pressed . connect ( func ( ) : choose_route ( id ) , CONNECT_DEFERRED )
return button
func render_route_review ( ) - > void :
add_full_wash ( Color ( 0.01 , 0.025 , 0.06 , 0.38 ) )
render_hud ( tr_text ( " 路线确认 · %s " % route_name ( route_choice ) , " Route Review · %s " % route_name ( route_choice ) ) )
var panel := PanelContainer . new ( )
panel . name = " RouteReviewPanel "
panel . position = Vector2 ( size . x * 0.12 , 76 )
panel . size = Vector2 ( size . x * 0.76 , size . y - 96 )
panel . add_theme_stylebox_override ( " panel " , parchment_style ( ) )
display_root . add_child ( panel )
var box := VBoxContainer . new ( )
box . alignment = BoxContainer . ALIGNMENT_CENTER
box . add_theme_constant_override ( " separation " , 6 )
panel . add_child ( box )
box . add_child ( centered_label ( " ◆ " + route_name ( route_choice ) + " ◆ " , 22 , COLOR_INK ) )
box . add_child ( centered_label ( tr_text ( " 已知压力: %s \n 暴露: %s 奖励:+ %d 金币 " % [ route_pressure ( route_choice ) , route_exposure ( route_choice ) , route_reward ( route_choice ) ] , " Known pressure: %s \n Exposure: %s Reward: + %d Coin " % [ route_pressure ( route_choice ) , route_exposure ( route_choice ) , route_reward ( route_choice ) ] ) , 12 , Color ( " #4c402d " ) ) )
var coverage := centered_label ( route_coverage_reason ( route_choice ) , 12 , Color ( " #6d5523 " ) )
coverage . name = " RouteReviewCoverage "
box . add_child ( coverage )
box . add_child ( centered_label ( tr_text ( " 确认后锁定当前 %s ;路线入场不花金币。 " % build_summary_short ( ) , " Commit freezes the current %s ; route entry costs no Coin. " % build_summary_short ( ) ) , 10 , Color ( " #4c402d " ) ) )
var actions := HBoxContainer . new ( )
actions . add_theme_constant_override ( " separation " , 8 )
box . add_child ( actions )
var cancel := game_button ( " CancelRouteReviewButton " , tr_text ( " 返回比较 " , " Back to compare " ) , cancel_route_review , false )
cancel . size_flags_horizontal = Control . SIZE_EXPAND_FILL
actions . add_child ( cancel )
var commit := game_button ( " CommitRouteButton " , tr_text ( " 锁定路线并进入战斗 " , " Commit route and enter battle " ) , commit_route , true )
commit . size_flags_horizontal = Control . SIZE_EXPAND_FILL
actions . add_child ( commit )
func render_second_cycle_result ( ) - > void :
add_full_wash ( Color ( 0.02 , 0.035 , 0.075 , 0.18 ) )
render_hud ( tr_text ( " 第二轮路线完成 " , " Second-Cycle Route Complete " ) )
var actor_height : float = clampf ( size . y * 0.28 , 96.0 , 176.0 )
actor_at ( animation_texture ( front_choice , " victory " if front_choice == " Hearthguard " else " idle " , victory_clock ) , Vector2 ( size . x * 0.05 , size . y - actor_height - 36 ) , Vector2 ( actor_height , actor_height ) , " CycleResultFront " )
actor_at ( animation_texture ( rear_choice , " idle " , victory_clock ) , Vector2 ( size . x * 0.18 , size . y - actor_height * 0.90 - 36 ) , Vector2 ( actor_height * 0.90 , actor_height * 0.90 ) , " CycleResultRear " )
if not added_rear . is_empty ( ) :
actor_at ( animation_texture ( added_rear , " idle " , victory_clock ) , Vector2 ( size . x * 0.30 , size . y - actor_height * 0.84 - 36 ) , Vector2 ( actor_height * 0.84 , actor_height * 0.84 ) , " CycleResultAddedRear " )
actor_at ( animation_texture ( " Riftblade " , " death " , 99.0 ) , Vector2 ( size . x * 0.45 , size . y - actor_height * 0.50 - 34 ) , Vector2 ( actor_height * 0.50 , actor_height * 0.50 ) , " CycleResultEnemy " )
var result := PanelContainer . new ( )
result . name = " SecondCycleResultPanel "
result . position = Vector2 ( size . x * 0.56 , 76 )
result . size = Vector2 ( size . x * 0.41 , size . y - 98 )
result . add_theme_stylebox_override ( " panel " , parchment_style ( ) )
display_root . add_child ( result )
var box := VBoxContainer . new ( )
box . alignment = BoxContainer . ALIGNMENT_CENTER
box . add_theme_constant_override ( " separation " , 5 )
result . add_child ( box )
box . add_child ( centered_label ( " ◆ " + route_result_headline ( ) + " ◆ " , 17 , COLOR_INK ) )
var explanation := centered_label ( route_result_explanation ( ) , 10 , Color ( " #4c402d " ) )
explanation . name = " SecondCycleExplanation "
box . add_child ( explanation )
var company_size := 3 if not added_rear . is_empty ( ) else 2
var facts := centered_label ( tr_text ( " 奖励 + %d 金币 | 当前金币 %d \n 小队 %d 人 | 前排 %d 后排 %d | %.2f 秒 " % [ route_reward ( route_choice ) , coin , company_size , front_hp , rear_hp , fourth_result_time ] , " Reward + %d Coin | Current %d \n Company %d | Front %d Rear %d | %.2f s " % [ route_reward ( route_choice ) , coin , company_size , front_hp , rear_hp , fourth_result_time ] ) , 11 , Color ( " #6d5523 " ) )
facts . name = " SecondCycleFacts "
box . add_child ( facts )
box . add_child ( game_button ( " ReplaySecondCycleButton " , tr_text ( " 重新开始完整体验 " , " Restart the full experience " ) , request_restart , true ) )
box . add_child ( game_button ( " SecondCycleHomeButton " , tr_text ( " 回到主页 " , " Home " ) , go_home , false ) )
func render_restart_confirmation ( ) - > void :
var wash := ColorRect . new ( )
wash . name = " RestartWash "
@@ -984,7 +1195,7 @@ func depart() -> void:
func advance_battle_phase ( ) - > void :
if page not in [ AppPage . BATTLE , AppPage . SECOND_BATTLE , AppPage . THIRD_BATTLE ] or not battle_playing :
if page not in [ AppPage . BATTLE , AppPage . SECOND_BATTLE , AppPage . THIRD_BATTLE , AppPage . FOURTH_BATTLE ] or not battle_playing :
return
commit_phase_impact ( battle_phase , false )
battle_phase + = 1
@@ -997,6 +1208,15 @@ func advance_battle_phase() -> void:
page = AppPage . SESSION_RESULT
render ( )
return
if page == AppPage . FOURTH_BATTLE :
if battle_phase == 7 :
battle_playing = false
victory_events + = 1
fourth_result_time = fourth_battle_elapsed
commit_route_reward ( )
page = AppPage . SECOND_CYCLE_RESULT
render ( )
return
match battle_phase :
6 :
battle_playing = false
@@ -1011,6 +1231,18 @@ func advance_battle_phase() -> void:
func phase_impact_time ( phase : int ) - > float :
if page == AppPage . FOURTH_BATTLE :
match phase :
1 :
return 3.0 / 12.0 if selected_build_profile ( ) == " tempo " and route_choice == " R6-02 " else - 1.0
2 :
return 4.0 / 12.0
3 , 4 :
return 2.0 / 7.0
5 :
return 3.0 / 10.0
6 :
return 5.0 / 10.0
if page == AppPage . THIRD_BATTLE :
match phase :
1 :
@@ -1048,6 +1280,11 @@ func phase_impact_time(phase: int) -> float:
func phase_duration ( phase : int ) - > float :
if page == AppPage . FOURTH_BATTLE :
var fourth_key := " %s : %s " % [ route_choice , selected_build_profile ( ) ]
if FOURTH_BATTLE_PHASE_DURATIONS . has ( fourth_key ) :
var fourth_durations : Array = FOURTH_BATTLE_PHASE_DURATIONS [ fourth_key ]
return float ( fourth_durations [ phase ] ) if phase > = 0 and phase < fourth_durations . size ( ) else 0.0
if page == AppPage . THIRD_BATTLE :
var route := " recruit " if not added_rear . is_empty ( ) else retained_item
if THIRD_BATTLE_PHASE_DURATIONS . has ( route ) :
@@ -1069,6 +1306,9 @@ func commit_phase_impact(phase: int, refresh_runtime := true) -> void:
var qualifier_zh := " "
var qualifier_en := " "
var tier := " medium "
if page == AppPage . FOURTH_BATTLE :
commit_fourth_phase_impact ( phase , refresh_runtime )
return
if page == AppPage . THIRD_BATTLE :
commit_third_phase_impact ( phase , refresh_runtime )
return
@@ -1293,6 +1533,129 @@ func commit_third_phase_impact(phase: int, refresh_runtime := true) -> void:
render ( )
func commit_fourth_phase_impact ( phase : int , refresh_runtime := true ) - > void :
if phase_impact_committed . get ( phase , false ) :
return
var profile := selected_build_profile ( )
var target := " "
var delta := 0
var qualifier_zh := " "
var qualifier_en := " "
var tier := " medium "
match phase :
1 :
if route_choice != " R6-02 " or profile != " tempo " :
return
var previous_enemy_hp := enemy_hp
enemy_hp = 90
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 晨钟抢速 "
qualifier_en = " tempo counter "
play_cue ( " hit " )
2 :
if route_choice == " R6-03 " :
var previous_rear_hp := rear_hp
rear_hp = 65
target = " rear "
delta = rear_hp - previous_rear_hp
qualifier_zh = " 后排分击 "
qualifier_en = " rear split "
else :
var previous_front_hp := front_hp
if route_choice == " R6-01 " :
front_hp = 90 if profile == " guarded " else ( 60 if profile == " tempo " else 70 )
else :
front_hp = 70
target = " front "
delta = front_hp - previous_front_hp
qualifier_zh = " 胫甲抗碎 " if route_choice == " R6-01 " and profile == " guarded " else ( " 速度爆发 " if route_choice == " R6-02 " else " 正面粉碎 " )
qualifier_en = " brace answered " if route_choice == " R6-01 " and profile == " guarded " else ( " tempo burst " if route_choice == " R6-02 " else " front crush " )
play_cue ( " hit " )
3 :
if rear_choice == " Farstring " :
var previous_enemy_hp := enemy_hp
enemy_hp = mini ( enemy_hp , 72 )
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 弓手命中 "
qualifier_en = " Archer hit "
else :
if route_choice == " R6-03 " :
var previous_rear_hp := rear_hp
rear_hp = 88 if profile == " full_line " else mini ( 100 , rear_hp + 12 )
target = " rear "
delta = rear_hp - previous_rear_hp
qualifier_zh = " 后排自救 "
qualifier_en = " rear recovery "
else :
var previous_front_hp := front_hp
front_hp = mini ( 100 , front_hp + 12 )
target = " front "
delta = front_hp - previous_front_hp
qualifier_zh = " 治疗 "
qualifier_en = " healing "
rear_events + = 1
play_cue ( " role " )
4 :
if not added_rear . is_empty ( ) :
if added_rear == " Farstring " :
var previous_enemy_hp := enemy_hp
enemy_hp = mini ( enemy_hp , 72 )
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 新弓手命中 "
qualifier_en = " new Archer hit "
else :
if route_choice == " R6-03 " :
var previous_rear_hp := rear_hp
rear_hp = 88
target = " rear "
delta = rear_hp - previous_rear_hp
qualifier_zh = " 双后排恢复 "
qualifier_en = " rear-line recovery "
else :
var previous_front_hp := front_hp
front_hp = mini ( 100 , front_hp + 12 )
target = " front "
delta = front_hp - previous_front_hp
qualifier_zh = " 新治疗支援 "
qualifier_en = " new Healer support "
rear_events + = 1
play_cue ( " role " )
else :
var previous_enemy_hp := enemy_hp
enemy_hp = mini ( enemy_hp , 44 )
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 构筑反击 "
qualifier_en = " build counter "
play_cue ( " hit " )
5 :
var previous_enemy_hp := enemy_hp
enemy_hp = 28
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 路线突破 "
qualifier_en = " route break "
play_cue ( " hit " )
6 :
var previous_enemy_hp := enemy_hp
enemy_hp = 0
target = " enemy "
delta = enemy_hp - previous_enemy_hp
qualifier_zh = " 路线守卫击破 "
qualifier_en = " warden defeated "
tier = " large "
play_cue ( " victory " )
_ :
return
phase_impact_committed [ phase ] = true
emit_battle_feedback ( phase , target , delta , qualifier_zh , qualifier_en , tier )
if refresh_runtime and page == AppPage . FOURTH_BATTLE :
render ( )
func emit_battle_feedback ( phase : int , target : String , delta : int , qualifier_zh : String , qualifier_en : String , tier : String ) - > void :
active_battle_feedback = {
" phase " : phase ,
@@ -1326,7 +1689,7 @@ func expire_battle_feedback() -> void:
func _process ( delta : float ) - > void :
if page in [ AppPage . VICTORY , AppPage . ADAPTATION , AppPage . SECOND_RESULT , AppPage . GUILD_RETURN , AppPage . SESSION_RESULT ] :
if page in [ AppPage . VICTORY , AppPage . ADAPTATION , AppPage . SECOND_RESULT , AppPage . GUILD_RETURN , AppPage . SESSION_RESULT , AppPage . ROUTE_BOARD , AppPage . ROUTE_REVIEW , AppPage . SECOND_CYCLE_RESULT ] :
if front_choice . is_empty ( ) or rear_choice . is_empty ( ) :
return
victory_clock + = delta
@@ -1334,7 +1697,7 @@ func _process(delta: float) -> void:
victory_front_sprite . texture = animation_texture ( front_choice , " victory " if front_choice == " Hearthguard " else " idle " , victory_clock )
victory_rear_sprite . texture = animation_texture ( rear_choice , " idle " , victory_clock )
return
if page not in [ AppPage . BATTLE , AppPage . SECOND_BATTLE , AppPage . THIRD_BATTLE ] or not battle_playing or restart_confirmation :
if page not in [ AppPage . BATTLE , AppPage . SECOND_BATTLE , AppPage . THIRD_BATTLE , AppPage . FOURTH_BATTLE ] or not battle_playing or restart_confirmation :
return
var feedback_was_active := not active_battle_feedback . is_empty ( )
battle_clock + = delta
@@ -1342,6 +1705,8 @@ func _process(delta: float) -> void:
second_battle_elapsed + = delta
elif page == AppPage . THIRD_BATTLE :
third_battle_elapsed + = delta
elif page == AppPage . FOURTH_BATTLE :
fourth_battle_elapsed + = delta
var impact_time := phase_impact_time ( battle_phase )
if impact_time > = 0.0 and battle_clock > = impact_time :
commit_phase_impact ( battle_phase )
@@ -1363,7 +1728,7 @@ func _process(delta: float) -> void:
if added_rear_sprite != null and is_instance_valid ( added_rear_sprite ) :
added_rear_sprite . texture = animation_texture ( added_rear , added_rear_animation_state ( ) , battle_clock )
enemy_sprite . texture = animation_texture ( " Riftblade " , enemy_animation_state ( ) , battle_clock )
var projectile_actor_is_active := ( battle_phase == 3 and rear_choice == " Farstring " ) or ( page == AppPage . THIRD_BATTLE and battle_phase == 4 and added_rear == " Farstring " )
var projectile_actor_is_active := ( battle_phase == 3 and rear_choice == " Farstring " ) or ( page in [ AppPage . THIRD_BATTLE , AppPage . FOURTH_BATTLE ] and battle_phase == 4 and added_rear == " Farstring " )
if projectile != null and is_instance_valid ( projectile ) and projectile_actor_is_active and not reduced_motion :
var projectile_start := 0.08
var projectile_end := phase_impact_time ( battle_phase ) + 0.10
@@ -1471,6 +1836,70 @@ func begin_third_battle() -> void:
render ( )
func open_route_board ( ) - > void :
if page != AppPage . SESSION_RESULT or not guild_committed :
return
route_choice = " "
route_committed = false
route_reward_committed = false
page = AppPage . ROUTE_BOARD
play_cue ( " confirm " )
render ( )
func choose_route ( id : String ) - > void :
if page != AppPage . ROUTE_BOARD or route_committed or not ROUTES . has ( id ) :
return
route_choice = id
play_cue ( " select " )
render ( )
func open_route_review ( ) - > void :
if page != AppPage . ROUTE_BOARD or route_choice . is_empty ( ) or route_committed :
return
page = AppPage . ROUTE_REVIEW
play_cue ( " confirm " )
render ( )
func cancel_route_review ( ) - > void :
if page != AppPage . ROUTE_REVIEW or route_committed :
return
page = AppPage . ROUTE_BOARD
play_cue ( " select " )
render ( )
func commit_route ( ) - > void :
if page != AppPage . ROUTE_REVIEW or route_choice . is_empty ( ) or route_committed or not ROUTES . has ( route_choice ) :
return
route_committed = true
route_reward_committed = false
battle_phase = 0
battle_clock = 0.0
battle_playing = true
front_hp = 100
rear_hp = 100
enemy_hp = 120
guard_events = 0
rear_events = 0
phase_impact_committed . clear ( )
reset_battle_feedback ( )
fourth_battle_elapsed = 0.0
fourth_result_time = 0.0
page = AppPage . FOURTH_BATTLE
play_cue ( " depart " )
render ( )
func commit_route_reward ( ) - > void :
if route_reward_committed or not route_committed or not ROUTES . has ( route_choice ) :
return
route_reward_committed = true
coin + = route_reward ( route_choice )
func request_restart ( ) - > void :
restart_confirmation = true
render ( )
@@ -1521,6 +1950,12 @@ func clear_experience() -> void:
added_rear = " "
third_battle_elapsed = 0.0
third_result_time = 0.0
rear_hp = 100
route_choice = " "
route_committed = false
route_reward_committed = false
fourth_battle_elapsed = 0.0
fourth_result_time = 0.0
phase_impact_committed . clear ( )
reset_battle_feedback ( )
victory_clock = 0.0
@@ -1550,6 +1985,8 @@ func toggle_mute() -> void:
func battle_event_text ( ) - > String :
if page == AppPage . FOURTH_BATTLE :
return fourth_battle_event_text ( )
if page == AppPage . THIRD_BATTLE :
return third_battle_event_text ( )
if page == AppPage . SECOND_BATTLE :
@@ -1614,6 +2051,135 @@ func third_battle_event_text() -> String:
return " "
func fourth_battle_event_text ( ) - > String :
match battle_phase :
0 :
return tr_text ( " 小队进入 %s … " % route_name ( route_choice ) , " The Company enters %s … " % route_name ( route_choice ) )
1 :
if route_choice == " R6-01 " :
return tr_text ( " 路线守卫蓄力:正面粉碎即将命中前排 " , " The Warden charges a front crush " )
if route_choice == " R6-02 " :
return tr_text ( " 晨钟抢到先手 " if selected_build_profile ( ) == " tempo " else " 敌方速度爆发抢先行动 " , " Early Bell seizes initiative " if selected_build_profile ( ) == " tempo " else " Enemy tempo bursts first " )
return tr_text ( " 双辉标记锁定了原后排 " , " Twinflare marks the original rear " )
2 :
if route_choice == " R6-03 " :
return tr_text ( " 分裂打击越过前排,命中后排 -35 " , " The split strike bypasses the front and hits rear -35 " )
return tr_text ( " 钉阵胫甲把粉碎降为 -10 " if route_choice == " R6-01 " and selected_build_profile ( ) == " guarded " else " 路线压力命中前排 " , " Frontpin reduces the crush to -10 " if route_choice == " R6-01 " and selected_build_profile ( ) == " guarded " else " Route pressure hits the front " )
3 :
return tr_text ( " 原弓手从后排反击 " , " The original Archer counters " ) if rear_choice == " Farstring " else tr_text ( " 原治疗修复受损阵线 " , " The original Healer restores the damaged line " )
4 :
return tr_text ( " 第三名成员补上第二种后排能力 " , " The third member adds the missing rear response " ) if not added_rear . is_empty ( ) else tr_text ( " 保留构筑发动第二次反击 " , " The retained build follows up " )
5 :
return tr_text ( " 小队突破路线守卫的防线 " , " The Company breaks the route defense " )
6 :
return tr_text ( " 路线守卫被击败,奖励已结算 " , " Route Warden defeated; reward settled " )
return " "
func selected_build_profile ( ) - > String :
if not added_rear . is_empty ( ) :
return " full_line "
return " guarded " if retained_item == " EQ-002 " else " tempo "
func route_name ( id : String ) - > String :
if not ROUTES . has ( id ) :
return " "
return ROUTES [ id ] [ " name_zh " ] if locale == " zh_CN " else ROUTES [ id ] [ " name_en " ]
func route_pressure ( id : String ) - > String :
if not ROUTES . has ( id ) :
return " "
return ROUTES [ id ] [ " pressure_zh " ] if locale == " zh_CN " else ROUTES [ id ] [ " pressure_en " ]
func route_exposure ( id : String ) - > String :
if not ROUTES . has ( id ) :
return " "
return ROUTES [ id ] [ " exposure_zh " ] if locale == " zh_CN " else ROUTES [ id ] [ " exposure_en " ]
func route_reward ( id : String ) - > int :
return int ( ROUTES [ id ] [ " reward " ] ) if ROUTES . has ( id ) else 0
func route_coverage_state ( id : String ) - > String :
var profile := selected_build_profile ( )
if id == " R6-01 " :
return " covered " if profile == " guarded " else ( " partial " if profile == " full_line " else " gap " )
if id == " R6-02 " :
return " covered " if profile == " tempo " else ( " partial " if profile == " full_line " else " gap " )
if id == " R6-03 " :
return " covered " if profile == " full_line " else " gap "
return " gap "
func route_coverage_label ( id : String ) - > String :
match route_coverage_state ( id ) :
" covered " :
return tr_text ( " 应对充分 " , " COVERED " )
" partial " :
return tr_text ( " 部分应对 " , " PARTIAL " )
return tr_text ( " 存在缺口 " , " GAP " )
func route_coverage_reason ( id : String ) - > String :
var profile := selected_build_profile ( )
if id == " R6-01 " :
if profile == " guarded " :
return tr_text ( " 应对充分:钉阵胫甲直接降低正面粉碎。 " , " Covered: Frontpin directly reduces the front crush. " )
if profile == " full_line " :
return tr_text ( " 部分应对:三人阵线能治疗,但不能减少首次重击。 " , " Partial: the full line heals after impact but cannot reduce it. " )
return tr_text ( " 存在缺口:晨钟加快行动,但不会减少正面伤害。 " , " Gap: Early Bell adds speed but does not reduce front damage. " )
if id == " R6-02 " :
if profile == " tempo " :
return tr_text ( " 应对充分:晨钟让前排抢在速度爆发之前行动。 " , " Covered: Early Bell acts before the tempo burst. " )
if profile == " full_line " :
return tr_text ( " 部分应对:完整后排能恢复,但不能打断爆发。 " , " Partial: the full rear line recovers but cannot interrupt the burst. " )
return tr_text ( " 存在缺口:钉阵胫甲抗重击,但不改变敌方先手。 " , " Gap: Frontpin resists force but does not answer enemy initiative. " )
if profile == " full_line " :
return tr_text ( " 应对充分:弓手与治疗分担后排打击和恢复。 " , " Covered: Archer and Healer divide rear damage and recovery. " )
return tr_text ( " 存在缺口:两件前排装备都无法保护被标记的后排。 " , " Gap: neither front item protects the marked rear. " )
func coverage_color ( id : String ) - > Color :
match route_coverage_state ( id ) :
" covered " :
return COLOR_TEAL
" partial " :
return COLOR_GOLD
return COLOR_CRIMSON
func build_summary_short ( ) - > String :
match selected_build_profile ( ) :
" guarded " :
return tr_text ( " 钉阵两人小队 " , " Frontpin two-person line " )
" tempo " :
return tr_text ( " 晨钟两人小队 " , " Early Bell two-person line " )
return tr_text ( " 弓手 + 治疗三人小队 " , " Archer + Healer three-person line " )
func build_summary_text ( ) - > String :
var company_size := 3 if selected_build_profile ( ) == " full_line " else 2
return tr_text ( " 金币 %d | 小队 %d 人 \n %s " % [ coin , company_size , build_summary_short ( ) ] , " Coin %d | Company %d \n %s " % [ coin , company_size , build_summary_short ( ) ] )
func route_board_guidance ( ) - > String :
if route_choice . is_empty ( ) :
return tr_text ( " 没有预选或推荐。先比较压力、奖励和当前应对。 " , " Nothing is preselected or recommended. Compare pressure, reward and coverage. " )
return tr_text ( " 已选择 %s ;确认前仍可切换。 " % route_name ( route_choice ) , " %s selected; you can still switch before review. " % route_name ( route_choice ) )
func route_result_headline ( ) - > String :
return tr_text ( " %s 在 %s 的作用 " % [ build_summary_short ( ) , route_name ( route_choice ) ] , " %s on %s " % [ build_summary_short ( ) , route_name ( route_choice ) ] )
func route_result_explanation ( ) - > String :
return tr_text ( " 压力: %s \n %s \n 结果来自上一轮构筑与本轮压力;奖励只结算一次。 " % [ route_pressure ( route_choice ) , route_coverage_reason ( route_choice ) ] , " Pressure: %s \n %s \n The inherited build and route pressure caused this result; reward settled once. " % [ route_pressure ( route_choice ) , route_coverage_reason ( route_choice ) ] )
func adaptation_name ( id : String ) - > String :
return tr_text ( " 钉阵胫甲 " , " Frontpin Greaves " ) if id == " EQ-002 " else tr_text ( " 晨钟 " , " Early Bell " )
@@ -1733,8 +2299,8 @@ func render_battle_feedback(actor_width: float, actor_height: float) -> void:
return
var target : String = active_battle_feedback [ " target " ]
var delta : int = int ( active_battle_feedback [ " delta " ] )
var target_position := front_base_position if target == " front " else enemy_base_position
var target_scale := 1.0 if target == " front " else 0.82
var target_position := front_base_position if target == " front " else ( rear_base_position if target == " rear " else enemy_base_position )
var target_scale := 1.0 if target == " front " else ( 0.90 if target == " rear " else 0.82 )
var feedback_width := clampf ( size . x * 0.20 , 170.0 , 260.0 )
battle_feedback_origin = Vector2 (
clampf ( target_position . x + actor_width * target_scale * 0.5 - feedback_width * 0.5 , 12.0 , size . x - feedback_width - 12.0 ) ,
@@ -1790,6 +2356,8 @@ func update_battle_feedback_visual() -> void:
var recoil := sin ( battle_feedback_clock / BATTLE_FEEDBACK_IMPACT_WINDOW * PI ) * 8.0
if active_battle_feedback [ " target " ] == " front " and front_sprite != null and is_instance_valid ( front_sprite ) :
front_sprite . position . x = front_base_position . x - recoil
elif active_battle_feedback [ " target " ] == " rear " and rear_sprite != null and is_instance_valid ( rear_sprite ) :
rear_sprite . position . x = rear_base_position . x - recoil
elif active_battle_feedback [ " target " ] == " enemy " and enemy_sprite != null and is_instance_valid ( enemy_sprite ) :
enemy_sprite . position . x = enemy_base_position . x + recoil
@@ -1904,6 +2472,20 @@ func candidate_style(selected: bool, hover: bool, slot_color: Color) -> StyleBox
return style
func route_node_style ( selected : bool , hover : bool , route_color : Color ) - > StyleBoxFlat :
var fill := Color ( 0.03 , 0.07 , 0.12 , 0.94 if not hover else 1.0 )
var border := COLOR_GOLD_LIGHT if selected else route_color
var style := lacquer_style ( fill , COLOR_WHITE if hover else border , 4 if selected else 2 )
style . corner_radius_top_left = 42
style . corner_radius_top_right = 42
style . corner_radius_bottom_left = 42
style . corner_radius_bottom_right = 42
style . shadow_color = Color ( 0.01 , 0.015 , 0.03 , 0.78 )
style . shadow_size = 8 if selected else 5
style . shadow_offset = Vector2 ( 0 , 3 )
return style
func dashed_slot_style ( border : Color ) - > StyleBoxFlat :
var style := lacquer_style ( Color ( 0.035 , 0.06 , 0.12 , 0.58 ) , border , 2 )
style . corner_radius_top_left = 28