/* ==========================================================================
 * Len'en cut-in and spellcard title display library by RemindMeToCheck, 2018
 * Len'en assets (pictures and sound effects) are © JynX.
 *
 * Refer to TRMTCLenenCutin's docs further below for usage instructions.
 *
 * Any and all Len'en audio assets are completely unmodified. Textures,
 * however, were resized to sizes powers of two for better quality in DNH.
 *
 * The file "img/spell_016.png" is a merged version of all "img/spell_001.png"
 * through "img/spell_008.png" spell cut-in pictures.
 * ========================================================================== */

// Prefixing with RMTC_ to avoid naming clashes; this is a LIBRARY after all!
let RMTC_CD = GetCurrentScriptDirectory();
let t;

// Modify these constants/paths to match your file/directory/script's layout
let RMTC_LENEN_CUTIN_TEXT_TEXTURE = RMTC_CD ~ "img/spell_000.png";
let RMTC_LENEN_CUTIN_CHAIN_TEXTURE = RMTC_CD ~ "img/spell_009.png";
let RMTC_LENEN_CUTIN_PIECE_TEXTURES = [
  RMTC_CD ~ "img/spell_001.png",
  RMTC_CD ~ "img/spell_002.png",
  RMTC_CD ~ "img/spell_003.png",
  RMTC_CD ~ "img/spell_004.png",
  RMTC_CD ~ "img/spell_005.png",
  RMTC_CD ~ "img/spell_006.png",
  RMTC_CD ~ "img/spell_007.png",
  RMTC_CD ~ "img/spell_008.png",
  RMTC_CD ~ "img/spell_016.png" // <-- all previous pieces in one file
                                //     all other textures are 100% unmodified
];
let RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES = [
  RMTC_CD ~ "img/spell_010.png",
  RMTC_CD ~ "img/spell_011.png",
  RMTC_CD ~ "img/spell_012.png",
  RMTC_CD ~ "img/spell_013.png",
  RMTC_CD ~ "img/spell_014.png",
  RMTC_CD ~ "img/spell_015.png"
];
let RMTC_LENEN_CUTIN_SE = RMTC_CD ~ "se/spell_0.wav";

// Mandatory setup goes here.
// Preload all of the above textures here or elsewhere if needed be.
LoadSound(RMTC_LENEN_CUTIN_SE);

/*
 * Creates a Len'en spellcard cut-in animated effect and/or a spellcard title.
 *
 * -- bossTexture: path to your (Len'en?) boss' picture. If it's a "" string,
 *    then the cut-in effect won't play, but the boss' title may be displayed.
 * -- title: your spellcard's name. If it's a "" string, then no spellcard name
 *    will be displayed, but your boss cut-in effect may be displayed.
 * -- playSE: if true, Len'en's standard spellcard SE will be played.
 * -- red: red color value for the cut-in effect and text's background image
 * -- green: green color value for the cut-in effect and text's background image
 * -- blue: blue color value for the cut-in effect and text's background image
 *
 * Author: RemindMeToCheck, 2018.
 * Assets (pictures and audio files) are © JynX, Len'en's creator!
 */
task TRMTCLenenCutin(bossTexture, title, playSE, red, green, blue) {
  if (bossTexture == "" && title != "") {
    TRMTCLenenCutinTitle(title, red, green, blue);

    if (playSE) {
      PlaySE(RMTC_LENEN_CUTIN_SE);
    }

    return;
  } else if (title == "" && bossTexture != "") {
    TRMTCLenenCutinPortrait(bossTexture, red, green, blue);

    if (playSE) {
      PlaySE(RMTC_LENEN_CUTIN_SE);
    }

    return;
  } else if (title == "" && bossTexture == "") {
    return;
  }

  TRMTCLenenCutinPortrait(bossTexture, red, green, blue);
  TRMTCLenenCutinTitle(title, red, green, blue);

  if (playSE) {
    PlaySE(RMTC_LENEN_CUTIN_SE);
  }
}

/*
 * Displays a spellcard's title.
 *
 * Used mainly by TRMTCLenenCutin(), but it can also be used directly.
 * This function plays no audio.
 *
 * -- title: the spellcard's title. If a "" string, this function does nothing.
 *
 * Author: RemindMeToCheck, 2018.
 * Assets (pictures and audio files) are © JynX, Len'en's creator!
 */
task TRMTCLenenCutinTitle(title, red, green, blue) {
  if (title == "") {
    return;
  }

  let background = ObjPrim_Create(OBJ_SPRITE_2D);
  Obj_SetRenderPriorityI(background, 79);
  ObjRender_SetColor(background, red, green, blue);
  ObjPrim_SetTexture(background, RMTC_LENEN_CUTIN_TEXT_TEXTURE);
  ObjSprite2D_SetSourceRect(background, 0, 0, 255, 40);
  ObjSprite2D_SetDestRect(background, 0, 0, 255, 40);

  let text = ObjText_Create();
  let textXOffset = -12;
  let textYOffset = 10;
  Obj_SetRenderPriorityI(text, 79);
  ObjText_SetText(text, title);
  ObjText_SetMaxWidth(text, GetStgFrameWidth());
  ObjText_SetHorizontalAlignment(text, ALIGNMENT_RIGHT);
  ObjText_SetFontColorTop(text, 255, 255, 255);
  ObjText_SetFontColorBottom(text, 255, 255, 255);
  ObjText_SetFontType(text, "MS UI Gothic");
  ObjText_SetFontSize(text, 16);
  ObjText_SetFontBorderWidth(text, 1);
  ObjText_SetFontBorderType(text, BORDER_FULL);
  ObjText_SetFontBorderColor(text, 0, 0, 0);
  ObjText_SetSidePitch(text, 1);

  let x = [
    GetStgFrameWidth() - 255,
    GetStgFrameWidth() - 255
  ];

  let y = [
    GetStgFrameHeight() * 0.91,
    GetStgFrameHeight() * 0.08
  ];

  // Set starting positions
  ObjRender_SetPosition(background, x[0], y[0], 0);
  ObjRender_SetPosition(text, textXOffset, y[0] + textYOffset, 0);

  // Fade in and scale down
  t = 70;
  TRMTCLinearRenderAlphaFade(background, 0, 255, t-30);
  TRMTCLinearRenderAlphaFade(text, 0, 255, t-30);
  TRMTCLinearRenderScaleFade(background, -3, 1, 1, 1, 1, 1, t-60);
  TRMTCLinearRenderScaleFade(text, -3, 1, 1, 1, 1, 1, t-60);
  RMTCwait(t);

  // Move it up
  
  t = 45;
  TRMTCLinearRenderPositionFade(background, x[0], x[1], y[0], y[1], 0, 0, t);
  TRMTCLinearRenderPositionFade(text, textXOffset, textXOffset, y[0] + textYOffset, y[1] + textYOffset, 0, 0, t);
  RMTCwait(t);

  // Lower the text's alpha if the player moves closer to it
  task TRMTCLenenCutinTextAlpha() {
    let alpha;

    while (!Obj_IsDeleted(background) && !Obj_IsDeleted(text)) {
      alpha = max(0.125, GetPlayerY() / GetStgFrameHeight()) * 255;

      ObjRender_SetAlpha(background, alpha);
      ObjRender_SetAlpha(text, alpha);

      yield;
    }
  }

  TRMTCLenenCutinTextAlpha();
}

/*
 * Creates an animated Len'en boss cut-in effect.
 *
 * Used mainly by TRMTCLenenCutin(), but it can also be used directly.
 * This function plays no audio.
 *
 * -- bossTexture: path to your (Len'en?) boss' picture. If it's a "" string,
 *    then this function does absolutely nothing.
 * -- red: red color value for the cut-in effect.
 * -- green: green color value for the cut-in effect.
 * -- blue: blue color value for the cut-in effect.
 *
 * Author: RemindMeToCheck, 2018.
 * Assets (pictures and audio files) are © JynX, Len'en's creator!
 */
task TRMTCLenenCutinPortrait(bossTexture, red, green, blue) {
	if (bossTexture == "") {
		return;
	}

	task TRMTCLenenCutinBossFollow(sc, r, al, n) {
		let portrai = ObjPrim_Create(OBJ_SPRITE_2D);
		ObjRender_SetColor(portrai, red, green, blue);
		ObjRender_SetScaleXYZ(portrai, sc, sc, 1);
		ObjRender_SetPosition(portrai, GetStgFrameWidth() / 2, GetStgFrameHeight() / 2, 0);
		ObjRender_SetAlpha(portrai, 0);  ObjRender_SetBlendType(portrai, BLEND_ADD_ARGB);
		
		Obj_SetRenderPriorityI(portrai, r);
		ObjPrim_SetTexture(portrai, bossTexture);
		ObjSprite2D_SetSourceRect(portrai, 0, 0, GetTextureWidth(bossTexture), GetTextureHeight(bossTexture));
		ObjSprite2D_SetDestCenter(portrai);

		let x = [
		  GetStgFrameWidth() + GetTextureWidth(bossTexture) / 2,
		  GetStgFrameWidth() * 0.55,
		  GetStgFrameWidth() * 0.45,
		  -GetTextureWidth(bossTexture) / 2
		];

		let y = [
		  GetStgFrameHeight() * 0.5 + 64,
		  GetStgFrameHeight() * 0.5,
		  GetStgFrameHeight() * 0.5 - 16,
		  GetStgFrameHeight() * 0.5 - 64
		];

		// Fade in and move in from the right side
		t = 20;
		TRMTCLinearRenderAlphaFade(portrai, 0, al, t);
		TRMTCLinearRenderPositionFade(portrai, x[0]+n, x[1]+n, y[0], y[1], 0, 0, t);
		RMTCwait(t);

		// Slowly move the portrait left and a bit up
		t = 60*2.35;
		TRMTCLinearRenderPositionFade(portrai, x[1]+n, x[2]+n, y[1], y[2], 0, 0, t+25);
		RMTCwait(t);

		// Move left, off-screen and fade out for the remaining frames
		t = 20;
		TRMTCLinearRenderAlphaFade(portrai, al, 0, t+30);
		TRMTCLinearRenderPositionFade(portrai, x[2]+n, x[3]+n, y[2], y[3], 0, 0, t);
		RMTCwait(t);

		// Cleanup
		Obj_Delete(portrai);
  }
	
	  task TRMTCLenenCutinBoss() {
		let portrait = ObjPrim_Create(OBJ_SPRITE_2D);
		ObjRender_SetScaleXYZ(portrait, 0.8, 0.8, 1);
		ObjRender_SetPosition(portrait, GetStgFrameWidth() / 2, GetStgFrameHeight() / 2, 0);
		ObjRender_SetAlpha(portrait, 0);
		Obj_SetRenderPriorityI(portrait, 79);
		ObjPrim_SetTexture(portrait, bossTexture);
		ObjSprite2D_SetSourceRect(portrait, 0, 0, GetTextureWidth(bossTexture), GetTextureHeight(bossTexture));
		ObjSprite2D_SetDestCenter(portrait);

		let x = [
		  GetStgFrameWidth() + GetTextureWidth(bossTexture) / 2,
		  GetStgFrameWidth() * 0.55,
		  GetStgFrameWidth() * 0.45,
		  -GetTextureWidth(bossTexture) / 2
		];

		let y = [
		  GetStgFrameHeight() * 0.5 + 64,
		  GetStgFrameHeight() * 0.5,
		  GetStgFrameHeight() * 0.5 - 16,
		  GetStgFrameHeight() * 0.5 - 64
		];

		// Fade in and move in from the right side
		t = 20;
		TRMTCLinearRenderAlphaFade(portrait, 0, 224, t);
		TRMTCLinearRenderPositionFade(portrait, x[0], x[1], y[0], y[1], 0, 0, t);
		RMTCwait(t);

		// Slowly move the portrait left and a bit up
		t = 60*2.35;
		TRMTCLinearRenderPositionFade(portrait, x[1], x[2], y[1], y[2], 0, 0, t+25);
		RMTCwait(t);

		// Move left, off-screen and fade out for the remaining frames
		t = 20;
		TRMTCLinearRenderAlphaFade(portrait, 224, 0, t+30);
		TRMTCLinearRenderPositionFade(portrait, x[2], x[3], y[2], y[3], 0, 0, t);
		RMTCwait(t);

		// Cleanup
		Obj_Delete(portrait);
	  }

	  task TRMTCLenenCutinPieces() {
		let pieces = [];
		let piece;

		let scale;
		let sidesXDisplacementSign;
		let sticcRotation;
		let sidesRotation;
		let alpha;

		let frame;
		let i;

		// Initialize all textures, including the "combined" one
		ascent (i in 0 .. length(RMTC_LENEN_CUTIN_PIECE_TEXTURES)) {
		  piece = ObjPrim_Create(OBJ_SPRITE_2D);
		  Obj_SetVisible(piece, false);
		  Obj_SetRenderPriorityI(piece, 71 + i);
		  ObjRender_SetScaleXYZ(piece, 0.865, 0.865, 1);
		  ObjRender_SetPosition(piece, GetStgFrameWidth() / 2, GetStgFrameHeight() / 2, 0);
		  ObjRender_SetAlpha(piece, 192);
		  ObjRender_SetColor(piece, red, green, blue);
		  ObjPrim_SetTexture(piece, RMTC_LENEN_CUTIN_PIECE_TEXTURES[i]);
		  ObjSprite2D_SetSourceRect(piece, 0, 0, 190, 500);
		  ObjSprite2D_SetDestCenter(piece);

		  pieces = pieces ~ [piece];
		}

		// All textures except for the combined one will be visible for now
		Obj_SetRenderPriorityI(pieces[8], 78);
		Obj_SetVisible(pieces[8], true);

		// Fade and zoom out the merged texture
		t = 24;
		TRMTCLinearRenderScaleFade(pieces[8], 10.38, 0.865, 10.38, 0.865, 1, 1, t-10);
		TRMTCLinearRenderAlphaFade(pieces[8], 0, 192, t-10);
		RMTCwait(t);

		// Delete the merged texture, make all other sub-textures visible
		Obj_Delete(pieces[8]);
		pieces = erase(pieces, 8);

		ascent (i in 0 .. length(pieces)) {
		  Obj_SetVisible(pieces[i], true);
		}

		// Expand d--- vertical stick, just a bit
		t = 9;
		TRMTCLinearRenderScaleFade(pieces[4], 0.865, 0.865, 0.865, 1, 1, 1, t);
		RMTCwait(t);

		// Rotate and expand the inner circles
		TRMTCLinearRenderAngleZFade(pieces[5], 0, 1800, 235);
		TRMTCLinearRenderAngleZFade(pieces[6], 0, -1800, 235);
		TRMTCLinearRenderAngleZFade(pieces[7], 0, 1800, 235);
		TRMTCLinearRenderScaleFade(pieces[5], 0.865, 1.4, 0.865, 1.4, 1, 1, 235);
		TRMTCLinearRenderScaleFade(pieces[6], 0.865, 1, 0.865, 1, 1, 1, 235);
		TRMTCLinearRenderScaleFade(pieces[7], 0.865, 0.6, 0.865, 0.6, 1, 1, 235);

		// Expand the four corners and displace them to either side
		t = 12;
		ascent (i in 0 .. 4) {
		  if (i % 2 == 0) {
			sidesXDisplacementSign = -1;
		  } else {
			sidesXDisplacementSign = 1;
		  }

		  TRMTCLinearRenderScaleFade(pieces[i], 0.865, 1, 0.865, 1, 1, 1, t+15);
		  TRMTCLinearRenderPositionFade(pieces[i], GetStgFrameWidth() / 2, GetStgFrameWidth() / 2 + 16 * sidesXDisplacementSign, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t+15);
		}
		RMTCwait(t);

		// Rotate the stick
		t = 27;
		TRMTCLinearRenderAngleZFade(pieces[4], 0, 2160, t+180);

		RMTCwait(t);

		// Rotate, move and contract the four corners
		t = 81;
		TRMTCLinearRenderAngleZFade(pieces[0], 0, -180, t+25);
		TRMTCLinearRenderAngleZFade(pieces[1], 0, 180, t+25);
		TRMTCLinearRenderAngleZFade(pieces[2], 0, 180, t+25);
		TRMTCLinearRenderAngleZFade(pieces[3], 0, -180, t+25);
		
		ascent (i in 0 .. 4) {
		  if (i % 2 == 0) {
			sidesXDisplacementSign = -1;
		  } else {
			sidesXDisplacementSign = 1;
		  }

		  TRMTCLinearRenderScaleFade(pieces[i], 1, 0.5, 1, 0.5, 1, 1, t+25);
		  TRMTCLinearRenderPositionFade(pieces[i], GetStgFrameWidth() / 2 + 16 * sidesXDisplacementSign, GetStgFrameWidth() / 2 + 28 * sidesXDisplacementSign, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t+25);
		}
		RMTCwait(t);

		// Fade everything out
		t = 18;
		ascent (i in 0 .. length(pieces)) {
		  TRMTCLinearRenderAlphaFade(pieces[i], 192, 0, t);
		}
		RMTCwait(t);

		// Cleanup section
		ascent (i in 0 .. length(pieces)) {
		  Obj_Delete(pieces[i]);
		}
	  }

	  task TRMTCLenenCutinChains() {
		let chains = [];
		let chain;
		let x;
		let y;
		let i;

		// Initialize the 4chains
		ascent (i in 0 .. 4) {
		  chain = ObjPrim_Create(OBJ_SPRITE_2D);
		  Obj_SetRenderPriorityI(chain, 70);
		  ObjRender_SetAlpha(chain, 0);
		  ObjPrim_SetTexture(chain, RMTC_LENEN_CUTIN_CHAIN_TEXTURE);
		  ObjSprite2D_SetSourceRect(chain, 0, 0, 20, 2200);
		  ObjSprite2D_SetDestCenter(chain);

		  chains = chains ~ [chain];
		}

		// Fade them in and set their starting positions, angle and movement
		t = 90;
		x = GetStgFrameWidth() * 0.5;
		y = GetStgFrameHeight() * 0.75;
		ObjRender_SetPosition(chains[0], x, y, 0);
		ObjRender_SetAngleZ(chains[0], 105);
		TRMTCLinearRenderPositionFade(chains[0], x, x - 600, y, y - 100, 0, 0, t+25);
		TRMTCLinearRenderAlphaFade(chains[0], 0, 255, absolute(t-230));

		x = GetStgFrameWidth() * 0.125;
		y = GetStgFrameHeight() * 0.6;
		ObjRender_SetPosition(chains[1], x, y, 0);
		ObjRender_SetAngleZ(chains[1], -30);
		TRMTCLinearRenderPositionFade(chains[1], x, x - 300, y, y - 700, 0, 0, t+25);
		TRMTCLinearRenderAlphaFade(chains[1], 0, 255, absolute(t-230));

		x = GetStgFrameWidth() * 0.75;
		y = GetStgFrameHeight() * 0.4;
		ObjRender_SetPosition(chains[2], x, y, 0);
		ObjRender_SetAngleZ(chains[2], -45);
		TRMTCLinearRenderPositionFade(chains[2], x, x + 500, y, y + 500, 0, 0, t+25);
		TRMTCLinearRenderAlphaFade(chains[2], 0, 255, absolute(t-230));

		x = GetStgFrameWidth() * 0.7;
		y = GetStgFrameHeight() * 0.5;
		ObjRender_SetPosition(chains[3], x, y, 0);
		ObjRender_SetAngleZ(chains[3], 15);
		TRMTCLinearRenderPositionFade(chains[3], x, x + 100, y, y - 800, 0, 0, t+25);
		TRMTCLinearRenderAlphaFade(chains[3], 0, 255, absolute(t-230));

		RMTCwait(t);

		// Fade out
		t = 45;
		ascent (i in 0 .. length(chains)) {
		  TRMTCLinearRenderAlphaFade(chains[i], 255, 0, t);
		}
		RMTCwait(t);

		// Cleanup
		ascent (i in 0 .. length(chains)) {
		  Obj_Delete(chains[i]);
		}
	  }

	  task TRMTCLenenCutinPentagrams() {
	let pentagrams = [];
	let pentagram;
	let i;

		task TRMTCPentagramAnimation() {
			let sprite = 0;

			while (!Obj_IsDeleted(pentagrams[0])) {
				ObjPrim_SetTexture(pentagrams[0], RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES[sprite % length(RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES)]);
				ObjPrim_SetTexture(pentagrams[1], RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES[sprite % length(RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES)]);

				sprite++;
				RMTCwait(3);
			}
		}

		// Initialize the pentagrams
		ascent (i in 0 .. 2) {
			pentagram = ObjPrim_Create(OBJ_SPRITE_2D);
			Obj_SetRenderPriorityI(pentagram, 70);
			ObjRender_SetScaleXYZ(pentagram, 0, 0, 1);
			ObjRender_SetPosition(pentagram, GetStgFrameWidth() / 2, GetStgFrameHeight() / 2, 0);
			ObjRender_SetAlpha(pentagram, 0);
			ObjPrim_SetTexture(pentagram, RMTC_LENEN_CUTIN_PENTAGRAM_TEXTURES[0]);
			ObjSprite2D_SetSourceRect(pentagram, 0, 0, 80, 391);
			ObjSprite2D_SetDestCenter(pentagram);

			pentagrams = pentagrams ~ [pentagram];
		}

		// Animate them
		TRMTCPentagramAnimation();

		// Expand, fade in and separate them
		t = 80;
		TRMTCLinearRenderAlphaFade(pentagrams[0], 0, 192, absolute(t-90));
		TRMTCLinearRenderAlphaFade(pentagrams[1], 0, 192, absolute(t-90));
		TRMTCLinearRenderScaleFade(pentagrams[0], 0, 0.865, 0, 0.865, 1, 1, absolute(t-90));
		TRMTCLinearRenderScaleFade(pentagrams[1], 0, 0.865, 0, 0.865, 1, 1, absolute(t-90));
		TRMTCLinearRenderPositionFade(pentagrams[0], GetStgFrameWidth() / 2, 32, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t);
		TRMTCLinearRenderPositionFade(pentagrams[1], GetStgFrameWidth() / 2, GetStgFrameWidth() - 32, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t);
		RMTCwait(t);

		// Move them closer
		t = 60;
		TRMTCLinearRenderPositionFade(pentagrams[0], 32, GetStgFrameWidth() * 0.25, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t+25);
		TRMTCLinearRenderPositionFade(pentagrams[1], GetStgFrameWidth() - 32, GetStgFrameWidth() * 0.75, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, t+25);
		RMTCwait(t);

		// Fade them out
		t = 15;
		TRMTCLinearRenderAlphaFade(pentagrams[0], 255, 0, t);
		TRMTCLinearRenderAlphaFade(pentagrams[1], 255, 0, t);
		RMTCwait(t);

		// Cleanup
		Obj_Delete(pentagrams[0]);
		Obj_Delete(pentagrams[1]);
	}

	 // Launch the main 4 tasks
	TRMTCLenenCutinBoss(); 
	
	TRMTCLenenCutinPieces();
	TRMTCLenenCutinChains();
	TRMTCLenenCutinPentagrams();
	ascent(i in 1..8){
		TRMTCLenenCutinBossFollow(0.8+(i*0.0125), 30, 250-i*5, 0);
		RMTCwait(0.05);
	}
}

// UTILITY FUNCTIONS AND TASKS BELOW - USED FOR TIMING AND ANIMATION
// =================================================================
function RMTCwait(frames) {
  loop (frames) {
    yield;
  }
}

function RMTClinearValueTransition(valueFrom, valueTo, totalFrameDuration, currentFrame) {
  return valueFrom - (valueFrom - valueTo) / (totalFrameDuration - 1) * currentFrame;
}

task TRMTCLinearRenderAlphaFade(object, from, to, duration) {
  let frame = 0;

  ascent (i in 0 .. duration) {
    if (Obj_IsDeleted(object)) {
      break;
    }

    ObjRender_SetAlpha(object, RMTClinearValueTransition(from, to, duration, frame));

    frame++;
    yield;
  }
}

task TRMTCLinearRenderAngleZFade(object, from, to, duration) {
  let frame = 0;

  ascent (i in 0 .. duration) {
    if (Obj_IsDeleted(object)) {
      break;
    }

    ObjRender_SetAngleZ(object, RMTClinearValueTransition(from, to, duration, frame));

    frame++;
    yield;
  }
}

task TRMTCLinearRenderScaleFade(object, xsFrom, xsTo, ysFrom, ysTo, zsFrom, zsTo, duration) {
  let frame = 0;

  ascent (i in 0 .. duration) {
    if (Obj_IsDeleted(object)) {
      break;
    }

    ObjRender_SetScaleXYZ(
      object,
      RMTClinearValueTransition(xsFrom, xsTo, duration, frame),
      RMTClinearValueTransition(ysFrom, ysTo, duration, frame),
      RMTClinearValueTransition(zsFrom, zsTo, duration, frame)
    );

    frame++;
    yield;
  }
}

task TRMTCLinearRenderPositionFade(object, xFrom, xTo, yFrom, yTo, zFrom, zTo, duration) {
  let frame = 0;

  ascent (i in 0 .. duration) {
    if (Obj_IsDeleted(object)) {
      break;
    }

    ObjRender_SetPosition(
      object,
      RMTClinearValueTransition(xFrom, xTo, duration, frame),
      RMTClinearValueTransition(yFrom, yTo, duration, frame),
      RMTClinearValueTransition(zFrom, zTo, duration, frame)
    );

    frame++;
    yield;
  }
}
