@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); }
/** * 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); }
@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); }
// 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; }