You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1161 lines
40 KiB

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
name="Areia_Consider"
author="Areia"
id="434bc4b92d5e6ddf77d8e20c"
language="Lua"
purpose="Quick kill with consider"
save_state="y"
date_written="2021-04-11 21:00:00"
requires="5.06"
version="1.20"
>
<description trim="y">
<![CDATA[
]]>
</description>
</plugin>
<include name="constants.lua"/>
<aliases>
</aliases>
<triggers>
</triggers>
<script>
<![CDATA[
require "commas"
require "copytable"
require "gmcphelper"
require "json"
require "serialize"
require "tprint"
require "var"
require "wait"
dofile(GetInfo(60) .. "aardwolf_colors.lua")
--------------------------------------------------
-- Main
--------------------------------------------------
Main = {}
function Main.initialize()
AddAlias("alias_main_kill",
"^ac(?:k|\\s+kill)(?:\\s+(?<index>\\d+))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Main.kill"
)
AddAlias("alias_main_kill_all",
"^ac(?:a|\\s+killall)$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Main.kill_all"
)
end
function Main.kill(alias, line, wc)
local state = GMCPHandler.get_char_state()
if not (state == GMCPHandler.CHAR_STATE.ACTIVE or state == GMCPHandler.CHAR_STATE.FIGHTING) then
Utility.print("Character's state does not allow kill.")
return
end
if (#Consider.mobs == 0) then
Utility.print("No mobs considered. Considering...")
Consider.start()
return
end
local index = tonumber(wc.index) or 1
-- mobs are stored in reverse order, so we need to do a little math to find
-- the right one
local mob = Consider.mobs[#Consider.mobs + 1 - index]
if (not mob) then
Utility.print(string.format("There are only %d mobs currently indexed. Enter @Yac @wto refresh.", #Consider.mobs))
return
end
if (mob.ignore) then
Utility.print("That mob is currently being ignored.")
return
end
Execute(Settings.get_kill_cmd(mob))
end
function Main.kill_all(alias, line, wc)
local state = GMCPHandler.get_char_state()
if not (state == GMCPHandler.CHAR_STATE.ACTIVE or state == GMCPHandler.CHAR_STATE.FIGHTING) then
Utility.print("Character's state does not allow kill.")
return
end
if (#Consider.mobs == 0) then
Utility.print("No mobs considered. Considering...")
Consider.start()
return
end
local cmds = {}
for _, mob in ipairs(Consider.mobs) do
if (not mob.ignore) then
table.insert(cmds, Settings.get_kill_cmd(mob))
end
end
if (#cmds == 0) then
Utility.print("All mobs in room are ignored.")
return
end
local stackChar = GetAlphaOption("command_stack_character")
Execute(table.concat(cmds, stackChar))
end
--------------------------------------------------
-- Settings
--------------------------------------------------
Settings = {}
function Settings.initialize()
AddAlias("alias_settings_auto_consider",
"^ac\\s+auto(?:\\s+(?<setting>\\w+))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.auto_consider"
)
AddAlias("alias_settings_ignore_flag_display",
"^ac\\s+ignore\\s+flags?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_flag_display"
)
AddAlias("alias_settings_ignore_flag_toggle",
"^ac\\s+ignore\\s+flags?\\s+(?<flag>\\w+)(?:\\s+(?<setting>\\w+))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_flag_toggle"
)
AddAlias("alias_settings_ignore_level_display",
"^ac\\s+ignore\\s+levels?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_level_display"
)
AddAlias("alias_settings_ignore_level_toggle",
"^ac\\s+ignore\\s+levels?\\s+(?<level>\\d+)(?:\\s+(?<setting>\\w+))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_level_toggle"
)
AddAlias("alias_settings_ignore_mob_display",
"^ac\\s+ignore\\s+names?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_mob_display"
)
AddAlias("alias_settings_ignore_mob_toggle",
"^ac\\s+ignore\\s+names?\\s+(?<mob>.+?)$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.ignore_mob_toggle"
)
AddAlias("alias_settings_kill_cmd",
"^ac\\s+killcmd(?:\\s+(?<setting>.+?))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.kill_cmd"
)
AddAlias("alias_settings_mob_keyword_display_all",
"^ac\\s+keywords?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.mob_keywords_display_all"
)
AddAlias("alias_settings_mob_keyword_display_single",
"^ac\\s+keywords?\\s+(?<mob>[^\"]+?)$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.mob_keywords_display_single"
)
AddAlias("alias_settings_mob_keyword_set",
"^ac\\s+keywords?\\s+(?<mob>[^\"]+?)\\s+\"(?<keywords>[\\w\\s]*)\"$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.mob_keywords"
)
AddAlias("alias_settings_consider_output",
"^ac\\s+show(?:\\s+(?<setting>\\w+))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Settings.consider_output"
)
Settings.load()
end
function Settings.default()
local default = {
autoConsider = false,
ignore = {
flags = {
aimed = true, evil = false, good = false,
sanctuary = true, wounded = true,
},
level = {},
mobs = {},
},
killCmds = {"backstab *"},
mobKeywords = {},
showConsiderOutput = false,
}
for i = 1, 13 do
table.insert(default.ignore.level, false)
end
return serialize.save_simple(default)
end
function Settings.load()
Settings.config = loadstring(string.format("return %s", var.config or Settings.default()))()
Settings.save()
end
function Settings.save()
var.config = serialize.save_simple(Settings.config)
end
function Settings.auto_consider(alias, line, wc)
local setting = wc.setting:lower()
if (setting == "") then
Settings.config.autoConsider = not Settings.config.autoConsider
elseif (setting == "on") then
Settings.config.autoConsider = true
elseif (setting == "off") then
Settings.config.autoConsider = false
else
Utility.print("Syntax: @Yac auto [on|off]")
return
end
Settings.save()
Utility.plugin_msg(string.format("Auto-consider %sabled@w.", Settings.config.autoConsider and "@Gen" or "@Rdis"))
end
function Settings.ignore_flag_display()
Utility.plugin_msg("Ignored flags:")
for _, flag in ipairs{"aimed", "evil", "good", "sanctuary", "wounded"} do
Utility.print(string.format(" @Y%-11.11s (%-5.5s@w)",
Utility.pascal_case(flag), Settings.config.ignore.flags[flag] and "@GYes" or "@RNo"
))
end
end
function Settings.ignore_flag_toggle(alias, line, wc)
local flag = wc.flag:lower()
if not (flag == "aimed" or flag == "evil" or flag == "good"
or flag == "sanctuary" or flag == "wounded") then
Utility.print("Valid flags: @Yaimed@w, @Yevil@w, @Ygood@w, @Ysanctuary@w, @Ywounded")
return
end
local setting = wc.setting:lower()
if (setting == "") then
Settings.config.ignore.flags[flag] = not Settings.config.ignore.flags[flag]
elseif (setting == "on") then
Settings.config.ignore.flags[flag] = true
elseif (setting == "off") then
Settings.config.ignore.flags[flag] = false
else
Utility.print(string.format("Syntax: @Yac ignore flag %s [on|off]", flag))
return
end
Settings.save()
Utility.plugin_msg(string.format("%s @wignoring mobs with the @Y%s @wflag.",
Settings.config.ignore.flags[flag] and "@GNow" or "@RNo longer", flag
))
end
function Settings.ignore_level_display()
Utility.plugin_msg("Ignored levels:")
for i, range in ipairs(Consider.LEVEL_RANGE) do
Utility.print(string.format(" @Y%2.d@w. %-11.11s (%-5.5s@w)",
i, range, Settings.config.ignore.level[i] and "@GYes" or "@RNo"
))
end
end
function Settings.ignore_level_toggle(alias, line, wc)
local level = tonumber(wc.level)
if not (level >= 1 and level <= 13) then
Utility.print("Valid level ranges: @Y1 @wto @Y13")
return
end
local setting = wc.setting:lower()
if (setting == "") then
Settings.config.ignore.level[level] = not Settings.config.ignore.level[level]
elseif (setting == "on") then
Settings.config.ignore.level[level] = true
elseif (setting == "off") then
Settings.config.ignore.level[level] = false
else
Utility.print(string.format("Syntax: @Yac ignore level %d [on|off]", level))
return
end
Settings.save()
Utility.plugin_msg(string.format("%s @wignoring mobs in level range @Y%s@w.",
Settings.config.ignore.level[level] and "@GNow" or "@RNo longer",
Consider.LEVEL_RANGE[level]
))
end
function Settings.ignore_mob_display()
local alphabetized = {}
for name in pairs(Settings.config.ignore.mobs) do
table.insert(alphabetized, Utility.pascal_case(name))
end
table.sort(alphabetized)
Utility.plugin_msg("Ignored mobs:")
if (#alphabetized == 0) then
Utility.print(" No mobs ignored")
return
end
for _, name in ipairs(alphabetized) do
Utility.print(string.format(" @Y%s", name))
end
end
function Settings.ignore_mob_toggle(alias, line, wc)
local mob = wc.mob:lower()
Settings.config.ignore.mobs[mob] = not Settings.config.ignore.mobs[mob] and true or nil
Settings.save()
Utility.plugin_msg(string.format("%s @wignoreing mobs named @Y%s@w.",
Settings.config.ignore.mobs[mob] and "@GNow" or "@RNo longer",
Utility.pascal_case(mob)
))
end
function Settings.should_ignore(mob)
-- ignore mob if it has divine protection
local ignoreMob = mob.protected
-- ignore if already ignored or mob's name is ignored
ignoreMob = ignoreMob or Settings.config.ignore.mobs[mob.name:lower()]
-- ignore if already ignored or mob's level range is set to ignored
ignoreMob = ignoreMob or Settings.config.ignore.level[mob.levelRange]
for flag in pairs(mob.flags) do
-- ignore if already ignored or mob has a flag that is set to be ignored
ignoreMob = ignoreMob or Settings.config.ignore.flags[flag]
end
return ignoreMob
end
function Settings.kill_cmd(alias, line, wc)
if (wc.setting ~= "") then
local stackChar = GetAlphaOption("command_stack_character")
Settings.config.killCmds = utils.split(wc.setting, stackChar)
Settings.save()
end
Utility.plugin_msg("Kill command:",
string.format("@Y%s", Settings.get_kill_cmd_str())
)
end
function Settings.get_kill_cmd(mob)
local cmdList = {}
local mobStr = string.format("%d.'%s'", mob.index, mob.keywords)
for _, cmd in ipairs(Settings.config.killCmds) do
local cmdStr = cmd:gsub("%*", mobStr)
table.insert(cmdList, cmdStr)
end
local stackChar = GetAlphaOption("command_stack_character")
return table.concat(cmdList, stackChar)
end
function Settings.get_kill_cmd_str()
local cmdList = {}
for _, cmd in ipairs(Settings.config.killCmds) do
local cmdStr = cmd:gsub("%*", "<target>")
table.insert(cmdList, cmdStr)
end
local stackChar = GetAlphaOption("command_stack_character")
return table.concat(cmdList, stackChar)
end
function Settings.mob_keywords_display_all()
local alphabetized = {}
for name, keywords in pairs(Settings.config.mobKeywords) do
table.insert(alphabetized, {name=name, keywords=keywords})
end
table.sort(alphabetized, function(e1,e2) return e1.name < e2.name end)
Utility.plugin_msg("Custom mob keywords:")
if (#alphabetized == 0) then
Utility.print(" No custom keywords set")
return
end
for _, mob in ipairs(alphabetized) do
Utility.print(string.format(" @Y%-40.40s@w: @Y%s",
Utility.pascal_case(mob.name), mob.keywords
))
end
end
function Settings.mob_keywords_display_single(alias, line, wc)
local mob = wc.mob:lower()
local keywords = Settings.config.mobKeywords[mob]
if (keywords) then
Utility.plugin_msg(
string.format(
"Custom keywords for @Y%s@w:", Utility.pascal_case(mob)
),
string.format("@Y%s", keywords)
)
return
end
Utility.plugin_msg(string.format("No custom keywords set for @Y%s@w.",
Utility.pascal_case(mob)
))
end
function Settings.mob_keywords(alias, line, wc)
local mob = wc.mob:lower()
local keywords = wc.keywords:lower()
Settings.config.mobKeywords[mob] = keywords ~= "" and keywords or nil
-- if this mob is already in cache and we don't remove it, the new keywords
-- won't be recognized
for name, keywords in pairs(Consider.keywordCache) do
if (name:lower() == mob) then
Consider.keywordCache[name] = nil
break
end
end
Settings.save()
Execute(string.format("ac keywords %s", mob))
end
function Settings.consider_output(alias, line, wc)
local setting = wc.setting:lower()
if (setting == "") then
Settings.config.showConsiderOutput = not Settings.config.showConsiderOutput
elseif (setting == "on") then
Settings.config.showConsiderOutput = true
elseif (setting == "off") then
Settings.config.showConsiderOutput = false
else
Utility.print("Syntax: @Yac show [on|off]")
return
end
Settings.save()
Utility.plugin_msg(string.format("Will %s @wdisplay mob info.", Settings.config.showConsiderOutput and "@Gnow" or "@Rno longer"))
end
--------------------------------------------------
-- Consider
--------------------------------------------------
Consider = {}
function Consider.initialize()
AddAlias("alias_consider_mobs",
"^ac$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Consider.start"
)
Consider.LEVEL_RANGE = {
"-20 or less", "-10 to -19", "-5 to -9", "-2 to -4",
"-1 to +1", "+2 to +4", "+5 to +9", "+10 to +15",
"+16 to +20", "+21 to +30", "+31 to +40", "+41 to +50",
"+51 or more"
}
Consider.OUTPUTS = {
"You would stomp (?<name>.+?) into the ground\\.",
"(?<name>.+?) would be easy, but is it even worth the work out\\?",
"No Problem! (?<name>.+?) is weak compared to you\\.",
"(?<name>.+?) looks a little worried about the idea\\.",
"(?<name>.+?) should be a fair fight!",
"(?<name>.+?) snickers nervously\\.",
"(?<name>.+?) chuckles at the thought of you fighting (?:him|her|it|them)\\.",
"Best run away from (?<name>.+?) while you can!",
"Challenging (?<name>.+?) would be either very brave or very stupid\\.",
"(?<name>.+?) would crush you like a bug!",
"(?<name>.+?) would dance on your grave!",
"(?<name>.+?) says 'BEGONE FROM MY SIGHT unworthy!'",
"You would be completely annihilated by (?<name>.+?)!",
"(?<name>.+?) has divine protection\\.",
}
for i, subpattern in ipairs(Consider.OUTPUTS) do
local triggerName = string.format("trigger_consider_mob%d", i)
AddTriggerEx(triggerName,
string.format("^(?<flags>(?:\\([\\w\\s]+\\)\\s*)+)?%s$", subpattern), "",
trigger_flag.OmitFromOutput + trigger_flag.RegularExpression + trigger_flag.Temporary,
custom_colour.NoChange, 0, "",
"Consider.mob", sendto.script, 100
)
SetTriggerOption(triggerName, "group", "trigger_group_consider_mobs")
end
AddTriggerEx("trigger_consider_mob_end",
"^\\{/consider\\}$", "",
trigger_flag.OmitFromOutput + trigger_flag.RegularExpression + trigger_flag.Temporary,
custom_colour.NoChange, 0, "",
"Consider.finish", sendto.script, 100
)
SetTriggerOption("trigger_consider_mob_end", "group", "trigger_group_consider_mobs")
Consider.TIMEOUT = 30
Consider.KEYWORDS_TO_IGNORE = {["a"] = true, ["an"] = true, ["and"] = true,
["but"] = true, ["for"] = true, ["in"] = true, ["nor"] = true,
["of"] = true, ["on"] = true, ["or"] = true, ["so"] = true, ["some"] = true,
["the"] = true, ["to"] = true, ["too"] = true, ["with"] = true, ["yet"] = true,
["merdevil"] = true, ["onyx"] = true,
}
Consider.mobs = {} -- main mob info obtained from consider
Consider.keywordCount = {} -- keywords-to-count record used to track proper indices (see Consider.mob)
Consider.keywordCache = {} -- session name-to-keywords record (see Consider.get_mob_keywords)
end
function Consider.clear()
Consider.mobs = {}
Consider.keywordCount = {}
end
function Consider.start()
local state = GMCPHandler.get_char_state()
if not (state == GMCPHandler.CHAR_STATE.ACTIVE or state == GMCPHandler.CHAR_STATE.FIGHTING) then
Utility.print("Character state does not allow consider.")
return false
end
if (GMCPHandler.get_room_flags().safe) then
Utility.print("This is a safe room.")
return false
end
SendNoEcho("echo {consider}")
SendNoEcho("consider all")
SendNoEcho("echo {/consider}")
wait.make(Consider.start_CR)
return true
end
function Consider.start_CR()
local line = wait.regexp("^\\{consider\\}$", Consider.TIMEOUT, trigger_flag.OmitFromOutput)
if (not line) then
Utility.print("@RTimeout@w. Failed to obtain consider output.")
BroadcastPlugin(0, "{}")
return
end
Consider.clear()
EnableTriggerGroup("trigger_group_consider_mobs", true)
end
function Consider.mob(trigger, line, wc)
local mob = {}
mob.name = wc.name
mob.keywords = Consider.get_mob_keywords(mob.name)
-- update count of mobs with these particular keywords. The count directly
-- determines the mob's proper index (i.e., is this 1.goblin, 2.goblin, etc.).
Consider.keywordCount[mob.keywords] = (Consider.keywordCount[mob.keywords] or 0) + 1
mob.index = Consider.keywordCount[mob.keywords]
mob.levelRange = tonumber(trigger:match("%d+"))
mob.protected = mob.levelRange == 14 -- divine protection
local flagsStr = wc.flags:lower()
mob.flags = {
aimed = flagsStr:match("%(a%)") or flagsStr:match("%(aimed%)"),
evil = flagsStr:match("%(r%)") or flagsStr:match("%(red aura%)"),
good = flagsStr:match("%(g%)") or flagsStr:match("%(golden aura%)"),
sanctuary = flagsStr:match("%(w%)") or flagsStr:match("%(white aura%)"),
wounded = flagsStr:match("%(wounded%)")
}
mob.ignore = Settings.should_ignore(mob)
table.insert(Consider.mobs, 1, mob)
if (Settings.config.showConsiderOutput) then
Utility.print(string.format("%-10.10s%-35.35s | %-11.11s | %s",
mob.ignore and "(Ignored)" or "", mob.name,
Consider.LEVEL_RANGE[mob.levelRange] or "Protected",
flagsStr ~= "" and wc.flags or "None"
))
end
end
function Consider.finish()
EnableTriggerGroup("trigger_group_consider_mobs", false)
local total = #Consider.mobs
local validTargets = 0
for _, mob in ipairs(Consider.mobs) do
validTargets = not mob.ignore and validTargets + 1 or validTargets
end
if (validTargets ~= total) then
Utility.print(string.format("@Y%d@w/@Y%d @wmob%s found.",
validTargets, total, total == 1 and "" or "s"
))
elseif (total > 0) then
-- We don't print a msg if `total` is 0 because the game out-
-- put itself handles that situation.
Utility.print(string.format("@Y%d @wmob%s found.",
total, total == 1 and "" or "s"
))
end
BroadcastPlugin(0, Consider.as_string())
end
function Consider.get_mob_keywords(name)
if (Consider.keywordCache[name]) then
return Consider.keywordCache[name]
end
local customKeywords = Settings.config.mobKeywords[name:lower()]
if (customKeywords) then
Consider.keywordCache[name] = customKeywords
return customKeywords
end
local keywords = {}
for word in name:lower():gmatch("[^%s]+") do -- iterate through each string of non-whitespace chars
if (not Consider.KEYWORDS_TO_IGNORE[word]) then -- if not in omit table, we want to use it
-- take only the first part of hyphenated (eg, long-haired) and weird
-- apostrophe'd (eg, N'Kari) words
word = word:gsub("(%a+)['-]%a+", "%1")
-- then strip away any other non-alphabetic characters
word = word:gsub("%A", "")
if (word ~= "") then
table.insert(keywords, word)
end
end
end
if (#keywords == 0) then
-- Must be quite an odd name... just return it and let the user deal with it
return name
end
local keywordsStr = table.concat(keywords, " ")
Consider.keywordCache[name] = keywordsStr
return keywordsStr
end
function Consider.as_string()
return json.encode(Consider.mobs)
end
--------------------------------------------------
-- GMCPHandler
--------------------------------------------------
GMCPHandler = {}
function GMCPHandler.initialize()
GMCPHandler.CHAR_STATE = {
["LOGIN"] = 1, ["MOTD"] = 2, ["ACTIVE"] = 3, ["AFK"] = 4, ["NOTE"] = 5,
["BUILDING"] = 6, ["PAGER"] = 7, ["FIGHTING"] = 8, ["SLEEPING"] = 9,
["SITTING"] = 11, ["RUNNING"] = 12,
}
GMCPHandler.gmcpInitialized = false
GMCPHandler.prevRoom = {}
GMCPHandler.mapperRunning = false
end
function GMCPHandler.get_char_state()
-- We fudge the values a bit here. Because there are things like recall,
-- portals, cexits, etc., it is possible that the GMCP data might show char
-- status 3 while we're in the middle of mapper running us somewhere (eg, we
-- have just entered a portal [which causes GMCP to send new room data] but
-- have not actually started running yet). Such cases would cause this script
-- to auto-consider multiple times during a single run and thus spam and fail.
-- Because this script is more concerned with whether we are in the process
-- of being moved, rather than with the actual value of char state, for purposes
-- of auto-consider, we track the mapper's state (see OnPluginBroadcastbelow)
-- and, when it is mid-run, return a 'fake' value of 12. Otherwise we fall
-- back on the 'real' GMCP data.
return GMCPHandler.mapperRunning and GMCPHandler.CHAR_STATE.RUNNING or tonumber(gmcp("char.status.state"))
end
function GMCPHandler.get_room_flags()
local flags = {}
local details = GMCPHandler.prevRoom.details or ""
for flag in details:gmatch("[^,]+") do
flags[flag] = true
end
return flags
end
function GMCPHandler.received_repop()
local state = GMCPHandler.get_char_state()
if (Settings.config.autoConsider and (state == GMCPHandler.CHAR_STATE.ACTIVE
or state == GMCPHandler.CHAR_STATE.FIGHTING) and not GMCPHandler.get_room_flags().safe) then
Consider.start()
end
end
function GMCPHandler.received_room(room)
Consider.clear()
-- If zone changes and auto-mode is on, then turn it off. This
-- will maybe, hopefully cut down on people spamming groups...
if (Settings.config.autoConsider and GMCPHandler.prevRoom
and GMCPHandler.prevRoom.zone ~= room.zone) then
Utility.plugin_msg("Disabling auto mode due to zone change.")
Settings.auto_consider(nil, nil, {setting="off"})
end
GMCPHandler.prevRoom = room
if (Settings.config.autoConsider and GMCPHandler.get_char_state() ~= GMCPHandler.CHAR_STATE.RUNNING
and not GMCPHandler.get_room_flags().safe) then
Consider.start()
end
end
--------------------------------------------------
-- Update
--------------------------------------------------
Update = {}
function Update.initialize()
AddAlias("alias_update",
"^ac\\s+update$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Update.update"
)
Update.URL = "https://raw.githubusercontent.com/AreiaAard/Areia_Consider/main/areia_consider.xml"
end
function Update.update()
async_ok, async = pcall (require, "async")
if async_ok then
plugin_page = async.doAsyncRemoteRequest(Update.URL, Update.raw_get, "HTTPS")
else
Utility.plugin_msg("Error. Update failed.")
end
end
function Update.raw_get(retval, page, status, headers, full_status, request_url)
local PLUGIN_NAME = GetPluginInfo(GetPluginID(), 1)
local PLUGIN_VERSION = GetPluginInfo(GetPluginID(), 19)
local raw_version = -1
if (status == 200) then
raw_version = tonumber(string.match(page, '%s%s+version="([0-9%.]+)"'))
end
if (raw_version == PLUGIN_VERSION) then
Utility.plugin_msg(string.format("@Y%s @wis up-to-date with the current version.", PLUGIN_NAME))
elseif (raw_version > PLUGIN_VERSION) then
Utility.plugin_msg(string.format("Updating @Y%s @wfrom v%s to v%s.", PLUGIN_NAME, PLUGIN_VERSION, raw_version))
Utility.print(" Please do not touch anything.")
local file = io.open(GetPluginInfo(GetPluginID(), 6), "w")
file:write(page)
file:close()
end
if (GetAlphaOption("script_prefix") == "") then
SetAlphaOption("script_prefix", "\\\\\\")
end
Execute(string.format(
"%sDoAfterSpecial(1, \"ReloadPlugin('%s')\", sendto.script)",
GetAlphaOption("script_prefix"), GetPluginID()
))
Utility.plugin_msg("Update completed.")
end
--------------------------------------------------
-- Help
--------------------------------------------------
Help = {}
function Help.initialize()
Help.TOPICS = {
["auto"] = "Help.topic_auto", ["consider"] = "Help.topic_consider",
["ignore"] = "Help.topic_ignore", ["keywords"] = "Help.topic_keywords",
["kill"] = "Help.topic_kill", ["killall"] = "Help.topic_killall",
["killcmd"] = "Help.topic_killcmd", ["show"] = "Help.topic_show",
["update"] = "Help.topic_update",
}
AddAlias("alias_help_main",
"^ac\\s+help(?:\\s+(?<topic>.+?))?$", "",
alias_flag.Enabled + alias_flag.IgnoreAliasCase + alias_flag.RegularExpression + alias_flag.Temporary,
"Help.main"
)
end
function Help.main(alias, line, wc)
local topic = wc.topic:lower()
if (topic == "") then
Help.topic_main()
return
end
if (not Help.TOPICS[topic]) then
Utility.plugin_msg(string.format(
"@Y%s @wis not a valid help topic.",
Utility.pascal_case(topic)
))
Help.topic_main()
return
end
CallPlugin(GetPluginID(), Help.TOPICS[topic])
end
function Help.title(str)
local titleStr = string.format("Consider Help - %s", str)
Utility.print(string.format("@W%s", Utility.center(titleStr, 70)))
Utility.print(string.rep("=", 70))
end
function Help.syntax(str)
Utility.print(string.format(" Syntax: @Y%s", str))
end
function Help.topic_main()
local alphabetized = {}
for topic in pairs(Help.TOPICS) do
table.insert(alphabetized, topic)
end
table.sort(alphabetized)
local hyperlinkAction = string.format("!!%s:%%s()", GetPluginID())
Help.title("Help Topics")
for i, topic in ipairs(alphabetized) do
local action = hyperlinkAction:format(Help.TOPICS[topic])
local hint = string.format("Click to view help on %s.", topic)
Hyperlink(action, topic, hint, "yellow", "black", false)
if (i % 4 == 0 or i == #alphabetized) then
Note("") -- end line, with four topics per line
else
ColourTell("silver", "black", ",", "", "", string.rep(" ", 20 - (#topic + 1)))
end
end
Utility.print("Click on a topic or submit @Yac help <topic> @wto view it.")
end
function Help.topic_auto()
Help.title("Auto")
Help.syntax("ac auto [on|off]")
Utility.print([[
Set (or toggle, if no argument is given) auto-consider mode. This mode
causes the plugin to collect mob info whenever your area repops or
you move into a new room.
@R***NOTE***@w: This mode does not and should @R*NOT* @wautomatically kill any
mobs upon a successful consider. You must do this yourself using the
@Yac kill @wor @Yac killall @wcommands. Modifying or otherwise utilizing this
plugin to kill mobs automatically is against botting rules and will
likely get you nuked.
]])
end
function Help.topic_consider()
Help.title("Consider")
Help.syntax("ac")
Utility.print([[
Collect info about the mobs in your room via consider. This must be
done before you will be able to kill mobs using the @Yac kill @wand
@Yac killall @wcommands. See @Yac help auto @wfor info on setting the
plugin to do this automatically.
Plugin authors: As of v1.20, you can request JSON-formatted consider
data from this script programmatically. To initiate a consider re-
quest, call `Consider.start()`, which returns true/false to indicate
whether consider can currently be sent to the MUD. To receive the
resulting data, check for a broadcast msg 0 from this plugin in your
`OnPluginBroadcast()` function; the `text` argument will contain the
JSON. Here is an example from another of my plugins:
```
local CON_PLUGIN = "434bc4b92d5e6ddf77d8e20c"
function Aura.count(alias, line, wc)
if (Aura.report) then
Utility.msg("Another aura count is already in progress.")
return
end
local _, considering = CallPlugin(CON_PLUGIN, "Consider.start")
if (considering) then
local report = trim(wc.report):lower()
report = report == "" and "echo" or report
Aura.report = report
Utility.msg("Counting auras...")
end
end
function OnPluginBroadcast(msg, id, name, text)
if (id == CON_PLUGIN) then
Aura.receive_mob_list(text)
end
end
```
The `Aura.receive_mob_list()` handler then decodes the string, does
whatever it must with the data, and then sets `Aura.report` back to
nil so that another request can be made later.
]])
end
function Help.topic_ignore()
Help.title("Ignore")
Help.syntax("ac ignore flag [<flag> [on|off]]")
Help.syntax("ac ignore level [<index> [on|off]]")
Help.syntax("ac ignore name [<name>]")
Utility.print([[
It is often helpful to be able to ignore certain mobswhile casually
leveling and pupping. For example, most would rather not attack mobs
with sanctuary, as it slows leveling. Or maybe there is a particular
mob in a frequented area that is immune to your main damtype that you
would wish not to attack. Using the above commands, you can view and
set various ways in which the plugin will ignore mobs when you ask it
to kill or killall.
Type @Yac ignore flag @wto see which flags currently cause the plugin to
ignore a mob. @YAc ignore flag <flag> @wwill toggle the setting for the
given flag. Valid flag names are aimed, evil, good, sanctuary, and
wounded.
Similarly, @Yac ignore level @wshows which level ranges you will not
attack, and @Yac ignore level <index> @wtoggles the setting for the given
level range. Range is from 1 (20 levels or less under yours) to 13
(51 or more above).
Finally, @Yac ignore name @wand @Yac ignore name <name> @wshow and toggle
the ignoring of mobs with a particular name. Note that punctuation
(e.g., commas, hyphens) must match the actual mob's name, but the name
is not case-sensitive.
]])
end
function Help.topic_keywords()
Help.title("Keywords")
Help.syntax("ac keywords [<mob name> [\"[<keywords>]\"]]")
Utility.print([[
When considering mobs in a room, the plugin does its best to guess at
a mob's keywords (the words used to interact with it) based on its
description (what you see on your screen). Unfortunately, keywords do
not always match descriptions, which can cause the plugin to fail to
target a mob properly.
If you notice that the plugin fails to kill a certain mob, simply sub-
mit @Yac keyword <mob name> "<keywords>" @wto set custom keywords for mobs
with that name. For example, there is a mob called 'a male villager'
in Radiance Woods. The plugin reasonably assumes that 'male villager'
will work to target this mob, but it turns out that 'male' is not one
of its valid keywords, so the plugin fails to attack it. To fix, type
@Yac keywords a male villager "villager" @w(quotes around the keywords),
re-consider the room, and now you can attack it correctly.
To remove custom keywords, type the same command as above, but with
nothing between the quotes (e.g., @Yac keywords a male villager ""@w).
Supplying the mob name alone, without anything in quotes, will display
the custom keywords set for that mob (if any), and typing simply
@Yac keywords @wwill provide you with a list of all mobs associated with
custom keywords. Neither names nor keywords are case-sensitive.
]])
end
function Help.topic_kill()
Help.title("Kill")
Help.syntax("ac kill <#>")
Help.syntax("ack <#>")
Utility.print([[
So you've used the @Yac @wcommand to consider a room. Now what? To get
killing, use one of the above commands (the second is simply a short-
cut to the first), providing the index of the mob you wish to kill.
For instance, to kill the third mob in the room, submit @Yack 3@w.
This method is probably most useful while grouping. The @Ykillall @wcom-
mand is much easier and more powerful to use while solo, but it tends
to cause a lot of unnecessary stacking and missed attacks due to the
speed and order in which mobs are killed by a group.
]])
end
function Help.topic_killall()
Help.title("Killall")
Help.syntax("ac killall")
Help.syntax("aca")
Utility.print([[
So you've used the @Yac @wcommand to consider a room. Now what? To get
killing, use one of the above commands (the second is simply a short-
cut to the first).
This is the best method to kill mobs while solo-leveling, assuming
an area not entirely full of aggressive mobs. It will execute your
chosen kill command (see @Yac help killcmd@w) once on each non-ignored mob
in the room.
]])
end
function Help.topic_killcmd()
Help.title("Killcmd")
Help.syntax("ac killcmd [<command(s)>]")
Utility.print([[
This shows or sets (if an argument is given) the command(s) used to
attack mobs by @Yac kill @wand @Yac killall@w. The string is sent to 'Execute'
(i.e., the MUSHClient command processor), so it can be a simple
command like 'kick', an alias, a script that starts with your client's
script prefix, etc.
Use '@Y*@w' to indicate where in the command string a target name should
be added. For example, @Yac killcmd bs * @wwould set the plugin to back-
stab your targets. @YAc killcmd bs *;;spiral @wwould set it to backstab
the target and then add a single, non-targeted spiral immediately
after. This can be useful if you're not exactly sure what might be
necessary to kill your mobs. If you wanted to backstab and spiral each
and every mob, you would use @Yac killcmd bs *;;spiral * @w(though note
this might add extra lag if the mob is already dead before the spiral
executes). -Thanks to DarkSobek for suggesting this feature.
]])
end
function Help.topic_show()
Help.title("Show")
Help.syntax("ac show [on|off]")
Utility.print([[
By default, the plugin hides most consider output that it requests, to
save you from having a bunch of unnecessary lines in your output win-
dow. The above command allows you to set or toggle this option. Turn-
ing it on will display consider output, though in a slightly different
format from that which the MUD outputs. You are able to see which mobs
the plugin is ignoring, along with a more helpful description of its
level relative to yours.
In either case, the plugin always provides a summary of the total
number of mobs found to be in the room. If one or more mobs are to be
ignored, you are given both the number of valid targets and the total
number of mobs present (e.g., '@Y3@w/@Y4 @wmobs found.' tells you that there
are three valid targets out of four total mobs present).
]])
end
function Help.topic_update()
Help.title("Update")
Help.syntax("ac update")
Utility.print([[
Occasionally fixes or improvements might be made to the plugin. You
can use the above command any time to check for a newer version. If a
version more recent than your current version exists, it will auto-
matically be downloaded and installed, ready to be used further with
your previously saved settings in tact.
]])
end
--------------------------------------------------
-- Utility
--------------------------------------------------
Utility = {}
function Utility.initialize()
-- General aliases
local initializers = {
Main.initialize,
Settings.initialize,
Consider.initialize,
GMCPHandler.initialize,
Update.initialize,
Help.initialize,
}
for _, initializer in ipairs(initializers) do
initializer()
end
end
function Utility.deinitialize()
local aliases = GetAliasList()
if (aliases) then
for i = 1, #aliases do
EnableAlias(aliases[i], false)
DeleteAlias(aliases[i])
end
end
local triggers = GetTriggerList()
if (triggers) then
for i = 1, #triggers do
EnableTrigger(triggers[i], false)
DeleteTrigger(triggers[i])
end
end
end
function Utility.print(str)
-- Lets us use Aard color codes in our ColourNotes
AnsiNote(stylesToANSI(ColoursToStyles(string.format("@w%s@w", str))))
end
function Utility.plugin_msg(str, ...)
Utility.print(string.format("[@YConsider@w]: %s", str))
for _, arg in ipairs{...} do
Utility.print(string.format(" %s", arg))
end
end
function Utility.display_greeting()
Utility.plugin_msg("Areia Consider plugin installed.",
"Enter @Yac help @wfor command help."
)
end
function Utility.center(str, width, fill)
fill = fill or " "
local strWidth = #str
local leftWidth = math.floor((width - strWidth) / 2)
local rightWidth = math.ceil((width - strWidth) / 2)
local centeredStr = "%s%s%s"
return centeredStr:format(fill:rep(leftWidth), str, fill:rep(rightWidth))
end
function Utility.pascal_case(str)
str = str:gsub("(%a)([%w_']*)",
function(first,remainder)
return string.format("%s%s", first:upper(), remainder:lower())
end
)
return str
end
--------------------------------------------------
-- Plugin Callbacks
--------------------------------------------------
function OnPluginInstall()
Utility.initialize()
Utility.display_greeting()
end
function OnPluginEnable()
OnPluginInstall()
end
function OnPluginClose()
Utility.deinitialize()
end
function OnPluginDisable()
OnPluginClose()
end
function OnPluginBroadcast(msg, id, name, text)
if (id == "3e7dedbe37e44942dd46d264") then
if (not GMCPHandler.gmcpInitialized) then
GMCPHandler.gmcpInitialized = true
end
if (text == "room.info") then
GMCPHandler.received_room(gmcp("room.info"))
elseif (text == "comm.repop") then
GMCPHandler.received_repop()
end
elseif (id == "b6eae87ccedd84f510b74714") then -- GMCP Mapper
if (text == "kinda_busy") then
GMCPHandler.mapperRunning = true
elseif (text == "ok_you_can_go_now") then
GMCPHandler.mapperRunning = false
end
end
end
]]>
</script>
</muclient>