# +---------------
# |
# | d S e n t r y
# |
# | Sentry rescripted in Denizen!
#
# @author mcmonkey
-# @date 2014 / 10 / 19
+# @date 2014 / 11 / 19
# @build 1534
# @version 6.2
#
# Installation:
# This script requires a server restart before it will begin running.
# Alternately, you can use the command: /ex run dsentry_task_logic
#
# Usage:
# Create NPCs as normal, and use the /dsentry commands to upgrade them.
# Example NPC setup path:
# /npc create JoeTheKiller # Create and spawn the NPC
# /dsentry create # Turn it into a dSentry
# /ex equip <npc> hand:bow # Give the dSentry a bow
# # The dSentry is now a complete and working bowguard, targetting monsters by default
# # Feel free to adjust the targets to your liking, or any other setting you desire.
#
#
# ---------------------------- END HEADER ----------------------------
# Handles all world events for dSentry
dsentry_world_handler:
type: world
debug: false
events:
on server start:
- if <server.has_file[dsentry_saves.yml]> {
- yaml load:dsentry_saves.yml id:dsentry_saves
}
else {
- yaml create id:dsentry_saves
}
- run dsentry_task_logic delay:10t
on player damaged by player:
- foreach <server.get_npcs_flagged[dsentry.is_dsentry]> {
- if <def[value].is_spawned> && <def[value].flag[dsentry.target]||null> == null
&& <def[value].flag[dsentry.eventtargets].as_list||li@> contains PvP
&& <def[value].location.distance[<context.damager.location>]> < <def[value].flag[dsentry.range]> {
- flag <def[value]> dsentry.target:<context.damager>
}
}
on entity damaged by player:
- if <context.entity> matches player queue clear
- if <context.entity> matches npc queue clear
- foreach <server.get_npcs_flagged[dsentry.is_dsentry]> {
- if <def[value].is_spawned> && <def[value].flag[dsentry.target]||null> == null
&& <def[value].flag[dsentry.eventtargets].as_list||li@> contains PvE
&& <def[value].location.distance[<context.damager.location>]> < <def[value].flag[dsentry.range]> {
- flag <def[value]> dsentry.target:<context.damager>
}
}
on npc damaged by player:
- if <npc.flag[dsentry.is_dsentry]> flag <context.entity> dsentry.target:<context.damager>
# TODO: Better way to prevent accidental damage
#on npc damaged by npc:
#- if <context.entity.as_npc.flag[dsentry.is_dsentry]||false> && <context.damager.as_npc.flag[dsentry.is_dsentry]||false> determine cancelled
on npc damages entity:
- if !<context.damager.as_npc.flag[dsentry.is_dsentry]> queue clear
- determine <context.damager.as_npc.flag[dsentry.damage]>
on dsentry command:
- determine passively fulfilled
- if <context.server> {
- define mynpc <server.selected_npc>
}
else {
- if !<player.has_permission[dsentry.command]||false> {
- narrate "<&6>You do not have permission to use the dSentry command."
- queue clear
}
- define mynpc <player.selected_npc>
}
- if %mynpc% == null && <context.args.get[1].length||0> != 0 {
- narrate "<&6>You do not have an NPC selected!"
- queue clear
}
- if !<def[mynpc].flag[dsentry.is_dsentry]> && <context.args.get[1].length||0> != 0 && !<context.args.get[1].is[==].to[create]||false> {
- narrate "<&6>Your selected NPC is not a dSentry!"
- queue clear
}
- define arg1 <c.args.get[1].escaped||null>
- define argList li@create|save|range|followrange|speed|attackrate|damage|add|remove|info|guard
- if <def[argList].contains[%arg1%]> inject dsentry_command_%arg1%
else {
- narrate "<&6>/dSentry create [save-type]"
- narrate "<&6>/dSentry save <<>save-type<>>"
- narrate "<&6>/dSentry range <<>range<>>"
- narrate "<&6>/dSentry followrange <<>range<>>"
- narrate "<&6>/dSentry speed <<>speed<>>"
- narrate "<&6>/dSentry damage <<>damage<>>"
- narrate "<&6>/dSentry attackrate <<>attackrate<>>"
- narrate "<&6>/dSentry add <<>target<>>"
- narrate "<&6>/dSentry remove <<>target<>>"
- narrate "<&6>/dSentry guard [player-name]"
- narrate "<&6>/dSentry info"
}
dsentry_command_info:
type: task
debug: false
script:
- narrate "<&6>Your selected NPC <&2>is<&6> a dSentry with stats<&co>"
- narrate "<&6>Range<&co> <&b><def[mynpc].flag[dsentry.range]>"
- narrate "<&6>Damage<&co> <&b><def[mynpc].flag[dsentry.damage]>"
- narrate "<&6>Walk speed<&co> <&b><def[mynpc].flag[dsentry.walkspeed]>"
- narrate "<&6>Attack rate<&co> <&b><def[mynpc].flag[dsentry.attackrate]>"
- if <def[mynpc].flag[dsentry.flagtargets].size||0> == 0 {
- narrate "<&6>Targets<&co> <&b><def[mynpc].flag[dsentry.targets].formatted||None>"
}
else {
- narrate "<&6>Targets<&co> <&b><def[mynpc].flag[dsentry.targets].formatted||None><&6>, or players flagged <&b><def[mynpc].flag[dsentry.flagtargets].formatted||None>"
}
dsentry_command_guard:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[guard]||true> {
- flag %mynpc% dsentry.follow_target:!
- narrate "<&c>Guarding nothing."
- queue clear
}
- if <util.player_is_valid[<context.args.get[2].escaped>]> {
- define player <player[<context.args.get[2].escaped>]||null>
- if %player% == null {
- narrate "<&c>Unknown player."
- queue clear
}
- flag %mynpc% dsentry.follow_target:%player%
- narrate "<&c>Guarding <&b><def[player].name><&c>!"
}
else {
- narrate "<&c>Unknown player."
- queue clear
}
dsentry_command_add:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry add <<>target<>>"
- narrate "<&6>Target = an entity type, EG creeper or pig"
- narrate "<&6>You can also do flagged:FlagName"
- queue clear
}
- if <context.args.get[2].starts_with[flagged<&co>]> {
- if <def[mynpc].flag[dsentry.flagtargets].contains[<context.args.get[2].replace[flagged<&co>]>]||false> {
- narrate "<&6>That flag target is already set."
- queue clear
}
- flag %mynpc% dsentry.flagtargets:->:<context.args.get[2].replace[flagged<&co>]>
- narrate "<&6>Flag target added."
- queue clear
}
- if <context.args.get[2].starts_with[event<&co>]> {
- if <def[mynpc].flag[dsentry.eventtargets].contains[<context.args.get[2].replace[event<&co>]>]> {
- narrate "<&6>That event target is already set."
- queue clear
}
- flag %mynpc% dsentry.eventtargets:->:<context.args.get[2].replace[event<&co>]>
- narrate "<&6>Event target added."
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That target doesn't look like a proper entity type."
- queue clear
}
- if <def[mynpc].flag[dsentry.targets].contains[<context.args.get[2]>]||false> {
- narrate "<&6>That target is already set."
- queue clear
}
- flag %mynpc% dsentry.targets:->:<context.args.get[2]>
- narrate "<&6>Added target."
dsentry_command_remove:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry remove <<>target<>>"
- narrate "<&6>Target = an entity type, EG creeper or pig"
- queue clear
}
- if <context.args.get[2].starts_with[flagged<&co>]> {
- if !<def[mynpc].flag[dsentry.flagtargets].contains[<context.args.get[2].replace[flagged<&co>]>]||false> {
- narrate "<&6>That flag target isn't set."
- queue clear
}
- flag %mynpc% dsentry.flagtargets:<-:<context.args.get[2].replace[flagged<&co>]>
- narrate "<&6>Flag target removed."
- queue clear
}
- if <context.args.get[2].starts_with[event<&co>]> {
- if !<def[mynpc].flag[dsentry.eventtargets].contains[<context.args.get[2].replace[event<&co>]>]> {
- narrate "<&6>That event target isn't set."
- queue clear
}
- flag %mynpc% dsentry.eventtargets:<-:<context.args.get[2].replace[event<&co>]>
- narrate "<&6>Event target removed."
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That target doesn't look like a proper entity type."
- queue clear
}
- if !<def[mynpc].flag[dsentry.targets].contains[<context.args.get[2]>]||false> {
- narrate "<&6>That target isn't set."
- queue clear
}
- flag %mynpc% dsentry.targets:<-:<context.args.get[2]>
- narrate "<&6>Removed target."
dsentry_command_range:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry range <<>range<>>"
- narrate "<&6>Range = a number representing how far away (in blocks) the dSentry can attack targets at"
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That range value doesn't look like a proper number."
- queue clear
}
- if !<context.args.get[2].is[matches].to[integer]> {
- narrate "<&6>That range value doesn't look like a proper number."
- queue clear
}
- flag %mynpc% dsentry.range:<context.args.get[2]>
- narrate "<&6>Range set."
dsentry_command_followrange:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry followrange <<>range<>>"
- narrate "<&6>Range = a number representing how far away (in blocks) the dSentry can follow players at"
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That range value doesn't look like a proper number."
- queue clear
}
- if !<context.args.get[2].is[matches].to[integer]> {
- narrate "<&6>That range value doesn't look like a proper number."
- queue clear
}
- flag %mynpc% dsentry.follow_range:<context.args.get[2]>
- narrate "<&6>Follow range set."
dsentry_command_damage:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry damage <<>damage<>>"
- narrate "<&6>Damage = a number representing how many hearts of damage the NPC deals when it attacks"
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That damage value doesn't look like a proper number."
- queue clear
}
- if !<context.args.get[2].is[matches].to[integer]> {
- narrate "<&6>That damage value doesn't look like a proper number."
- queue clear
}
- flag %mynpc% dsentry.damage:<context.args.get[2]>
- narrate "<&6>Damage set."
dsentry_command_speed:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry speed <<>speed<>>"
- narrate "<&6>Speed = a number representing how fast the NPC should walk while attacking"
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That speed value doesn't look like a proper number."
- queue clear
}
- if !<context.args.get[2].is[matches].to[integer]> {
- narrate "<&6>That speed value doesn't look like a proper number."
- queue clear
}
- flag %mynpc% dsentry.walkspeed:<context.args.get[2]>
- narrate "<&6>Speed set."
dsentry_command_attackrate:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry attackrate <<>attackrate<>>"
- narrate "<&6>AttackRate = a number representing how many ticks should pass between an NPC's attacks"
- narrate "<&6>The minimum is 10. All higher values must be multiples of 10."
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That attackrate value doesn't look like a proper number."
- queue clear
}
- if !<context.args.get[2].is[matches].to[integer]> {
- narrate "<&6>That attackrate value doesn't look like a proper number."
- queue clear
}
- flag %mynpc% dsentry.attackrate:<context.args.get[2]>
- flag %mynpc% dsentry.attackping:0
- narrate "<&6>AttackRate set."
dsentry_command_save:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- narrate "<&6>/dSentry save <<>save-type<>>"
- narrate "<&6>Save-type = a name that can be used to recreate the dSentry with later"
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That save name looks potentially problematic."
- queue clear
}
- yaml write:dsentry.<context.args.get[2]>.valid value:true id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.range value:<def[mynpc].flag[dsentry.range]> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.follow_range value:<def[mynpc].flag[dsentry.follow_range]> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.follow_target value:<def[mynpc].flag[dsentry.follow_target]||null> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.attackrate value:<def[mynpc].flag[dsentry.attackrate]> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.walkspeed value:<def[mynpc].flag[dsentry.walkspeed]> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.damage value:<def[mynpc].flag[dsentry.damage]> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.targets value:<def[mynpc].flag[dsentry.targets].as_list||li@> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.flagtargets value:<def[mynpc].flag[dsentry.flagtargets].as_list||li@> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.hand value:<def[mynpc].item_in_hand> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.helmet value:<def[mynpc].equipment.helmet> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.chestplate value:<def[mynpc].equipment.chestplate> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.leggings value:<def[mynpc].equipment.leggings> id:dsentry_saves
- yaml write:dsentry.<context.args.get[2]>.boots value:<def[mynpc].equipment.boots> id:dsentry_saves
- yaml savefile:dsentry_saves.yml id:dsentry_saves
- narrate "<&6>dSentry type saved!"
dsentry_command_create:
type: task
debug: false
script:
- if <context.args.get[2].is[==].to[null]||true> {
- flag %mynpc% dsentry.range:15
- flag %mynpc% dsentry.follow_range:3
- flag %mynpc% dsentry.follow_target:!
- flag %mynpc% dsentry.walkspeed:1
- flag %mynpc% dsentry.attackrate:30
- flag %mynpc% dsentry.attackping:0
- flag %mynpc% dsentry.damage:2
- flag %mynpc% dsentry.targets:!
- flag %mynpc% dsentry.targets:|:zombie|creeper|spider|skeleton|witch
- flag %mynpc% dsentry.flagtargets:!
- adjust %mynpc% teleport_on_stuck:true
- flag %mynpc% dsentry.is_dsentry
- adjust %mynpc% teleport_on_stuck:false
- narrate "<&6>NPC turned into a default dSentry."
- queue clear
}
- if !<context.args.get[2].is[==].to[<context.args.get[2].escaped>]> {
- narrate "<&6>That save name looks potentially problematic."
- queue clear
}
- define base dsentry.<context.args.get[2]>.
- if !<yaml[dsentry_saves].read[%base%valid]||null> != true {
- narrate "<&6>There are no saves by that name."
- queue clear
}
- - equip %mynpc% hand:<yaml[dsentry_saves].read[%base%hand]> head:<yaml[dsentry_saves].read[%base%helmet]> chest:<yaml[dsentry_saves].read[%base%chestplate]> legs:<yaml[dsentry_saves].read[%base%leggings]> boots:<yaml[dsentry_saves].read[%base%boots]>
+ - equip %mynpc% hand:<yaml[dsentry_saves].read[%base%hand]> head:<yaml[dsentry_saves].read[%base%helmet]> chest:<yaml[dsentry_saves].read[%base%chestplate]>
+ legs:<yaml[dsentry_saves].read[%base%leggings]> boots:<yaml[dsentry_saves].read[%base%boots]>
- flag %mynpc% dsentry.follow_range:<yaml[dsentry_saves].read[%base%follow_range]>
- flag %mynpc% dsentry.follow_target:<yaml[dsentry_saves].read[%base%follow_target]>
- flag %mynpc% dsentry.range:<yaml[dsentry_saves].read[%base%range]>
- flag %mynpc% dsentry.walkspeed:<yaml[dsentry_saves].read[%base%walkspeed]>
- flag %mynpc% dsentry.attackrate:<yaml[dsentry_saves].read[%base%attackrate]>
- flag %mynpc% dsentry.attackping:0
- flag %mynpc% dsentry.damage:<yaml[dsentry_saves].read[%base%damage]>
- flag %mynpc% dsentry.targets:!
- flag %mynpc% dsentry.targets:|:<yaml[dsentry_saves].read[%base%targets]>
- flag %mynpc% dsentry.flagtargets:!
- flag %mynpc% dsentry.flagtargets:|:<yaml[dsentry_saves].read[%base%flagtargets]>
- flag %mynpc% dsentry.is_dsentry
- narrate "<&6>NPC turned into a custom dSentry."
# Runs repeatedly as the dSentry logic loop
dsentry_task_logic:
type: task
debug: false
script:
- foreach <server.get_npcs_flagged[dsentry.is_dsentry]> {
- if <def[value].is_spawned> {
- run dsentry_task_attack npc:<def[value]> instantly
- if <def[value].flag[dsentry.target]||null> == null {
- resume waypoints npc:%value%
}
}
}
- run dsentry_task_logic delay:10t
# Chooses a nearby target
dsentry_proc_picktarget:
type: procedure
debug: false
script:
- if <npc.flag[dsentry.target]||null> != null {
- if <npc.flag[dsentry.target].as_entity.is_spawned> {
- if <npc.location.distance[<npc.flag[dsentry.target].as_entity.location>]> < <npc.flag[dsentry.range]> {
- determine <npc.flag[dsentry.target]>
}
}
}
- if <npc.flag[dsentry.targets].as_list||null> == null {
- announce to_console "<&6>NPC <npc.id> is marked as a dSentry but does not have a targets list!"
- queue clear
}
- if <npc.flag[dsentry.targets].size> == 0 {
- announce to_console "<&6>NPC <npc.id> is marked as a dSentry but does not have a targets list!"
- queue clear
}
- define entities <npc.location.find.entities[<npc.flag[dsentry.targets].as_list>|player].within[<npc.flag[dsentry.range]>].exclude[<npc>]>
- define entity null
- foreach %entities% {
- if %value% matches npc {
- define entities <def[entities].exclude[%value%]>
}
}
- foreach %entities% {
- if %value% matches player {
- define playrar %value%
- define TEMP_PLAYER_PASS <npc.flag[dsentry.targets].contains[player]||false>
- foreach <npc.flag[dsentry.flagtargets].as_list||li@> {
- if <def[playrar].flag[%value%]||null> != null {
- define TEMP_PLAYER_PASS true
}
}
- if <def[playrar].gamemode> == CREATIVE {
- define TEMP_PLAYER_PASS false
}
- if !%TEMP_PLAYER_PASS% {
- define entities <def[entities].exclude[%playrar%]>
}
}
}
- foreach %entities% {
- if <npc.can_see[%value%]> {
- define entity %value%
- foreach stop
}
}
- if %entity% != null {
- determine %entity%
}
- determine null
# Makes a sentry NPC attack the target
dsentry_task_attack:
type: task
debug: false
script:
- flag npc dsentry.attackping:<npc.flag[dsentry.attackping].add[10].asint||0>
- if <npc.flag[dsentry.attackping]> >= <npc.flag[dsentry.attackrate]> {
- flag npc dsentry.attackping:0
}
else {
- queue clear
}
- if <npc.flag[dsentry.target]||null> == null {
- flag npc dsentry.target:<proc[dsentry_proc_picktarget]>
}
else {
- if !<util.entity_is_spawned[<npc.flag[dsentry.target]||null>]> || !<npc.can_see[<npc.flag[dsentry.target]||null>]||false> {
- flag npc dsentry.target:<proc[dsentry_proc_picktarget]>
}
}
- if <npc.flag[dsentry.follow_target].as_player.is_online||false> {
- if <npc.location.distance[<npc.flag[dsentry.follow_target].as_player.location>]> > 100 {
- teleport <npc> <npc.flag[dsentry.follow_target].as_player.location>
}
- if <npc.flag[dsentry.target]||null> == null || <npc.item_in_hand.material.name> == bow {
- if <npc.location.distance[<npc.flag[dsentry.follow_target].as_player.location>]> > <npc.flag[dsentry.follow_range]> {
- walk <npc> <npc.flag[dsentry.follow_target].as_player.location> auto_range radius:<npc.flag[dsentry.follow_range]>
}
}
}
- if <npc.flag[dsentry.target]||null> == null {
- animate <npc> animation:stop_use_item
- queue clear
}
- if <npc.item_in_hand.material.name> != bow {
- pause waypoints
}
- define loc <npc.flag[dsentry.target].as_entity.location.add[0,0.33,0]>
- look %loc%
- if <npc.item_in_hand.material.name> == bow {
# todo: should critical=true be enabled in some cases? (corrupts damage calc)
- define settings knockback=1
- if <npc.item_in_hand.enchantments||li@> contains ARROW_FIRE {
- define settings %settings%;fire_time=1m
}
- animate <npc> animation:stop_use_item
- shoot arrow[%settings%] origin:<npc> destination:%loc% speed:50 lead:<npc.flag[dsentry.target].as_entity.velocity> script:dsentry_task_removearrow
- wait 1t
- animate <npc> animation:start_use_item
}
else if <npc.location.distance[%loc%]> > 3 {
- walk %loc% speed:<npc.flag[dsentry.walkspeed]>
}
else {
- if <npc.entity_type> == player animate <npc> animation:arm_swing
- hurt <npc.flag[dsentry.target]> <npc.flag[dsentry.damage]>
- if <npc.item_in_hand> != i@air {
- if <npc.item_in_hand.enchantments||li@> contains FIRE_ASPECT {
- adjust <npc.flag[dsentry.target]> fire_time:10s
}
}
}
dsentry_task_removearrow:
type: task
debug: false
script:
- if %hit_entities% == li@ remove %shot_entities%