|  |  |  | extends Node3D | 
					
						
							|  |  |  | class_name Battle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @export var hit_back_limit_curve: Curve | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @onready var character: Character = (get_owner() as Character) | 
					
						
							|  |  |  | @onready var status: Status = (%Status as Status) | 
					
						
							|  |  |  | @onready var skill: Skill = (%Skill as Skill) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HitInfo: | 
					
						
							|  |  |  | 	var from: int | 
					
						
							|  |  |  | 	var to: int | 
					
						
							|  |  |  | 	var dir: Vector2 | 
					
						
							|  |  |  | 	var attack: AttackCfg | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func attack1(): | 
					
						
							|  |  |  | 	var attack: AttackCfg        = status.skill_cfg.get_attack1() | 
					
						
							|  |  |  | 	var attack_box: AttackBoxCfg = status.skill_cfg.get_attack1_box() | 
					
						
							|  |  |  | 	call_deferred("_attack", attack, attack_box) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func attack2(): | 
					
						
							|  |  |  | 	var attack: AttackCfg        = status.skill_cfg.get_attack2() | 
					
						
							|  |  |  | 	var attack_box: AttackBoxCfg = status.skill_cfg.get_attack2_box() | 
					
						
							|  |  |  | 	call_deferred("_attack", attack, attack_box) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _attack(attack: AttackCfg, attack_box: AttackBoxCfg): | 
					
						
							|  |  |  | 	var pos: Vector3        = character.pos() | 
					
						
							|  |  |  | 	var attack_dir: Vector2 = status.skill_dir.normalized() | 
					
						
							|  |  |  | 	var offset_xz: Vector2  = attack_dir * attack_box.offset.x | 
					
						
							|  |  |  | 	var offset_y: float     = attack_box.offset.y | 
					
						
							|  |  |  | 	pos += Vector3(offset_xz.x, offset_y, offset_xz.y) | 
					
						
							|  |  |  | 	var result: Array[Character] = Util.raycast_character(attack_box.shape, pos, attack_dir) | 
					
						
							|  |  |  | 	var is_stuck: bool            = false | 
					
						
							|  |  |  | 	for target: Character in result: | 
					
						
							|  |  |  | 		if target.team() == character.team(): | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		target.add_attack(character.id(), attack_dir, attack) | 
					
						
							|  |  |  | 		is_stuck = true | 
					
						
							|  |  |  | 	if !is_stuck and !attack.is_force_pause: | 
					
						
							|  |  |  | 		skill.on_attack_miss() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func add_attack(from: int, dir: Vector2, attack: AttackCfg): | 
					
						
							|  |  |  | 	var hit_info = HitInfo.new() | 
					
						
							|  |  |  | 	hit_info.from = from | 
					
						
							|  |  |  | 	hit_info.to = character.id() | 
					
						
							|  |  |  | 	hit_info.dir = dir | 
					
						
							|  |  |  | 	hit_info.attack = attack | 
					
						
							|  |  |  | 	settle(hit_info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func settle(hit_info: HitInfo) -> bool: | 
					
						
							|  |  |  | 	var character_from: Character = Global.character_mgr.get_character(hit_info.from) as Character | 
					
						
							|  |  |  | 	var character_to: Character   = Global.character_mgr.get_character(hit_info.to) as Character | 
					
						
							|  |  |  | 	if !character_from or !character_to: | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var cfg_from: CharacterCfg = character_from.cfg() | 
					
						
							|  |  |  | 	var cfg_to: CharacterCfg   = character_to.cfg() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var is_stun    = character_to.get_status("is_stun") | 
					
						
							|  |  |  | 	var hp         = character_to.get_status("hp") | 
					
						
							|  |  |  | 	var shield     = character_to.get_status("shield") | 
					
						
							|  |  |  | 	var has_shield = shield > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var attack: AttackCfg = hit_info.attack | 
					
						
							|  |  |  | 	var is_floating: bool = attack.is_floating or not character_to.get_status("is_on_floor") | 
					
						
							|  |  |  | 	var is_hit_down: bool = attack.is_hit_down | 
					
						
							|  |  |  | 	var is_rebound: bool  = attack.is_rebound | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var is_break_shield: bool = false | 
					
						
							|  |  |  | 	var is_break_stun: bool   = false | 
					
						
							|  |  |  | 	var is_block: bool        = false | 
					
						
							|  |  |  | 	var is_kill: bool         = false | 
					
						
							|  |  |  | 	var is_break_skill: bool  = false | 
					
						
							|  |  |  | 	var pause_time: float     = attack.pause_time | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#造成伤害 | 
					
						
							|  |  |  | 	var damage: float = attack.damage_rate * cfg_from.attack | 
					
						
							|  |  |  | 	if has_shield: | 
					
						
							|  |  |  | 		damage = min(shield, damage) | 
					
						
							|  |  |  | 		character_to.set_status("shield", shield-damage) | 
					
						
							|  |  |  | 		is_break_shield = damage == shield | 
					
						
							|  |  |  | 		if is_break_shield: | 
					
						
							|  |  |  | 			character_to.remove_buff("shield_recover_cd") | 
					
						
							|  |  |  | 			character_to.remove_buff("shield_recover") | 
					
						
							|  |  |  | 			has_shield = false | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			character_to.remove_buff("shield_recover") | 
					
						
							|  |  |  | 			character_to.add_buff("shield_recover_cd", cfg_to.shield.recover_cd) | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		damage = min(hp, damage) | 
					
						
							|  |  |  | 		character_to.set_status("hp", hp-damage) | 
					
						
							|  |  |  | 		is_kill = damage == hp | 
					
						
							|  |  |  | 		if is_kill: | 
					
						
							|  |  |  | 			character_to.add_buff("die", 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#眩晕值累加 | 
					
						
							|  |  |  | 	if not is_stun: | 
					
						
							|  |  |  | 		var stun_damage: float = attack.stun_attack | 
					
						
							|  |  |  | 		var stun               = character_to.get_status("stun") | 
					
						
							|  |  |  | 		var stun_max           = character_to.get_status("stun_max") | 
					
						
							|  |  |  | 		stun_damage = min(stun_max-stun, stun_damage) | 
					
						
							|  |  |  | 		character_to.set_status("stun", stun+stun_damage) | 
					
						
							|  |  |  | 		is_break_stun = stun_damage == stun_max-stun | 
					
						
							|  |  |  | 		if is_break_stun: | 
					
						
							|  |  |  | 			is_stun = true | 
					
						
							|  |  |  | 			character_to.set_status("is_stun", true) | 
					
						
							|  |  |  | 			character_to.remove_buff("stun_recover_cd") | 
					
						
							|  |  |  | 			character_to.remove_buff("stun_recover") | 
					
						
							|  |  |  | 			character_to.add_buff("stun_recover_break_cd", cfg_to.stun.recover_break_cd) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			character_to.remove_buff("stun_recover") | 
					
						
							|  |  |  | 			character_to.add_buff("stun_recover_cd", cfg_to.stun.recover_cd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#mp累加 | 
					
						
							|  |  |  | 	character_from.add_mp(damage * cfg_from.mp.add_rate_attack) | 
					
						
							|  |  |  | 	character_from.remove_buff("mp_recover") | 
					
						
							|  |  |  | 	character_from.add_buff("mp_recover_cd", status.cfg.mp.recover_cd) | 
					
						
							|  |  |  | 	character_to.add_mp(damage * cfg_to.mp.add_rate_hit) | 
					
						
							|  |  |  | 	character_to.remove_buff("mp_recover") | 
					
						
							|  |  |  | 	character_to.add_buff("mp_recover_cd", status.cfg.mp.recover_cd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#硬直等级 | 
					
						
							|  |  |  | 	var break_level_def: int = cfg_to.shield.break_level_on if has_shield else cfg_to.shield.break_level_off | 
					
						
							|  |  |  | 	var break_level_sub: int = clampi(attack.break_level - break_level_def, 0, 3) | 
					
						
							|  |  |  | 	is_break_skill = break_level_sub > 0 | 
					
						
							|  |  |  | 	if is_break_skill: | 
					
						
							|  |  |  | 		#取消技能 | 
					
						
							|  |  |  | 		if character_to.get_status("is_skill_running"): | 
					
						
							|  |  |  | 			character_to.cancel_skill() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		#停止移动 | 
					
						
							|  |  |  | 		character_to.move_stop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		#受击动画 | 
					
						
							|  |  |  | 		var trigger_hit: String = "" | 
					
						
							|  |  |  | 		if is_rebound: trigger_hit="rebound" | 
					
						
							|  |  |  | 		elif is_floating: trigger_hit = "air_hit_down" if is_hit_down else "air_hit_up" | 
					
						
							|  |  |  | 		elif is_stun: trigger_hit="stun_hit" | 
					
						
							|  |  |  | 		elif break_level_sub == 3: trigger_hit="lhit" | 
					
						
							|  |  |  | 		elif break_level_sub == 2: trigger_hit="mhit" | 
					
						
							|  |  |  | 		elif break_level_sub == 1: trigger_hit="hit" | 
					
						
							|  |  |  | 		character_to.set_view_trigger(trigger_hit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		#浮空 击落 强制位移 | 
					
						
							|  |  |  | 		var hit_up_speed: float   = attack.hit_up_speed | 
					
						
							|  |  |  | 		var hit_back_speed: float = attack.hit_back_speed | 
					
						
							|  |  |  | 		if is_floating: | 
					
						
							|  |  |  | 			character_to.add_buff("stagger", -1) | 
					
						
							|  |  |  | 			character_to.add_buff("floating", -1) | 
					
						
							|  |  |  | 			if hit_info.dir.x!=0: | 
					
						
							|  |  |  | 				character_to.set_status("is_right", hit_info.dir.x<0) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			character_to.add_buff("stagger", 1) | 
					
						
							|  |  |  | 			hit_up_speed = 0 | 
					
						
							|  |  |  | 		if is_hit_down: | 
					
						
							|  |  |  | 			character_to.add_buff("hit_down", -1) | 
					
						
							|  |  |  | 		if hit_back_limit_curve: | 
					
						
							|  |  |  | 			var dir: Vector2 = character_from.pos2D() - character_to.pos2D() | 
					
						
							|  |  |  | 			var dist         = clamp(dir.length(), 0, 1) | 
					
						
							|  |  |  | 			hit_back_speed = max(hit_back_limit_curve.sample(dist), hit_back_speed) | 
					
						
							|  |  |  | 		character_to.set_hit_move(hit_info.dir, hit_back_speed, hit_up_speed) | 
					
						
							|  |  |  | 		character_to.add_buff("hit_back", attack.hit_back_duration) | 
					
						
							|  |  |  | 		character_to.add_buff("hit_up", attack.hit_up_duration) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#受击特效 | 
					
						
							|  |  |  | 	match attack.damage_type: | 
					
						
							|  |  |  | 		Enum.EDamageType.Sharp: character_to.cast_particle(ResourceManager.particle_hit_sharp, false) | 
					
						
							|  |  |  | 		Enum.EDamageType.Blunt: character_to.cast_particle(ResourceManager.particle_hit_blunt, false) | 
					
						
							|  |  |  | 		Enum.EDamageType.Ground: character_to.cast_particle(ResourceManager.particle_hit_ground, false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#抖动 | 
					
						
							|  |  |  | 	character_to.add_buff("shake_x", 0.2, true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#闪白 | 
					
						
							|  |  |  | 	character_to.add_buff("flash_white", 0.1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#卡帧 | 
					
						
							|  |  |  | 	character_from.set_pause_time(pause_time) | 
					
						
							|  |  |  | 	character_to.set_pause_time(pause_time) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#全局特效 | 
					
						
							|  |  |  | 	Global.camera_mgr.effect(pause_time) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#伤害跳字 | 
					
						
							|  |  |  | 	character_to.show_hit_damage(damage) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	#状态跳字 | 
					
						
							|  |  |  | 	if is_break_shield: character_to.show_hit_text("Break") | 
					
						
							|  |  |  | 	elif is_break_stun: character_to.show_hit_text("Stun") | 
					
						
							|  |  |  | 	elif not is_break_skill: character_to.show_hit_text("Block") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func add_mp(value: float): | 
					
						
							|  |  |  | 	var mp         = character.get_status("mp") | 
					
						
							|  |  |  | 	var mp_max     = character.get_status("mp_max") | 
					
						
							|  |  |  | 	var mp_sub     = character.get_status("mp_sub") | 
					
						
							|  |  |  | 	var mp_sub_max = character.get_status("mp_sub_max") | 
					
						
							|  |  |  | 	value = min(mp_sub_max-mp_sub, value) | 
					
						
							|  |  |  | 	if value == mp_sub_max-mp_sub: | 
					
						
							|  |  |  | 		var mp_add = min(mp_max-mp, 1) | 
					
						
							|  |  |  | 		if mp_add > 0: | 
					
						
							|  |  |  | 			character.set_status("mp", mp+mp_add) | 
					
						
							|  |  |  | 			character.set_status("mp_sub", 0) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			character.set_status("mp_sub", mp_sub_max) | 
					
						
							|  |  |  | 			character.remove_buff("mp_recover_cd") | 
					
						
							|  |  |  | 			character.remove_buff("mp_recover") | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		character.set_status("mp_sub", mp_sub+value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func check_ground(): | 
					
						
							|  |  |  | 	skill.on_check_ground() |