## =========================================== ## ## Manhunt minigame implemented using Denizen ## ## by @seb303 ## ## v1.0.0 2022-05-20 ## ## Requires Denizen-1.2.4-b6247-DEV or newer ## https://ci.citizensnpcs.co/job/Denizen_Developmental/ ## Requires Multiverse-Core ## https://dev.bukkit.org/projects/multiverse-core ## Optional Multiverse-NetherPortals ## https://dev.bukkit.org/projects/multiverse-netherportals ## Optional Multiverse-Inventories ## https://dev.bukkit.org/projects/multiverse-inventories ## =========================================== ## ## To do: # Team chat for private chat between runners or hunters # Automatic world generation/reset from random seed # Nether Fortress tracker for runners # Piglin enderpearl drop rate boost for runners # Requires the following noted cuboids: # hunt_runner = the portal through which the runner(s) join the game (should not be in hunt world) # hunt_hunter = the portal through which the hunter(s) join the game (should not be in hunt world) # # Requires the following noted locations: # hunt_start = Start location in hunt world (recommended to be very near world spawn) # hunt_endlobby = Location to teleport to when leaving hunt or when hunt ends # # To note a location, stand in the location and use command: # /ex note as: # To note cuboids use the Denizen Area Selector Tool # https://forum.denizenscript.com/resources/area-selector-tool.1/ with this command: # /selnote # Or if you have WorldEdit & Depenizen installed, you can use the WorldEdit wand and command: # /ex note as: ## -------- ## ## COMMANDS ## ## -------- ## # # To start the hunt # /hunt start [] # Permission: custom.hunt # # To leave the hunt and return to hunt_endlobby # /hunt leave # Permission: custom.hunt # # To reset the hunt and all flags # /hunt reset # Permission: custom.hunt & custom.hunt.reset ## CONFIG STARTS ## hunt_config: type: data # Default head start time in seconds for runners # Can be overridden in the /hunt start command head_start_time: 30 # List of the worlds where the hunt takes places worlds: - manhunt - manhunt_nether - manhunt_the_end ## CONFIG ENDS ## # ---------------------------------------------------------------------------------------- # Server flags: # hunt.waiting.joining - hunt is waiting for players to join # hunt.waiting.headstart - hunt is waiting for runners to have a head start, value is countdown timer # hunt.active - hunt is active (set after head start) # # Player flags: # hunt.runner - player is a runner # hunt.hunter - player is a hunter # hunt.spectator - player is a spectator (dead runner or joined after start) # hunt.joins - player is just joining server # hunt.tracking - runner that hunter's compass is tracking # hunt.lastportal.NORMAL - location of last portal that a runner used in the overworld # hunt.lastportal.NETHER - location of last portal that a runner used in the nether # hunt.lastportal.THE_END - location of last portal that a runner used in the end # ---------------------------------------------------------------------------------------- hunt_events: type: world debug: false events: # Secondly event to update titles, etc. during waiting phases on delta time secondly server_flagged:hunt.waiting: - define runners - define hunters - define all_players <[runners].include[<[hunters]>]> # List names of runners and hunters - define sidebar_text Runners]> - if <[runners].size> == 0: - define "sidebar_text:->:<&a> None!" - else: - foreach <[runners]> as:__player: - define "sidebar_text:->:<&a> " - define sidebar_text:|:|<&a>Hunters - if <[hunters].size> == 0: - define "sidebar_text:->:<&a> None!" - else: - foreach <[hunters]> as:__player: - define "sidebar_text:->:<&a> " - if : # Joining phase - define sidebar_title "Waiting for Players" - define "sidebar_text:|:|<&c>To start the hunt:|<&b>/hunt start||<&c>To leave the hunt:|<&b>/hunt leave" - sidebar set title:<&6><[sidebar_title]> values:<[sidebar_text]> players:<[all_players]> - else if : # Head start phase - if <= 0: # End of head start - flag server hunt: - sidebar remove players:<[all_players]> - foreach <[hunters]> as:__player: - inject hunt_events.set_hunter_active - else: # Head start phase - define sidebar_title "Runners Go!" - define less_text "The hunters will be released in s" - define "sidebar_text:|:|<&a><[less_text]>||<&c>To leave the hunt:|<&b>/hunt leave" - sidebar set title:<&6><[sidebar_title]> values:<[sidebar_text]> players:<[runners]> - sidebar remove players:<[hunters]> - actionbar <&c><[less_text]> targets:<[hunters]> - flag server hunt.waiting.headstart:-- # The 2 entry portals on player enters hunt_runner: - run hunt_enters_portal def:runner on player enters hunt_hunter: - run hunt_enters_portal def:hunter on player teleports flagged:hunt: # Allow a player just joining the server to first teleport to lobby - if && == lobby: - flag hunt.joins:! - stop # Only allow leaving using the "/hunt leave" command - if !]>: - narrate "<&[error]>To leave use command <&[emphasis]>/hunt leave" - determine cancelled # Enforce spectator on world change - if : # Need to wait a short while before setting gamemode otherwise Multiverse will override # 2t seems to be enough, but wait 5t just in case - wait 5t - adjust gamemode:spectator on player joins flagged:hunt bukkit_priority:LOWEST: - if && > 1: # There is another player in an active hunt, so join back # Flag that initial join teleport should be allowed - flag hunt.joins:1 # Wait a short while to allow initial teleport to lobby to occur # (otherwise /mv tp will ignore last_location) - wait 2t - execute as_server "mv tp " - stop # No active hunt # Clear server flag if it was active - if !: - flag server hunt:! # Clear player flag and sidebar - flag hunt:! - sidebar remove on player drops item flagged:hunt.hunter: # Don't allow hunters to drop their compass - if == compass: - determine cancelled on player left clicks block flagged:hunt.hunter: # Hunters left clicks compass - if == compass: - determine passively cancelled - ratelimit 1t - define onlineRunners # Currently tracking an online runner? - if && : # Get next runner to track - if <[onlineRunners].size> > 1: - define pickNext false - foreach <[onlineRunners]> as:runner: - if <[pickNext]>: - flag hunt.tracking:<[runner]> - actionbar "<&7>Now tracking: <&a><[runner].name>" - stop - if <[runner]> == : - define pickNext true # Pick first online runner to track - flag hunt.tracking:<[onlineRunners].first.if_null[null]> - if == null: - actionbar "<&c>No runner to track" - else: - actionbar "<&7>Now tracking: <&a>" - stop on player right clicks block flagged:hunt.hunter: # Hunters right clicks compass - if == compass: - determine passively cancelled - ratelimit 1t # Check if runner online and still a runner (not died or left) - if ! || !: - flag hunt.tracking: - define tracking - define hunterEnv - if <[tracking]> == null: # No online runner to track - actionbar "<&c>No runner to track" - inject hunt_events.reset_compass_location - stop - else if <[tracking].location.world.name> != : # Runner in different world # Check for runner's last used portal - if <[tracking].has_flag[hunt.lastportal.<[hunterEnv]>]>: - define trackingLocation <[tracking].flag[hunt.lastportal.<[hunterEnv]>]> - define distance ].round> - actionbar "<&7>Distance: <&b><[distance]>m <&7>Hunting: <&a><[tracking].name>'s portal" - inject hunt_events.set_compass_location - stop - if <[hunterEnv]> == NORMAL: # Shouldn't ever get to here since the runner must have used a portal - actionbar "<&c>Hmm, can't find any trace of the runner!" - inject hunt_events.reset_compass_location - stop - else: # Hunter in Nether or End, and runner didn't leave through a portal, so find another runner to track - define found false - foreach as:runner: - if <[runner].location.world.name> == : - flag hunt.tracking:<[runner]> - define tracking <[runner]> - define found true - if !<[found]>: - choose <[hunterEnv]>: - case NETHER: - actionbar "<&c>No runner in The Nether to track" - case THE_END: - actionbar "<&c>No runner in The End to track" - default: - actionbar "<&c>No runner in <[hunterEnv]> to track" - inject hunt_events.reset_compass_location - stop # Runner in same world - define trackingLocation <[tracking].location> - define distance ].round> - actionbar "<&7>Distance: <&b><[distance]>m <&7>Hunting: <&a><[tracking].name>" - inject hunt_events.set_compass_location on player dies flagged:hunt.runner: # If a runner dies, switch them to spectator - determine passively cancelled - adjust gamemode:spectator - flag hunt: - if > 0: - title "title:You Died!" fade_in:0 stay:5 fade_out:2 - else: - inject hunt_events.check_for_winner on player enters portal flagged:hunt.runner: # Runner wins the game? - if == THE_END: - title "title:The Runner Wins!" fade_in:0 stay:5 fade_out:2 targets: - wait 5 - foreach as:__player: - flag hunt:! - sidebar remove - teleport hunt_endlobby - flag server hunt:! # Record last used portals by runners on player uses portal flagged:hunt.runner: - flag hunt.lastportal.: after player dies flagged:hunt.runner: - flag hunt.lastportal:! # At beginning of head start phase set_runner_active: - inventory clear - adjust health:20 - feed amount:20 - adjust gamemode:survival # At beginning of head start phase set_hunter_frozen: - inventory clear - adjust health:20 - feed amount:20 - inventory set o:compass slot:1 - flag hunt.tracking:null # Freeze hunter during head start phase - define blindness - define slow - define jump - adjust potion_effects:].include_single[<[slow]>].include_single[<[jump]>]> # Set world time & weather - adjust time:1000 - weather sunny # After head start phase set_hunter_active: - adjust remove_effects - inject hunt_events.set_runner_active - inventory set o:compass slot:1 # Set compass to tracking location set_compass_location: - if <[hunterEnv]> == NORMAL: - compass <[trackingLocation]> - foreach as:slot: - inventory adjust slot:<[slot]> lodestone_tracked:false - inventory adjust slot:<[slot]> lodestone_location: - else: - foreach as:slot: - inventory adjust slot:<[slot]> lodestone_tracked:false - inventory adjust slot:<[slot]> lodestone_location:<[trackingLocation]> # Reset compass reset_compass_location: - if <[hunterEnv]> == NORMAL: - compass reset - foreach as:slot: - inventory adjust slot:<[slot]> lodestone_tracked:false - inventory adjust slot:<[slot]> lodestone_location: - else: - foreach as:slot: - inventory adjust slot:<[slot]> lodestone_tracked:true check_for_winner: - if == 0: - title "title:The Hunter Wins!" fade_in:0 stay:5 fade_out:2 targets: - wait 5 - foreach as:__player: - flag hunt:! - sidebar remove - teleport hunt_endlobby - flag server hunt:! hunt_enters_portal: type: task debug: false # runner or hunter definitions: which teleport: - teleport hunt_start - wait 1t # Check world in case Multiverse has teleported player to last_location in another dimension - if != : - teleport hunt_start script: - if !: # Hunt not active, so player can join at hunt_start - flag hunt:=1]> - inject hunt_enters_portal.teleport # Set world time & weather - adjust time:1000 - weather sunny # Need to wait a short while before setting gamemode otherwise Multiverse will override # 2t seems to be enough, but wait 5t just in case - wait 5t - adjust gamemode:adventure # Clear inventory, heal & feed - inventory clear - adjust health:20 - feed amount:20 - if !: # Start/continue joining phase - flag server hunt.waiting.joining - else: # Already in head start phase - if <[which]> == runner: - inject hunt_events.set_runner_active - else: - inject hunt_events.set_hunter_frozen - else: # Hunt active, new player must spectate - flag hunt: - inject hunt_enters_portal.teleport # Need to wait a short while before setting gamemode otherwise Multiverse will override # 2t seems to be enough, but wait 5t just in case - wait 5t - adjust gamemode:spectator - title "title:Hunt already started" "subtitle:You are spectating" fade_in:0 stay:5 fade_out:2 - wait 5 hunt_command: type: command debug: false name: hunt description: Speedrunners vs Hunters command usage: /hunt start|leave|reset tab completions: 1: start|leave|reset permission: custom.hunt show_docs: - narrate <&7><&sp.repeat[70].strikethrough> - narrate "<&6>/hunt start <&o>[<<>head start time<>>]<&f> Start the hunt" - narrate "<&6>/hunt leave <&f> Leave the hunt and return to hub" - if : - narrate "<&6>/hunt reset <&f> Reset the hunt" - narrate <&7><&sp.repeat[70].strikethrough> - stop no_permission: - narrate "<&[error]>You don't have permission to use this command" script: - if == 0: - inject hunt_command.show_docs - choose : # /hunt start - case start: # Override configured head_start_time time? - if == 2: - define head_start_time - if !<[head_start_time].matches_character_set[0123456789]>: - inject hunt_command.show_docs - else if == 1: - define head_start_time - else: - inject hunt_command.show_docs # Check that conditions are valid for command to proceed - if : - narrate "<&[error]>The hunt has already started" - stop - define runners - define hunters - if <[runners].size> == 0: - narrate "<&[error]>There must be at least 1 runner to start" - stop - if <[hunters].size> == 0: - narrate "<&[error]>There must be at least 1 hunter to start" - stop # Start head start phase of hunt - flag server hunt.waiting:]> - foreach <[runners]> as:__player: - inject hunt_events.set_runner_active - foreach <[hunters]> as:__player: - inject hunt_events.set_hunter_frozen # Display help info for hunters - if <[head_start_time]> < 6: # Min time to show help info - define head_start_time 6 - define title "<&a>Use the Compass" - define subtitle "Right click to track a runner" - if <[runners].size> == 1: - title title:<[title]> subtitle:<[subtitle]> fade_in:0 stay:<[head_start_time]> fade_out:1 targets:<[hunters]> - else: - define subtitle2 "Left click to switch runners" - repeat <[head_start_time].div[6].round_down>: - title title:<[title]> subtitle:<[subtitle]> fade_in:0 stay:3 fade_out:1 targets:<[hunters]> - wait 3 - title title:<[title]> subtitle:<[subtitle2]> fade_in:0 stay:3 fade_out:1 targets:<[hunters]> - wait 3 # /hunt leave - case leave: - if != 1: - inject hunt_command.show_docs - flag hunt:! - sidebar remove - teleport hunt_endlobby - if == 0: - flag server hunt:! - else: - inject hunt_events.check_for_winner # /hunt reset - case reset: - if != 1: - inject hunt_command.show_docs - if !: - inject hunt_command.no_permission - stop - foreach as:__player: - flag hunt:! - sidebar remove - teleport hunt_endlobby - flag server hunt:! - default: - inject hunt_command.show_docs