## ========================================== ##
## Spleef minigame implemented using Denizen  ##
## by @seb303                                 ##
## v1.0 2022-05-02                            ##
## Requires Denizen-1.2.4-b6218-DEV or newer  ##
## ========================================== ##

# Requires the following noted locations/cuboids which should be in the same world:
# spleef_lobby      = Location of lobby spawn. The lobby area should normally have a view of the game floor.
# spleef_game_floor = 1 high cuboid encompassing game floor, should be at least 20x20.
# spleef_fall_zone  = When a player enters this cuboid under the game floor they are out of the game.
#
# endgame_lobby     = Location to teleport after game (can be in a different world)
#
# For locations:
# /ex note <player.location> as:<noted name>
# Easiest way to note cuboids is to use WorldEdit wand and command:
# /ex note <player.we_selection> as:<noted name>

# Permissions:
# custom.spleef.join  -> /spleef join (permitted by default)
# custom.spleef.reset -> /spleef reset

## CONFIG STARTS ##
spleef_config:
    type: data

    # Countdown time in seconds before game starts
    countdown_time: 5
    # Time in seconds before players get shovel at start of game
    shovel_time: 5

    # Time in seconds before player gets warning to keep moving
    standing_warn: 5
    # Time in seconds before block breaks under standing still player
    standing_time: 7

    # Minimal players for game to start
    min_players: 2

    # Item to exit game lobby
    exit_item: iron_door
    # Tool to break floor blocks
    shovel: netherite_shovel
    # Material of floor blocks
    floor_block: snow_block

    # Text at bottom of sidebar
    sidebar_footer: "PiglinCraft MiniGames"

## CONFIG ENDS ##



# ------------------------------------------------------------
# Server flags:
#
# spleef_status     - unset, 'queue', 'start', 'play', 'reset'
# spleef_timer      - timer/counter
# spleef_queued     - list of players queued to play
# spleef_playing    - list of players still playing
# spleef_spectating - list of players spectating
# spleef_player_pos - map of player positions
# ------------------------------------------------------------

spleef_command_spleef:
    type: command
    debug: true
    name: spleef
    description: Spleef minigame
    usage: /spleef join|reset
    tab completions:
        1: join|reset

    show_docs:
        - narrate <&7><&sp.repeat[59].strikethrough>
        - if <player.has_permission[custom.spleef.join]>:
            - narrate "<&6>/spleef join <&f> Join the queue to play"
        - if <player.has_permission[custom.spleef.reset]>:
            - narrate "<&6>/spleef reset <&f> Resets the game"
        - narrate <&7><&sp.repeat[59].strikethrough>

    no_permission:
        - narrate "<&[error]>You don't have permission to use this command"

    reset:
        - foreach <server.flag[spleef_queued].if_null[<list>]> as:p:
            - sidebar remove players:<[p]>
            - teleport <[p]> <location[endgame_lobby]>
        - foreach <server.flag[spleef_playing].if_null[<list>]> as:p:
            - sidebar remove players:<[p]>
            - teleport <[p]> <location[endgame_lobby]>
        - foreach <server.flag[spleef_spectating].if_null[<list>]> as:p:
            - sidebar remove players:<[p]>
            - teleport <[p]> <location[endgame_lobby]>
        - modifyblock <cuboid[spleef_game_floor]> <script[spleef_config].data_key[floor_block]>
        - flag server spleef_status:!
        - flag server spleef_timer:!
        - flag server spleef_queued:!
        - flag server spleef_playing:!
        - flag server spleef_spectating:!
        - flag server spleef_player_pos:!

    script:
        - if <context.args.size> != 1:
            - inject spleef_command_spleef.show_docs
            - stop
        - choose <context.args.first>:

            # /spleef join
            - case join:
                - if !<player.has_permission[custom.spleef.join]>:
                    - inject spleef_command_spleef.no_permission
                    - stop

                - choose <server.flag[spleef_status].if_null[inactive]>:
                    - case inactive:
                        - teleport <player> <location[spleef_lobby]>
                        - adjust <player.location.world> allow_pvp:false
                        - run spleef_set_player_list def:<player>|queued
                        - flag server spleef_status:queue
                        - flag server spleef_timer:<script[spleef_config].data_key[countdown_time]>
                    - case queue:
                        - teleport <player> <location[spleef_lobby]>
                        - run spleef_set_player_list def:<player>|queued
                    - case start:
                        - teleport <player> <location[spleef_lobby]>
                        - run spleef_set_player_list def:<player>|spectating
                    - case play:
                        - teleport <player> <location[spleef_lobby]>
                        - run spleef_set_player_list def:<player>|spectating
                    - case reset:
                        - narrate "<&[error]>The arena is resetting, please wait"
                        - stop

                - inventory clear
                - adjust <player> gamemode:adventure
                - adjust <player> health:20
                - feed <player> amount:20
                - inventory set o:<script[spleef_config].data_key[exit_item]>[display=<red>Leave] slot:9

            # /spleef reset
            - case reset:
                - if !<player.has_permission[custom.spleef.reset]>:
                    - inject spleef_command_spleef.no_permission
                    - stop
                - inject spleef_command_spleef.reset

            - default:
                - inject spleef_command_spleef.show_docs
                - stop


spleef_set_player_list:
    type: task
    debug: true
    definitions: player|listname
    script:
        # Initialise lists
        - if !<server.has_flag[spleef_queued]>:
            - flag server spleef_queued:<list>
        - if !<server.has_flag[spleef_playing]>:
            - flag server spleef_playing:<list>
        - if !<server.has_flag[spleef_spectating]>:
            - flag server spleef_spectating:<list>
        # Ensure player is not in any list
        - flag server spleef_queued:<server.flag[spleef_queued].exclude[<player>]>
        - flag server spleef_playing:<server.flag[spleef_playing].exclude[<player>]>
        - flag server spleef_spectating:<server.flag[spleef_spectating].exclude[<player>]>
        # Add player to specified list
        - if <[listname].exists>:
            - flag server spleef_<[listname]>:->:<player>


spleef_events:
    type: world
    debug: false
    events:
        on server prestart:
            - adjust server register_permission:<map[name=custom.spleef.join;default=true]>
            - flag server spleef_status:!
            - flag server spleef_timer:!
            - flag server spleef_queued:!
            - flag server spleef_playing:!
            - flag server spleef_spectating:!
            - flag server spleef_player_pos:!

        on delta time secondly server_flagged:spleef_status:
            - define sidebar_footer <yellow><script[spleef_config].data_key[sidebar_footer]>
            - if <server.flag[spleef_status]> == queue:
                - define player_count <server.flag[spleef_queued].size>
                - if <server.flag[spleef_timer]> <= 0 && <[player_count]> >= <script[spleef_config].data_key[min_players]>:
                    # Countdown ended & enough players, so switch to start mode
                    - flag server spleef_playing:<server.flag[spleef_queued]>
                    - flag server spleef_queued:<list>
                    - flag server spleef_status:start
                    - flag server spleef_timer:<script[spleef_config].data_key[shovel_time]>
                    - define floor_center <cuboid[spleef_game_floor].center>
                    - define start_area <[floor_center].add[-10,0,-10].to_cuboid[<[floor_center].add[10,0,10]>]>
                    - foreach <server.flag[spleef_playing]> as:p:
                        - teleport <[p]> <[start_area].random.above>
                        - inventory clear player:<[p]>

                - else:
                    # In countdown / waiting for players phase
                    - if <[player_count]> < <script[spleef_config].data_key[min_players]>:
                        - define need_players <script[spleef_config].data_key[min_players].sub[<[player_count]>]>
                        - if <[need_players]> == 1:
                            - define players player
                        - else:
                            - define players players
                        - define status "Need <[need_players]> more <[players]>..."
                        - flag server spleef_timer:<script[spleef_config].data_key[countdown_time]>
                    - else:
                        - define status "Starting in: <green><server.flag[spleef_timer]>"
                        - flag server spleef_timer:--
                    - sidebar set title:<bold>SPLEEF "values:|Players waiting: <green><[player_count]>||<[status]>||<[sidebar_footer]>" players:<server.flag[spleef_queued]>

            - if <server.flag[spleef_status]> == start:
                - if <server.flag[spleef_timer]> > 0:
                    # Shovel countdown phase
                    - define player_count <server.flag[spleef_playing].size>
                    - title "title:Shovel coming in <green><server.flag[spleef_timer]>" fade_in:0 stay:2 fade_out:1 targets:<server.flag[spleef_playing]>
                    - flag server spleef_timer:--
                    - sidebar set title:<bold>SPLEEF "values:|Players remaining: <green><[player_count]>||<[sidebar_footer]>" players:<server.flag[spleef_playing]>
                    - sidebar set title:<bold>SPLEEF "values:|Players remaining: <green><[player_count]>||<[sidebar_footer]>" players:<server.flag[spleef_spectating]>

                - else:
                    # Start play mode
                    - flag server spleef_status:play
                    - flag server spleef_timer:0
                    - flag server spleef_player_pos:<map>
                    - title title:<green>GO! fade_in:0 stay:2 fade_out:1 targets:<server.flag[spleef_playing]>
                    # Give shovel
                    - define snow_shovel <script[spleef_config].data_key[shovel]>[can_destroy=<script[spleef_config].data_key[floor_block]>]
                    - foreach <server.flag[spleef_playing]> as:p:
                        - inventory set o:<[snow_shovel]> slot:1 player:<[p]>
                        - adjust <[p]> item_slot:1

            - if <server.flag[spleef_status]> == play:
                # Play mode
                - define player_count <server.flag[spleef_playing].size>
                - sidebar set title:<bold>SPLEEF "values:|Players remaining: <green><[player_count]>||<[sidebar_footer]>" players:<server.flag[spleef_playing]>
                - sidebar set title:<bold>SPLEEF "values:|Players remaining: <green><[player_count]>||<[sidebar_footer]>" players:<server.flag[spleef_spectating]>
                - inject spleef_events.check_for_winner

                # Check for players standing still for too long
                - define spleef_player_pos <server.flag[spleef_player_pos]>
                - foreach <server.flag[spleef_playing]> as:p:
                    - define current_x <[p].location.x.round_down>
                    - define current_z <[p].location.z.round_down>
                    - if <server.flag[spleef_player_pos].contains[<[p].uuid>]>:
                        - define last_x <[spleef_player_pos].get[<[p].uuid>].get[x]>
                        - define last_z <[spleef_player_pos].get[<[p].uuid>].get[z]>
                        - define time <[spleef_player_pos].get[<[p].uuid>].get[time]>
                        - if <[last_x]> == <[current_x]> && <[last_z]> == <[current_z]>:
                            - define time:++
                        - else:
                            - define time:0
                        - if <[time]> == <script[spleef_config].data_key[standing_warn]>:
                            - title "title:<gold>Keep moving!" fade_in:0 stay:2 fade_out:1 targets:<[p]>
                        - if <[time]> == <script[spleef_config].data_key[standing_time]>:
                            - modifyblock <[p].location.with_y[<cuboid[spleef_game_floor].center.y>].find_blocks[<script[spleef_config].data_key[floor_block]>].within[1]> air
                        - define new_values <map[x=<[current_x]>;z=<[current_z]>;time=<[time]>]>
                    - else:
                        - define new_values <map[x=<[current_x]>;z=<[current_z]>;time=0]>
                    - define spleef_player_pos <[spleef_player_pos].with[<[p].uuid>].as[<[new_values]>]>
                - flag server spleef_player_pos:<[spleef_player_pos]>


        on player changes world server_flagged:spleef_status:
            - if <context.destination_world.name> != <location[spleef_lobby].world.name>:
                - inject spleef_events.player_leaves
        on player quits:
            - inject spleef_events.player_leaves

        on player enters spleef_fall_zone:
            - if <server.flag[spleef_status]> == reset:
                - stop
            - teleport <player> <location[spleef_lobby]>
            - inventory clear
            - title title:<red>OOPS! fade_in:0 stay:2 fade_out:1
            - run spleef_set_player_list def:<player>|spectating
            - inject spleef_events.check_for_winner

        on snowball hits player server_flagged:spleef_status:
            - if <server.flag[spleef_playing].contains[<player>]>:
                - adjust <player> velocity:<context.projectile.velocity>

        on player drops item server_flagged:spleef_status:
            - inject spleef_events.cancel_event
        on player clicks in inventory server_flagged:spleef_status:
            - inject spleef_events.cancel_event
        on player swaps items server_flagged:spleef_status:
            - inject spleef_events.cancel_event

        on player right clicks block server_flagged:spleef_status:
            - if <context.item.material.name> == iron_door && <player.world.name> == <location[spleef_lobby].world.name>:
                - if <server.flag[spleef_queued].contains[<player>]> || <server.flag[spleef_spectating].contains[<player>]>:
                    - run spleef_set_player_list def:<player>|
                    - sidebar remove players:<player>|
                    - teleport <player> <location[endgame_lobby]>

        # Players shouldn't die, but just in case
        after player respawns server_flagged:spleef_status:
            - if <server.flag[spleef_queued].contains[<player>]> || <server.flag[spleef_playing].contains[<player>]> || <server.flag[spleef_spectating].contains[<player>]>:
                - run spleef_set_player_list def:<player>|
                - sidebar remove players:<player>|
                - teleport <player> <location[endgame_lobby]>



    check_for_winner:
        - if <server.flag[spleef_playing].size> == 1:
            - flag server spleef_status:reset
            - define winner <server.flag[spleef_playing].get[1]>
            - title title:<green>WINNER! fade_in:0 stay:2 fade_out:1 targets:<[winner]>
            - define sidebar_footer <yellow><script[spleef_config].data_key[sidebar_footer]>
            - sidebar set title:<bold>SPLEEF "values:|<green><[winner].name><white> is the winner!||<[sidebar_footer]>" players:<server.flag[spleef_playing]>
            - sidebar set title:<bold>SPLEEF "values:|<green><[winner].name><white> is the winner!||<[sidebar_footer]>" players:<server.flag[spleef_spectating]>
            - repeat 5:
                - firework <[winner].location> star primary:yellow fade:white
                - firework <[winner].location> star primary:red fade:white
                - firework <[winner].location> star primary:green fade:blue
                - wait 1s
            - wait 2s

        - if <server.flag[spleef_playing].size> <= 1:
            - flag server spleef_status:reset
            - inject spleef_command_spleef.reset

    player_leaves:
        - if <server.flag[spleef_queued].contains[<player>]> || <server.flag[spleef_playing].contains[<player>]> || <server.flag[spleef_spectating].contains[<player>]>:
            - run spleef_set_player_list def:<player>|
            - sidebar remove players:<player>|

    cancel_event:
        - if <server.flag[spleef_queued].contains[<player>]> || <server.flag[spleef_playing].contains[<player>]> || <server.flag[spleef_spectating].contains[<player>]>:
            - determine cancelled
