/*
  A mildly interactive Pokémon-inspired encounter (not a battle, all love here)
  sequence.

  Triggered when the player moves onto a particular tile while possessing the
  croissant item.

  1. Flash + conic-gradient wipe on enter (detected via scroll-state:snapped
     query, as with all other tile effects.
  2. Wipe reveals an encounter scene. We make the user press a "Ready" button
     so we can use a dialog (means we can trap keyboard focus and return it
     after)
  3. When the user opens the dialog we slide with player and opponent in, just
     like in Gameboy-era Pokémon, and show some flavour text.
  4. The user checks #encounter-give to throw the croissant item to opponent
  5. We show some more flavour text and a close button
*/

.tile.encounter {
  position: relative;

  .encounter-ui {
    position: absolute;
  }

  .encounter-transition {
    position: absolute;
    inset: 0;
    z-index: var(--stacking-layer-ui);
    opacity: 0;
    pointer-events: none;
  }

  .encounter-ready {
    position: absolute;
    inset: 0;
    z-index: var(--stacking-layer-ui);
    opacity: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;

    > .button {
      bottom: -1rem;
    }
  }

  .encounter-give-result {
    display: none;
  }

  /*
    Once the user has checked this box (styled like a button), the encounter
    is complete. This game state is used later in the game to infer that the
    character has received the stego back plate item.
  */
  &:has(#encounter-give:checked) {
    /* Show the thank you message and close button */
    .encounter-give-result {
      display: flex;
    }

    /* Don't show prompts or UI once completed */
    .encounter-ui:has(dialog:not(:open)) {
      opacity: 0;
      pointer-events: none;
    }
    > .button {
      opacity: 0;
      pointer-events: none;
    }
    .encounter-transition {
      display: none;
    }
  }
}

/*
  Flash and double-clock-wipe animation to mimic the Gameboy-era Pokémon games
  of my youth <3

  The @property is the enabler of the gradient animation. Temani called it again:
  https://dev.to/afif/we-can-finally-animate-css-gradient-kdk

  We could have probably done this with some rotate() but this is cooler, right?
*/
@property --wipe-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@keyframes encounter-flash {
  0% {
    opacity: 0;
  }
  20%,
  60% {
    opacity: 0.55;
  }
  40%,
  80% {
    opacity: 0.1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes pokemon-wipe {
  from {
    --wipe-angle: 0deg;
  }
  to {
    --wipe-angle: 180deg;
  }
}

@keyframes encounter-slide-in-left {
  from {
    translate: -120% 0;
  }
  to {
    translate: 0 0;
  }
}

@keyframes encounter-slide-in-right {
  from {
    translate: 120% 0;
  }
  to {
    translate: 0 0;
  }
}

@keyframes encounter-speech-reveal {
  from {
    opacity: 0;
    translate: 0 0.5rem;
  }
  to {
    opacity: 1;
    translate: 0 0;
  }
}

/*
  OK, we didn't really need to animate the croissant quite this much, but I
  think it looks pretty neat.
*/
/* Fling it across */
@keyframes encounter-croissant-fly-x {
  from {
    left: 10%;
  }
  to {
    left: 50%;
  }
}
/* Arc it up then down */
@keyframes encounter-croissant-fly-y {
  0% {
    bottom: 0;
    animation-timing-function: ease-out;
  }
  40% {
    bottom: 80%;
    animation-timing-function: ease-in;
  }
  100% {
    bottom: 45%;
  }
}
/* Simulate it flying towards the horizon */
@keyframes encounter-croissant-fly-z {
  0%,
  75% {
    scale: 1;
  }
  100% {
    scale: 0.5;
  }
}
/* Spinning is so much cooler than not spinning */
@keyframes encounter-croissant-spin {
  from {
    rotate: 0deg;
  }
  to {
    rotate: 720deg;
  }
}

.encounter-container {
  position: absolute;
  left: 50%;
  top: 50%;
  --z-index: var(--stacking-layer-action);
  translate: -50% calc(-50% + var(--tile-height) / 2);
  width: var(--viewport-width);
  height: var(--viewport-height);
}

/*
  When the encounter tile snaps into view and the player has the croissant,
  fire the flash + wipe transition sequence.
*/
@container scroll-state(snapped: x) or scroll-state(snapped: y) {
  body:has(#item-croissant:checked)
    .tile.encounter
    .encounter-transition::before {
    content: "";
    position: absolute;
    inset: 0;
    background: black;
    opacity: 0;
    animation: encounter-flash 1.5s ease-in-out forwards;
  }

  body:has(#item-croissant:checked) .tile.encounter .encounter-ui {
    top: calc(-0.5 * var(--viewport-height) + 0.5 * var(--tile-height));
    left: calc(-0.5 * var(--viewport-width) + 0.5 * var(--tile-width));
    bottom: 0;
    right: 0;
    width: var(--viewport-width);
    height: var(--viewport-height);
  }
  body:has(#item-croissant:checked) .tile.encounter .encounter-ready {
    animation: fade-in 0.1s linear 2s forwards;
  }

  body:has(#item-croissant:checked) .tile.encounter .encounter-transition {
    display: flex;
    justify-content: center;
    align-items: center;
    opacity: 1;
    pointer-events: auto;
    /*
      Two opposing conic wedges sweep from 0° to 180° to give us the Pokémon
      wipe, with a lowered frame rate. I didn't go so far as to make the
      edges blocky - maybe in future!
    */
    background: conic-gradient(
      black 0deg var(--wipe-angle),
      transparent var(--wipe-angle) 180deg,
      black 180deg calc(180deg + var(--wipe-angle)),
      transparent calc(180deg + var(--wipe-angle)) 360deg
    );
    animation: pokemon-wipe 0.6s steps(8) 1.3s forwards;
  }
}

/*
  Encounter scene layout
*/
.encounter-scene {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    to bottom,
    #5898f8 0%,
    #5898f8 25%,
    #b3b74a 25%,
    #b3b74a 45%,
    #b8bf3c 45%,
    #b8bf3c 100%
  );
  container-type: size;
  overflow: hidden;
}

/* Top portion with player and enemy sprites */
.encounter-area {
  position: relative;
  flex: 1 1 60%;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  padding: 2rem 2rem 0 2rem;
}

/* Player rear-view sprite, slides in from the left */
.encounter-player {
  width: calc(var(--tile-width) * 2);
  height: calc(var(--tile-height) * 2);
  background: url("../assets/player/player-walk-up.png") 0 0 / auto 100%
    no-repeat;
  image-rendering: pixelated;
  align-self: flex-start;
  animation: encounter-slide-in-left 0.6s steps(8) both;
}

.encounter-enemy {
  animation: encounter-slide-in-right 0.6s steps(8) 0.3s both;
}
.encounter-enemy-sprite {
  --enemy-size: calc(var(--viewport-height) / 3);
  width: var(--enemy-size);
  height: var(--enemy-size);
  background-image: url("../assets/npcs/stegosaurus-encounter-idle.png");
  background-size: cover;
  background-position: 0 0;
  image-rendering: pixelated;
  align-self: flex-start;
  /*
    Shared spritesheet keyframe from animations.css doesn't seem to work here,
    possibly because the sprite is scaled up?
  */
  animation: encounter-enemy-idle 1s steps(7) infinite;
}

@keyframes encounter-enemy-idle {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: -700% 0;
  }
}

/* Bottom tray with speech bubbles and actions */
.encounter-dialogue {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 6rem;
  padding: 1rem;
  background: #ccc;
  border-top: 1px solid #888;
}

/* Speech bubbles start hidden; each gets a delayed reveal animation */
.encounter-speech {
  opacity: 0;
  white-space: normal;
}

.encounter-speech-1 {
  animation: encounter-speech-reveal 0.3s steps(6) 1s forwards;
}

/* Croissant offer appears after first speech bubble */
.encounter-action {
  opacity: 0;
  cursor: pointer;
  animation: encounter-speech-reveal 0.3s steps(6) 2s forwards;
}

/* Hidden checkbox that drives the post-give state */
#encounter-give {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/*
  Post-give sequence - triggered when #encounter-give is checked.
  The croissant flies to the enemy, thank-you speech plays, close button appears.
*/
.encounter-give-result {
  position: relative;
  display: flex;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
}

/* Croissant sprite that arcs across the screen when given */
.encounter-croissant {
  position: absolute;
  bottom: 100%;
  left: 10%;
  width: 6rem;
  height: 6rem;
  background-image: url("../assets/items/croissant.png");
  background-size: cover;
  image-rendering: pixelated;
  opacity: 0;
  pointer-events: none;
}

/* :checked state kicks off the flying croissant and sequential speech */
.encounter-scene:has(#encounter-give:checked) {
  .encounter-action {
    display: none;
  }

  .encounter-speech-1 {
    display: none;
  }

  .encounter-croissant {
    opacity: 1;
    animation:
      encounter-croissant-fly-x 1s linear forwards,
      encounter-croissant-fly-y 1s linear forwards,
      encounter-croissant-fly-z 1s linear forwards,
      encounter-croissant-spin 1s linear forwards;
  }

  .encounter-speech-2 {
    animation:
      encounter-speech-reveal 0.3s steps(6) 2s forwards,
      fade-out-hard 0.3s steps(6) 5s forwards;
  }

  .encounter-speech-3 {
    margin-top: 0.25rem;
    animation: encounter-speech-reveal 0.3s steps(6) 5.5s forwards;
  }

  .encounter-give-close {
    display: block;
    opacity: 0;
    cursor: pointer;
    animation: encounter-speech-reveal 0.3s steps(6) 7.5s forwards;
  }
}

.encounter-give-close {
  display: none;
  appearance: none;
  border: none;
  padding: 0;
  font: inherit;
  text-align: left;

  .frame-inner {
    transition: background-color 0.3s steps(3);
  }
  &:is(:hover, :focus) .frame-inner {
    background-color: #9f664b;
  }
}
