<!-- NEW7 — TEST 7 · THE WOVEN CITY · part 275 -->
	 *
	 * @param {number} timeScale - The time scale to set.
	 * @return {AnimationAction} A reference to this animation action.
	 */
	setEffectiveTimeScale( timeScale ) {

		this.timeScale = timeScale;
		this._effectiveTimeScale = this.paused ? 0 : timeScale;

		return this.stopWarping();

	}

	/**
	 * Returns the effective time scale of this action.
	 *
	 * @return {number} The effective time scale.
	 */
	getEffectiveTimeScale() {

		return this._effectiveTimeScale;

	}

	/**
	 * Sets the duration for a single loop of this action.
	 *
	 * @param {number} duration - The duration to set.
	 * @return {AnimationAction} A reference to this animation action.
	 */
	setDuration( duration ) {

		this.timeScale = this._clip.duration / duration;

		return this.stopWarping();

	}

	/**
	 * Synchronizes this action with the passed other action.
	 *
	 * @param {AnimationAction} action - The action to sync with.
	 * @return {AnimationAction} A reference to this animation action.
	 */
	syncWith( action ) {

		this.time = action.time;
		this.timeScale = action.timeScale;

		return this.stopWarping();

	}

	/**
	 * Decelerates this animation's speed to `0` within the passed time interval.
	 *
	 * @param {number} duration - The duration.
	 * @return {AnimationAction} A reference to this animation action.
	 */
	halt( duration ) {

		return this.warp( this._effectiveTimeScale, 0, duration );

	}

	/**
	 * Changes the playback speed, within the passed time interval, by modifying
	 * {@link AnimationAction#timeScale} gradually from `startTimeScale` to
	 * `endTimeScale`.
	 *
	 * @param {number} startTimeScale - The start time scale.
	 * @param {number} endTimeScale - The end time scale.
	 * @param {number} duration - The duration.
	 * @return {AnimationAction} A reference to this animation action.
	 */
	warp( startTimeScale, endTimeScale, duration ) {

		const mixer = this._mixer,
			now = mixer.time,
			timeScale = this.timeScale;

		let interpolant = this._timeScaleInterpolant;

		if ( interpolant === null ) {

			interpolant = mixer._lendControlInterpolant();
			this._timeScaleInterpolant = interpolant;

		}

		const times = interpolant.parameterPositions,
			values = interpolant.sampleValues;

		times[ 0 ] = now;
		times[ 1 ] = now + duration;

		values[ 0 ] = startTimeScale / timeScale;
		values[ 1 ] = endTimeScale / timeScale;

		return this;

	}

	/**
	 * Stops any scheduled warping which is applied to this action.
	 *
	 * @return {AnimationAction} A reference to this animation action.
	 */
	stopWarping() {

		const timeScaleInterpolant = this._timeScaleInterpolant;

		if ( timeScaleInterpolant !== null ) {

			this._timeScaleInterpolant = null;
			this._mixer._takeBackControlInterpolant( timeScaleInterpolant );

		}

		this._restoreTimeScale = null;

		return this;

	}

	/**
	 * Returns the animation mixer of this animation action.
	 *
	 * @return {AnimationMixer} The animation mixer.
	 */
	getMixer() {

		return this._mixer;

	}

	/**
	 * Returns the animation clip of this animation action.
	 *
	 * @return {AnimationClip} The animation clip.
	 */
	getClip() {

		return this._clip;

	}

	/**
	 * Returns the root object of this animation action.
	 *
	 * @return {Object3D} The root object.
	 */
	getRoot() {

		return this._localRoot || this._mixer._root;

	}

	// Internal

	_update( time, deltaTime, timeDirection, accuIndex ) {

		// called by the mixer

		if ( ! this.enabled ) {

			// call ._updateWeight() to update ._effectiveWeight

			this._updateWeight( time );
			return;

		}

		const startTime = this._startTime;

		if ( startTime !== null ) {

			// check for scheduled start of action

			const timeRunning = ( time - startTime ) * timeDirection;
			if ( timeRunning < 0 || timeDirection === 0 ) {

				deltaTime = 0;

			} else {


				this._startTime = null; // unschedule
				deltaTime = timeDirection * timeRunning;

			}

		}

		// apply time scale and advance time

		deltaTime *= this._updateTimeScale( time );
		const clipTime = this._updateTime( deltaTime );

		// note: _updateTime may disable the action resulting in
		// an effective weight of 0

		const weight = this._updateWeight( time );

		if ( weight > 0 ) {

			const interpolants = this._interpolants;
			const propertyMixers = this._propertyBindings;

			switch ( this.blendMode ) {

				case AdditiveAnimationBlendMode:

					for ( let j = 0, m = interpolants.length; j !== m; ++ j ) {

						interpolants[ j ].evaluate( clipTime );
						propertyMixers[ j ].accumulateAdditive( weight );

					}

					break;

				case NormalAnimationBlendMode:
				default:

					for ( let j = 0, m = interpolants.length; j !== m; ++ j ) {

						interpolants[ j ].evaluate( clipTime );
						propertyMixers[ j ].accumulate( accuIndex, weight );

					}

			}

		}

	}

	_updateWeight( time ) {

		let weight = 0;

		if ( this.enabled ) {

			weight = this.weight;
			const interpolant = this._weightInterpolant;

			if ( interpolan