// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2014/07/12 16:24
//
// License Copyright (c) Daniele Giardini.
// This work is subject to the terms at http://dotween.demigiant.com/license.php
using System;
using System.Collections;
#if COMPATIBLE
using DOVector3 = DG.Tweening.Core.Surrogates.Vector3Wrapper;
using DOQuaternion = DG.Tweening.Core.Surrogates.QuaternionWrapper;
#else
using DOVector3 = UnityEngine.Vector3;
using DOQuaternion = UnityEngine.Quaternion;
#endif
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins.Core;
using DG.Tweening.Plugins.Options;
using UnityEngine;
namespace DG.Tweening
{
///
/// Animates a single value
///
public abstract class Tweener : Tween
{
// TRUE when start value has been changed via From or ChangeStart/Values (allows DoStartup to take it into account).
// Reset by TweenerCore
internal bool hasManuallySetStartValue;
internal bool isFromAllowed = true; // if FALSE from tweens won't be allowed. Reset by TweenerCore
internal Tweener() {}
// ===================================================================================
// ABSTRACT METHODS ------------------------------------------------------------------
/// Changes the start value of a tween and rewinds it (without pausing it).
/// Has no effect with tweens that are inside Sequences
/// The new start value
/// If bigger than 0 applies it as the new tween duration
public abstract Tweener ChangeStartValue(object newStartValue, float newDuration = -1);
/// Changes the end value of a tween and rewinds it (without pausing it).
/// Has no effect with tweens that are inside Sequences
/// The new end value
/// If bigger than 0 applies it as the new tween duration
/// If TRUE the start value will become the current target's value, otherwise it will stay the same
public abstract Tweener ChangeEndValue(object newEndValue, float newDuration = -1, bool snapStartValue = false);
/// Changes the end value of a tween and rewinds it (without pausing it).
/// Has no effect with tweens that are inside Sequences
/// The new end value
/// If TRUE the start value will become the current target's value, otherwise it will stay the same
public abstract Tweener ChangeEndValue(object newEndValue, bool snapStartValue);
/// Changes the start and end value of a tween and rewinds it (without pausing it).
/// Has no effect with tweens that are inside Sequences
/// The new start value
/// The new end value
/// If bigger than 0 applies it as the new tween duration
public abstract Tweener ChangeValues(object newStartValue, object newEndValue, float newDuration = -1);
internal abstract Tweener SetFrom(bool relative);
// ===================================================================================
// INTERNAL METHODS ------------------------------------------------------------------
// CALLED BY DOTween when spawning/creating a new Tweener.
// Returns TRUE if the setup is successful
internal static bool Setup(
TweenerCore t, DOGetter getter, DOSetter setter, T2 endValue, float duration, ABSTweenPlugin plugin = null
)
where TPlugOptions : struct, IPlugOptions
{
if (plugin != null) t.tweenPlugin = plugin;
else {
if (t.tweenPlugin == null) t.tweenPlugin = PluginsManager.GetDefaultPlugin();
if (t.tweenPlugin == null) {
// No suitable plugin found. Kill
Debugger.LogError("No suitable plugin found for this type");
return false;
}
}
t.getter = getter;
t.setter = setter;
t.endValue = endValue;
t.duration = duration;
// Defaults
t.autoKill = DOTween.defaultAutoKill;
t.isRecyclable = DOTween.defaultRecyclable;
t.easeType = DOTween.defaultEaseType; // Set to INTERNAL_Zero in case of 0 duration, but in DoStartup
t.easeOvershootOrAmplitude = DOTween.defaultEaseOvershootOrAmplitude;
t.easePeriod = DOTween.defaultEasePeriod;
t.loopType = DOTween.defaultLoopType;
t.isPlaying = DOTween.defaultAutoPlay == AutoPlay.All || DOTween.defaultAutoPlay == AutoPlay.AutoPlayTweeners;
return true;
}
// CALLED BY TweenerCore
// Returns the elapsed time minus delay in case of success,
// -1 if there are missing references and the tween needs to be killed
internal static float DoUpdateDelay(TweenerCore t, float elapsed) where TPlugOptions : struct, IPlugOptions
{
float tweenDelay = t.delay;
if (elapsed > tweenDelay) {
// Delay complete
t.elapsedDelay = tweenDelay;
t.delayComplete = true;
return elapsed - tweenDelay;
}
t.elapsedDelay = elapsed;
return 0;
}
// CALLED VIA Tween the moment the tween starts, AFTER any delay has elapsed
// (unless it's a FROM tween, in which case it will be called BEFORE any eventual delay).
// Returns TRUE in case of success,
// FALSE if there are missing references and the tween needs to be killed
internal static bool DoStartup(TweenerCore t) where TPlugOptions : struct, IPlugOptions
{
t.startupDone = true;
// Special startup operations
if (t.specialStartupMode != SpecialStartupMode.None) {
if (!DOStartupSpecials(t)) return false;
}
if (!t.hasManuallySetStartValue) {
// Take start value from current target value
if (DOTween.useSafeMode) {
try {
t.startValue = t.tweenPlugin.ConvertToStartValue(t, t.getter());
} catch (Exception e) {
if (Debugger.logPriority >= 1) {
Debugger.LogWarning(string.Format(
"Tween startup failed (NULL target/property - {0}): the tween will now be killed ► {1}", e.TargetSite, e.Message
), t);
}
DOTween.safeModeReport.Add(SafeModeReport.SafeModeReportType.StartupFailure);
return false; // Target/field doesn't exist: kill tween
}
} else t.startValue = t.tweenPlugin.ConvertToStartValue(t, t.getter());
}
if (t.isRelative) t.tweenPlugin.SetRelativeEndValue(t);
t.tweenPlugin.SetChangeValue(t);
// Duration based startup operations
DOStartupDurationBased(t);
// Applied here so that the eventual duration derived from a speedBased tween has been set
if (t.duration <= 0) t.easeType = Ease.INTERNAL_Zero;
return true;
}
// CALLED BY TweenerCore
internal static TweenerCore DoChangeStartValue(
TweenerCore t, T2 newStartValue, float newDuration
) where TPlugOptions : struct, IPlugOptions
{
t.hasManuallySetStartValue = true;
t.startValue = newStartValue;
if (t.startupDone) {
if (t.specialStartupMode != SpecialStartupMode.None) {
if (!DOStartupSpecials(t)) return null;
}
t.tweenPlugin.SetChangeValue(t);
}
if (newDuration > 0) {
t.duration = newDuration;
if (t.startupDone) DOStartupDurationBased(t);
}
// Force rewind
DoGoto(t, 0, 0, UpdateMode.IgnoreOnUpdate);
return t;
}
// CALLED BY TweenerCore
internal static TweenerCore DoChangeEndValue(
TweenerCore t, T2 newEndValue, float newDuration, bool snapStartValue
) where TPlugOptions : struct, IPlugOptions
{
t.endValue = newEndValue;
t.isRelative = false;
if (t.startupDone) {
if (t.specialStartupMode != SpecialStartupMode.None) {
if (!DOStartupSpecials(t)) return null;
}
if (snapStartValue) {
// Reassign startValue with current target's value
if (DOTween.useSafeMode) {
try {
t.startValue = t.tweenPlugin.ConvertToStartValue(t, t.getter());
} catch (Exception e) {
// Target/field doesn't exist: kill tween
if (Debugger.logPriority >= 1) {
Debugger.LogWarning(string.Format(
"Target or field is missing/null ({0}) ► {1}\n\n{2}\n\n", e.TargetSite, e.Message, e.StackTrace
), t);
}
TweenManager.Despawn(t);
DOTween.safeModeReport.Add(SafeModeReport.SafeModeReportType.TargetOrFieldMissing);
return null;
}
} else t.startValue = t.tweenPlugin.ConvertToStartValue(t, t.getter());
}
t.tweenPlugin.SetChangeValue(t);
}
if (newDuration > 0) {
t.duration = newDuration;
if (t.startupDone) DOStartupDurationBased(t);
}
// Force rewind
DoGoto(t, 0, 0, UpdateMode.IgnoreOnUpdate);
return t;
}
internal static TweenerCore DoChangeValues(
TweenerCore t, T2 newStartValue, T2 newEndValue, float newDuration
) where TPlugOptions : struct, IPlugOptions
{
t.hasManuallySetStartValue = true;
t.isRelative = t.isFrom = false;
t.startValue = newStartValue;
t.endValue = newEndValue;
if (t.startupDone) {
if (t.specialStartupMode != SpecialStartupMode.None) {
if (!DOStartupSpecials(t)) return null;
}
t.tweenPlugin.SetChangeValue(t);
}
if (newDuration > 0) {
t.duration = newDuration;
if (t.startupDone) DOStartupDurationBased(t);
}
// Force rewind
DoGoto(t, 0, 0, UpdateMode.IgnoreOnUpdate);
return t;
}
// Commands shared by DOStartup/ChangeStart/End/Values if the tween has already started up
// and thus some settings needs to be reapplied.
// Returns TRUE in case of SUCCESS, FALSE if there were managed errors
static bool DOStartupSpecials(TweenerCore t) where TPlugOptions : struct, IPlugOptions
{
try {
switch (t.specialStartupMode) {
case SpecialStartupMode.SetLookAt:
if (!SpecialPluginsUtils.SetLookAt(t as TweenerCore)) return false;
break;
case SpecialStartupMode.SetPunch:
if (!SpecialPluginsUtils.SetPunch(t as TweenerCore)) return false;
break;
case SpecialStartupMode.SetShake:
if (!SpecialPluginsUtils.SetShake(t as TweenerCore)) return false;
break;
case SpecialStartupMode.SetCameraShakePosition:
if (!SpecialPluginsUtils.SetCameraShakePosition(t as TweenerCore)) return false;
break;
}
return true;
} catch {
// Error in SpecialPluginUtils (usually due to target being destroyed)
return false;
}
}
static void DOStartupDurationBased(TweenerCore t) where TPlugOptions : struct, IPlugOptions
{
if (t.isSpeedBased) t.duration = t.tweenPlugin.GetSpeedBasedDuration(t.plugOptions, t.duration, t.changeValue);
t.fullDuration = t.loops > -1 ? t.duration * t.loops : Mathf.Infinity;
}
}
}