|
|
|
|
extends AnimationPlayer
|
|
|
|
|
class_name Skill
|
|
|
|
|
|
|
|
|
|
@onready var character: Character = (get_owner() as Character)
|
|
|
|
|
@onready var view: View = (%View as View)
|
|
|
|
|
@onready var status: Status = (%Status as Status)
|
|
|
|
|
@onready var effect: Effect = (%Effect as Effect)
|
|
|
|
|
@onready var buff: Buff = (%Buff as Buff)
|
|
|
|
|
|
|
|
|
|
var skill_dict: Dictionary = {} #name -> skill
|
|
|
|
|
var skill_map: Dictionary = {} #input -> skillCfg[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func init():
|
|
|
|
|
cancel_skill()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func add_skill(action: String, skillCfg: SkillCfg):
|
|
|
|
|
if not action in skill_map:
|
|
|
|
|
skill_map[action] = []
|
|
|
|
|
skill_map[action].append(skillCfg)
|
|
|
|
|
skill_dict[skillCfg.get_res_name()] = skillCfg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func remove_skill(action: String, skillCfg: SkillCfg) -> void:
|
|
|
|
|
if not action in skill_map:
|
|
|
|
|
return
|
|
|
|
|
skill_map[action].filter(func(cfg): return cfg != skillCfg)
|
|
|
|
|
skill_dict.erase(skillCfg.get_res_name())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _process(delta):
|
|
|
|
|
if status.is_skill_running and status.is_pause == is_playing():
|
|
|
|
|
if status.is_pause: pause();
|
|
|
|
|
else: play();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_animation_finished(_anim_name):
|
|
|
|
|
cancel_skill()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func cast_skill_check(cfg: SkillCfg, break_level: Enum.EBreakLevel = Enum.EBreakLevel.Break) -> bool:
|
|
|
|
|
var animation_name: String = "animation_library/%s" % cfg.get_res_name()
|
|
|
|
|
if not has_animation(animation_name):
|
|
|
|
|
print("技能animation不存在", animation_name)
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
if current_animation:
|
|
|
|
|
var pos_offset = fmod(current_animation_position,0.1)
|
|
|
|
|
if pos_offset < 0.02 or pos_offset > 0.08:
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
#检查打断级别
|
|
|
|
|
if cfg.break_level > break_level:
|
|
|
|
|
return false
|
|
|
|
|
#检查姿态
|
|
|
|
|
var stance_from: int = cfg.stance_from
|
|
|
|
|
var check_any_ground: bool = (stance_from == Enum.EStance.GroundAny) and status.is_on_floor
|
|
|
|
|
var check_any_air: bool = (stance_from == Enum.EStance.AirAny) and not status.is_on_floor
|
|
|
|
|
var check_any: bool = (stance_from == Enum.EStance.Any) or check_any_ground or check_any_air
|
|
|
|
|
if (stance_from != int(status.stance)) and not check_any:
|
|
|
|
|
return false
|
|
|
|
|
#检查武器限制
|
|
|
|
|
if cfg is SkillWeaponCfg:
|
|
|
|
|
if cfg.weapon:
|
|
|
|
|
if cfg.weapon != status.weapon_list[status.weapon_index]:
|
|
|
|
|
return false
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func cast_skill_mp_cost(cfg: SkillCfg) -> bool:
|
|
|
|
|
if status.mp < cfg.mp_cost:
|
|
|
|
|
#todo mp不足
|
|
|
|
|
return false
|
|
|
|
|
character.cost_mp(cfg.mp_cost)
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func cast_skill_by_name(name: String, cast_dir: Vector2):
|
|
|
|
|
var cfg: SkillCfg = skill_dict.get(name)
|
|
|
|
|
if cfg:
|
|
|
|
|
cast_skill(cfg, cast_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func cast_skill(cfg: SkillCfg, cast_dir: Vector2, action_key: String = ""):
|
|
|
|
|
var is_no_dir: bool = cast_dir.length() == 0
|
|
|
|
|
if is_no_dir:
|
|
|
|
|
cast_dir = Vector2.RIGHT if status.is_right else Vector2.LEFT
|
|
|
|
|
var is_cast_to_target: bool = status.target and (status.is_lock or is_no_dir)
|
|
|
|
|
if !cfg.free_lock and is_cast_to_target:
|
|
|
|
|
var target: Character = Global.character_mgr.get_character(status.target)
|
|
|
|
|
if target:
|
|
|
|
|
cast_dir = character.pos2D().direction_to(target.pos2D()).normalized()
|
|
|
|
|
|
|
|
|
|
break_skill()
|
|
|
|
|
status.speed_up_rate = -1
|
|
|
|
|
status.is_free_control = false
|
|
|
|
|
status.is_free_turn = false
|
|
|
|
|
status.is_skill_running = true
|
|
|
|
|
status.skill_cfg = cfg
|
|
|
|
|
status.skill_dir = cast_dir
|
|
|
|
|
status.break_level = Enum.EBreakLevel.None
|
|
|
|
|
status.stance = cfg.stance_to
|
|
|
|
|
status.is_charging = cfg.is_charging
|
|
|
|
|
status.skill_action_key = action_key
|
|
|
|
|
status.set_skill_break_level_add(cfg.mp_cost) #todo
|
|
|
|
|
character.set_body_scale(cfg.get_owner())
|
|
|
|
|
if cfg.is_charging:
|
|
|
|
|
buff.add_buff("charging", -1)
|
|
|
|
|
if cast_dir.x != 0: status.is_right = cast_dir.x > 0
|
|
|
|
|
|
|
|
|
|
#预警特效
|
|
|
|
|
match cfg.warn_type:
|
|
|
|
|
Enum.ESkillWarnType.Normal: character.cast_particle(ResourceManager.particle_warn_normal, true)
|
|
|
|
|
Enum.ESkillWarnType.Mid: character.cast_particle(ResourceManager.particle_warn_mid, true)
|
|
|
|
|
Enum.ESkillWarnType.Heavy: character.cast_particle(ResourceManager.particle_warn_heavy, true)
|
|
|
|
|
_: pass
|
|
|
|
|
|
|
|
|
|
var animation_name: String = "animation_library/%s" % cfg.get_res_name()
|
|
|
|
|
play(animation_name, -1, Setting.animation_speed_scale)
|
|
|
|
|
seek(0, true, true)
|
|
|
|
|
print("cast_skill",cfg.get_res_name())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func break_skill():
|
|
|
|
|
stop()
|
|
|
|
|
status.speed_up_rate = 0
|
|
|
|
|
status.skill_move_speed = 0
|
|
|
|
|
status.skill_float_speed = 0
|
|
|
|
|
status.is_free_control = true
|
|
|
|
|
status.is_free_turn = true
|
|
|
|
|
status.is_skill_running = false
|
|
|
|
|
status.skill_cfg = null
|
|
|
|
|
status.break_level = Enum.EBreakLevel.Walk
|
|
|
|
|
status.speed_down_push_rate = 0
|
|
|
|
|
status.skill_move_stop = false
|
|
|
|
|
status.is_speed_y_freeze = false
|
|
|
|
|
status.is_charging = false
|
|
|
|
|
status.charging_level = 0
|
|
|
|
|
status.skill_action_key = ""
|
|
|
|
|
status.set_skill_break_level_add(0)
|
|
|
|
|
buff.remove_buff("charging")
|
|
|
|
|
if status.throw_target != 0:
|
|
|
|
|
var character_to: Character = Global.character_mgr.get_character(status.throw_target)
|
|
|
|
|
if character_to:
|
|
|
|
|
character_to.set_status("is_be_throw", false)
|
|
|
|
|
status.throw_target = 0
|
|
|
|
|
effect.release_effect()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func cancel_skill():
|
|
|
|
|
break_skill()
|
|
|
|
|
character.set_body_scale(status.cfg)
|
|
|
|
|
view.reset()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func on_attack_miss():
|
|
|
|
|
# 攻击未命中时跳帧
|
|
|
|
|
_frame_forward()
|
|
|
|
|
status.set_skill_break_level_add(-1)#todo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func on_hold() -> void:
|
|
|
|
|
_frame_back(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func on_check_ground(frame_offset: int) -> void:
|
|
|
|
|
if status.is_on_floor:
|
|
|
|
|
# 落地检测成功时跳帧
|
|
|
|
|
_frame_forward()
|
|
|
|
|
else:
|
|
|
|
|
_frame_back(frame_offset)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func on_check_charging(charging_level: int) -> void:
|
|
|
|
|
if status.charging_level >= charging_level:
|
|
|
|
|
_frame_forward()
|
|
|
|
|
return
|
|
|
|
|
if status.mp >= charging_level:
|
|
|
|
|
status.charging_level += 1
|
|
|
|
|
_frame_forward()
|
|
|
|
|
return
|
|
|
|
|
_frame_back(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _frame_forward() -> void:
|
|
|
|
|
if not current_animation:
|
|
|
|
|
return
|
|
|
|
|
advance(Setting.animation_frame_rate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _frame_back(frame_offset: int) -> void:
|
|
|
|
|
if not current_animation:
|
|
|
|
|
return
|
|
|
|
|
var frame: int = int(current_animation_position / Setting.animation_frame_rate) - frame_offset
|
|
|
|
|
frame = max(0, frame)
|
|
|
|
|
var frame_pos: float = frame * Setting.animation_frame_rate
|
|
|
|
|
seek(frame_pos- Setting.animation_frame_rate / 5, true, true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func on_cast_sub_character() -> void:
|
|
|
|
|
var cfg: SkillCfg = status.skill_cfg
|
|
|
|
|
if not cfg or not cfg.sub_character:
|
|
|
|
|
return
|
|
|
|
|
var pos: Vector3 = character.pos()
|
|
|
|
|
var dir: Vector2 = status.skill_dir
|
|
|
|
|
Global.character_mgr.create_character(cfg.sub_character, status.team, pos, dir, status.id)
|