try this
extends Node
@onready var attacker1:CharacterBody2D = $attacker1;
@onready var attacker2:CharacterBody2D = $attacker2;
@onready var attacker3:CharacterBody2D = $attacker3;
@onready var target1:Node2D = $"../target1"
@onready var target2:Node2D = $"../target2"
const IMPACT_DISTANCE = 40.
const TOTAL_LOCKON_TIME = 6.
const TOTAL_CHARGE_FORWARD_TIME = 0.5
const TOTAL_COOLDOWN_TIME = 2.
const TURN_SPEED = 5.
const CHARGE_FORWARD_SPEED = 500.
const BOUNCE_SPEED = 10.
const STRAFE_SPEED = 30.
const CHARGE_DECEL_RATE = 30.
const KNOCKBACK_RECOVER_RATE = 45.
static func reflect_bounce(_node:Node2D, _surfVec:Vector2, _knockback:Vector2):
_knockback = (_knockback * 0.6).reflect(_surfVec)
_node.transform.x = _node.transform.x.reflect(_surfVec)
_node.transform.y = _node.transform.y.reflect(_surfVec)
return _knockback
static func move_irregular(delta:float, _node:Node2D):
var _sintime = sin(Time.get_unix_time_from_system());
var _forward_move = _node.transform.x * BOUNCE_SPEED;
var _strafe_move = _node.transform.y * _sintime * STRAFE_SPEED;
_node.global_position += (_forward_move + _strafe_move) * delta;
_node.rotate(delta);
static func bounce_irregular(delta, _node:Node2D, _knockback:Vector2) -> Vector2:
move_irregular(delta, _node);
var _origpos = _node.global_position;
var _newpos = _origpos.clamp( Vector2.ZERO, Vector2(Engine.get_main_loop().root.size));
_node.global_position = _newpos;
if ( !is_equal_approx(_newpos.x, _origpos.x) ):
_knockback = reflect_bounce(_node, Vector2(0., 1.), _knockback)
if ( !is_equal_approx(_newpos.y, _origpos.y) ):
_knockback = reflect_bounce(_node, Vector2(1., 0.), _knockback)
return _knockback;
static func apply_knockback(_delta:float, _node:Node2D, _knockback:Vector2)-> Vector2:
if (!_knockback.is_zero_approx()):
_node.global_position += (_knockback * _delta)
_knockback = _knockback.move_toward(Vector2.ZERO, _delta * KNOCKBACK_RECOVER_RATE);
return _knockback;
return Vector2.ZERO;
var collision:int = 0;
class demo_attacker:
var _locking_on_time_left:float
var _charging_forward_time_left:float
var _cooling_down_time_left:float
var killed:bool
var lockon_while_recovering:bool
func _init():
_locking_on_time_left = 1.
_charging_forward_time_left = 0.
_cooling_down_time_left = 0.
killed = false
lockon_while_recovering = false
func _dec(_value, delta) -> float:
return max(0., _value - delta);
var in_cooldown:bool:
get:
return _cooling_down_time_left > 0.
var is_charging_forward:bool:
get:
return _charging_forward_time_left > 0.
var is_locking_on:bool:
get:
return _locking_on_time_left > 0.
var cooldown_norm_progress:float:
get:
return _cooling_down_time_left / TOTAL_COOLDOWN_TIME;
var charge_forward_norm_progress: float:
get:
return _charging_forward_time_left / TOTAL_COOLDOWN_TIME;
func cooldown( delta) -> bool:
_cooling_down_time_left = _dec( _cooling_down_time_left, delta);
return in_cooldown
func charge_forward( delta):
_charging_forward_time_left = _dec( _charging_forward_time_left, delta);
func lock_on( delta):
_locking_on_time_left = _dec( _locking_on_time_left, delta);
func reset_lockon():
_locking_on_time_left = TOTAL_LOCKON_TIME
_charging_forward_time_left = 0.; _cooling_down_time_left = 0.
func reset_charging_forward():
_charging_forward_time_left = TOTAL_CHARGE_FORWARD_TIME
_locking_on_time_left = 0.; _cooling_down_time_left = 0.
func reset_cooldown():
_cooling_down_time_left = TOTAL_COOLDOWN_TIME
_locking_on_time_left = 0.; _charging_forward_time_left = 0.
@onready var all_attackers:Array[CharacterBody2D] = [ attacker1, attacker2, attacker3 ]
@onready var all_targets:Array[Node2D] = [ target1, target2 ]
var knockbacks:Dictionary
var info:Dictionary
func _ready():
knockbacks = {};
info = {}
for t in all_targets:
knockbacks[t] = Vector2.ZERO;
for t in all_attackers:
knockbacks[t] = Vector2.ZERO;
info[t] = demo_attacker.new()
info[t].reset_cooldown();
func _debug_lockon(_attacker):
var _dbgCol = Color.WHITE;
if (info[_attacker].in_cooldown):
_dbgCol = Color.ORANGE.lerp( Color.WHITE, info[_attacker].cooldown_norm_progress);
else:
if (!info[_attacker].killed):
if (info[_attacker].is_charging_forward):
_dbgCol = Color.YELLOW.lerp( Color.RED, info[_attacker].charge_forward_norm_progress );
else:
_dbgCol = Color.YELLOW;
else:
_dbgCol = Color.GRAY;
_attacker.modulate = _dbgCol
func _physics_process(delta):
for t in all_targets:
knockbacks[t] = apply_knockback(delta, t, bounce_irregular( delta, t, knockbacks[t] ));
for a in all_attackers:
var _info = info[a]
knockbacks[a] = apply_knockback(delta, a, knockbacks[a] );
_debug_lockon(a);
if _info.in_cooldown:
if _info.cooldown(delta):
if _info.lockon_while_recovering:
LOCKON(delta, a, false)
else:
_info.reset_lockon()
if !( _info.killed || _info.in_cooldown):
if _info.is_charging_forward:
CHARGE(delta, a)
elif _info.is_locking_on:
LOCKON(delta, a)
else:
a.velocity = Vector2(0,0)
func LOCKON(delta, _attacker:Node2D, charge_if_close:bool = true):
var _angle_towards = 10000.
for t in all_targets:
var _ang = _attacker.get_angle_to(t.position);
if abs(_ang) < abs(_angle_towards):
_angle_towards = _ang;
if abs(_angle_towards) > 0.01:
info[_attacker].lock_on(delta)
_attacker.rotation = rotate_toward(_attacker.rotation, _attacker.rotation + _angle_towards, delta * TURN_SPEED );
return
if (charge_if_close):
#print(_attacker.name, " got a lock-on, charging forward!")
info[_attacker].reset_charging_forward()
_attacker.velocity = _attacker.transform.x * CHARGE_FORWARD_SPEED
func CHARGE(delta, _attacker:Node2D):
info[_attacker].charge_forward( delta )
var _made_impact:bool = false;
for t in all_targets:
if (( t.global_position - _attacker.global_position ).length() < IMPACT_DISTANCE):
#print (_attacker.name, ": Got 'em! Gonna take a break for a second. (cooldown reset, delay re-lockon)")
knockbacks[t] = _attacker.velocity * 2.5;
knockbacks[_attacker] = -_attacker.velocity * 0.25;
_made_impact = true;
info[_attacker].lockon_while_recovering = false
break;
if (_made_impact || !info[_attacker].is_charging_forward):
if (!_made_impact):
#print (_attacker.name, ": Phew, I'm out of breath from all that charging forward! Where'd he go? (immediate re-lockon)")
info[_attacker].lockon_while_recovering = true
info[_attacker].reset_cooldown()
_attacker.velocity = Vector2.ZERO
else:
_attacker.velocity = _attacker.velocity.move_toward(Vector2.ZERO, delta * CHARGE_DECEL_RATE);
_attacker.move_and_slide()