# # Storyboard NPC Tasks # # Adds a complex set of per-player state managed NPC routines which # allow for the creation of RPG-like stories, similar to Undertale. # # The routines and state system is also as memory efficient and lazy # as possible. It utilizes Denizen and Citizens to their best, taking # advantage of Denizen's innate scripting possibilites. # # Allocates an NPC with a given name at some location for the current player. # # Optionally, specify a display name, display name visibility, and a skin_blob. # Generate Skin Blobs using https://mineskin.org - it cannot be done automatically # at this time. # # If the NPC is reallocated from player state, it will try to re-fill these values. # You can specify new values to change these. # Note it will only try to fill these optional values, not "name", "type", and "at". # # The name of the NPC must be unique - this is not the same as the display # name of the NPC; rather it is an internal name, so it's recommended to # keep it lowercase. # # If the NPC is not allocated but was saved in the player's mem state, it # will be created and its state will be restored. # If the NPC is already allocated, it will simply teleport the NPC. # # All NPCs are memfree'd when a player leaves, which does not reduce storage # overhead, but does greatly reduce overhead when hiding/showing per-player NPCs. # They are automatically memalloc'ed when a player rejoins. storyboard_npc_memalloc: debug: false type: task definitions: player|name|type|at|display_name|show_name|skin_blob script: - define registry registry_<[player].uuid> - define npc_id npc_<[player].uuid>_<[name]> - if !].if_null[].contains[<[npc_id]>]>: - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - create <[type]> <[npc_id]> <[at]> registry:<[registry]> save:npc - define npc - define npc_state - define assignment null - if <[npcs].contains[<[name]>]>: - define npc_data <[npcs].get[<[name]>]> - define npc_state <[npc_data].get[state]> - if <[display_name].if_null[null]> == null: - define display_name <[npc_data].get[display_name]> - if <[show_name].if_null[null]> == null: - define show_name <[npc_data].get[show_name]> - if <[skin_blob].if_null[null]> == null: - define skin_blob <[npc_data].get[skin_blob]> - define assignment <[npc_data].get[assignment].if_null[null]> - else: - if <[display_name].if_null[null]> == null: - define display_name <[display_name].if_null[<[name].to_sentence_case>]> - if <[show_name].if_null[null]> == null: - define show_name <[show_name].if_null[true]> - if <[skin_blob].if_null[null]> == null: - define skin_blob <[skin_blob].if_null[null]> - definemap npc_data: name: <[name]> type: <[type]> at: <[at]> state: <[npc_state]> display_name: <[display_name]> show_name: <[show_name]> skin_blob: <[skin_blob]> allocated: true assignment: <[assignment]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> - adjust <[npc]> auto_update_skin:false - adjust <[npc]> name_visible:false - lookclose <[npc]> state:true range:4 - if !<[show_name]>: - adjust <[npc]> hologram_lines: - else: - adjust <[npc]> "hologram_lines:<&f>鐓 <[display_name]>" - if <[skin_blob]> != null: - adjust <[npc]> skin_blob:<[skin_blob]> - if <[assignment]> != null: - assignment set script:<[assignment]> to:<[npc]> - wait 2t - run storyboard_npc_internal_show_to_player def.player:<[player]> def.npc:<[npc]> - else: - define index ].find[<[npc_id]>]> - define npc ,<[registry]>]> - teleport <[npc]> <[at]> # Retrieves an NPC from the player's unique NPC registry by name. storyboard_npc_by_name: debug: false type: procedure definitions: player|name script: - define registry registry_<[player].uuid> - define npc_id npc_<[player].uuid>_<[name]> - determine ].filter_tag[<[filter_value].name.equals[<[npc_id]>]>].get[1]> # Frees an NPC from memory, but does not destroy its state. # # This is often done automatically when the player leaves the server, however it may # be useful for programmers to manually call this routine for optimization purposes. # # If you don't want the server to reallocate the NPC on player join, provide # the reallocate definition as 'reallocate' (or just don't provide it). storyboard_npc_memfree: debug: false type: task definitions: player|name|reallocate script: - define npc |<[name]>]> - define reallocate <[reallocate].if_null[false]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define npc_data <[npc_data].with[allocated].as[<[reallocate]>]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> - remove <[npc]> # Destroys an NPC from memory, which destroys its state. # Further memalloc's will re-create the NPC with new state data. # Use this when you are 100% done with using an NPC. # If you only want to remove an NPC until later, but keep its state, # consider calling storyboard_npc_memfree instead. storyboard_npc_memdestroy: debug: false type: task definitions: player|name script: - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npcs <[npcs].exclude[<[name]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> - define npc |<[name]>]> - remove <[npc]> # Flags the NPC by name, mapping the given key to the given value. storyboard_npc_state_set: debug: false type: task definitions: player|name|key|value script: - define npc |<[name]>]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define state <[npc_data].get[state].if_null[]> - define state <[state].with[<[key]>].as[<[value]>]> - define npc_data <[npc_data].with[state].as[<[state]>]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> # Gets a state value from the NPC by name and key; effectively like reading a flag. # If the given key does not exist/is not set, determines null. storyboard_npc_state_get: debug: false type: procedure definitions: player|name|key script: - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define state <[npc_data].get[state].if_null[]> - determine <[state].get[<[key]>].if_null[null]> # Deletes a flag from the NPC by name and key. storyboard_npc_state_clear: debug: false type: task definitions: player|name|key script: - define npc |<[name]>]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define state <[npc_data].get[state].if_null[]> - define state <[state].exclude[<[key]>]> - define npc_data <[npc_data].with[state].as[<[state]>]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> # Sets an NPCs assignment, which also persists across memfrees. storyboard_npc_set_assignment: debug: false type: procedure definitions: player|name|assignment script: - define npc |<[name]>]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define npc_data <[npc_data].with[assignment].as[<[assignment]>]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> - assignment set script:<[assignment]> to:<[npc]> # Moves an NPC to a new location, both physically, and in memory also. # When an NPC is moved by normal teleports or the walk command, its location isn't stored. # Use this to either commit its final position, or teleport it after a cutscene. storyboard_npc_movement_commit: debug: false type: procedure definitions: player|name|new_location script: - define npc |<[name]>]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[]> - define npc_data <[npcs].get[<[name]>]> - define npc_data <[npc_data].with[at].as[<[new_location]>]> - define npcs <[npcs].with[<[name]>].as[<[npc_data]>]> - flag <[player]> storyboard_state:<[player].flag[storyboard_state].if_null[].with[npcs].as[<[npcs]>]> - teleport <[npc]> <[new_location]> ## Internal only! storyboard_npc_internal_auto_memory_management: debug: false type: world events: after player joins: - define npcs ]> - foreach <[npcs]> key:name as:data: - if <[data].get[allocated]> == reallocate: - define name <[data].get[name]> - define type <[data].get[type]> - define at <[data].get[at]> - run storyboard_npc_memalloc def.player: def.name:<[name]> def.type:<[type]> def.at:<[at]> on player quits: - define registry registry_ - define npcs ].if_null[]> - define substr_length _].length.add[1]> - foreach <[npcs]> as:npc: - define name <[npc].name.substring[<[substr_length]>]> - run storyboard_npc_memfree def.player: def.name:<[name]> def.reallocate:reallocate storyboard_npc_internal_show_to_player: debug: false type: task definitions: player|npc script: - adjust <[npc]> hide_from_players - adjust <[player]> show_entity:<[npc]> - foreach <[npc].hologram_npcs.if_null[]> as:hologram: - adjust <[hologram]> hide_from_players - adjust <[player]> show_entity:<[hologram]> storyboard_npc_internal_auto_display_entities: debug: false type: world events: on player joins bukkit_priority:high: - foreach ]> as:target: - define registry registry_<[target].uuid> - define npcs ].if_null[]> - foreach <[npcs]> as:npc: - adjust hide_entity:<[npc]> - foreach <[npc].hologram_npcs.if_null[]> as:hologram: - adjust hide_entity:<[hologram]>