246 lines
5.9 KiB
GDScript3
246 lines
5.9 KiB
GDScript3
|
# Copyright (c) 2019 Lawnjelly
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
# of this software and associated documentation files (the "Software"), to deal
|
||
|
# in the Software without restriction, including without limitation the rights
|
||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
# copies of the Software, and to permit persons to whom the Software is
|
||
|
# furnished to do so, subject to the following conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included in all
|
||
|
# copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
# SOFTWARE.
|
||
|
|
||
|
extends Node2D
|
||
|
|
||
|
export(NodePath) var target : NodePath setget set_target, get_target
|
||
|
|
||
|
var _m_Target : Node2D
|
||
|
|
||
|
var m_Pos_curr : Vector2 = Vector2()
|
||
|
var m_Pos_prev : Vector2 = Vector2()
|
||
|
|
||
|
var m_Angle_curr : float
|
||
|
var m_Angle_prev : float
|
||
|
|
||
|
var m_Scale_curr : Vector2 = Vector2()
|
||
|
var m_Scale_prev : Vector2 = Vector2()
|
||
|
|
||
|
const SF_ENABLED = 1 << 0
|
||
|
const SF_TRANSLATE = 1 << 1
|
||
|
const SF_ROTATE = 1 << 2
|
||
|
const SF_SCALE = 1 << 3
|
||
|
const SF_GLOBAL_IN = 1 << 4
|
||
|
const SF_GLOBAL_OUT = 1 << 5
|
||
|
const SF_DIRTY = 1 << 6
|
||
|
const SF_INVISIBLE = 1 << 7
|
||
|
|
||
|
export (int, FLAGS, "enabled", "translate", "rotate", "scale", "global in", "global out") var flags : int = SF_ENABLED | SF_TRANSLATE setget _set_flags, _get_flags
|
||
|
|
||
|
##########################################################################################
|
||
|
# USER FUNCS
|
||
|
|
||
|
# call this on e.g. starting a level, AFTER moving the target
|
||
|
# so we can update both the previous and current values
|
||
|
func teleport():
|
||
|
var temp_flags = flags
|
||
|
_SetFlags(SF_TRANSLATE | SF_ROTATE | SF_SCALE)
|
||
|
|
||
|
_RefreshTransform()
|
||
|
m_Pos_prev = m_Pos_curr
|
||
|
m_Angle_prev = m_Angle_curr
|
||
|
m_Scale_prev = m_Scale_curr
|
||
|
|
||
|
# call frame upate to make sure all components of the node are set
|
||
|
_process(0)
|
||
|
|
||
|
# get back the old flags
|
||
|
flags = temp_flags
|
||
|
|
||
|
func set_enabled(var bEnable : bool):
|
||
|
_ChangeFlags(SF_ENABLED, bEnable)
|
||
|
_SetProcessing()
|
||
|
|
||
|
func is_enabled():
|
||
|
return _TestFlags(SF_ENABLED)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
##########################################################################################
|
||
|
|
||
|
|
||
|
func _ready():
|
||
|
m_Angle_curr = 0
|
||
|
m_Angle_prev = 0
|
||
|
pass
|
||
|
|
||
|
func set_target(new_value):
|
||
|
target = new_value
|
||
|
if is_inside_tree():
|
||
|
_FindTarget()
|
||
|
|
||
|
func get_target():
|
||
|
return target
|
||
|
|
||
|
func _set_flags(new_value):
|
||
|
flags = new_value
|
||
|
# we may have enabled or disabled
|
||
|
_SetProcessing()
|
||
|
|
||
|
func _get_flags():
|
||
|
return flags
|
||
|
|
||
|
func _SetProcessing():
|
||
|
var bEnable = _TestFlags(SF_ENABLED)
|
||
|
if _TestFlags(SF_INVISIBLE):
|
||
|
bEnable = false
|
||
|
|
||
|
set_process(bEnable);
|
||
|
set_physics_process(bEnable);
|
||
|
pass
|
||
|
|
||
|
func _enter_tree():
|
||
|
# might have been moved
|
||
|
_FindTarget()
|
||
|
pass
|
||
|
|
||
|
func _notification(what):
|
||
|
match what:
|
||
|
# invisible turns off processing
|
||
|
NOTIFICATION_VISIBILITY_CHANGED:
|
||
|
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
|
||
|
_SetProcessing()
|
||
|
|
||
|
|
||
|
|
||
|
func _RefreshTransform():
|
||
|
_ClearFlags(SF_DIRTY);
|
||
|
|
||
|
if _HasTarget() == false:
|
||
|
return
|
||
|
|
||
|
if _TestFlags(SF_GLOBAL_IN):
|
||
|
if _TestFlags(SF_TRANSLATE):
|
||
|
m_Pos_prev = m_Pos_curr
|
||
|
m_Pos_curr = _m_Target.get_global_position()
|
||
|
|
||
|
if _TestFlags(SF_ROTATE):
|
||
|
m_Angle_prev = m_Angle_curr
|
||
|
m_Angle_curr = _m_Target.get_global_rotation()
|
||
|
|
||
|
if _TestFlags(SF_SCALE):
|
||
|
m_Scale_prev = m_Scale_curr
|
||
|
m_Scale_curr = _m_Target.get_global_scale()
|
||
|
else:
|
||
|
if _TestFlags(SF_TRANSLATE):
|
||
|
m_Pos_prev = m_Pos_curr
|
||
|
m_Pos_curr = _m_Target.get_position()
|
||
|
|
||
|
if _TestFlags(SF_ROTATE):
|
||
|
m_Angle_prev = m_Angle_curr
|
||
|
m_Angle_curr = _m_Target.get_rotation()
|
||
|
|
||
|
if _TestFlags(SF_SCALE):
|
||
|
m_Scale_prev = m_Scale_curr
|
||
|
m_Scale_curr = _m_Target.get_scale()
|
||
|
|
||
|
|
||
|
func _FindTarget():
|
||
|
_m_Target = null
|
||
|
if target.is_empty():
|
||
|
return
|
||
|
|
||
|
_m_Target = get_node(target)
|
||
|
|
||
|
if _m_Target is Node2D:
|
||
|
return
|
||
|
|
||
|
_m_Target = null
|
||
|
|
||
|
|
||
|
func _HasTarget()->bool:
|
||
|
if _m_Target == null:
|
||
|
return false
|
||
|
|
||
|
# has not been deleted?
|
||
|
if is_instance_valid(_m_Target):
|
||
|
return true
|
||
|
|
||
|
_m_Target = null
|
||
|
return false
|
||
|
|
||
|
|
||
|
func _process(_delta):
|
||
|
if _TestFlags(SF_DIRTY):
|
||
|
_RefreshTransform()
|
||
|
|
||
|
var f = Engine.get_physics_interpolation_fraction()
|
||
|
|
||
|
|
||
|
if _TestFlags(SF_GLOBAL_OUT):
|
||
|
# translate
|
||
|
if _TestFlags(SF_TRANSLATE):
|
||
|
set_global_position(m_Pos_prev.linear_interpolate(m_Pos_curr, f))
|
||
|
|
||
|
# rotate
|
||
|
if _TestFlags(SF_ROTATE):
|
||
|
var r = _LerpAngle(m_Angle_prev, m_Angle_curr, f)
|
||
|
set_global_rotation(r)
|
||
|
|
||
|
if _TestFlags(SF_SCALE):
|
||
|
set_global_scale(m_Scale_prev.linear_interpolate(m_Scale_curr, f))
|
||
|
else:
|
||
|
# translate
|
||
|
if _TestFlags(SF_TRANSLATE):
|
||
|
set_position(m_Pos_prev.linear_interpolate(m_Pos_curr, f))
|
||
|
|
||
|
# rotate
|
||
|
if _TestFlags(SF_ROTATE):
|
||
|
var r = _LerpAngle(m_Angle_prev, m_Angle_curr, f)
|
||
|
set_rotation(r)
|
||
|
|
||
|
if _TestFlags(SF_SCALE):
|
||
|
set_scale(m_Scale_prev.linear_interpolate(m_Scale_curr, f))
|
||
|
|
||
|
pass
|
||
|
|
||
|
func _physics_process(_delta):
|
||
|
# take care of the special case where multiple physics ticks
|
||
|
# occur before a frame .. the data must flow!
|
||
|
if _TestFlags(SF_DIRTY):
|
||
|
_RefreshTransform()
|
||
|
|
||
|
_SetFlags(SF_DIRTY)
|
||
|
pass
|
||
|
|
||
|
func _LerpAngle(var from : float, var to : float, var weight : float)->float:
|
||
|
return from + _ShortAngleDist(from, to) * weight
|
||
|
|
||
|
func _ShortAngleDist(var from : float, var to : float)->float:
|
||
|
var max_angle : float = 2 * PI
|
||
|
var diff : float = fmod(to-from, max_angle)
|
||
|
return fmod(2.0 * diff, max_angle) - diff
|
||
|
|
||
|
func _SetFlags(var f):
|
||
|
flags |= f
|
||
|
|
||
|
func _ClearFlags(var f):
|
||
|
flags &= ~f
|
||
|
|
||
|
func _TestFlags(var f):
|
||
|
return (flags & f) == f
|
||
|
|
||
|
func _ChangeFlags(var f, var bSet):
|
||
|
if bSet:
|
||
|
_SetFlags(f)
|
||
|
else:
|
||
|
_ClearFlags(f)
|