class_name Anim extends RefCounted ## Short animations made with tweens, used all over the game: popping, fading, flashing ## and pulsing. ## ## Every animation is short so a quiz never waits for one. With ## [member Settings.reduce_animations] on, nothing moves: each helper jumps straight to ## where the animation would end. ## How long quick feedback, such as a button press, takes. const FAST: float = 0.12 ## How long most animations take. const NORMAL: float = 0.2 ## How long flashes and bigger moments take. const SLOW: float = 0.45 ## Whether animations are on. static func enabled() -> bool: return !Settings.reduce_animations ## A new tween on [param node] for the animation called [param key], stopping an earlier ## one with the same key so they don't fight. Returns null when animations are off or the ## node is not in the tree. static func tween(node: Node, key: StringName = &"anim") -> Tween: stop(node, key) if !enabled() || !node.is_inside_tree(): return null var new_tween: Tween = node.create_tween() node.set_meta(StringName("_tween_" + key), new_tween) return new_tween ## Stops the animation called [param key] on [param node], if one is running. static func stop(node: Node, key: StringName = &"anim") -> void: var meta: StringName = StringName("_tween_" + key) var old: Variant = node.get_meta(meta) if node.has_meta(meta) else null if old is Tween && (old as Tween).is_valid(): (old as Tween).kill() ## Makes [param control] grow from its middle. static func center_pivot(control: Control) -> void: control.pivot_offset = control.size / 2 ## Makes [param control] jump to [param peak] times its size and spring back. static func pop(control: Control, peak: float = 1.3, duration: float = NORMAL) -> void: var pop_tween: Tween = tween(control, &"pop") control.scale = Vector2.ONE if pop_tween == null: return center_pivot(control) control.scale = Vector2.ONE * peak pop_tween.tween_property(control, "scale", Vector2.ONE, duration) \ .set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) ## Makes [param control] grow from [param from] times its size with a bounce. static func bounce_in(control: Control, from: float = 0.4, duration: float = SLOW) -> void: var bounce_tween: Tween = tween(control, &"pop") control.scale = Vector2.ONE if bounce_tween == null: return center_pivot(control) control.scale = Vector2.ONE * from bounce_tween.tween_property(control, "scale", Vector2.ONE, duration * 1.6) \ .set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT) ## Fades [param item] in from invisible. static func fade_in(item: CanvasItem, duration: float = NORMAL) -> void: var fade_tween: Tween = tween(item, &"fade") item.modulate.a = 1.0 if fade_tween == null: return item.modulate.a = 0.0 fade_tween.tween_property(item, "modulate:a", 1.0, duration) ## Slides [param control] in from [param offset] away from where it is, fading in. static func slide_in(control: Control, offset: Vector2, duration: float = NORMAL) -> void: var slide_tween: Tween = tween(control, &"slide") if control.has_meta(&"_slide_home"): control.position = control.get_meta(&"_slide_home") control.modulate.a = 1.0 if slide_tween == null: return var home: Vector2 = control.position control.set_meta(&"_slide_home", home) control.position = home + offset control.modulate.a = 0.0 slide_tween.set_parallel() slide_tween.tween_property(control, "position", home, duration) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) slide_tween.tween_property(control, "modulate:a", 1.0, duration) slide_tween.chain().tween_callback(func() -> void: control.remove_meta(&"_slide_home")) ## Scales [param control] to [param target] times its size, e.g. a little bigger under ## the mouse. static func scale_to(control: Control, target: float, duration: float = FAST) -> void: var scale_tween: Tween = tween(control, &"scale") if scale_tween == null: control.scale = Vector2.ONE return center_pivot(control) scale_tween.tween_property(control, "scale", Vector2.ONE * target, duration) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) ## Lights up [param container] with [param colour] for a moment, e.g. a row that just ## changed. A coloured layer is laid over the container and fades away. static func flash(container: Container, colour: Color, duration: float = SLOW) -> void: if !enabled() || !container.is_inside_tree(): return var layer: ColorRect = _layer(container, colour) var flash_tween: Tween = layer.create_tween() flash_tween.tween_property(layer, "color:a", 0.0, duration) \ .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) flash_tween.tween_callback(layer.queue_free) ## Keeps [param container] glowing in [param colour], softly pulsing, until the returned ## layer is freed. With animations off the glow stays still. static func pulse(container: Container, colour: Color) -> ColorRect: var layer: ColorRect = _layer(container, colour) layer.name = "Pulse" if enabled(): var pulse_tween: Tween = layer.create_tween().set_loops() pulse_tween.tween_property(layer, "color:a", colour.a * 0.3, 0.6) \ .set_trans(Tween.TRANS_SINE) pulse_tween.tween_property(layer, "color:a", colour.a, 0.6) \ .set_trans(Tween.TRANS_SINE) return layer # A layer of [param colour] over the whole of [param container] that lets clicks through static func _layer(container: Container, colour: Color) -> ColorRect: var layer: ColorRect = ColorRect.new() layer.color = colour layer.mouse_filter = Control.MOUSE_FILTER_IGNORE container.add_child(layer) return layer