/* ==========================================================================
 * 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();

// 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.04
  ];

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

  // Fade in and scale down
  TRMTCLinearRenderAlphaFade(background, 0, 255, 120);
  TRMTCLinearRenderAlphaFade(text, 0, 255, 120);
  TRMTCLinearRenderScaleFade(background, -3, 1, 1, 1, 1, 1, 90);
  TRMTCLinearRenderScaleFade(text, -3, 1, 1, 1, 1, 1, 90);
  RMTCwait(150);

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

  // 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 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
    TRMTCLinearRenderAlphaFade(portrait, 0, 224, 30);
    TRMTCLinearRenderPositionFade(portrait, x[0], x[1], y[0], y[1], 0, 0, 30);
    RMTCwait(30);

    // Slowly move the portrait left and a bit up
    TRMTCLinearRenderPositionFade(portrait, x[1], x[2], y[1], y[2], 0, 0, 225);
    RMTCwait(225);

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

    // 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
    TRMTCLinearRenderScaleFade(pieces[8], 10.38, 0.865, 10.38, 0.865, 1, 1, 30);
    TRMTCLinearRenderAlphaFade(pieces[8], 0, 192, 30);
    RMTCwait(30 + 10);

    // 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
    TRMTCLinearRenderScaleFade(pieces[4], 0.865, 0.865, 0.865, 1, 1, 1, 10);
    RMTCwait(10);

    // 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
    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, 30);
      TRMTCLinearRenderPositionFade(pieces[i], GetStgFrameWidth() / 2, GetStgFrameWidth() / 2 + 16 * sidesXDisplacementSign, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 30);
    }
    RMTCwait(15);

    // Rotate the stick
    TRMTCLinearRenderAngleZFade(pieces[4], 0, 2160, 220);

    RMTCwait(15 + 25);

    // Rotate, move and contract the four corners
    TRMTCLinearRenderAngleZFade(pieces[0], 0, -180, 180);
    TRMTCLinearRenderAngleZFade(pieces[1], 0, 180, 180);
    TRMTCLinearRenderAngleZFade(pieces[2], 0, 180, 180);
    TRMTCLinearRenderAngleZFade(pieces[3], 0, -180, 180);

    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, 180);
      TRMTCLinearRenderPositionFade(pieces[i], GetStgFrameWidth() / 2 + 16 * sidesXDisplacementSign, GetStgFrameWidth() / 2 + 28 * sidesXDisplacementSign, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 180);
    }
    RMTCwait(155);

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

    // 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
    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, 285);
    TRMTCLinearRenderAlphaFade(chains[0], 0, 255, 30);

    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, 285);
    TRMTCLinearRenderAlphaFade(chains[1], 0, 255, 30);

    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, 285);
    TRMTCLinearRenderAlphaFade(chains[2], 0, 255, 30);

    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, 285);
    TRMTCLinearRenderAlphaFade(chains[3], 0, 255, 30);

    RMTCwait(260);

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

    // 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
    TRMTCLinearRenderAlphaFade(pentagrams[0], 0, 192, 30);
    TRMTCLinearRenderAlphaFade(pentagrams[1], 0, 192, 30);
    TRMTCLinearRenderScaleFade(pentagrams[0], 0, 0.865, 0, 0.865, 1, 1, 30);
    TRMTCLinearRenderScaleFade(pentagrams[1], 0, 0.865, 0, 0.865, 1, 1, 30);
    TRMTCLinearRenderPositionFade(pentagrams[0], GetStgFrameWidth() / 2, 32, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 120);
    TRMTCLinearRenderPositionFade(pentagrams[1], GetStgFrameWidth() / 2, GetStgFrameWidth() - 32, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 120);
    RMTCwait(120);

    // Move them closer
    TRMTCLinearRenderPositionFade(pentagrams[0], 32, GetStgFrameWidth() * 0.25, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 165);
    TRMTCLinearRenderPositionFade(pentagrams[1], GetStgFrameWidth() - 32, GetStgFrameWidth() * 0.75, GetStgFrameHeight() / 2, GetStgFrameHeight() / 2, 0, 0, 165);
    RMTCwait(140);

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

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

  // Launch the main 4 tasks
  TRMTCLenenCutinBoss();
  TRMTCLenenCutinPieces();
  TRMTCLenenCutinChains();
  TRMTCLenenCutinPentagrams();
}

// 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;
  }
}
