// Fun Stuff - SERVER script. COPY TO kubejs/server_scripts/funstuff_server.js
// Handles: gun ammo objectives + hoverboard creative-style flight (mayfly,
// the proven Grand Line phoenix pattern - double-tap space to fly).
console.log('[Fun Stuff] server script loading...')

// ---- full Rubik's cube engine: 54-sticker state string, faces U R F D L B
// (9 each, rows top-to-bottom). fsCubeRot applies ONE clockwise quarter-turn
// of face f (0..5): rotates the face's 9 stickers + cycles the 4 side strips.
// Scrambles use these same moves, so every scramble is guaranteed solvable.
function fsCubeRot(state, f) {
    var s = state.split('')
    var o = f * 9
    var t = s.slice(o, o + 9)
    var m = [t[6], t[3], t[0], t[7], t[4], t[1], t[8], t[5], t[2]]
    for (var i = 0; i < 9; i++) s[o + i] = m[i]
    var CY = [
        [[18, 19, 20], [36, 37, 38], [45, 46, 47], [9, 10, 11]],
        [[20, 23, 26], [2, 5, 8], [51, 48, 45], [29, 32, 35]],
        [[6, 7, 8], [9, 12, 15], [29, 28, 27], [44, 41, 38]],
        [[24, 25, 26], [15, 16, 17], [51, 52, 53], [42, 43, 44]],
        [[0, 3, 6], [18, 21, 24], [27, 30, 33], [53, 50, 47]],
        [[2, 1, 0], [36, 39, 42], [33, 34, 35], [17, 14, 11]]
    ][f]
    var tmp = [s[CY[3][0]], s[CY[3][1]], s[CY[3][2]]]
    for (var g = 3; g > 0; g--) for (var i2 = 0; i2 < 3; i2++) s[CY[g][i2]] = s[CY[g - 1][i2]]
    for (var i3 = 0; i3 < 3; i3++) s[CY[0][i3]] = tmp[i3]
    return s.join('')
}
// ===== REAL 3D cube - rendered DATA-SIDE (Palladium) ======================
// KubeJS particles do NOT render here, but Palladium ability dust DOES. So KubeJS
// runs the cube SIMULATION and just writes the state out to scoreboards: fs_cs0..53
// = each sticker's colour (0-5), and fs_cubeview = a countdown. The fs_cube power's
// hidden 'cube_render' marker reads those and draws the 54 dust stickers in front
// of the player. Letter -> colour index: W Y R O B G = 0 1 2 3 4 5.
function fsCubeIdx(ch) {
    if (ch === 'W') return 0
    if (ch === 'Y') return 1
    if (ch === 'R') return 2
    if (ch === 'O') return 3
    if (ch === 'B') return 4
    if (ch === 'G') return 5
    return 0
}
// push the 54 sticker colours to scoreboards + (re)arm the on-screen countdown
function fsCubeWrite(p, st) {
    for (var i = 0; i < 54; i++) {
        p.runCommandSilent('scoreboard players set @s fs_cs' + i + ' ' + fsCubeIdx(st.charAt(i)))
    }
    p.runCommandSilent('scoreboard players set @s fs_cubeview 220')
}

ServerEvents.loaded(event => {
  var s = event.server
  for (var o of ['fs_ammo_ak', 'fs_ammo_glock', 'fs_ammo_deagle', 'fs_cubeview', 'fs_cubeblink']) {
    s.runCommandSilent('scoreboard objectives add ' + o + ' dummy')
  }
  for (var i = 0; i < 54; i++) { s.runCommandSilent('scoreboard objectives add fs_cs' + i + ' dummy') }
  console.log('[Fun Stuff] ammo + cube objectives ready')
})

ServerEvents.tick(event => {
  var server = event.server
  if (server.tickCount % 2 !== 0) return
  for (var p of server.players) {
    try {
      var pd = p.persistentData
      var hover = false
      try { hover = p.tags.contains('fs_hover_on') } catch (e) {}
      var prot = false
      try { prot = p.isCreative() || p.isSpectator() } catch (e) {}
      if (!prot) {
        var ab = p.getAbilities()
        if (hover) {
          if (!ab.mayfly) { ab.mayfly = true; p.onUpdateAbilities() }
          pd.putBoolean('fs_hover', true)
        } else if (pd.getBoolean('fs_hover')) {
          ab.mayfly = false; ab.flying = false; p.onUpdateAbilities()
          pd.putBoolean('fs_hover', false)
        }
      }
      if (hover) p.runCommandSilent('tag @s remove fs_hover_on')

      // (SABER THROW is now fully data-side in the fs_force power - the guns prove
      //  Palladium ability particles render, KubeJS runCommandSilent ones do not.)
      // SAFETY NET for the throw's hand-swap: if the invisible saber_thrown is
      // still in hand ~3s after a throw started (last_tick restore failed -
      // death/disconnect edge case), force the real saber back so it can't be lost.
      var stuck = ''
      try {
        if (('' + p.mainHandItem.id) === 'funstuff:saber_thrown') {
          for (var sbc of ['saber_blue', 'saber_green', 'saber_red']) {
            try { if (p.tags.contains('fs_sbcol_' + sbc)) stuck = sbc } catch (e) {}
          }
        }
      } catch (e) {}
      if (stuck !== '') {
        var sbT = pd.getInt('fs_sabfix') + 2
        pd.putInt('fs_sabfix', sbT)
        if (sbT > 60) {
          p.runCommandSilent('item replace entity @s weapon.mainhand with funstuff:' + stuck + ' 1')
          for (var sbc2 of ['saber_blue', 'saber_green', 'saber_red']) p.runCommandSilent('tag @s remove fs_sbcol_' + sbc2)
          pd.putInt('fs_sabfix', 0)
        }
      } else if (pd.getInt('fs_sabfix') !== 0) { pd.putInt('fs_sabfix', 0) }

      // ---- REAL Rubik's cube (full 54-sticker simulation, hard as real life) ----
      var cScr = false, cIns = false
      try { cScr = p.tags.contains('fs_cube_scr') } catch (e) {}
      try { cIns = p.tags.contains('fs_cube_ins') } catch (e) {}
      var cMove = -1
      for (var mi = 0; mi < 12; mi++) {
        var hasM = false
        try { hasM = p.tags.contains('fs_cv' + mi) } catch (e) {}
        if (hasM) { cMove = mi; p.runCommandSilent('tag @s remove fs_cv' + mi) }
      }
      if (cScr) p.runCommandSilent('tag @s remove fs_cube_scr')
      if (cIns) p.runCommandSilent('tag @s remove fs_cube_ins')
      if (cScr || cIns || cMove >= 0) {
        var st = pd.getString('fs_cube_state')
        if (!st || st.length !== 54) st = 'WWWWWWWWWRRRRRRRRRGGGGGGGGGYYYYYYYYYOOOOOOOOOBBBBBBBBB'
        if (cScr) {
          for (var sc = 0; sc < 25; sc++) {
            var sf = Math.floor(Math.random() * 6)
            var sn = 1 + Math.floor(Math.random() * 3)
            for (var sr = 0; sr < sn; sr++) st = fsCubeRot(st, sf)
          }
          pd.putInt('fs_cube_start', server.tickCount)
          pd.putInt('fs_cube_moves', 0)
          pd.putBoolean('fs_cube_live', true)
          p.tell(Text.gold('CUBE SCRAMBLED! (25 random moves) - solve it with the face-turn abilities, just like a real cube.'))
          if (!pd.getBoolean('fs_cube_toldmodel')) {
            pd.putBoolean('fs_cube_toldmodel', true)
            p.tell(Text.gray('Note: the cube in your hand is a fixed model. The REAL 3D cube is projected in front of you - it reflects the true state and updates on every turn. Press Inspect Cube any time to bring it back up.'))
          }
          fsCubeWrite(p, st)
        }
        if (cMove >= 0) {
          var face = Math.floor(cMove / 2)
          var reps = (cMove % 2 === 0) ? 1 : 3
          for (var r2 = 0; r2 < reps; r2++) st = fsCubeRot(st, face)
          pd.putInt('fs_cube_moves', pd.getInt('fs_cube_moves') + 1)
        }
        pd.putString('fs_cube_state', st)
        var solved = true
        for (var fc = 0; fc < 6; fc++) {
          var c0 = st.charAt(fc * 9)
          for (var si = 1; si < 9; si++) { if (st.charAt(fc * 9 + si) !== c0) { solved = false; break } }
          if (!solved) break
        }
        // a turn or inspect happened: push the new colours + re-arm the view
        if (cMove >= 0 || cIns) { fsCubeWrite(p, st) }
        if (cIns) {
          p.tell(Text.gray('Cube projected in front of you - turn faces to change it. It fades after ~11s; press Inspect to bring it back.'))
        }
        if (cMove >= 0 && solved && pd.getBoolean('fs_cube_live')) {
          var tenths = Math.floor((server.tickCount - pd.getInt('fs_cube_start')) / 2)
          var secs2 = (tenths / 10).toFixed(1)
          var pb2 = pd.getInt('fs_cube_pb')
          var mvs = pd.getInt('fs_cube_moves')
          var m2 = 'CUBE SOLVED! ' + secs2 + 's in ' + mvs + ' moves'
          if (pb2 === 0 || tenths < pb2) { pd.putInt('fs_cube_pb', tenths); m2 += ' - NEW PERSONAL BEST!' }
          else { m2 += ' (PB ' + (pb2 / 10).toFixed(1) + 's)' }
          pd.putBoolean('fs_cube_live', false)
          p.tell(Text.gold(m2))
          server.runCommandSilent('execute as ' + p.username + ' at @s run playsound minecraft:ui.toast.challenge_complete master @s ~ ~ ~ 1 1')
        }
      }

      // ---- STUNT COMBO meter (skate-game style multiplier) ----
      var trick = false
      try { trick = p.tags.contains('fs_trick') } catch (e) {}
      if (trick) {
        p.runCommandSilent('tag @s remove fs_trick')
        var combo = pd.getInt('fs_combo') + 1
        pd.putInt('fs_combo', combo)
        pd.putInt('fs_combo_t', 160)
        var style = combo * 100
        pd.putInt('fs_style', pd.getInt('fs_style') + style)
        var colr = combo >= 5 ? '"color":"gold","bold":true' : (combo >= 3 ? '"color":"yellow","bold":true' : '"color":"aqua"')
        p.runCommandSilent('title @s actionbar {"text":"STUNT COMBO x' + combo + '!  +' + style + ' style",' + colr + '}')
        p.runCommandSilent('playsound minecraft:entity.experience_orb.pickup master @s ~ ~ ~ 1 ' + (0.8 + Math.min(combo, 8) * 0.15).toFixed(2))
      }
      var cbt = pd.getInt('fs_combo_t')
      if (cbt > 0) {
        cbt -= 2
        pd.putInt('fs_combo_t', cbt)
        if (cbt <= 0) {
          var fcb = pd.getInt('fs_combo')
          if (fcb >= 2) p.tell(Text.aqua('COMBO ENDED: x' + fcb + '!  total style points: ' + pd.getInt('fs_style')))
          pd.putInt('fs_combo', 0)
        }
      }
    } catch (err) {}
  }
})

console.log('[Fun Stuff] server script loaded')