예제 #1
0
  @EventHandler
  public void asyncChatTrigger(final AsyncPlayerChatEvent event) {
    if (event.isCancelled()) return;

    // Return if "Use asynchronous event" is false in config file
    if (!Settings.ChatAsynchronous()) return;

    Callable<Boolean> call =
        new Callable<Boolean>() {
          public Boolean call() {
            return process(event.getPlayer(), event.getMessage());
          }
        };

    Boolean cancelled = false;

    try {
      cancelled =
          event.isAsynchronous()
              ? Bukkit.getScheduler().callSyncMethod(DenizenAPI.getCurrentInstance(), call).get()
              : call.call();
    } catch (InterruptedException e) {
      // dB.echoError(e);
    } catch (ExecutionException e) {
      dB.echoError(e);
    } catch (Exception e) {
      dB.echoError(e);
    }

    event.setCancelled(cancelled);
  }
예제 #2
0
 /**
  * Sets a dNPC's ENGAGED status. Engaged NPCs do not respond to Player interaction. Note: Denizen
  * NPC will automatically disengage after the engage_timeout_in_seconds which is set in the
  * Denizen config.yml.
  *
  * @param npc the dNPC affected
  * @param engaged true sets the dNPC engaged, false sets the dNPC as disengaged
  */
 public static void setEngaged(NPC npc, boolean engaged) {
   if (engaged)
     currentlyEngaged.put(
         npc,
         System.currentTimeMillis()
             + (long) (Duration.valueOf(Settings.engageTimeoutInSeconds()).getSeconds()) * 1000);
   if (!engaged) currentlyEngaged.remove(npc);
 }
예제 #3
0
  @EventHandler
  public void syncChatTrigger(final PlayerChatEvent event) {
    if (event.isCancelled()) return;

    // Return if "Use asynchronous event" is true in config file
    if (Settings.ChatAsynchronous()) return;

    Boolean cancelled = process(event.getPlayer(), event.getMessage());
    event.setCancelled(cancelled);
  }
예제 #4
0
  // Technically defined in TriggerTrait, but placing here instead.
  // <--[action]
  // @Actions
  // chat
  //
  // @Triggers when a player chats to the NPC.
  //
  // @Context
  // <context.message> returns the triggering message
  // <context.keyword> returns the keyword matched by a RegEx trigger
  //
  // -->
  public Boolean process(Player player, String message) {

    // Check if there is an NPC within range of a player to chat to.
    dNPC npc = Utilities.getClosestNPC_ChatTrigger(player.getLocation(), 25);
    dPlayer denizenPlayer = dPlayer.mirrorBukkitPlayer(player);

    // No NPC? Nothing else to do here.
    if (npc == null) return false;

    // If the NPC doesn't have triggers, or the triggers are not enabled, then
    // just return false.
    if (!npc.getCitizen().hasTrait(TriggerTrait.class)) return false;
    if (!npc.getCitizen().getTrait(TriggerTrait.class).isEnabled(name)) return false;

    // Check range
    if (npc.getTriggerTrait().getRadius(name) < npc.getLocation().distance(player.getLocation()))
      return false;

    // The Denizen config can require some other criteria for a successful chat-with-npc.
    // Should we check 'line of sight'? Players cannot talk to NPCs through walls
    // if enabled. Should the Player chat only when looking at the NPC? This may
    // reduce accidental chats with NPCs.

    if (Settings.ChatMustSeeNPC()) if (!player.hasLineOfSight(npc.getEntity())) return false;

    if (Settings.ChatMustLookAtNPC())
      if (!Rotation.isFacingEntity(player, npc.getEntity(), 45)) return false;

    Boolean ret = false;

    // Denizen should be good to interact with. Let's get the script.
    InteractScriptContainer script = npc.getInteractScript(denizenPlayer, ChatTrigger.class);

    Map<String, dObject> context = new HashMap<String, dObject>();
    context.put("message", new Element(message));

    // If engaged or not cool, calls On Unavailable, if cool, calls On Chat
    // If available (not engaged, and cool) sets cool down and returns true.
    if (!npc.getTriggerTrait().trigger(ChatTrigger.this, denizenPlayer, context)) {
      // If the NPC is not interactable, Settings may allow the chat to filter
      // through. Check the Settings if this is enabled.
      if (Settings.ChatGloballyIfUninteractable()) {
        dB.echoDebug(
            script,
            ChatColor.YELLOW
                + "Resuming. "
                + ChatColor.WHITE
                + "The NPC is currently cooling down or engaged.");
        return false;
      } else {
        ret = true;
      }
    }

    // Debugger
    dB.report(
        script,
        name,
        aH.debugObj("Player", player.getName())
            + aH.debugObj("NPC", npc.toString())
            + aH.debugObj(
                "Radius(Max)",
                npc.getLocation().distance(player.getLocation())
                    + "("
                    + npc.getTriggerTrait().getRadius(name)
                    + ")")
            + aH.debugObj("Trigger text", message)
            + aH.debugObj("LOS", String.valueOf(player.hasLineOfSight(npc.getEntity())))
            + aH.debugObj(
                "Facing", String.valueOf(Rotation.isFacingEntity(player, npc.getEntity(), 45))));

    if (script == null) return false;

    // Check if the NPC has Chat Triggers for this step.
    if (!script.containsTriggerInStep(
        InteractScriptHelper.getCurrentStep(denizenPlayer, script.getName()), ChatTrigger.class)) {

      // If this is a Chatbot, make it chat anything it wants if
      // it has no chat triggers for this step
      if (npc.getCitizen().hasTrait(ChatbotTrait.class)) {
        Utilities.talkToNPC(message, denizenPlayer, npc, Settings.ChatToNpcOverhearingRange());
        npc.getCitizen().getTrait(ChatbotTrait.class).chatTo(player, message);
        return true;
      }
      // No chat trigger for this step.. do we chat globally, or to the NPC?
      else if (!Settings.ChatGloballyIfNoChatTriggers()) {
        dB.echoDebug(
            script,
            player.getName() + " says to " + npc.getNicknameTrait().getNickname() + ", " + message);
        return true;
      } else return ret;
    }

    // Parse the script and match Triggers.. if found, cancel the text! The
    // parser will take care of everything else.
    String id = null;
    boolean matched = false;
    String replacementText = null;
    String regexId = null;
    String regexMessage = null;

    // Use TreeMap to sort chat triggers alphabetically
    TreeMap<String, String> idMap = new TreeMap<String, String>();
    idMap.putAll(script.getIdMapFor(ChatTrigger.class, denizenPlayer));

    if (!idMap.isEmpty()) {
      // Iterate through the different id entries in the step's chat trigger
      for (Map.Entry<String, String> entry : idMap.entrySet()) {

        // Check if the chat trigger specified in the specified id's 'trigger:' key
        // matches the text the player has said
        String triggerText = TagManager.tag(denizenPlayer, npc, entry.getValue());
        Matcher matcher = triggerPattern.matcher(triggerText);
        while (matcher.find()) {
          if (!script.checkSpecificTriggerScriptRequirementsFor(
              ChatTrigger.class, denizenPlayer, npc, entry.getKey())) continue;
          String keyword = TagManager.tag(denizenPlayer, npc, matcher.group().replace("/", ""));
          // Check if the trigger is REGEX, but only if we don't have a REGEX
          // match already (thus using alphabetical priority for triggers)
          if (regexId == null && isKeywordRegex(keyword)) {
            Pattern pattern = Pattern.compile(keyword.substring(6));
            Matcher m = pattern.matcher(message);
            if (m.find()) {
              // REGEX matches are left for last, so save it in case non-REGEX
              // matches don't exist
              regexId = entry.getKey();
              regexMessage = triggerText.replace(matcher.group(), m.group());
              dB.log(
                  "entry value: "
                      + triggerText
                      + "  keyword: "
                      + keyword
                      + "  m.group: "
                      + m.group()
                      + "  matcher.group: "
                      + matcher.group());
              context.put("keyword", new Element(m.group()));
            }
          } else if (isKeywordStrict(keyword)) {
            if (message.toUpperCase().equalsIgnoreCase(keyword.toUpperCase())) {
              // Trigger matches
              id = entry.getKey();
              replacementText = triggerText.replace("/", "");
              matched = true;
            }
          } else if (message.toUpperCase().contains(keyword.toUpperCase())) {
            // Trigger matches
            id = entry.getKey();
            replacementText = triggerText.replace("/", "");
            matched = true;
          }
        }
        if (matched) break;
      }
    }

    if (!matched && regexId != null) {
      id = regexId;
      replacementText = regexMessage;
    }

    // If there was a match, the id of the match should have been returned.
    if (id != null) {
      Utilities.talkToNPC(
          replacementText, denizenPlayer, npc, Settings.ChatToNpcOverhearingRange());
      parse(npc, denizenPlayer, script, id, context);
      return true;
    } else {
      // If this is a Chatbot, make it chat anything it wants if
      // none of its chat triggers worked
      if (npc.getCitizen().hasTrait(ChatbotTrait.class)) {
        Utilities.talkToNPC(message, denizenPlayer, npc, Settings.ChatToNpcOverhearingRange());
        npc.getCitizen().getTrait(ChatbotTrait.class).chatTo(player, message);
        return true;
      } else if (!Settings.ChatGloballyIfFailedChatTriggers()) {
        Utilities.talkToNPC(message, denizenPlayer, npc, Settings.ChatToNpcOverhearingRange());
        return true;
      }
      // No matching chat triggers, and the config.yml says we
      // should just ignore the interaction...
    }
    return ret;
  }