-- This file is the core of the Automatizer -- Please do not touch unless you know what you are doing __rules = {} __rules_max = 0 rule_aux = {} -- Rule apply function, does .. nothing function auto_nothing(obj, item) return end function auto_inscribe(obj, item, note) if obj.note ~= 0 then return end msg_print("") obj.note = quark_add(note) return TRUE end -- Rule apply function, pickup object function auto_pickup(obj, item) if item >= 0 then return end if inven_carry_okay(obj) == FALSE then return end msg_print("") object_pickup(-item) return TRUE end -- Rule apply function, destroy item function auto_destroy(obj, item) -- be carefull to what we can destroy -- Unaware things won't be destroyed. if is_aware(obj) == FALSE then return end -- Inscribed things won't be destroyed! if obj.note ~= 0 then return end -- Keep Artifacts -- they cannot be destroyed anyway if is_artifact(obj) == TRUE then return end -- Cannot destroy CURSE_NO_DROP objects local f1, f2, f3, f4, f5, esp = object_flags(obj); if band(f4, TR4_CURSE_NO_DROP) ~= 0 and band(obj.ident, IDENT_CURSED) then return end msg_print("") -- Eliminate the item (from the pack) if item >= 0 then inven_item_increase(item, -obj.number) inven_item_describe(item) inven_item_optimize(item) -- Eliminate the item (from the floor) else floor_item_increase(0 - item, -obj.number) floor_item_describe(0 - item) floor_item_optimize(0 - item) end return TRUE end -- Report the status of an object function object_status(obj) local sense = { [SENSE_CURSED] = "bad", [SENSE_WORTHLESS] = "very bad", [SENSE_AVERAGE] = "average", [SENSE_GOOD_LIGHT] = "good", [SENSE_GOOD_HEAVY] = "good", [SENSE_EXCELLENT] = "very good", [SENSE_SPECIAL] = "special", [SENSE_TERRIBLE] = "terrible", } if is_known(obj) == FALSE then if sense[obj.sense] then return sense[obj.sense] else return "" end else if nil then -- test local osense = -1 local type = select_sense(obj, TRUE, TRUE) if type == 1 then osense = value_check_aux1(obj) elseif type == 2 then osense = value_check_aux1_magic(obj) end print("type : "..type) if sense[osense] then print("sense: "..sense[osense]) return sense[osense] else print("sense: ") return "" end else -- the real one local slot = wield_slot_ideal(obj, TRUE) -- Arts items if is_artifact(obj) == TRUE then if band(obj.ident, IDENT_CURSED) == 0 then return "special" else return "terrible" end -- Ego items elseif (obj.name2 > 0 or obj.name2b > 0) then if band(obj.ident, IDENT_CURSED) == 0 then return "very good" else return "very bad" end -- weapon elseif (slot == INVEN_WIELD) or (slot == INVEN_BOW) or (slot == INVEN_AMMO) or (slot == INVEN_TOOL) then if obj.to_h + obj.to_d < 0 then return "bad" elseif obj.to_h + obj.to_d > 0 then return "good" else return "average" end -- armor elseif (slot >= INVEN_BODY) and (slot <= INVEN_FEET) then if obj.to_a < 0 then return "bad" elseif obj.to_a > 0 then return "good" else return "average" end -- ring elseif slot == INVEN_RING then if (obj.to_d + obj.to_h < 0) or (obj.to_a < 0) or (obj.pval < 0) then return "bad" else return "average" end -- amulet elseif slot == INVEN_NECK then if (obj.pval < 0) then return "bad" else return "average" end -- chests elseif obj.tval == TV_CHEST then if obj.pval == 0 then return "empty" elseif obj.pval < 0 then return "disarmed" else return "average" end else return "average" end end end end -- Recursive function to generate a rule function tree function gen_rule_fct(r) -- It is a test rule (or, and, ...) if r.label == "and" or r.label == "or" then local i local fct_tbl = {} for i = 1, getn(r) do if r[i].label ~= "comment" then tinsert(fct_tbl, gen_rule_fct(r[i])) end end if r.label == "and" then return function(object) local fcts = %fct_tbl local i for i = 1, getn(fcts) do if not fcts[i](object) then return end end return TRUE end elseif r.label == "or" then return function(object) local fcts = %fct_tbl local i for i = 1, getn(fcts) do if fcts[i](object) then return TRUE end end end end -- It is a condition rule (name, type, level, ...) else if r.label == "not" then local f if not r[1] then f = function (object) return TRUE end else f = gen_rule_fct(r[1]) end return function(object) return not %f(object) end elseif r.label == "inventory" then local f if not r[1] then f = function(object) return end else f = gen_rule_fct(r[1]) end return function(object) local i = 0 while i < INVEN_WIELD do if %f(player.inventory(i)) then return TRUE end i = i + 1 end end elseif r.label == "equipment" then local f if not r[1] then f = function(object) return end else f = gen_rule_fct(r[1]) end return function(object) local i = INVEN_WIELD while i < INVEN_TOTAL do if %f(player.inventory(i)) then return TRUE end i = i + 1 end end elseif r.label == "name" then return function(object) if strlower(object_desc(object, -1, 0)) == strlower(%r[1]) then return TRUE end end elseif r.label == "contain" then return function(object) if strfind(strlower(object_desc(object, -1, 0)), strlower(%r[1])) then return TRUE end end elseif r.label == "symbol" then return function(object) if strchar(get_kind(object).d_char) == %r[1] then return TRUE end end elseif r.label == "inscribed" then return function(object) if object.note ~= 0 and strfind(strlower(quark_str(object.note)), strlower(%r[1])) then return TRUE end end elseif r.label == "discount" then local d1 = r.args.min local d2 = r.args.max if tonumber(d1) == nil then d1 = getglobal(d1) else d1 = tonumber(d1) end if tonumber(d2) == nil then d2 = getglobal(d2) else d2 = tonumber(d2) end return function(object) if is_aware(object) == TRUE and object.discount >= %d1 and object.discount <= %d2 then return TRUE end end elseif r.label == "tval" then local tv = r[1] if tonumber(tv) == nil then tv = getglobal(tv) else tv = tonumber(tv) end return function(object) if object.tval == %tv then return TRUE end end elseif r.label == "sval" then assert(r.args.min and r.args.max, "sval rule lacks min or max") local sv1 = r.args.min local sv2 = r.args.max if tonumber(sv1) == nil then sv1 = getglobal(sv1) else sv1 = tonumber(sv1) end if tonumber(sv2) == nil then sv2 = getglobal(sv2) else sv2 = tonumber(sv2) end return function(object) if is_aware(object) == TRUE and object.sval >= %sv1 and object.sval <= %sv2 then return TRUE end end elseif r.label == "status" then return function(object) if object_status(object) == strlower(%r[1]) then return TRUE end end elseif r.label == "state" then if r[1] == "identified" then return function(object) if is_known(object) == TRUE then return TRUE end end else return function(object) if is_known(object) == FALSE then return TRUE end end end elseif r.label == "race" then return function(object) if strlower(get_race_name()) == strlower(%r[1]) then return TRUE end end elseif r.label == "subrace" then return function(object) if strlower(get_subrace_name()) == strlower(%r[1]) then return TRUE end end elseif r.label == "class" then return function(object) if strlower(get_class_name()) == strlower(%r[1]) then return TRUE end end elseif r.label == "level" then assert(r.args.min and r.args.max, "level rule lacks min or max") return function(object) if player.lev >= tonumber(%r.args.min) and player.lev <= tonumber(%r.args.max) then return TRUE end end elseif r.label == "skill" then assert(r.args.min and r.args.max, "skill rule lacks min or max") local s = find_skill_i(r[1]) assert(s ~= -1, "no skill "..r[1]) return function(object) if get_skill(%s) >= tonumber(%r.args.min) and get_skill(%s) <= tonumber(%r.args.max) then return TRUE end end elseif r.label == "ability" then local s = find_ability(r[1]) assert(s ~= -1, "no ability "..r[1]) return function(object) if has_ability(%s) == TRUE then return TRUE end end end end end function auto_inscribe_maker(inscription) return function(...) arg.n = arg.n + 1 arg[getn(arg)] = %inscription return call(auto_inscribe, arg) end end -- Generate a rule from a table function gen_full_rule(t) -- only honor rules for this module if not t.args.module then t.args.module = "ToME" end if not ((t.args.module == "all") or (t.args.module == game_module)) then return function() end end -- Check for which action to do local apply_fct = auto_nothing if t.args.type == "destroy" then apply_fct = auto_destroy elseif t.args.type == "pickup" then apply_fct = auto_pickup elseif t.args.type == "inscribe" then apply_fct = auto_inscribe_maker(t.args.inscription) end -- create the function tree local rf if t[1] then rf = gen_rule_fct(t[1]) else rf = function (object) end end -- create the final function return function(...) local rf = %rf if rf(arg[1]) then if call(%apply_fct, arg) == TRUE then return TRUE end end end end -- Create a function that checks for the rules(passed in xml form) function add_ruleset(s) local tbl = xml:collect(s) local i -- Add all rules for i = 1, getn(tbl) do local t = tbl[i] if t.label == "rule" then -- Create the function tree local fct = gen_full_rule(t) -- Create the test function __rules[__rules_max] = { ["table"] = t, ["fct"] = fct } __rules_max = __rules_max + 1 end end end -- Apply the current rules to an object -- call with at least (object, idx) function apply_rules(...) local i for i = 0, __rules_max - 1 do if call(__rules[i].fct, arg) then return TRUE end end return FALSE end -- Clear the current rules function clean_ruleset() __rules_max = 0 __rules = {} end ------ helper fonctions for the GUI auto_aux = {} auto_aux.stack = { n = 0 } auto_aux.idx = 1 auto_aux.rule = 1 function auto_aux:go_right() if auto_aux.rule[1] and type(auto_aux.rule[1]) == "table" then tinsert(auto_aux.stack, auto_aux.idx) tinsert(auto_aux.stack, auto_aux.rule) auto_aux.rule = auto_aux.rule[1] auto_aux.idx = 1 end end function auto_aux:go_left(sel) local n = getn(auto_aux.stack) if n > 0 then auto_aux.idx = auto_aux.stack[n - 1] auto_aux.rule = auto_aux.stack[n] tremove(auto_aux.stack) tremove(auto_aux.stack) end end function auto_aux:go_down() if getn(auto_aux.stack) > 1 then if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx + 1] then auto_aux.idx = auto_aux.idx + 1 auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx] end end end function auto_aux:go_up() if getn(auto_aux.stack) > 1 then if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx - 1] then auto_aux.idx = auto_aux.idx - 1 auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx] end end end function auto_aux:scroll_up() xml.write_off_y = xml.write_off_y - 1 end function auto_aux:scroll_down() xml.write_off_y = xml.write_off_y + 1 end function auto_aux:scroll_left() xml.write_off_x = xml.write_off_x + 1 end function auto_aux:scroll_right() xml.write_off_x = xml.write_off_x - 1 end function auto_aux:adjust_current(sel) if __rules_max == 0 then return end xml.write_off_y = 0 xml.write_off_x = 0 auto_aux.idx = 1 auto_aux.stack = { n = 0 } auto_aux.rule = __rules[sel].table end function auto_aux:move_up(sel) if sel > 0 then local u = __rules[sel - 1] local d = __rules[sel] __rules[sel - 1] = d __rules[sel] = u return sel - 1 end return sel end function auto_aux:move_down(sel) if sel < __rules_max - 1 then local u = __rules[sel] local d = __rules[sel + 1] __rules[sel + 1] = u __rules[sel] = d return sel + 1 end return sel end function auto_aux:new_rule(sel, nam, typ, arg) local r -- nam can also directly be the table itself if type(nam) == "table" then r = { ["table"] = nam, ["fct"] = function (object) end } elseif typ == "inscribe" then if arg == "" then arg = input_box("Inscription?", 79) end r = { ["table"] = { label = "rule", args = { name = nam, type = typ, inscription = arg, module = game_module }, }, ["fct"] = function (object) end } else r = { ["table"] = { label = "rule", args = { name = nam, type = typ, module = game_module }, }, ["fct"] = function (object) end } end tinsert(__rules, sel, r) __rules_max = __rules_max + 1 end function auto_aux:rename_rule(sel, nam) if sel >= 0 and sel < __rules_max then __rules[sel].table.args.name = nam end end function auto_aux:save_ruleset() xml.write = xml.write_file print_hook("clean_ruleset()\nadd_ruleset\n[[\n") local i for i = 0, __rules_max - 1 do xml:print_xml(__rules[i].table, '') end print_hook("]]\n") xml.write = xml.write_screen end function auto_aux:del_self(sel) if auto_aux.rule.label == "rule" then tremove(__rules, sel) __rules_max = __rules_max - 1 return sel - 1 else local idx = auto_aux.idx auto_aux:go_left(sel) tremove(auto_aux.rule, idx) return sel end end auto_aux.types_desc = { ["and"] = { "Check is true if all rules within it are true", xml:collect([[.........]]), function () return xml:collect("") end, }, ["or"] = { "Check is true if at least one rule within it is true", xml:collect([[.........]]), function () return xml:collect("") end, }, ["not"] = { "Invert the result of its child rule", xml:collect([[...]]), function () return xml:collect("") end, }, ["comment"] = { "Comments are meaningless", xml:collect([[Comment explaining something]]), function () local n = input_box("Comment?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["name"] = { "Check is true if object name matches name", xml:collect([[potion of healing]]), function () local n = input_box("Object name to match?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["contain"] = { "Check is true if object name contains word", xml:collect([[healing]]), function () local n = input_box("Word to find in object name?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["inscribed"] = { "Check is true if object inscription contains word", xml:collect([[=g]]), function () local n = input_box("Word to find in object inscription?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["discount"] = { "Check is true if object discount is between 2 values", xml:collect([[]]), function () local s = "" return xml:collect(s) end, }, ["symbol"] = { "Check is true if object symbol is ok", xml:collect([[!]]), function () local n = input_box("Symbol to match?", 1) if n == "" then return end return xml:collect(""..n.."") end, }, ["status"] = { "Check is true if object status is ok", xml:collect([[good]]), function () local n = msg_box("[t]errible, [v]ery bad, [b]ad, [a]verage, [G]ood, [V]ery good, [S]pecial?") local t = { ["t"] = "terrible", ["v"] = "very bad", ["b"] = "bad", ["a"] = "average", ["G"] = "good", ["V"] = "very good", ["S"] = "special", } if not t[strchar(n)] then return end return xml:collect(""..t[strchar(n)].."") end, }, ["state"] = { "Check is true if object is identified/unidentified", xml:collect([[identified]]), function () local n = msg_box("[i]dentified, [n]on identified?") local t = { ["i"] = "identified", ["n"] = "not identified", } if not t[strchar(n)] then return end return xml:collect(""..t[strchar(n)].."") end, }, ["tval"] = { "Check is true if object tval(from k_info.txt) is ok", xml:collect([[55]]), function () local n = input_box("Tval to match?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["sval"] = { { "Check is true if object sval(from k_info.txt) is between", "2 values", }, xml:collect([[]]), function () local s = "" return xml:collect(s) end, }, ["race"] = { "Check is true if player race is ok", xml:collect([[dunadan]]), function () local n = input_box("Player race to match?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["subrace"] = { "Check is true if player subrace is ok", xml:collect([[vampire]]), function () local n = input_box("Player subrace to match?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["class"] = { "Check is true if player class is ok", xml:collect([[sorceror]]), function () local n = input_box("Player class to match?", 79) if n == "" then return end return xml:collect(""..n.."") end, }, ["level"] = { "Check is true if player level is between 2 values", xml:collect([[]]), function () local s = "" return xml:collect(s) end, }, ["skill"] = { "Check is true if player skill level is between 2 values", xml:collect([[Divination]]), function () local s = "" n = input_box("Skill name?", 79) if n == "" then return end if find_skill_i(n) == -1 then return end s = s..n.."" return xml:collect(s) end, }, ["ability"] = { "Check is true if player has the ability", xml:collect([[Ammo creation]]), function() local n = input_box("Ability name?", 79) if n == "" then return end if find_ability(n) == -1 then return end return xml:collect(""..n.."") end, }, ["inventory"] = { { "Check is true if something in player's inventory matches", "the contained rule", }, xml:collect([[...]]), function () return xml:collect("") end, }, ["equipment"] = { { "Check is true if something in player's equipment matches", "the contained rule", }, xml:collect([[...]]), function () return xml:collect("") end, }, } function auto_aux:display_desc(sel) local d = auto_aux.types_desc[sel][1] if type(d) == "string" then c_prt(TERM_WHITE, d, 1, 17) else local k, e, i i = 0 for k, e in d do c_prt(TERM_WHITE, e, 1 + i, 17) i = i + 1 end end end function auto_aux:add_child(sel) -- and contain only one match if (auto_aux.rule.label == "rule" or auto_aux.rule.label == "not") and auto_aux.rule[1] then return end if (auto_aux.rule.label == "rule" or auto_aux.rule.label == "equipment") and auto_aux.rule[1] then return end if (auto_aux.rule.label == "rule" or auto_aux.rule.label == "inventory") and auto_aux.rule[1] then return end -- Only and can contain if auto_aux.rule.label ~= "rule" and auto_aux.rule.label ~= "and" and auto_aux.rule.label ~= "or" and auto_aux.rule.label ~= "not" and auto_aux.rule.label ~= "equipment" and auto_aux.rule.label ~= "inventory" then return end -- get it local r = auto_aux.types_desc[sel][3]() if not r then return end -- Ok add it tinsert(auto_aux.rule, r[1]) end function auto_aux.regen_ruleset() local i for i = 0, __rules_max - 1 do __rules[i].fct = gen_full_rule(__rules[i].table) end end -- Easily add new rules function easy_add_rule(typ, mode, do_status, obj) local detect_rule if mode == "tval" then detect_rule = ""..obj.tval.."" elseif mode == "tsval" then detect_rule = ""..obj.tval.."" elseif mode == "name" then detect_rule = ""..strlower(object_desc(obj, -1, 0)).."" end if do_status == TRUE then local status = object_status(obj) if status and not (status == "") then detect_rule = ""..detect_rule..""..status.."" end end local rule = ""..detect_rule.."" auto_aux:new_rule(0, xml:collect(rule)[1], '') auto_aux.regen_ruleset() msg_print("Rule added. Please go to the Automatizer screen (press = then T)") msg_print("to save the modified ruleset.") end