summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt13
-rw-r--r--README.txt13
-rw-r--r--lib/core/auto.lua859
-rw-r--r--lib/core/init.lua3
-rw-r--r--lib/core/xml.lua375
-rw-r--r--lib/mods/theme/core/auto.lua859
-rw-r--r--lib/mods/theme/core/init.lua3
-rw-r--r--lib/mods/theme/core/xml.lua375
-rw-r--r--src/dungeon.c6
-rw-r--r--src/externs.h5
-rw-r--r--src/init2.c4
-rw-r--r--src/loadsave.c2
-rw-r--r--src/modules.c26
-rw-r--r--src/squeltch.c3424
14 files changed, 3275 insertions, 2692 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8714ca8a..da5486b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,6 +5,9 @@ CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
# We want a readable feature summary.
INCLUDE(FeatureSummary)
+# pkg-config support
+INCLUDE(FindPkgConfig)
+
# Default flags.
IF(CMAKE_COMPILER_IS_GNUCC)
# Let's set sensible options.
@@ -17,6 +20,16 @@ ENDIF()
ADD_DEFINITIONS(-DUSE_PRECISE_CMOVIE)
#
+# JSON support
+#
+PKG_CHECK_MODULES(JANSSON REQUIRED jansson)
+IF(JANSSON_FOUND)
+ ADD_DEFINITIONS(${JANSSON_CFLAGS})
+ INCLUDE_DIRECTORIES(${JANSSON_INCLUDE_DIRS})
+ SET(LIBS ${LIBS} ${JANSSON_LIBRARIES})
+ENDIF()
+
+#
# X11 support (OPTIONAL)
#
FIND_PACKAGE(X11)
diff --git a/README.txt b/README.txt
index b422701e..a4d243ef 100644
--- a/README.txt
+++ b/README.txt
@@ -4,6 +4,15 @@ Using the CMake build system
There are basically two options for how to run ToME once built.
+Prerequisites
+=============
+
+You will need to have the following libraries installed
+on your system somewhere where CMake can find them:
+
+ - jansson
+ See http://www.digip.org/jansson/
+
Option #1 : Run ToME from the build directory
=============================================
@@ -46,6 +55,10 @@ probably missing the
build-essential
+package. You'll also need to install the
+
+ libjansson-dev
+
package.
Each frontend requires the additional packages listed below:
diff --git a/lib/core/auto.lua b/lib/core/auto.lua
deleted file mode 100644
index b758db52..00000000
--- a/lib/core/auto.lua
+++ /dev/null
@@ -1,859 +0,0 @@
--- 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("<Auto-Inscribe {"..note.."}>")
- 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("<Auto-pickup>")
- 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("<Auto-destroy>")
-
- -- 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([[<and><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></and>]]),
- function ()
- return xml:collect("<and></and>")
- end,
- },
- ["or"] =
- {
- "Check is true if at least one rule within it is true",
- xml:collect([[<or><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></or>]]),
- function ()
- return xml:collect("<or></or>")
- end,
- },
- ["not"] =
- {
- "Invert the result of its child rule",
- xml:collect([[<not><foo1>...</foo1></not>]]),
- function ()
- return xml:collect("<not></not>")
- end,
- },
- ["comment"] =
- {
- "Comments are meaningless",
- xml:collect([[<comment>Comment explaining something</comment>]]),
- function ()
- local n = input_box("Comment?", 79)
- if n == "" then return end
- return xml:collect("<comment>"..n.."</comment>")
- end,
- },
- ["name"] =
- {
- "Check is true if object name matches name",
- xml:collect([[<name>potion of healing</name>]]),
- function ()
- local n = input_box("Object name to match?", 79)
- if n == "" then return end
- return xml:collect("<name>"..n.."</name>")
- end,
- },
- ["contain"] =
- {
- "Check is true if object name contains word",
- xml:collect([[<contain>healing</contain>]]),
- function ()
- local n = input_box("Word to find in object name?", 79)
- if n == "" then return end
- return xml:collect("<contain>"..n.."</contain>")
- end,
- },
- ["inscribed"] =
- {
- "Check is true if object inscription contains word",
- xml:collect([[<inscribed>=g</inscribed>]]),
- function ()
- local n = input_box("Word to find in object inscription?", 79)
- if n == "" then return end
- return xml:collect("<inscribed>"..n.."</inscribed>")
- end,
- },
- ["discount"] =
- {
- "Check is true if object discount is between 2 values",
- xml:collect([[<sval min='50' max='100'></sval>]]),
- function ()
- local s = "<discount "
-
- local n = input_box("Min discount?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max discount?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></discount>"
- return xml:collect(s)
- end,
- },
- ["symbol"] =
- {
- "Check is true if object symbol is ok",
- xml:collect([[<symbol>!</symbol>]]),
- function ()
- local n = input_box("Symbol to match?", 1)
- if n == "" then return end
- return xml:collect("<symbol>"..n.."</symbol>")
- end,
- },
- ["status"] =
- {
- "Check is true if object status is ok",
- xml:collect([[<status>good</status>]]),
- 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("<status>"..t[strchar(n)].."</status>")
- end,
- },
- ["state"] =
- {
- "Check is true if object is identified/unidentified",
- xml:collect([[<state>identified</state>]]),
- 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("<state>"..t[strchar(n)].."</state>")
- end,
- },
- ["tval"] =
- {
- "Check is true if object tval(from k_info.txt) is ok",
- xml:collect([[<tval>55</tval>]]),
- function ()
- local n = input_box("Tval to match?", 79)
- if n == "" then return end
- return xml:collect("<tval>"..n.."</tval>")
- end,
- },
- ["sval"] =
- {
- {
- "Check is true if object sval(from k_info.txt) is between",
- "2 values",
- },
- xml:collect([[<sval min='0' max='100'></sval>]]),
- function ()
- local s = "<sval "
-
- local n = input_box("Min sval?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max sval?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></sval>"
- return xml:collect(s)
- end,
- },
- ["race"] =
- {
- "Check is true if player race is ok",
- xml:collect([[<race>dunadan</race>]]),
- function ()
- local n = input_box("Player race to match?", 79)
- if n == "" then return end
- return xml:collect("<race>"..n.."</race>")
- end,
- },
- ["subrace"] =
- {
- "Check is true if player subrace is ok",
- xml:collect([[<subrace>vampire</subrace>]]),
- function ()
- local n = input_box("Player subrace to match?", 79)
- if n == "" then return end
- return xml:collect("<subrace>"..n.."</subrace>")
- end,
- },
- ["class"] =
- {
- "Check is true if player class is ok",
- xml:collect([[<class>sorceror</class>]]),
- function ()
- local n = input_box("Player class to match?", 79)
- if n == "" then return end
- return xml:collect("<class>"..n.."</class>")
- end,
- },
- ["level"] =
- {
- "Check is true if player level is between 2 values",
- xml:collect([[<level min='20' max='50'></level>]]),
- function ()
- local s = "<level "
-
- local n = input_box("Min player level?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max player level?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></level>"
-
- return xml:collect(s)
- end,
- },
- ["skill"] =
- {
- "Check is true if player skill level is between 2 values",
- xml:collect([[<skill min='10' max='20'>Divination</skill>]]),
- function ()
- local s = "<skill "
-
- local n = input_box("Min skill level?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max skill level?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'>"
-
- n = input_box("Skill name?", 79)
- if n == "" then return end
- if find_skill_i(n) == -1 then return end
- s = s..n.."</skill>"
-
- return xml:collect(s)
- end,
- },
- ["ability"] =
- {
- "Check is true if player has the ability",
- xml:collect([[<ability>Ammo creation</ability>]]),
- function()
- local n = input_box("Ability name?", 79)
- if n == "" then return end
- if find_ability(n) == -1 then return end
- return xml:collect("<ability>"..n.."</ability>")
- end,
- },
- ["inventory"] =
- {
- {
- "Check is true if something in player's inventory matches",
- "the contained rule",
- },
- xml:collect([[<inventory><foo1>...</foo1></inventory>]]),
- function ()
- return xml:collect("<inventory></inventory>")
- end,
- },
- ["equipment"] =
- {
- {
- "Check is true if something in player's equipment matches",
- "the contained rule",
- },
- xml:collect([[<equipment><foo1>...</foo1></equipment>]]),
- function ()
- return xml:collect("<equipment></equipment>")
- 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)
- -- <rule> and <not> 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> and <or> 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 = "<tval>"..obj.tval.."</tval>"
- elseif mode == "tsval" then
- detect_rule = "<and><tval>"..obj.tval.."</tval><sval min='"..obj.sval.."' max='"..obj.sval.."'></sval></and>"
- elseif mode == "name" then
- detect_rule = "<name>"..strlower(object_desc(obj, -1, 0)).."</name>"
- end
-
- if do_status == TRUE then
- local status = object_status(obj)
- if status and not (status == "") then
- detect_rule = "<and>"..detect_rule.."<status>"..status.."</status></and>"
- end
- end
-
- local rule = "<rule module='"..game_module.."' name='"..typ.."' type='"..typ.."'>"..detect_rule.."</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
diff --git a/lib/core/init.lua b/lib/core/init.lua
index 51dfc1d2..44ef9a7a 100644
--- a/lib/core/init.lua
+++ b/lib/core/init.lua
@@ -3,9 +3,6 @@
-- Load the system functions
--
--- Very thin xml parser(49 lines ;)
-tome_dofile_anywhere(ANGBAND_DIR_CORE, "xml.lua")
-
-- various vital helper code
tome_dofile_anywhere(ANGBAND_DIR_CORE, "util.lua")
tome_dofile_anywhere(ANGBAND_DIR_CORE, "player.lua")
diff --git a/lib/core/xml.lua b/lib/core/xml.lua
deleted file mode 100644
index 14f0511f..00000000
--- a/lib/core/xml.lua
+++ /dev/null
@@ -1,375 +0,0 @@
--- The xml module
-xml = {}
-
-function xml:parseargs (s)
- local arg = {}
- gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
- %arg[w] = a
- end)
- return arg
-end
-
--- s is a xml stream, returns a table
-function xml:collect (s)
- local stack = {n=0}
- local top = {n=0}
- tinsert(stack, top)
- local ni,c,label,args, empty
- local i, j = 1, 1
- while 1 do
- ni,j,c,label,args, empty = strfind(s, "<(%/?)(%w+)(.-)(%/?)>", j)
- if not ni then break end
- local text = strsub(s, i, ni-1)
- if not strfind(text, "^%s*$") then
- tinsert(top, text)
- end
- if empty == "/" then -- empty element tag
- tinsert(top, {n=0, label=label, args=xml:parseargs(args), empty=1})
- elseif c == "" then -- start tag
- top = {n=0, label=label, args=xml:parseargs(args)}
- tinsert(stack, top) -- new level
- else -- end tag
- local toclose = tremove(stack) -- remove top
- top = stack[stack.n]
- if stack.n < 1 then
- error("nothing to close with "..label)
- end
- if toclose.label ~= label then
- error("trying to close "..toclose.label.." with "..label)
- end
- tinsert(top, toclose)
- end
- i = j+1
- end
- local text = strsub(s, i)
- if not strfind(text, "^%s*$") then
- tinsert(stack[stack.n], text)
- end
- if stack.n > 1 then
- error("unclosed "..stack[stack.n].label)
- end
- return stack[1]
-end
-
--- Viewport coordinates
-xml.write_out_y = 0
-xml.write_out_x = 0
-xml.write_out_h = 24
-xml.write_out_w = 80
-
--- Offsets
-xml.write_off_y = 0
-xml.write_off_x = 0
-
--- Current position
-xml.write_y = 0
-xml.write_x = 0
-
-xml.write_screen = function(color, s)
- local i
- for i = 1, strlen(s) do
- local c = strsub(s, i, i + 1)
- if c ~= "\n" then
- if xml.write_y - xml.write_off_y >= 0 and xml.write_y - xml.write_off_y < xml.write_out_h and xml.write_x - xml.write_off_x >= 0 and xml.write_x - xml.write_off_x < xml.write_out_w then
- Term_putch(xml.write_x - xml.write_off_x + xml.write_out_x, xml.write_y - xml.write_off_y + xml.write_out_y, color, strbyte(c))
- end
- xml.write_x = xml.write_x + 1
- else
- xml.write_x = 0
- xml.write_y = xml.write_y + 1
- end
- end
-end
-
-xml.write_file = function (color, s)
- print_hook(s)
-end
-
-xml.write = xml.write_screen
-
-xml.rule2string = {
- ['name'] = {"Its ", "name", " is"},
- ['contain'] = {"Its ", "name", " contains"},
- ['symbol'] = {"Its ", "symbol", " is"},
- ['inscribed'] = {"Its ", "inscription", " contains"},
- ['state'] = {"Its ", "state", " is"},
- ['status'] = {"Its ", "status", " is"},
- ['tval'] = {"Its ", "tval", " is"},
- ['race'] = {"Your ", "race", " is"},
- ['subrace'] = {"Your ", "subrace", " is"},
- ['class'] = {"Your ", "class", " is"},
- ['foo1'] = {"The result of ", "test 1 ", "is"},
- ['foo2'] = {"The result of ", "test 2 ", "is"},
- ['foo3'] = {"The result of ", "test 3 ", "is"},
-}
-
-xml.display_english = 1
-function xml:display_xml(t, tab)
- if xml.display_english then
- xml:english_xml(t, tab)
- else
- xml:print_xml(t, tab)
- end
-end
-
-function xml:english_xml(t, tab, not_flag)
- local i, k, e
- local pre, post, recurse
- local children_not_flag
- local nextlevel
- local bcol, ecol = TERM_L_GREEN, TERM_GREEN
-
- if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
-
- nextlevel = tab .. " "
-
- recurse = 1
-
- if t.label == "rule" then
- if t.args.type == "inscribe" then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "A rule named \"")
- xml.write(TERM_WHITE, tostring(t.args.name))
- xml.write(ecol, "\" to ")
- xml.write(bcol, "inscribe")
- xml.write(ecol, " an item with \"")
- xml.write(TERM_WHITE, t.args.inscription)
- xml.write(ecol, "\" when")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "A rule named \"")
- xml.write(TERM_WHITE, tostring(t.args.name))
- xml.write(ecol, "\" to ")
- xml.write(bcol, t.args.type)
- xml.write(ecol, " when")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "and" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "At least one of the following is false:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "All of the following are true:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "or" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "All of the following are false:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "At least one of the following are true:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "not" then
- if bcol == TERM_VIOLET or getn(t) == 0 then
- xml.write(ecol, "(a negating rule)")
- xml.write(TERM_WHITE, "\n")
- else
- nextlevel = tab
- end
- children_not_flag = not nil
- elseif t.label == "inventory" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Nothing in your ")
- xml.write(bcol, "inventory")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Something in your ")
- xml.write(bcol, "inventory")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "equipment" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Nothing in your ")
- xml.write(bcol, "equipment")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Something in your ")
- xml.write(bcol, "equipment")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "comment" then
- xml.write(TERM_WHITE, tab)
- xml.write(TERM_WHITE, "(" .. t[1] .. ")")
- xml.write(TERM_WHITE, "\n")
- elseif t.label == "skill" then
- local s = t[1]
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your skill in ")
- xml.write(bcol, s)
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your skill in ")
- xml.write(bcol, s)
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "ability" then
- local s = t[1]
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "You do not have the ")
- xml.write(bcol, s)
- xml.write(ecol, " ability")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "You have the ")
- xml.write(bcol, s)
- xml.write(ecol, " ability")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "level" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your ")
- xml.write(bcol, "level")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your ")
- xml.write(bcol, "level")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "sval" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "sval")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "sval")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "discount" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "discount")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "discount")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- else
- if xml.rule2string[t.label] then
- local rule = xml.rule2string[t.label]
- a, b, c = rule[1], rule[2], rule[3]
- if not_flag then c = c .. " not" end
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, a)
- xml.write(bcol, b)
- xml.write(ecol, c)
- xml.write(ecol, " \"")
- xml.write(TERM_WHITE, t[1])
- xml.write(ecol, "\"")
- xml.write(TERM_WHITE, "\n")
- else
- if not_flag then
- xml.write(bcol, "Not:\n")
- tab = tab .. " "
- xml:print_xml(t, tab)
- return
- end
- end
- end
-
- for i = 1, getn(t) do
- if type(t[i]) == "string" then
- -- xml.write(TERM_WHITE, t[i].."\n")
- else
- xml:english_xml(t[i], nextlevel, children_not_flag)
- end
- end
-end
-
-function xml:print_xml(t, tab)
- local i, k, e
- local inside = nil
- local bcol, ecol = TERM_L_GREEN, TERM_GREEN
-
- if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
-
- xml.write(bcol, tab.."<"..t.label)
- for k, e in t.args do
- xml.write(TERM_L_BLUE, " "..k)
- xml.write(TERM_WHITE, "=\"")
- xml.write(TERM_YELLOW, e)
- xml.write(TERM_WHITE, "\"")
- end
- xml.write(bcol, ">")
-
- for i = 1, getn(t) do
- if type(t[i]) == "string" then
- xml.write(TERM_WHITE, t[i])
- else
- if not inside then xml.write(TERM_WHITE, "\n") end
- inside = not nil
- xml:print_xml(t[i], tab.." ")
- end
- end
-
- if not inside then
- xml.write(ecol, "</"..t.label..">\n")
- else
- xml.write(ecol, tab.."</"..t.label..">\n")
- end
-end
-
--- t is a table representing xml, outputs the xml code via xml.write()
-function xml:output(t)
- local i
- for i = 1, getn(t) do
- xml:print_xml(t[i], "")
- end
-end
diff --git a/lib/mods/theme/core/auto.lua b/lib/mods/theme/core/auto.lua
deleted file mode 100644
index b758db52..00000000
--- a/lib/mods/theme/core/auto.lua
+++ /dev/null
@@ -1,859 +0,0 @@
--- 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("<Auto-Inscribe {"..note.."}>")
- 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("<Auto-pickup>")
- 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("<Auto-destroy>")
-
- -- 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([[<and><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></and>]]),
- function ()
- return xml:collect("<and></and>")
- end,
- },
- ["or"] =
- {
- "Check is true if at least one rule within it is true",
- xml:collect([[<or><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></or>]]),
- function ()
- return xml:collect("<or></or>")
- end,
- },
- ["not"] =
- {
- "Invert the result of its child rule",
- xml:collect([[<not><foo1>...</foo1></not>]]),
- function ()
- return xml:collect("<not></not>")
- end,
- },
- ["comment"] =
- {
- "Comments are meaningless",
- xml:collect([[<comment>Comment explaining something</comment>]]),
- function ()
- local n = input_box("Comment?", 79)
- if n == "" then return end
- return xml:collect("<comment>"..n.."</comment>")
- end,
- },
- ["name"] =
- {
- "Check is true if object name matches name",
- xml:collect([[<name>potion of healing</name>]]),
- function ()
- local n = input_box("Object name to match?", 79)
- if n == "" then return end
- return xml:collect("<name>"..n.."</name>")
- end,
- },
- ["contain"] =
- {
- "Check is true if object name contains word",
- xml:collect([[<contain>healing</contain>]]),
- function ()
- local n = input_box("Word to find in object name?", 79)
- if n == "" then return end
- return xml:collect("<contain>"..n.."</contain>")
- end,
- },
- ["inscribed"] =
- {
- "Check is true if object inscription contains word",
- xml:collect([[<inscribed>=g</inscribed>]]),
- function ()
- local n = input_box("Word to find in object inscription?", 79)
- if n == "" then return end
- return xml:collect("<inscribed>"..n.."</inscribed>")
- end,
- },
- ["discount"] =
- {
- "Check is true if object discount is between 2 values",
- xml:collect([[<sval min='50' max='100'></sval>]]),
- function ()
- local s = "<discount "
-
- local n = input_box("Min discount?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max discount?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></discount>"
- return xml:collect(s)
- end,
- },
- ["symbol"] =
- {
- "Check is true if object symbol is ok",
- xml:collect([[<symbol>!</symbol>]]),
- function ()
- local n = input_box("Symbol to match?", 1)
- if n == "" then return end
- return xml:collect("<symbol>"..n.."</symbol>")
- end,
- },
- ["status"] =
- {
- "Check is true if object status is ok",
- xml:collect([[<status>good</status>]]),
- 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("<status>"..t[strchar(n)].."</status>")
- end,
- },
- ["state"] =
- {
- "Check is true if object is identified/unidentified",
- xml:collect([[<state>identified</state>]]),
- 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("<state>"..t[strchar(n)].."</state>")
- end,
- },
- ["tval"] =
- {
- "Check is true if object tval(from k_info.txt) is ok",
- xml:collect([[<tval>55</tval>]]),
- function ()
- local n = input_box("Tval to match?", 79)
- if n == "" then return end
- return xml:collect("<tval>"..n.."</tval>")
- end,
- },
- ["sval"] =
- {
- {
- "Check is true if object sval(from k_info.txt) is between",
- "2 values",
- },
- xml:collect([[<sval min='0' max='100'></sval>]]),
- function ()
- local s = "<sval "
-
- local n = input_box("Min sval?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max sval?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></sval>"
- return xml:collect(s)
- end,
- },
- ["race"] =
- {
- "Check is true if player race is ok",
- xml:collect([[<race>dunadan</race>]]),
- function ()
- local n = input_box("Player race to match?", 79)
- if n == "" then return end
- return xml:collect("<race>"..n.."</race>")
- end,
- },
- ["subrace"] =
- {
- "Check is true if player subrace is ok",
- xml:collect([[<subrace>vampire</subrace>]]),
- function ()
- local n = input_box("Player subrace to match?", 79)
- if n == "" then return end
- return xml:collect("<subrace>"..n.."</subrace>")
- end,
- },
- ["class"] =
- {
- "Check is true if player class is ok",
- xml:collect([[<class>sorceror</class>]]),
- function ()
- local n = input_box("Player class to match?", 79)
- if n == "" then return end
- return xml:collect("<class>"..n.."</class>")
- end,
- },
- ["level"] =
- {
- "Check is true if player level is between 2 values",
- xml:collect([[<level min='20' max='50'></level>]]),
- function ()
- local s = "<level "
-
- local n = input_box("Min player level?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max player level?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'></level>"
-
- return xml:collect(s)
- end,
- },
- ["skill"] =
- {
- "Check is true if player skill level is between 2 values",
- xml:collect([[<skill min='10' max='20'>Divination</skill>]]),
- function ()
- local s = "<skill "
-
- local n = input_box("Min skill level?", 79)
- if n == "" then return end
- s = s.."min='"..n.."' "
-
- n = input_box("Max skill level?", 79)
- if n == "" then return end
- s = s.."max='"..n.."'>"
-
- n = input_box("Skill name?", 79)
- if n == "" then return end
- if find_skill_i(n) == -1 then return end
- s = s..n.."</skill>"
-
- return xml:collect(s)
- end,
- },
- ["ability"] =
- {
- "Check is true if player has the ability",
- xml:collect([[<ability>Ammo creation</ability>]]),
- function()
- local n = input_box("Ability name?", 79)
- if n == "" then return end
- if find_ability(n) == -1 then return end
- return xml:collect("<ability>"..n.."</ability>")
- end,
- },
- ["inventory"] =
- {
- {
- "Check is true if something in player's inventory matches",
- "the contained rule",
- },
- xml:collect([[<inventory><foo1>...</foo1></inventory>]]),
- function ()
- return xml:collect("<inventory></inventory>")
- end,
- },
- ["equipment"] =
- {
- {
- "Check is true if something in player's equipment matches",
- "the contained rule",
- },
- xml:collect([[<equipment><foo1>...</foo1></equipment>]]),
- function ()
- return xml:collect("<equipment></equipment>")
- 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)
- -- <rule> and <not> 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> and <or> 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 = "<tval>"..obj.tval.."</tval>"
- elseif mode == "tsval" then
- detect_rule = "<and><tval>"..obj.tval.."</tval><sval min='"..obj.sval.."' max='"..obj.sval.."'></sval></and>"
- elseif mode == "name" then
- detect_rule = "<name>"..strlower(object_desc(obj, -1, 0)).."</name>"
- end
-
- if do_status == TRUE then
- local status = object_status(obj)
- if status and not (status == "") then
- detect_rule = "<and>"..detect_rule.."<status>"..status.."</status></and>"
- end
- end
-
- local rule = "<rule module='"..game_module.."' name='"..typ.."' type='"..typ.."'>"..detect_rule.."</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
diff --git a/lib/mods/theme/core/init.lua b/lib/mods/theme/core/init.lua
index 51dfc1d2..44ef9a7a 100644
--- a/lib/mods/theme/core/init.lua
+++ b/lib/mods/theme/core/init.lua
@@ -3,9 +3,6 @@
-- Load the system functions
--
--- Very thin xml parser(49 lines ;)
-tome_dofile_anywhere(ANGBAND_DIR_CORE, "xml.lua")
-
-- various vital helper code
tome_dofile_anywhere(ANGBAND_DIR_CORE, "util.lua")
tome_dofile_anywhere(ANGBAND_DIR_CORE, "player.lua")
diff --git a/lib/mods/theme/core/xml.lua b/lib/mods/theme/core/xml.lua
deleted file mode 100644
index 14f0511f..00000000
--- a/lib/mods/theme/core/xml.lua
+++ /dev/null
@@ -1,375 +0,0 @@
--- The xml module
-xml = {}
-
-function xml:parseargs (s)
- local arg = {}
- gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
- %arg[w] = a
- end)
- return arg
-end
-
--- s is a xml stream, returns a table
-function xml:collect (s)
- local stack = {n=0}
- local top = {n=0}
- tinsert(stack, top)
- local ni,c,label,args, empty
- local i, j = 1, 1
- while 1 do
- ni,j,c,label,args, empty = strfind(s, "<(%/?)(%w+)(.-)(%/?)>", j)
- if not ni then break end
- local text = strsub(s, i, ni-1)
- if not strfind(text, "^%s*$") then
- tinsert(top, text)
- end
- if empty == "/" then -- empty element tag
- tinsert(top, {n=0, label=label, args=xml:parseargs(args), empty=1})
- elseif c == "" then -- start tag
- top = {n=0, label=label, args=xml:parseargs(args)}
- tinsert(stack, top) -- new level
- else -- end tag
- local toclose = tremove(stack) -- remove top
- top = stack[stack.n]
- if stack.n < 1 then
- error("nothing to close with "..label)
- end
- if toclose.label ~= label then
- error("trying to close "..toclose.label.." with "..label)
- end
- tinsert(top, toclose)
- end
- i = j+1
- end
- local text = strsub(s, i)
- if not strfind(text, "^%s*$") then
- tinsert(stack[stack.n], text)
- end
- if stack.n > 1 then
- error("unclosed "..stack[stack.n].label)
- end
- return stack[1]
-end
-
--- Viewport coordinates
-xml.write_out_y = 0
-xml.write_out_x = 0
-xml.write_out_h = 24
-xml.write_out_w = 80
-
--- Offsets
-xml.write_off_y = 0
-xml.write_off_x = 0
-
--- Current position
-xml.write_y = 0
-xml.write_x = 0
-
-xml.write_screen = function(color, s)
- local i
- for i = 1, strlen(s) do
- local c = strsub(s, i, i + 1)
- if c ~= "\n" then
- if xml.write_y - xml.write_off_y >= 0 and xml.write_y - xml.write_off_y < xml.write_out_h and xml.write_x - xml.write_off_x >= 0 and xml.write_x - xml.write_off_x < xml.write_out_w then
- Term_putch(xml.write_x - xml.write_off_x + xml.write_out_x, xml.write_y - xml.write_off_y + xml.write_out_y, color, strbyte(c))
- end
- xml.write_x = xml.write_x + 1
- else
- xml.write_x = 0
- xml.write_y = xml.write_y + 1
- end
- end
-end
-
-xml.write_file = function (color, s)
- print_hook(s)
-end
-
-xml.write = xml.write_screen
-
-xml.rule2string = {
- ['name'] = {"Its ", "name", " is"},
- ['contain'] = {"Its ", "name", " contains"},
- ['symbol'] = {"Its ", "symbol", " is"},
- ['inscribed'] = {"Its ", "inscription", " contains"},
- ['state'] = {"Its ", "state", " is"},
- ['status'] = {"Its ", "status", " is"},
- ['tval'] = {"Its ", "tval", " is"},
- ['race'] = {"Your ", "race", " is"},
- ['subrace'] = {"Your ", "subrace", " is"},
- ['class'] = {"Your ", "class", " is"},
- ['foo1'] = {"The result of ", "test 1 ", "is"},
- ['foo2'] = {"The result of ", "test 2 ", "is"},
- ['foo3'] = {"The result of ", "test 3 ", "is"},
-}
-
-xml.display_english = 1
-function xml:display_xml(t, tab)
- if xml.display_english then
- xml:english_xml(t, tab)
- else
- xml:print_xml(t, tab)
- end
-end
-
-function xml:english_xml(t, tab, not_flag)
- local i, k, e
- local pre, post, recurse
- local children_not_flag
- local nextlevel
- local bcol, ecol = TERM_L_GREEN, TERM_GREEN
-
- if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
-
- nextlevel = tab .. " "
-
- recurse = 1
-
- if t.label == "rule" then
- if t.args.type == "inscribe" then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "A rule named \"")
- xml.write(TERM_WHITE, tostring(t.args.name))
- xml.write(ecol, "\" to ")
- xml.write(bcol, "inscribe")
- xml.write(ecol, " an item with \"")
- xml.write(TERM_WHITE, t.args.inscription)
- xml.write(ecol, "\" when")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "A rule named \"")
- xml.write(TERM_WHITE, tostring(t.args.name))
- xml.write(ecol, "\" to ")
- xml.write(bcol, t.args.type)
- xml.write(ecol, " when")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "and" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "At least one of the following is false:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "All of the following are true:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "or" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "All of the following are false:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "At least one of the following are true:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "not" then
- if bcol == TERM_VIOLET or getn(t) == 0 then
- xml.write(ecol, "(a negating rule)")
- xml.write(TERM_WHITE, "\n")
- else
- nextlevel = tab
- end
- children_not_flag = not nil
- elseif t.label == "inventory" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Nothing in your ")
- xml.write(bcol, "inventory")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Something in your ")
- xml.write(bcol, "inventory")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "equipment" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Nothing in your ")
- xml.write(bcol, "equipment")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Something in your ")
- xml.write(bcol, "equipment")
- xml.write(ecol, " matches the following:")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "comment" then
- xml.write(TERM_WHITE, tab)
- xml.write(TERM_WHITE, "(" .. t[1] .. ")")
- xml.write(TERM_WHITE, "\n")
- elseif t.label == "skill" then
- local s = t[1]
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your skill in ")
- xml.write(bcol, s)
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your skill in ")
- xml.write(bcol, s)
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "ability" then
- local s = t[1]
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "You do not have the ")
- xml.write(bcol, s)
- xml.write(ecol, " ability")
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "You have the ")
- xml.write(bcol, s)
- xml.write(ecol, " ability")
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "level" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your ")
- xml.write(bcol, "level")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Your ")
- xml.write(bcol, "level")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "sval" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "sval")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "sval")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- elseif t.label == "discount" then
- if not_flag then
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "discount")
- xml.write(ecol, " is not from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- else
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, "Its ")
- xml.write(bcol, "discount")
- xml.write(ecol, " is from ")
- xml.write(TERM_WHITE, tostring(t.args.min))
- xml.write(ecol, " to ")
- xml.write(TERM_WHITE, tostring(t.args.max))
- xml.write(TERM_WHITE, "\n")
- end
- else
- if xml.rule2string[t.label] then
- local rule = xml.rule2string[t.label]
- a, b, c = rule[1], rule[2], rule[3]
- if not_flag then c = c .. " not" end
- xml.write(TERM_WHITE, tab)
- xml.write(ecol, a)
- xml.write(bcol, b)
- xml.write(ecol, c)
- xml.write(ecol, " \"")
- xml.write(TERM_WHITE, t[1])
- xml.write(ecol, "\"")
- xml.write(TERM_WHITE, "\n")
- else
- if not_flag then
- xml.write(bcol, "Not:\n")
- tab = tab .. " "
- xml:print_xml(t, tab)
- return
- end
- end
- end
-
- for i = 1, getn(t) do
- if type(t[i]) == "string" then
- -- xml.write(TERM_WHITE, t[i].."\n")
- else
- xml:english_xml(t[i], nextlevel, children_not_flag)
- end
- end
-end
-
-function xml:print_xml(t, tab)
- local i, k, e
- local inside = nil
- local bcol, ecol = TERM_L_GREEN, TERM_GREEN
-
- if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
-
- xml.write(bcol, tab.."<"..t.label)
- for k, e in t.args do
- xml.write(TERM_L_BLUE, " "..k)
- xml.write(TERM_WHITE, "=\"")
- xml.write(TERM_YELLOW, e)
- xml.write(TERM_WHITE, "\"")
- end
- xml.write(bcol, ">")
-
- for i = 1, getn(t) do
- if type(t[i]) == "string" then
- xml.write(TERM_WHITE, t[i])
- else
- if not inside then xml.write(TERM_WHITE, "\n") end
- inside = not nil
- xml:print_xml(t[i], tab.." ")
- end
- end
-
- if not inside then
- xml.write(ecol, "</"..t.label..">\n")
- else
- xml.write(ecol, tab.."</"..t.label..">\n")
- end
-end
-
--- t is a table representing xml, outputs the xml code via xml.write()
-function xml:output(t)
- local i
- for i = 1, getn(t) do
- xml:print_xml(t[i], "")
- end
-end
diff --git a/src/dungeon.c b/src/dungeon.c
index af46976d..ce259344 100644
--- a/src/dungeon.c
+++ b/src/dungeon.c
@@ -5420,7 +5420,11 @@ static void load_all_pref_files(void)
process_pref_file(buf);
/* Process player specific automatizer sets */
- tome_dofile_anywhere(ANGBAND_DIR_USER, format("%s.atm", player_name), FALSE);
+ /* TODO: Disabled temporarily because it causes duplicate
+ * rules on save and subsequent game load. */
+ /* sprintf(buf2, "%s.atm", player_name); */
+ /* path_build(buf, sizeof(buf), ANGBAND_DIR_USER, buf2); */
+ /* automatizer_init(buf); */
}
/*
diff --git a/src/externs.h b/src/externs.h
index f2c38f08..71d7b393 100644
--- a/src/externs.h
+++ b/src/externs.h
@@ -994,7 +994,7 @@ extern u32b fake_text_size;
extern bool_ gen_joke_monsters(void *data, void *in, void *out);
/* loadsave.c */
-extern bool_ file_exist(char *buf);
+extern bool_ file_exist(cptr buf);
extern s16b rd_variable(void);
extern void wr_variable(s16b *var);
extern void wr_scripts(void);
@@ -2275,7 +2275,7 @@ extern void squeltch_grid(void);
extern void do_cmd_automatizer(void);
extern void automatizer_add_rule(object_type *o_ptr, bool_ destroy);
extern bool_ automatizer_create;
-
+extern void automatizer_init(cptr file_name);
/*
@@ -2363,6 +2363,7 @@ extern bool_ module_savefile_loadable(cptr savefile_mod);
extern void tome_intro();
extern void theme_intro();
extern void init_hooks_module();
+extern int find_module(cptr name);
/* lua_bind.c */
diff --git a/src/init2.c b/src/init2.c
index 0ce8c1a0..2f897d9b 100644
--- a/src/init2.c
+++ b/src/init2.c
@@ -2779,8 +2779,8 @@ void init_angband(void)
process_pref_file(buf);
/* Initialise the automatizer */
- tome_dofile_anywhere(ANGBAND_DIR_CORE, "auto.lua", TRUE);
- tome_dofile_anywhere(ANGBAND_DIR_USER, "automat.atm", FALSE);
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "automat.atm");
+ automatizer_init(buf);
/* Done */
note("[Initialisation complete]");
diff --git a/src/loadsave.c b/src/loadsave.c
index 45f493a6..08cb53b3 100644
--- a/src/loadsave.c
+++ b/src/loadsave.c
@@ -854,7 +854,7 @@ bool_ save_player(void)
return (result);
}
-bool_ file_exist(char *buf)
+bool_ file_exist(cptr buf)
{
int fd;
bool_ result;
diff --git a/src/modules.c b/src/modules.c
index c4bea6eb..9376cc3d 100644
--- a/src/modules.c
+++ b/src/modules.c
@@ -181,6 +181,22 @@ bool_ module_savefile_loadable(cptr savefile_mod)
/* Did the player force a module on command line */
cptr force_module = NULL;
+/* Find module index by name. Returns -1 if matching module not found */
+int find_module(cptr name)
+{
+ int i = 0;
+
+ for (i=0; i<MAX_MODULES; i++)
+ {
+ if (streq(name, modules[i].meta.name))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
/* Display possible modules and select one */
bool_ select_module()
{
@@ -196,15 +212,7 @@ bool_ select_module()
sel = -1;
if (force_module) {
/* Find module by name */
- int i=0;
- for (i=0; i<MAX_MODULES; i++) {
- if (strcmp(force_module, modules[i].meta.name) == 0) {
- break;
- }
- }
- if (i<MAX_MODULES) {
- sel = i;
- }
+ sel = find_module(force_module);
}
/* Only a single choice */
if (max == 1) {
diff --git a/src/squeltch.c b/src/squeltch.c
index 603eaa0e..d47f8a11 100644
--- a/src/squeltch.c
+++ b/src/squeltch.c
@@ -13,25 +13,1394 @@
#include "angband.h"
#include "lua/lua.h"
#include "tolua.h"
+#include <jansson.h>
-extern lua_State *L;
+#define RULES_MAX 4096
+#define STACK_MAX 1024
+
+typedef enum { BAD, VERY_BAD, AVERAGE,
+ GOOD, VERY_GOOD, SPECIAL,
+ TERRIBLE, NONE, CHEST_EMPTY,
+ CHEST_DISARMED } status_type;
+
+struct status_map_type {
+ status_type status;
+ cptr status_s;
+};
+
+status_type object_status(object_type *o_ptr)
+{
+ if (!object_known_p(o_ptr))
+ {
+ switch (o_ptr->sense)
+ {
+ case SENSE_CURSED: return BAD;
+ case SENSE_WORTHLESS: return VERY_BAD;
+ case SENSE_AVERAGE: return AVERAGE;
+ case SENSE_GOOD_LIGHT: return GOOD;
+ case SENSE_GOOD_HEAVY: return GOOD;
+ case SENSE_EXCELLENT: return VERY_GOOD;
+ case SENSE_SPECIAL: return SPECIAL;
+ case SENSE_TERRIBLE: return TERRIBLE;
+ default: return NONE;
+ }
+ }
+ else
+ {
+ s16b slot = wield_slot_ideal(o_ptr, TRUE);
+
+ if (artifact_p(o_ptr))
+ {
+ if (!(o_ptr->ident & IDENT_CURSED))
+ {
+ return SPECIAL;
+ }
+ else
+ {
+ return TERRIBLE;
+ }
+ }
+ else if ((o_ptr->name2 > 0) ||
+ (o_ptr->name2b > 0))
+ {
+ if (!(o_ptr->ident & IDENT_CURSED))
+ {
+ return VERY_GOOD;
+ }
+ else
+ {
+ return VERY_BAD;
+ }
+ }
+ else if ((slot == INVEN_WIELD) ||
+ (slot == INVEN_BOW) ||
+ (slot == INVEN_AMMO) ||
+ (slot == INVEN_TOOL))
+ {
+ if (o_ptr->to_h + o_ptr->to_d < 0)
+ {
+ return BAD;
+ }
+ else if (o_ptr->to_h + o_ptr->to_d > 0)
+ {
+ return GOOD;
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+ else if ((slot >= INVEN_BODY) &&
+ (slot <= INVEN_FEET))
+ {
+ if (o_ptr->to_a < 0)
+ {
+ return BAD;
+ }
+ else if (o_ptr->to_a > 0)
+ {
+ return GOOD;
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+ else if (slot == INVEN_RING)
+ {
+ if ((o_ptr->to_d + o_ptr->to_h < 0) ||
+ (o_ptr->to_a < 0) ||
+ (o_ptr->pval < 0))
+ {
+ return BAD;
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+ else if (slot == INVEN_NECK)
+ {
+ if (o_ptr->pval < 0)
+ {
+ return BAD;
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+ else if (o_ptr->tval == TV_CHEST)
+ {
+ if (o_ptr->pval == 0)
+ {
+ return CHEST_EMPTY;
+ }
+ else if (o_ptr->pval < 0)
+ {
+ return CHEST_DISARMED;
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+ else
+ {
+ return AVERAGE;
+ }
+ }
+}
+
+#define STATUS_MAP_SIZE 10
+struct status_map_type status_map[STATUS_MAP_SIZE] = {
+ { BAD, "bad" },
+ { VERY_BAD, "very bad" },
+ { AVERAGE, "average" },
+ { GOOD, "good" },
+ { VERY_GOOD, "very good" },
+ { SPECIAL, "special" },
+ { TERRIBLE, "terrible" },
+ { NONE, "none" },
+ { CHEST_EMPTY, "(empty chest)" },
+ { CHEST_DISARMED, "(disarmed chest)" },
+};
+
+static cptr status_to_string(status_type status)
+{
+ int i;
+
+ for (i = 0; i < STATUS_MAP_SIZE; i++)
+ {
+ if (status_map[i].status == status)
+ {
+ return status_map[i].status_s;
+ }
+ }
+
+ assert(FALSE);
+ return NULL;
+}
+
+static bool_ status_from_string(cptr s, status_type *status)
+{
+ int i;
+
+ for (i = 0; i < STATUS_MAP_SIZE; i++)
+ {
+ if (streq(status_map[i].status_s, s))
+ {
+ *status = status_map[i].status;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Type of automatizer actions */
+typedef enum { AUTO_DESTROY,
+ AUTO_PICKUP,
+ AUTO_INSCRIBE } action_type;
+
+/* Convert action to/from string */
+struct action_map_type {
+ action_type action;
+ cptr action_s;
+};
+
+#define ACTION_MAP_SIZE 3
+struct action_map_type action_map[ACTION_MAP_SIZE] = {
+ { AUTO_DESTROY, "destroy" },
+ { AUTO_PICKUP, "pickup" },
+ { AUTO_INSCRIBE, "inscribe" }
+};
+
+static cptr action_to_string(action_type action)
+{
+ int i = 0;
+
+ for (i = 0; i < ACTION_MAP_SIZE; i++)
+ {
+ if (action == action_map[i].action)
+ {
+ return action_map[i].action_s;
+ }
+ }
+
+ assert(FALSE);
+ return NULL;
+}
+
+static bool_ action_from_string(cptr s, action_type *action)
+{
+ int i = 0;
+
+ for (i = 0; i < ACTION_MAP_SIZE; i++)
+ {
+ if (streq(action_map[i].action_s, s))
+ {
+ *action = action_map[i].action;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Identification state */
+typedef enum { IDENTIFIED, NOT_IDENTIFIED } identification_state;
+
+#define S_IDENTIFIED "identified"
+#define S_NOT_IDENTIFIED "not identified"
+
+cptr identification_state_to_string(identification_state i)
+{
+ switch (i)
+ {
+ case IDENTIFIED: return S_IDENTIFIED;
+ case NOT_IDENTIFIED: return S_NOT_IDENTIFIED;
+ }
+
+ assert(FALSE);
+ return NULL;
+}
+
+bool_ identification_state_from_string(cptr s, identification_state *state)
+{
+ if (streq(s, S_IDENTIFIED))
+ {
+ *state = IDENTIFIED;
+ return TRUE;
+ }
+ else if (streq(s, S_NOT_IDENTIFIED))
+ {
+ *state = NOT_IDENTIFIED;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* Match type */
+typedef enum { M_AND , M_OR , M_NOT , M_NAME , M_CONTAIN ,
+ M_INSCRIBED, M_DISCOUNT, M_SYMBOL , M_STATE , M_STATUS ,
+ M_TVAL , M_SVAL , M_RACE , M_SUBRACE , M_CLASS ,
+ M_LEVEL , M_SKILL , M_ABILITY, M_INVENTORY, M_EQUIPMENT }
+ match_type;
+
+struct match_type_map {
+ match_type i;
+ cptr s;
+};
+
+#define MATCH_TYPE_MAP_SIZE 20
+struct match_type_map match_type_map[MATCH_TYPE_MAP_SIZE] = {
+ { M_AND, "and" },
+ { M_OR, "or" },
+ { M_NOT, "not" },
+ { M_NAME, "name" },
+ { M_CONTAIN, "contain" },
+ { M_INSCRIBED, "inscribed" },
+ { M_DISCOUNT, "discount" },
+ { M_SYMBOL, "symbol" },
+ { M_STATE, "state" },
+ { M_STATUS, "status" },
+ { M_TVAL, "tval" },
+ { M_SVAL, "sval" },
+ { M_RACE, "race" },
+ { M_SUBRACE, "subrace" },
+ { M_CLASS, "class" },
+ { M_LEVEL, "level" },
+ { M_SKILL, "skill" },
+ { M_ABILITY, "ability" },
+ { M_INVENTORY, "inventory" },
+ { M_EQUIPMENT, "equipment" },
+};
+
+cptr match_type_to_string(match_type m)
+{
+ int i;
+
+ for (i = 0; i < MATCH_TYPE_MAP_SIZE; i++)
+ {
+ if (match_type_map[i].i == m)
+ {
+ return match_type_map[i].s;
+ }
+ }
+
+ assert(FALSE);
+ return NULL;
+}
+
+bool_ match_type_from_string(cptr s, match_type *match)
+{
+ int i;
+
+ for (i = 0; i < MATCH_TYPE_MAP_SIZE; i++)
+ {
+ if (streq(match_type_map[i].s, s))
+ {
+ *match = match_type_map[i].i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Forward declarations */
+typedef struct condition_type condition_type;
+struct condition_type;
+
+/* List of conditions */
+typedef struct condition_list condition_list;
+struct condition_list {
+ condition_type *condition;
+ condition_list *next;
+};
+
+int compare_condition_list(condition_list *a, condition_list *b)
+{
+ assert(FALSE);
+}
+
+SGLIB_DEFINE_LIST_PROTOTYPES(condition_list, compare_condition_list, next);
+SGLIB_DEFINE_LIST_FUNCTIONS(condition_list, compare_condition_list, next);
+
+/* Condition instance */
+struct condition_type
+{
+ /* What do we want to match? */
+ match_type match;
+ /* Sub-conditions for logical connectives; if applicable */
+ struct {
+ condition_list *c;
+ } conditions;
+ /* Sub-condition for cases where there is only a single subcondition */
+ condition_type *subcondition;
+ /* Tval to match if applicable. */
+ byte tval;
+ /* Sval range if applicable. */
+ struct {
+ byte min;
+ byte max;
+ } sval_range;
+ /* Discount range. */
+ struct {
+ int min;
+ int max;
+ } discount;
+ /* Level range */
+ struct {
+ int min;
+ int max;
+ } level_range;
+ /* Skill range */
+ struct {
+ s16b min;
+ s16b max;
+ s16b skill_idx;
+ } skill_range;
+ /* Identification state to match if applicable */
+ identification_state identification_state;
+ /* Status to match if applicable */
+ status_type status;
+ /* Name to match */
+ char *name;
+ /* Symbol to match if applicable */
+ char symbol;
+ /* Inscription to find */
+ char *inscription;
+ /* Subrace to match if applicable */
+ char *subrace;
+ /* Race to match if applicable */
+ char *race;
+ /* Class to match if applicable */
+ char *klass;
+ /* Ability to match if applicable */
+ s16b ability;
+ /* Comment */
+ char *comment;
+};
+
+static condition_type *condition_new(match_type match)
+{
+ condition_type *cp = malloc(sizeof(condition_type));
+ memset(cp, 0, sizeof(condition_type));
+ cp->match = match;
+ return cp;
+}
+
+static condition_type *condition_new_tval(byte tval)
+{
+ condition_type *cp = condition_new(M_TVAL);
+ cp->tval = tval;
+ return cp;
+}
+
+static condition_type *condition_new_sval(byte min, byte max)
+{
+ condition_type *cp = condition_new(M_SVAL);
+ cp->sval_range.min = min;
+ cp->sval_range.max = max;
+ return cp;
+}
+
+static condition_type *condition_new_and()
+{
+ condition_type *cp = condition_new(M_AND);
+ return cp;
+}
+
+static condition_type *condition_new_or()
+{
+ condition_type *cp = condition_new(M_OR);
+ return cp;
+}
+
+static condition_type *condition_new_not()
+{
+ condition_type *cp = condition_new(M_NOT);
+ return cp;
+}
+
+static condition_type *condition_new_name(cptr name)
+{
+ condition_type *cp = condition_new(M_NAME);
+ cp->name = strdup(name);
+ return cp;
+}
+
+static condition_type *condition_new_contain(cptr name)
+{
+ condition_type *cp = condition_new(M_CONTAIN);
+ cp->name = strdup(name);
+ return cp;
+}
+
+static condition_type *condition_new_inscribed(cptr name)
+{
+ condition_type *cp = condition_new(M_INSCRIBED);
+ cp->inscription = strdup(name);
+ return cp;
+}
+
+static condition_type *condition_new_status(status_type status)
+{
+ condition_type *cp = condition_new(M_STATUS);
+ cp->status = status;
+ return cp;
+}
+
+static condition_type *condition_new_state(identification_state state)
+{
+ condition_type *cp = condition_new(M_STATE);
+ cp->identification_state = state;
+ return cp;
+}
+
+static condition_type *condition_new_discount(int min, int max)
+{
+ condition_type *cp = condition_new(M_DISCOUNT);
+ cp->discount.min = min;
+ cp->discount.max = max;
+ return cp;
+}
+
+static condition_type *condition_new_symbol(char c)
+{
+ condition_type *cp = condition_new(M_SYMBOL);
+ cp->symbol = c;
+ return cp;
+}
+
+static condition_type *condition_new_race(cptr race)
+{
+ condition_type *cp = condition_new(M_RACE);
+ cp->race = strdup(race);
+ return cp;
+}
+
+static condition_type *condition_new_subrace(cptr subrace)
+{
+ condition_type *cp = condition_new(M_SUBRACE);
+ cp->subrace = strdup(subrace);
+ return cp;
+}
+
+static condition_type *condition_new_class(cptr klass)
+{
+ condition_type *cp = condition_new(M_CLASS);
+ cp->klass = strdup(klass);
+ return cp;
+}
+
+static condition_type *condition_new_level(int min, int max)
+{
+ condition_type *cp = condition_new(M_LEVEL);
+ cp->level_range.min = min;
+ cp->level_range.max = max;
+ return cp;
+}
+
+static condition_type *condition_new_skill(s16b min, s16b max, s16b skill_idx)
+{
+ condition_type *cp = condition_new(M_SKILL);
+ cp->skill_range.min = min;
+ cp->skill_range.max = max;
+ cp->skill_range.skill_idx = skill_idx;
+ return cp;
+}
+
+static condition_type *condition_new_ability(s16b ability_idx)
+{
+ condition_type *cp = condition_new(M_ABILITY);
+ cp->ability = ability_idx;
+ return cp;
+}
+
+static condition_type *condition_new_inventory()
+{
+ condition_type *cp = condition_new(M_INVENTORY);
+ return cp;
+}
+
+static condition_type *condition_new_equipment()
+{
+ condition_type *cp = condition_new(M_EQUIPMENT);
+ return cp;
+}
+
+static void condition_and_add(condition_type *and_c, condition_type *c)
+{
+ assert(and_c != NULL);
+ assert(c != NULL);
+ assert((and_c->match == M_AND) || (and_c->match == M_OR));
+
+ condition_list *cl = malloc(sizeof(condition_list));
+ cl->condition = c;
+ cl->next = NULL;
+
+ sglib_condition_list_add(&and_c->conditions.c, cl);
+}
+
+static void condition_or_add(condition_type *or_c, condition_type *c)
+{
+ condition_and_add(or_c, c);
+}
+
+static void condition_destroy(condition_type **cp)
+{
+ condition_type *c = NULL;
+ assert(cp != NULL);
+ assert(*cp != NULL);
+
+ c = *cp;
+
+ /* Free sub-conditions if any */
+ {
+ condition_list *current = NULL;
+ condition_list *next = NULL;
+
+ for (current = c->conditions.c;
+ current != NULL;
+ current = next)
+ {
+ condition_destroy(&current->condition);
+ next = current->next;
+ free(current);
+ }
+ }
+
+ /* Free sub-condition if any */
+ if (c->subcondition)
+ {
+ condition_destroy(&c->subcondition);
+ }
+
+ /* Free name if any */
+ if (c->name)
+ {
+ free(c->name);
+ c->name = NULL;
+ }
+
+ /* Free inscription if any */
+ if (c->inscription)
+ {
+ free(c->inscription);
+ c->inscription = NULL;
+ }
+
+ /* Free subrace if any */
+ if (c->subrace)
+ {
+ free(c->subrace);
+ c->subrace = NULL;
+ }
+
+ /* Free race if any */
+ if (c->race)
+ {
+ free(c->race);
+ c->race = NULL;
+ }
+
+ /* Free class if any */
+ if (c->klass)
+ {
+ free(c->klass);
+ c->klass = NULL;
+ }
+
+ /* Free comment if any */
+ if (c->comment)
+ {
+ free(c->comment);
+ c->comment = NULL;
+ }
+
+ /* Free the condition itself */
+ free(*cp);
+ *cp = NULL;
+}
+
+static bool_ condition_eval(condition_type *c, object_type *o_ptr)
+{
+ bool_ is_and = (c->match == M_AND);
+ bool_ is_or = (c->match == M_OR);
+
+ switch (c->match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ struct condition_list *child = NULL;
+
+ for (child = sglib_condition_list_it_init(&it, c->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ if (is_and && (!condition_eval(child->condition, o_ptr)))
+ {
+ return FALSE;
+ }
+
+ if (is_or && condition_eval(child->condition, o_ptr))
+ {
+ return TRUE;
+ }
+ }
+
+ if (is_and)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ case M_NOT:
+ {
+ if (c->subcondition == NULL)
+ {
+ return TRUE;
+ }
+
+ return !condition_eval(c->subcondition, o_ptr);
+ }
+
+ case M_INVENTORY:
+ {
+ int i;
+
+ if (c->subcondition == NULL)
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ if (condition_eval(c->subcondition, &p_ptr->inventory[i]))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+ case M_EQUIPMENT:
+ {
+ int i;
+
+ if (c->subcondition == NULL)
+ {
+ return FALSE;
+ }
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (condition_eval(c->subcondition, &p_ptr->inventory[i]))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+ case M_NAME:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ object_desc(buf1, o_ptr, -1, 0);
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->name);
+ strlower(buf2);
+
+ return streq(buf1, buf2);
+ }
+
+ case M_CONTAIN:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ object_desc(buf1, o_ptr, -1, 0);
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->name);
+ strlower(buf2);
+
+ return (strstr(buf1, buf2) != NULL);
+ }
+
+ case M_SYMBOL:
+ {
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ return k_ptr->d_char == c->symbol;
+ }
+
+ case M_INSCRIBED:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ if (o_ptr->note == 0)
+ {
+ return FALSE;
+ }
+
+ sprintf(buf1, "%s", quark_str(o_ptr->note));
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->inscription);
+ strlower(buf2);
+
+ return (strstr(buf1, buf2) != NULL);
+ }
+
+ case M_DISCOUNT:
+ {
+ return (object_aware_p(o_ptr) &&
+ (o_ptr->discount >= c->discount.min) &&
+ (o_ptr->discount <= c->discount.max));
+ }
+
+ case M_TVAL:
+ {
+ return (o_ptr->tval == c->tval);
+ }
+
+ case M_SVAL:
+ {
+ return (object_aware_p(o_ptr) &&
+ (o_ptr->sval >= c->sval_range.min) &&
+ (o_ptr->sval <= c->sval_range.max));
+ }
+
+ case M_STATUS:
+ {
+ return c->status == object_status(o_ptr);
+ }
+
+ case M_STATE:
+ {
+ switch (c->identification_state)
+ {
+ case IDENTIFIED:
+ return object_known_p(o_ptr);
+ case NOT_IDENTIFIED:
+ return !object_known_p(o_ptr);
+ default:
+ assert(FALSE);
+ }
+ }
+
+ case M_RACE:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ sprintf(buf1, "%s", rp_ptr->title + rp_name);
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->race);
+ strlower(buf2);
+
+ return streq(buf1, buf2);
+ }
+
+ case M_SUBRACE:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ sprintf(buf1, "%s", rmp_ptr->title + rmp_name);
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->subrace);
+ strlower(buf2);
+
+ return streq(buf1, buf2);
+ }
+
+ case M_CLASS:
+ {
+ char buf1[128];
+ char buf2[128];
+
+ sprintf(buf1, "%s", spp_ptr->title + c_name);
+ strlower(buf1);
+
+ sprintf(buf2, "%s", c->race);
+ strlower(buf2);
+
+ return streq(buf1, buf2);
+ }
+
+ case M_LEVEL:
+ {
+ return ((p_ptr->lev >= c->level_range.min) &&
+ (p_ptr->lev <= c->level_range.max));
+ }
+
+ case M_SKILL:
+ {
+ s16b sk = get_skill(c->skill_range.skill_idx);
+ return ((sk >= c->skill_range.min) &&
+ (sk <= c->skill_range.max));
+ }
+
+ case M_ABILITY:
+ {
+ return has_ability(c->ability);
+ }
+
+ }
+
+ /* Don't match by default */
+ return FALSE;
+}
+
+static json_t *condition_to_json(condition_type *c)
+{
+ json_t *json = NULL;
+
+ if (c == NULL)
+ {
+ return json_null();
+ }
+
+ json = json_object();
+ json_object_set_new(json, "type",
+ json_string(match_type_to_string(c->match)));
+
+ switch (c->match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ struct condition_list *child = NULL;
+
+ json_t *conditions_json = json_array();
+
+ for (child = sglib_condition_list_it_init(&it, c->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ json_array_append_new(conditions_json,
+ condition_to_json(child->condition));
+ }
+
+ json_object_set_new(json, "conditions", conditions_json);
+ break;
+ }
+
+ case M_NOT:
+ case M_INVENTORY:
+ case M_EQUIPMENT:
+ {
+ json_object_set_new(json, "condition",
+ condition_to_json(c->subcondition));
+ break;
+ }
+
+ case M_NAME:
+ {
+ json_object_set_new(json, "name",
+ json_string(c->name));
+ break;
+ }
+
+ case M_CONTAIN:
+ {
+ json_object_set_new(json, "contain",
+ json_string(c->name));
+ break;
+ }
+
+ case M_SYMBOL:
+ {
+ json_object_set_new(json, "symbol",
+ json_string(format("%c", c->symbol)));
+ break;
+ }
+
+ case M_INSCRIBED:
+ {
+ json_object_set_new(json, "inscription",
+ json_string(c->inscription));
+ break;
+ }
+
+ case M_DISCOUNT:
+ {
+ json_object_set_new(json, "min",
+ json_integer(c->discount.min));
+ json_object_set_new(json, "max",
+ json_integer(c->discount.max));
+ break;
+ }
+
+ case M_TVAL:
+ {
+ json_object_set_new(json, "tval",
+ json_integer(c->tval));
+ break;
+ }
+
+ case M_SVAL:
+ {
+ json_object_set_new(json, "min",
+ json_integer(c->sval_range.min));
+ json_object_set_new(json, "max",
+ json_integer(c->sval_range.max));
+ break;
+ }
+
+ case M_STATUS:
+ {
+ json_object_set_new(json, "status",
+ json_string(status_to_string(c->status)));
+ break;
+ }
+
+ case M_STATE:
+ {
+ json_object_set_new(json, "state",
+ json_string(identification_state_to_string(c->identification_state)));
+ break;
+ }
+
+ case M_RACE:
+ {
+ json_object_set_new(json, "race",
+ json_string(c->race));
+ break;
+ }
+
+ case M_SUBRACE:
+ {
+ json_object_set_new(json, "subrace",
+ json_string(c->subrace));
+ break;
+ }
+
+ case M_CLASS:
+ {
+ json_object_set_new(json, "class",
+ json_string(c->klass));
+ break;
+ }
+
+ case M_LEVEL:
+ {
+ json_object_set_new(json, "min",
+ json_integer(c->level_range.min));
+ json_object_set_new(json, "max",
+ json_integer(c->level_range.max));
+ break;
+ }
+
+ case M_SKILL:
+ {
+ json_object_set_new(json, "name",
+ json_string(s_info[c->skill_range.skill_idx].name + s_name));
+ json_object_set_new(json, "min",
+ json_integer(c->skill_range.min));
+ json_object_set_new(json, "max",
+ json_integer(c->skill_range.max));
+ break;
+ }
+
+ case M_ABILITY:
+ {
+ json_object_set_new(json, "ability",
+ json_string(ab_info[c->ability].name + ab_name));
+ break;
+ }
+
+ }
+
+ return json;
+}
/*
- * The functions here use direct lua stack manipulation for calls instead of
- * exec_lua(format()) because string manipulations are too slow for such
- * functions
+ * Cursor to maintain position in condition tree
*/
+static condition_type *cursor_stack[STACK_MAX];
+static int cursor_count = 0;
+
+static void cursor_push(condition_type *condition)
+{
+ assert(cursor_count < STACK_MAX);
+
+ cursor_stack[cursor_count] = condition;
+ cursor_count++;
+}
+
+static condition_type *cursor_pop()
+{
+ condition_type *c = NULL;
+
+ assert(cursor_count > 0);
+
+ c = cursor_stack[cursor_count-1];
+ cursor_stack[cursor_count] = NULL;
+ cursor_count--;
+ return c;
+}
+
+static condition_type *cursor_top()
+{
+ assert(cursor_count > 0);
+
+ return cursor_stack[cursor_count - 1];
+}
+
+static void cursor_clear()
+{
+ while (cursor_count > 0)
+ {
+ cursor_pop();
+ }
+}
+
+/* Rule */
+typedef struct arule_type arule_type;
+struct arule_type {
+ /* Rule name */
+ char *name;
+ /* Which action do we take? */
+ action_type action; /* Which action to take */
+ /* Which module does this rule apply to? */
+ int module_idx;
+ /* Inscription to use for inscription rules. */
+ char *inscription;
+ /* Condition. */
+ condition_type *condition; /* Condition for rule match */
+};
+
+/* Initialize a rule */
+static arule_type *rule_new(cptr name, action_type action, int module_idx, condition_type *condition, cptr inscription)
+{
+ arule_type *rp = malloc(sizeof(arule_type));
+ rp->name = strdup(name);
+ rp->action = action;
+ rp->module_idx = module_idx;
+ rp->condition = condition;
+ rp->inscription = (inscription == NULL) ? NULL : strdup(inscription);
+ return rp;
+}
+
+static void rule_set_name(arule_type *rule, cptr new_name)
+{
+ if (rule->name)
+ {
+ free(rule->name);
+ }
+
+ rule->name = strdup(new_name);
+}
+
+static void rule_destroy(arule_type **rp)
+{
+ if ((*rp)->name)
+ {
+ free((*rp)->name);
+ }
+
+ if ((*rp)->inscription)
+ {
+ free((*rp)->inscription);
+ }
+
+ if ((*rp)->condition)
+ {
+ condition_destroy(&(*rp)->condition);
+ }
+
+ free(*rp);
+ *rp = NULL;
+}
+
+/* Current list of rules. */
+static arule_type *rules[RULES_MAX];
+static int rules_count = 0; /* Number of rules currently in list */
+
+static int rules_append(arule_type *rule)
+{
+ assert(rules_count < RULES_MAX);
+
+ rules[rules_count] = rule;
+ rules_count++;
+ return rules_count-1;
+}
+
+static void rules_remove(arule_type *rule)
+{
+ int i, j;
+
+ for (i = 0; i < rules_count; i++)
+ {
+ if (rules[i] == rule)
+ {
+ /* Free the rule */
+ rule_destroy(&rule);
+ /* Move rest of rest "up" */
+ for (j = i+1; j < rules_count; j++)
+ {
+ rules[j-1] = rules[j];
+ }
+ /* We're done */
+ rules_count--;
+ return;
+ }
+ }
+}
+
+static void rules_swap(int i, int j)
+{
+ arule_type *tmp_rptr = NULL;
+
+ assert(i >= 0);
+ assert(i < rules_count);
+
+ assert(j >= 0);
+ assert(j < rules_count);
+
+ tmp_rptr = rules[i];
+ rules[i] = rules[j];
+ rules[j] = tmp_rptr;
+}
+
+static bool_* automatizer_auto_destroy(object_type *o_ptr, int item_idx)
+{
+ static bool_ TRUE_VAL = TRUE;
+
+ /* Must be identified */
+ if (object_aware_p(o_ptr) == FALSE)
+ {
+ return NULL;
+ }
+
+ /* Inscribed things won't be destroyed! */
+ if (o_ptr->note)
+ {
+ return NULL;
+ }
+
+ /* Ignore artifacts; cannot be destroyed anyway. */
+ if (artifact_p(o_ptr))
+ {
+ return NULL;
+ }
+
+ /* Cannot destroy CURSE_NO_DROP objects. */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f4 & TR4_CURSE_NO_DROP) != 0)
+ {
+ return NULL;
+ }
+ }
+
+ /* Destroy! */
+ msg_print("<Auto-destroy>");
+
+ inc_stack_size(item_idx, -o_ptr->number);
+ return &TRUE_VAL;
+}
+
+static bool_* automatizer_auto_pickup(object_type *o_ptr, int item_idx)
+{
+ static bool_ TRUE_VAL = TRUE;
+
+ if (item_idx >= 0)
+ {
+ return NULL;
+ }
+
+ if (!inven_carry_okay(o_ptr))
+ {
+ return NULL;
+ }
+
+ msg_print("<Auto-pickup>");
+
+ object_pickup(-item_idx);
+ return &TRUE_VAL;
+}
+
+/* Apply rules */
+static bool_ apply_rule(arule_type *rule, object_type *o_ptr, int item_idx)
+{
+ /* Check module */
+ if (rule->module_idx == game_module_idx)
+ {
+ return FALSE;
+ }
+
+ /* Check condition */
+ assert (rule->condition != NULL);
+ if (condition_eval(rule->condition, o_ptr))
+ {
+ switch (rule->action)
+ {
+
+ case AUTO_DESTROY:
+ {
+ automatizer_auto_destroy(o_ptr, item_idx);
+ break;
+ }
+
+ case AUTO_PICKUP:
+ {
+ automatizer_auto_pickup(o_ptr, item_idx);
+ break;
+ }
+
+ case AUTO_INSCRIBE:
+ {
+ /* Already inscribed? */
+ if (o_ptr->note != 0)
+ {
+ return FALSE;
+ }
+
+ /* Inscribe */
+ msg_format("<Auto-Inscribe {%s}>", rule->inscription);
+ o_ptr->note = quark_add(rule->inscription);
+ break;
+ }
+
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool_ apply_rules(object_type *o_ptr, int item_idx)
+{
+ int i;
+
+ for (i = 0; i < rules_count; i++)
+ {
+ if (apply_rule(rules[i], o_ptr, item_idx))
+ {
+ return TRUE;
+ }
+ }
+
+ /* Don't keep trying */
+ return FALSE;
+}
+
+static json_t *rule_to_json(arule_type *rule)
+{
+ json_t *rule_json = json_object();
+
+ json_object_set_new(rule_json,
+ "name",
+ json_string(rule->name));
+ json_object_set_new(rule_json,
+ "action",
+ json_string(action_to_string(rule->action)));
+ json_object_set_new(rule_json,
+ "module",
+ json_string(modules[rule->module_idx].meta.name));
+
+ if (rule->inscription)
+ {
+ json_object_set_new(rule_json,
+ "inscription",
+ json_string(rule->inscription));
+ }
+
+ json_object_set_new(rule_json,
+ "condition",
+ condition_to_json(rule->condition));
+
+ return rule_json;
+}
+
+static json_t *rules_to_json()
+{
+ int i;
+ json_t *rules_json = json_array();
+
+ for (i = 0; i < rules_count; i++)
+ {
+ json_array_append_new(rules_json, rule_to_json(rules[i]));
+ }
+
+ return rules_json;
+}
/* Check the floor for "crap" */
void squeltch_grid(void)
{
- int oldtop;
s16b this_o_idx, next_o_idx = 0;
if (!automatizer_enabled) return;
- oldtop = lua_gettop(L);
-
/* Scan the pile of objects */
for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
{
@@ -47,37 +1416,21 @@ void squeltch_grid(void)
/* Acquire next object */
next_o_idx = o_ptr->next_o_idx;
- /* Push the function */
- lua_settop(L, oldtop);
- lua_getglobal(L, "apply_rules");
-
- /* Push the args */
- tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
- tolua_pushnumber(L, -this_o_idx);
-
- /* Call the function */
- if (lua_call(L, 2, 0))
- {
- cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
- lua_settop(L, oldtop);
- return;
- }
+ /* Apply rules */
+ apply_rules(o_ptr, -this_o_idx);
}
- lua_settop(L, oldtop);
}
/* Check the inventory for "crap" */
void squeltch_inventory(void)
{
- int oldtop;
int i;
int num_iter = 0;
bool_ found = TRUE;
if (!automatizer_enabled) return;
- oldtop = lua_gettop(L);
while (found && num_iter ++ < 100)
{
/* Sometimes an index in the inventory is skipped */
@@ -87,24 +1440,7 @@ void squeltch_inventory(void)
{
object_type *o_ptr = &p_ptr->inventory[i];
- /* Push the function */
- lua_settop(L, oldtop);
- lua_getglobal(L, "apply_rules");
-
- /* Push the args */
- tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
- tolua_pushnumber(L, i);
-
- /* Call the function */
- if (lua_call(L, 2, 1))
- {
- cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
- lua_settop(L, oldtop);
- return;
- }
-
- /* Did it return TRUE */
- if (tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0))
+ if (apply_rules(o_ptr, i))
{
found = TRUE;
break;
@@ -115,73 +1451,423 @@ void squeltch_inventory(void)
{
cmsg_format(TERM_VIOLET, "'apply_rules' ran too often.");
}
- lua_settop(L, oldtop);
}
/********************** The interface **********************/
-static cptr *get_rule_list(int *max)
+static void get_rule_names(cptr *list)
{
- cptr *list;
int i;
- *max = exec_lua("return __rules_max");
- C_MAKE(list, *max, cptr);
-
- for (i = 0; i < *max; i++)
- {
- list[i] = string_exec_lua(format("return __rules[%d].table.args.name", i));
- }
-
- return list;
-}
-
-static cptr types_list[] =
-{
- "and",
- "or",
- "not",
- "name",
- "contain",
- "inscribed",
- "discount",
- "symbol",
- "state",
- "status",
- "tval",
- "sval",
- "race",
- "subrace",
- "class",
- "level",
- "skill",
- "ability",
- "inventory",
- "equipment",
- "comment",
- NULL,
+ for (i = 0; i < rules_count; i++)
+ {
+ list[i] = rules[i]->name;
+ }
+}
+
+typedef struct condition_metadata condition_metadata;
+struct condition_metadata {
+ match_type match;
+ cptr description[3];
+ condition_type *(*create_condition)();
+};
+
+#define TYPES_LIST_SIZE 21
+
+static condition_type *create_condition_name()
+{
+ cptr s = lua_input_box("Object name to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_name(s);
+}
+
+static condition_type *create_condition_contain()
+{
+ cptr s = lua_input_box("Word to find in object name?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_contain(s);
+}
+
+static condition_type *create_condition_inscribed()
+{
+ cptr s = lua_input_box("Word to find in object inscription?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_inscribed(s);
+}
+
+static condition_type *create_condition_discount()
+{
+ int min, max;
+
+ {
+ cptr s = lua_input_box("Min discount?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max discount?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ return condition_new_discount(min, max);
+}
+
+static condition_type *create_condition_symbol()
+{
+ char c;
+ cptr s = lua_input_box("Symbol to match?", 1);
+ if (sscanf(s, "%c", &c) < 1)
+ {
+ return NULL;
+ }
+
+ return condition_new_symbol(c);
+}
+
+static condition_type *create_condition_status()
+{
+ status_type status;
+ char c;
+
+ c = lua_msg_box("[t]errible, [v]ery bad, [b]ad, "
+ "[a]verage, [G]ood, [V]ery good, [S]pecial?");
+
+ switch (c)
+ {
+ case 't': status = TERRIBLE; break;
+ case 'v': status = VERY_BAD; break;
+ case 'b': status = BAD; break;
+ case 'a': status = AVERAGE; break;
+ case 'G': status = GOOD; break;
+ case 'V': status = VERY_GOOD; break;
+ case 'S': status = SPECIAL; break;
+ default: return NULL;
+ }
+
+ return condition_new_status(status);
+}
+
+static condition_type *create_condition_state()
+{
+ identification_state s;
+ char c;
+
+ c = lua_msg_box("[i]dentified, [n]on identified?");
+
+ switch (c)
+ {
+ case 'i': s = IDENTIFIED; break;
+ case 'n': s = NOT_IDENTIFIED; break;
+ default: return NULL;
+ }
+
+ return condition_new_state(s);
+}
+
+static condition_type *create_condition_tval()
+{
+ int tval;
+ cptr s = lua_input_box("Tval to match?", 79);
+ if (sscanf(s, "%d", &tval) < 1)
+ {
+ return NULL;
+ }
+
+ return condition_new_tval(tval);
+}
+
+static condition_type *create_condition_sval()
+{
+ int sval_min, sval_max;
+
+ {
+ cptr s = lua_input_box("Min sval?", 79);
+ if (sscanf(s, "%d", &sval_min) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max sval?", 79);
+ if (sscanf(s, "%d", &sval_max) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ return condition_new_sval(sval_min, sval_max);
+}
+
+static condition_type *create_condition_race()
+{
+ cptr s = lua_input_box("Player race to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_race(s);
+}
+
+static condition_type *create_condition_subrace()
+{
+ cptr s = lua_input_box("Player subrace to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_subrace(s);
+}
+
+static condition_type *create_condition_class()
+{
+ cptr s = lua_input_box("Player class to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_class(s);
+}
+
+static condition_type *create_condition_level()
+{
+ int min, max;
+
+ {
+ cptr s = lua_input_box("Min player level?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max player level?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ return condition_new_level(min, max);
+}
+
+static condition_type *create_condition_skill()
+{
+ int min, max;
+ s16b skill_idx;
+
+ {
+ cptr s = lua_input_box("Min skill level?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max skill level?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return NULL;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Skill name?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ skill_idx = find_skill_i(s);
+ if (skill_idx < 0)
+ {
+ return NULL;
+ }
+ }
+
+ return condition_new_skill(min, max, skill_idx);
+}
+
+static condition_type *create_condition_ability()
+{
+ s16b ai;
+ cptr s = lua_input_box("Ability name?", 79);
+ if (strlen(s) == 0)
+ {
+ return NULL;
+ }
+
+ ai = find_ability(s);
+ if (ai < 0)
+ {
+ return NULL;
+ }
+
+ return condition_new_ability(ai);
+}
+
+static condition_metadata types_list[TYPES_LIST_SIZE] =
+{
+ { M_AND,
+ { "Check is true if all rules within it are true",
+ NULL },
+ condition_new_and,
+ },
+ { M_OR,
+ { "Check is true if at least one rule within it is true",
+ NULL },
+ condition_new_or,
+ },
+ { M_NOT,
+ { "Invert the result of its child rule",
+ NULL },
+ condition_new_not,
+ },
+ { M_NAME,
+ { "Check is true if object name matches name",
+ NULL },
+ create_condition_name,
+ },
+ { M_CONTAIN,
+ { "Check is true if object name contains word",
+ NULL },
+ create_condition_contain,
+ },
+ { M_INSCRIBED,
+ { "Check is true if object inscription contains word",
+ NULL },
+ create_condition_inscribed,
+ },
+ { M_DISCOUNT,
+ { "Check is true if object discount is between two values",
+ NULL },
+ create_condition_discount,
+ },
+ { M_SYMBOL,
+ { "Check is true if object symbol is ok",
+ NULL },
+ create_condition_symbol,
+ },
+ { M_STATE,
+ { "Check is true if object is identified/unidentified",
+ NULL },
+ create_condition_state,
+ },
+ { M_STATUS,
+ { "Check is true if object status is ok",
+ NULL },
+ create_condition_status,
+ },
+ { M_TVAL,
+ { "Check is true if object tval(from k_info.txt) is ok",
+ NULL },
+ create_condition_tval,
+ },
+ { M_SVAL,
+ { "Check is true if object sval(from k_info.txt) is between",
+ "two values",
+ NULL },
+ create_condition_sval,
+ },
+ { M_RACE,
+ { "Check is true if player race is ok",
+ NULL },
+ create_condition_race,
+ },
+ { M_SUBRACE,
+ { "Check is true if player subrace is ok",
+ NULL },
+ create_condition_subrace,
+ },
+ { M_CLASS,
+ { "Check is true if player class is ok",
+ NULL },
+ create_condition_class,
+ },
+ { M_LEVEL,
+ { "Check is true if player level is between 2 values",
+ NULL },
+ create_condition_level,
+ },
+ { M_SKILL,
+ { "Check is true if player skill level is between 2 values",
+ NULL },
+ create_condition_skill,
+ },
+ { M_ABILITY,
+ { "Check is true if player has the ability",
+ NULL },
+ create_condition_ability,
+ },
+ { M_INVENTORY,
+ { "Check is true if something in player's inventory matches",
+ "the contained rule",
+ NULL },
+ condition_new_inventory,
+ },
+ { M_EQUIPMENT,
+ { "Check is true if something in player's equipment matches",
+ "the contained rule",
+ NULL },
+ condition_new_equipment,
+ },
};
+static void display_desc(condition_metadata *condition_metadata)
+{
+ int i;
+
+ assert(condition_metadata != NULL);
+
+ for (i = 0; condition_metadata->description[i] != NULL; i++)
+ {
+ c_prt(TERM_WHITE, condition_metadata->description[i], i + 1, 17);
+ }
+}
+
/* Create a new rule */
-static int automatizer_new_rule()
+static condition_metadata *automatizer_select_condition_type()
{
- int wid, hgt, max, begin = 0, sel = 0;
+ int wid, hgt, max = TYPES_LIST_SIZE, begin = 0, sel = 0, i;
char c;
+ cptr types_names[TYPES_LIST_SIZE];
- /* Get the number of types */
- max = 0;
- while (types_list[max] != NULL)
- max++;
+ /* Create list of names for display */
+ for (i = 0; i < TYPES_LIST_SIZE; i++)
+ {
+ types_names[i] = match_type_to_string(types_list[i].match);
+ }
while (1)
{
Term_clear();
Term_get_size(&wid, &hgt);
- display_list(0, 0, hgt - 1, 15, "Rule types", types_list, max, begin, sel, TERM_L_GREEN);
+ display_list(0, 0, hgt - 1, 15, "Rule types", types_names, max, begin, sel, TERM_L_GREEN);
- exec_lua(format("auto_aux:display_desc('%s')", types_list[sel]));
- c_prt(TERM_YELLOW, "Example:", 5, 17);
- exec_lua(format("xml.write_out_y = 6; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(auto_aux.types_desc['%s'][2][1], '')", hgt - 3 - 5, wid - 1 - 15 - 1, types_list[sel]));
+ display_desc(&types_list[sel]);
c = inkey();
@@ -209,10 +1895,10 @@ static int automatizer_new_rule()
}
else if (c == '\r')
{
- return sel;
+ return &types_list[sel];
}
}
- return -1;
+ return NULL;
}
static void adjust_begin(int *begin, int *sel, int max, int hgt)
@@ -233,18 +1919,872 @@ static void adjust_begin(int *begin, int *sel, int max, int hgt)
if (*sel >= *begin + hgt - 2) (*begin)++;
}
+static int create_new_rule()
+{
+ action_type action;
+ char name[20] = { '\0' };
+ char *inscription = NULL;
+ int wid = 0, hgt = 0;
+ char typ;
+ arule_type *rule = NULL;
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "%s", "No name");
+ if (!input_box("Name?", hgt / 2, wid / 2, name, sizeof(name)+1))
+ {
+ return -1;
+ }
+
+ typ = lua_msg_box("[D]estroy, [P]ickup, [I]nscribe?");
+
+ switch (typ)
+ {
+ case 'd':
+ case 'D':
+ action = AUTO_DESTROY;
+ break;
+
+ case 'p':
+ case 'P':
+ action = AUTO_PICKUP;
+ break;
+
+ case 'i':
+ case 'I':
+ {
+ char *i = NULL;
+
+ action = AUTO_INSCRIBE;
+
+ i = lua_input_box("Inscription?", 79);
+ if ((i == NULL) || (strlen(i) == 0))
+ {
+ return -1;
+ }
+
+ inscription = i;
+
+ break;
+ }
+
+ default:
+ return -1;
+ }
+
+ /* Make rule */
+ rule = rule_new(name, action, game_module_idx, NULL, inscription);
+
+ /* Append to list of rules */
+ return rules_append(rule);
+}
+
+static void add_child(condition_type *current)
+{
+ condition_metadata *metadata = NULL;
+
+ switch (current->match)
+ {
+ case M_NOT:
+ case M_EQUIPMENT:
+ case M_INVENTORY:
+ {
+ if (current->subcondition != NULL)
+ {
+ return;
+ }
+
+ metadata = automatizer_select_condition_type();
+ if (metadata == NULL)
+ {
+ return;
+ }
+
+ current->subcondition = metadata->create_condition();
+ break;
+ }
+
+ case M_AND:
+ metadata = automatizer_select_condition_type();
+ if (metadata == NULL)
+ {
+ return;
+ }
+ condition_and_add(current, metadata->create_condition());
+ break;
+
+ case M_OR:
+ metadata = automatizer_select_condition_type();
+ if (metadata == NULL)
+ {
+ return;
+ }
+ condition_or_add(current, metadata->create_condition());
+ break;
+
+ default:
+ /* No other types of conditions have children */
+ break;
+ }
+}
+
+static int tree_indent = 0;
+static int tree_write_out_y = 0;
+static int tree_write_out_x = 0;
+static int tree_write_out_h = 0;
+static int tree_write_out_w = 0;
+static int tree_write_y = 0;
+static int tree_write_x = 0;
+static int tree_write_off_x = 0;
+static int tree_write_off_y = 0;
+
+static void tree_write(byte color, cptr line)
+{
+ cptr p = line;
+
+ for (p = line; *p != '\0'; p++)
+ {
+ char c = *p;
+ int x = tree_write_x - tree_write_off_x + 3*tree_indent;
+ int y = tree_write_y - tree_write_off_y;
+
+ if (c != '\n')
+ {
+ if ((y >= 0) &&
+ (y < tree_write_out_h) &&
+ (x >= 0) &&
+ (x < tree_write_out_w))
+ {
+ Term_putch(x + tree_write_out_x,
+ y + tree_write_out_y,
+ color,
+ c);
+ }
+
+ tree_write_x += 1;
+ }
+ else
+ {
+ tree_write_x = 0;
+ tree_write_y += 1;
+ }
+ }
+}
+
+static void display_condition(condition_type *condition)
+{
+ byte bcol = TERM_L_GREEN;
+ byte ecol = TERM_GREEN;
+ int i;
+
+ assert(condition != NULL);
+
+ /* If this condition is present in the cursor stack,
+ then we use the "active" colors. */
+ for (i = 0; i < cursor_count; i++)
+ {
+ if (cursor_stack[i] == condition)
+ {
+ bcol = TERM_VIOLET;
+ ecol = TERM_VIOLET;
+ break;
+ }
+ }
+
+ tree_indent++;
+
+ switch (condition->match)
+ {
+ case M_INVENTORY:
+ case M_EQUIPMENT:
+ {
+ cptr where_s = (condition->match == M_INVENTORY)
+ ? "inventory"
+ : "equipment";
+
+ tree_write(ecol, "Something in your ");
+ tree_write(bcol, where_s);
+ tree_write(ecol, " matches the following:");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_SKILL:
+ {
+ cptr skill_name =
+ s_info[condition->skill_range.skill_idx].name + s_name;
+
+ tree_write(ecol, "Your skill in ");
+ tree_write(bcol, skill_name);
+ tree_write(ecol, " is from ");
+ tree_write(TERM_WHITE, format("%d", (int) condition->skill_range.min));
+ tree_write(ecol, " to ");
+ tree_write(TERM_WHITE, format("%d", (int) condition->skill_range.max));
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_ABILITY:
+ {
+ cptr ability_name =
+ ab_info[condition->ability].name + ab_name;
+
+ tree_write(ecol, "You have the ");
+ tree_write(bcol, ability_name);
+ tree_write(ecol, " ability");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_LEVEL:
+ {
+ tree_write(ecol, "Your ");
+ tree_write(bcol, "level");
+ tree_write(ecol, " is from ");
+
+ tree_write(TERM_WHITE, format("%d", condition->level_range.min));
+ tree_write(ecol, " to ");
+ tree_write(TERM_WHITE, format("%d", condition->level_range.max));
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_SVAL:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "sval");
+ tree_write(ecol, " is from ");
+ tree_write(TERM_WHITE, format("%d", condition->sval_range.min));
+ tree_write(ecol, " to ");
+ tree_write(TERM_WHITE, format("%d", condition->sval_range.max));
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_DISCOUNT:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "discount");
+ tree_write(ecol, " is from ");
+ tree_write(TERM_WHITE, format("%d", condition->discount.min));
+ tree_write(ecol, " to ");
+ tree_write(TERM_WHITE, format("%d", condition->discount.max));
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_AND:
+ {
+ struct sglib_condition_list_iterator it;
+ struct condition_list *child = NULL;
+
+ tree_write(ecol, "All of the following are true:");
+ tree_write(TERM_WHITE, "\n");
+
+ for (child = sglib_condition_list_it_init(&it, condition->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ display_condition(child->condition);
+ }
+
+ break;
+ }
+
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ struct condition_list *child = NULL;
+
+ tree_write(ecol, "At least one of the following are true:");
+ tree_write(TERM_WHITE, "\n");
+
+ for (child = sglib_condition_list_it_init(&it, condition->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ display_condition(child->condition);
+ }
+
+ break;
+ }
+
+ case M_NOT:
+ {
+ tree_write(ecol, "Negate the following:");
+ tree_write(TERM_WHITE, "\n");
+ display_condition(condition->subcondition);
+ break;
+ }
+
+ case M_NAME:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "name");
+ tree_write(ecol, " is \"");
+ tree_write(TERM_WHITE, condition->name);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_CONTAIN:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "name");
+ tree_write(ecol, " contains \"");
+ tree_write(TERM_WHITE, condition->name);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_INSCRIBED:
+ {
+ tree_write(ecol, "It is ");
+ tree_write(bcol, "inscribed");
+ tree_write(ecol, " with ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, condition->inscription);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_SYMBOL:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "symbol");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, format("%c", condition->symbol));
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_STATE:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "state");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, identification_state_to_string(condition->identification_state));
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_STATUS:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "status");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, status_to_string(condition->status));
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_TVAL:
+ {
+ tree_write(ecol, "Its ");
+ tree_write(bcol, "tval");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, format("%d", condition->tval));
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_RACE:
+ {
+ tree_write(ecol, "Player ");
+ tree_write(bcol, "race");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, condition->race);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_SUBRACE:
+ {
+ tree_write(ecol, "Player ");
+ tree_write(bcol, "subrace");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, condition->subrace);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case M_CLASS:
+ {
+ tree_write(ecol, "Player ");
+ tree_write(bcol, "class");
+ tree_write(ecol, " is ");
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, condition->klass);
+ tree_write(ecol, "\"");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ }
+
+ tree_indent--;
+}
+
+static void display_rule(arule_type *rule)
+{
+ cptr action_s;
+ int hgt, wid;
+
+ action_s = action_to_string(rule->action);
+
+ Term_get_size(&wid, &hgt);
+
+ tree_write_out_y = 1;
+ tree_write_out_x = 16;
+ tree_write_out_h = hgt - 4 - 1;
+ tree_write_out_w = wid - 1 - 15 - 1;
+ tree_write_y = 0;
+ tree_write_x = 0;
+
+ switch (rule->action)
+ {
+ case AUTO_DESTROY:
+ case AUTO_PICKUP:
+ {
+ tree_write(TERM_GREEN, "A rule named \"");
+ tree_write(TERM_WHITE, rule->name);
+ tree_write(TERM_GREEN, "\" to ");
+ tree_write(TERM_L_GREEN, action_s);
+ tree_write(TERM_GREEN, " when");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ case AUTO_INSCRIBE:
+ {
+ tree_write(TERM_GREEN, "A rule named \"");
+ tree_write(TERM_WHITE, rule->name);
+ tree_write(TERM_GREEN, "\" to ");
+ tree_write(TERM_L_GREEN, "inscribe");
+ tree_write(TERM_GREEN, " an item with \"");
+ tree_write(TERM_WHITE, rule->inscription);
+ tree_write(TERM_GREEN, "\" when");
+ tree_write(TERM_WHITE, "\n");
+ break;
+ }
+
+ }
+
+ /* Write out the condition */
+ if (rule->condition != NULL)
+ {
+ display_condition(rule->condition);
+ }
+}
+
+static void adjust_current(int sel)
+{
+ if (rules_count == 0)
+ {
+ cursor_clear();
+ return;
+ }
+
+ tree_write_off_y = 0;
+ tree_write_off_x = 0;
+
+ /* Put the top-level condition into cursor */
+ cursor_clear();
+ if (rules[sel]->condition != NULL)
+ {
+ cursor_push(rules[sel]->condition);
+ }
+}
+
+static void tree_scroll_up()
+{
+ tree_write_off_y = tree_write_off_y - 1;
+}
+
+static void tree_scroll_down()
+{
+ tree_write_off_y = tree_write_off_y + 1;
+}
+
+static void tree_scroll_left()
+{
+ tree_write_off_x = tree_write_off_x + 1;
+}
+
+static void tree_scroll_right()
+{
+ tree_write_off_x = tree_write_off_x - 1;
+}
+
+static void automatizer_save_rules()
+{
+ char name[30] = { '\0' };
+ char buf[1025];
+ char ch;
+ int hgt, wid;
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "automat.atm");
+ if (!input_box("Save name?", hgt / 2, wid / 2, name, sizeof(name)+1))
+ {
+ return;
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ if (file_exist(buf))
+ {
+ c_put_str(TERM_WHITE, "File exists, continue?[y/n]",
+ hgt / 2,
+ wid / 2 - 14);
+ ch = inkey();
+ if ((ch != 'Y') && (ch != 'y'))
+ {
+ return;
+ }
+ }
+
+ /* Write to file */
+ {
+ json_t *rules_json = rules_to_json();
+ int status = json_dump_file(rules_json, buf, JSON_INDENT(2) | JSON_SORT_KEYS);
+ if (status == 0)
+ {
+ c_put_str(TERM_WHITE, "Saved rules in file ",
+ hgt / 2,
+ wid / 2 - 14);
+ }
+ else
+ {
+ c_put_str(TERM_WHITE, "Saving rules failed! ",
+ hgt / 2,
+ wid / 2 - 14);
+ }
+
+ /* Deallocate JSON */
+ json_decref(rules_json);
+
+ /* Wait for keypress */
+ inkey();
+ }
+}
+
+static void rename_rule(arule_type *rule)
+{
+ char name[16];
+ int wid, hgt;
+
+ assert(rule != NULL);
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "%s", rule->name);
+ if (input_box("New name?", hgt / 2, wid / 2, name, sizeof(name)-1))
+ {
+ rule_set_name(rule, name);
+ }
+}
+
+static void add_new_condition(arule_type *current_rule)
+{
+ /* Top-level condition? */
+ if (current_rule->condition == NULL)
+ {
+ condition_metadata *metadata = NULL;
+
+ /* Sanity check for navigation stack */
+ assert(cursor_count == 0);
+
+ /* Select type of clause */
+ metadata = automatizer_select_condition_type();
+ if (metadata == NULL)
+ {
+ return;
+ }
+
+ /* Create the condition directly; we can
+ always add a top-level condition so there's
+ no need for the sanity checking in
+ add_child(). */
+ current_rule->condition = metadata->create_condition();
+ if (current_rule->condition != NULL)
+ {
+ cursor_push(current_rule->condition);
+ }
+ }
+ else
+ {
+ condition_type *current_condition = cursor_top();
+ add_child(current_condition);
+ }
+}
+
+static void tree_go_right()
+{
+ condition_type *top = cursor_top();
+
+ /* Can only go right if the currently selected condition
+ has children. */
+ switch (top->match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ /* Pick first child */
+ struct sglib_condition_list_iterator it;
+ condition_list *i = sglib_condition_list_it_init(&it, top->conditions.c);
+ /* Move right if possible */
+ if (i != NULL)
+ {
+ cursor_push(i->condition);
+ }
+ break;
+ }
+
+ case M_NOT:
+ case M_INVENTORY:
+ case M_EQUIPMENT:
+ {
+ if (top->subcondition != NULL)
+ {
+ cursor_push(top->subcondition);
+ }
+ break;
+ }
+
+ default:
+ /* Not possible to move */
+ break;
+ }
+}
+
+static void tree_go_left()
+{
+ if (cursor_count > 1)
+ {
+ cursor_pop();
+ }
+}
+
+static void tree_go_up()
+{
+ if (cursor_count > 1)
+ {
+ condition_type *prev_top = cursor_pop();
+ condition_type *top = cursor_top();
+
+ switch (top->match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ condition_list *child = NULL;
+ condition_list *prev_child = NULL;
+
+ /* We have a list of children */
+ for (child = sglib_condition_list_it_init(&it, top->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ if (child->condition == prev_top)
+ {
+ /* Do we have a previous child? */
+ if (prev_child == NULL)
+ {
+ /* No predecessor; don't move */
+ cursor_push(prev_top);
+ break;
+ }
+ else
+ {
+ cursor_push(prev_child->condition);
+ break; /* Done */
+ }
+ }
+ /* Keep track of previous child */
+ prev_child = child;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ /* No other match types have children; restore
+ original top. */
+ cursor_push(prev_top);
+ break;
+ }
+
+ }
+ }
+}
+
+static void tree_go_down()
+{
+ if (cursor_count > 1)
+ {
+ condition_type *prev_top = cursor_pop();
+ condition_type *top = cursor_top();
+
+ switch (top->match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ condition_list *child = NULL;
+
+ /* We have a list of children */
+ for (child = sglib_condition_list_it_init(&it, top->conditions.c);
+ child != NULL;
+ child = sglib_condition_list_it_next(&it))
+ {
+ if (child->condition == prev_top)
+ {
+ /* Move to next child (if any) */
+ child = sglib_condition_list_it_next(&it);
+ if (child == NULL)
+ {
+ /* No successor; don't move */
+ cursor_push(prev_top);
+ break;
+ }
+ else
+ {
+ cursor_push(child->condition);
+ break; /* Done */
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ /* No other match types have multiple children; restore
+ original top. */
+ cursor_push(prev_top);
+ break;
+ }
+
+ }
+ }
+}
+
+static int automatizer_del_self(int sel)
+{
+ /* If the cursor is at the top level then
+ we want to delete the rule itself */
+ if (cursor_count < 1)
+ {
+ rules_remove(rules[sel]);
+ return sel - 1; /* Move selection up */
+ }
+ else if (cursor_count == 1)
+ {
+ cursor_pop();
+ condition_destroy(&rules[sel]->condition);
+ return sel;
+ }
+ else
+ {
+ condition_type *prev_top = cursor_pop();
+ condition_type *top = cursor_top();
+
+ /* Jump up a level; this is a simple way to ensure a
+ valid cursor. We could be a little cleverer here by
+ trying to move inside the current level, but it's a
+ little complicated. */
+ tree_go_left();
+
+ /* Now we can remove the condition from its parent */
+ switch (top->match)
+ {
+
+ case M_AND:
+ case M_OR:
+ {
+ struct sglib_condition_list_iterator it;
+ condition_list *item = NULL;
+
+ /* We have a list of children */
+ for (item = sglib_condition_list_it_init(&it, top->conditions.c);
+ item != NULL;
+ item = sglib_condition_list_it_next(&it))
+ {
+ if (item->condition == prev_top)
+ {
+ /* Found */
+ break;
+ }
+ }
+
+ /* Must have found item; otherwise internal structure
+ is damaged. */
+ assert (item != NULL);
+ sglib_condition_list_delete(&top->conditions.c, item);
+
+ /* Destroy the condition */
+ condition_destroy(&prev_top);
+ break;
+ }
+
+ case M_NOT:
+ case M_EQUIPMENT:
+ case M_INVENTORY:
+ {
+ assert(top->subcondition != NULL);
+ condition_destroy(&top->subcondition);
+ break;
+ }
+
+ default:
+ /* If we get here, something's wrong with the
+ navigation structures. */
+ assert(FALSE);
+ break;
+ }
+
+ /* Keep selection */
+ return sel;
+ }
+}
+
#define ACTIVE_LIST 0
#define ACTIVE_RULE 1
void do_cmd_automatizer()
{
int wid = 0, hgt = 0;
char c;
- cptr *list = NULL;
int max, begin = 0, sel = 0;
int active = ACTIVE_LIST;
cptr keys;
cptr keys2;
cptr keys3;
+ cptr rule_names[RULES_MAX];
Term_get_size(&wid, &hgt);
@@ -260,37 +2800,38 @@ void do_cmd_automatizer()
screen_save();
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ adjust_current(sel);
while (1)
{
Term_clear();
Term_get_size(&wid, &hgt);
- list = get_rule_list(&max);
- display_list(0, 0, hgt - 1, 15, "Rules", list, max, begin, sel, (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN);
- C_FREE(list, max, cptr);
+ max = rules_count;
+ get_rule_names(rule_names);
+ display_list(0, 0, hgt - 1, 15, "Rules", rule_names, max, begin, sel, (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN);
draw_box(0, 15, hgt - 4, wid - 1 - 15);
if (active == ACTIVE_RULE)
{
keys = "#Bup#W/#Bdown#W/#Bleft#W/#Bright#W to navitage rule, #B9#W/#B3#W/#B7#W/#B1#W to scroll";
keys2 = "#Btab#W for switch, #Ba#Wdd clause, #Bd#Welete clause/rule";
- keys3 = "#Bx#W to toggle english/xml, #G?#W for Automatizer help";
- exec_lua("xml.write_active = not nil");
+ keys3 = "#G?#W for Automatizer help";
}
else
{
keys = "#Bup#W/#Bdown#W to scroll, #Btab#W to switch to the rule window";
keys2 = "#Bu#W/#Bd#W to move rules, #Bn#Wew rule, #Br#Wename rule, #Bs#Wave rules";
keys3 = "#Rk#W to #rdisable#W the automatizer, #G?#W for Automatizer help";
- exec_lua("xml.write_active = nil");
}
display_message(17, hgt - 3, strlen(keys), TERM_WHITE, keys);
display_message(17, hgt - 2, strlen(keys2), TERM_WHITE, keys2);
display_message(17, hgt - 1, strlen(keys3), TERM_WHITE, keys3);
- if (max) exec_lua(format("xml.write_out_y = 1; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(__rules[%d].table, '')", hgt - 4 - 1, wid - 1 - 15 - 1, sel));
+ if (max)
+ {
+ display_rule(rules[sel]);
+ }
c = inkey();
@@ -308,105 +2849,58 @@ void do_cmd_automatizer()
if (!max) continue;
sel--;
adjust_begin(&begin, &sel, max, hgt);
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ adjust_current(sel);
}
else if (c == '2')
{
if (!max) continue;
sel++;
adjust_begin(&begin, &sel, max, hgt);
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ adjust_current(sel);
}
else if (c == 'u')
{
- if (!max) continue;
- sel = exec_lua(format("return auto_aux:move_up(%d)", sel));
- adjust_begin(&begin, &sel, max, hgt);
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ if (sel > 0)
+ {
+ rules_swap(sel-1, sel);
+ sel -= 1;
+
+ adjust_begin(&begin, &sel, max, hgt);
+ adjust_current(sel);
+ }
}
else if (c == 'd')
{
if (!max) continue;
- sel = exec_lua(format("return auto_aux:move_down(%d)", sel));
- adjust_begin(&begin, &sel, max, hgt);
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+
+ if (sel < rules_count - 1)
+ {
+ rules_swap(sel, sel+1);
+ sel += 1;
+
+ adjust_begin(&begin, &sel, max, hgt);
+ adjust_current(sel);
+ }
}
else if (c == 'n')
{
- char name[20] = { '\0' };
- char typ;
-
- sprintf(name, "No name");
- if (input_box("Name?", hgt / 2, wid / 2, name, sizeof(name)+1))
+ int i = create_new_rule();
+ if (i >= 0)
{
- cptr styp = "nothing";
- typ = msg_box("[D]estroy, [P]ickup, [I]nscribe, [N]othing rule?", hgt / 2, wid / 2);
- if ((typ == 'd') || (typ == 'D')) styp = "destroy";
- else if ((typ == 'p') || (typ == 'P')) styp = "pickup";
- else if ((typ == 'i') || (typ == 'I')) styp = "inscribe";
- exec_lua(format("auto_aux:new_rule(%d, '%s', '%s', ''); auto_aux:adjust_current(%d)", sel, name, styp, sel));
+ sel = i;
+ adjust_current(sel);
active = ACTIVE_RULE;
}
}
else if (c == 's')
{
- char name[30] = { '\0' };
-
- sprintf(name, "automat.atm");
- if (input_box("Save name?", hgt / 2, wid / 2, name, sizeof(name)+1))
- {
- char buf[1025];
- char ch;
-
- /* Build the filename */
- path_build(buf, 1024, ANGBAND_DIR_USER, name);
-
- /* File type is "TEXT" */
- FILE_TYPE(FILE_TYPE_TEXT);
-
- if (file_exist(buf))
- {
- c_put_str(TERM_WHITE, "File exists, continue?[y/n]", hgt / 2, wid / 2 - 14);
- ch = inkey();
- if ((ch != 'Y') && (ch != 'y'))
- continue;
- }
-
- /* Open the non-existing file */
- hook_file = my_fopen(buf, "w");
-
- /* Invalid file */
- if (!hook_file)
- {
- /* Message */
- c_put_str(TERM_WHITE, "Saving rules failed! ", hgt / 2, wid / 2 - 14);
- (void) inkey();
-
- /* Error */
- continue;
- }
-
-
-
- exec_lua("auto_aux:save_ruleset()");
- my_fclose(hook_file);
- /* Overwrite query message */
- c_put_str(TERM_WHITE, "Saved rules in file ", hgt / 2, wid / 2 - 14);
- (void) inkey();
- }
+ automatizer_save_rules();
}
else if (c == 'r')
{
- char name[20];
-
if (!max) continue;
- sprintf(name, "%s", string_exec_lua(format("return __rules[%d].table.args.name", sel)));
- if (input_box("New name?", hgt / 2, wid / 2, name, 15))
- {
- exec_lua(format("auto_aux:rename_rule(%d, '%s')", sel, name));
- }
-
+ rename_rule(rules[sel]);
continue;
}
else if (c == 'k')
@@ -419,10 +2913,6 @@ void do_cmd_automatizer()
if (!max) continue;
active = ACTIVE_RULE;
}
- else if (c == 'x')
- {
- exec_lua("xml.display_english = not xml.display_english");
- }
}
else if (active == ACTIVE_RULE)
{
@@ -434,41 +2924,39 @@ void do_cmd_automatizer()
}
else if (c == '8')
{
- exec_lua("auto_aux:go_up()");
+ tree_go_up();
}
else if (c == '2')
{
- exec_lua("auto_aux:go_down()");
+ tree_go_down();
}
else if (c == '6')
{
- exec_lua("auto_aux:go_right()");
+ tree_go_right();
}
else if (c == '4')
{
- exec_lua(format("auto_aux:go_left(%d)", sel));
+ tree_go_left();
}
else if (c == '9')
{
- exec_lua("auto_aux:scroll_up()");
+ tree_scroll_up();
}
else if (c == '3')
{
- exec_lua("auto_aux:scroll_down()");
+ tree_scroll_down();
}
else if (c == '7')
{
- exec_lua("auto_aux:scroll_left()");
+ tree_scroll_left();
}
else if (c == '1')
{
- exec_lua("auto_aux:scroll_right()");
+ tree_scroll_right();
}
else if (c == 'a')
{
- int s = automatizer_new_rule();
- if (s == -1) continue;
- exec_lua(format("auto_aux:add_child('%s')", types_list[s]));
+ add_new_condition(rules[sel]);
}
else if (c == 'd')
{
@@ -476,12 +2964,12 @@ void do_cmd_automatizer()
{
int new_sel;
- new_sel = exec_lua(format("return auto_aux:del_self(%d)", sel));
+ new_sel = automatizer_del_self(sel);
if ((sel != new_sel) && (new_sel >= 0))
{
sel = new_sel;
adjust_begin(&begin, &sel, max, hgt);
- exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ adjust_current(sel);
}
else if (new_sel == -1)
{
@@ -493,30 +2981,84 @@ void do_cmd_automatizer()
{
active = ACTIVE_LIST;
}
- else if (c == 'x')
- {
- exec_lua("xml.display_english = not xml.display_english");
- }
}
}
- /* Recalculate the rules */
- exec_lua("auto_aux.regen_ruleset()");
-
screen_load();
}
+static void easy_add_rule(action_type action, cptr mode, bool_ do_status, object_type *o_ptr)
+{
+ condition_type *condition = NULL;
+
+ if (streq(mode, "tval"))
+ {
+ condition = condition_new_tval(o_ptr->tval);
+ }
+ else if (streq(mode, "tsval"))
+ {
+ condition_type *sval_condition =
+ condition_new_sval(o_ptr->sval, o_ptr->sval);
+ condition_type *tval_condition =
+ condition_new_tval(o_ptr->tval);
+
+ condition = condition_new_and();
+ condition_and_add(condition, tval_condition);
+ condition_and_add(condition, sval_condition);
+ }
+ else if (streq(mode, "name"))
+ {
+ char buf[128];
+ object_desc(buf, o_ptr, -1, 0);
+ strlower(buf);
+
+ condition = condition_new_name(buf);
+ }
+
+ /* Use object status? */
+ if (do_status == TRUE)
+ {
+ status_type status = object_status(o_ptr);
+ condition_type *status_condition =
+ condition_new_status(status);
+ condition_type *and_condition =
+ condition_new_and();
+
+ condition_and_add(and_condition, condition);
+ condition_and_add(and_condition, status_condition);
+ /* Replace condition */
+ condition = and_condition;
+ }
+
+ /* Build rule */
+ {
+ static arule_type *rule = NULL;
+ /* Make rule */
+ rule = rule_new(action_to_string(action),
+ action,
+ game_module_idx,
+ condition,
+ NULL);
+
+ /* Append to list of rules */
+ rules_append(rule);
+ }
+
+ msg_print("Rule added. Please go to the Automatizer screen (press = then T)");
+ msg_print("to save the modified ruleset.");
+}
+
/* Add a new rule in an easy way */
bool_ automatizer_create = FALSE;
void automatizer_add_rule(object_type *o_ptr, bool_ destroy)
{
char ch;
bool_ do_status = FALSE;
- cptr type = "destroy";
+ action_type action = AUTO_DESTROY;
if (!destroy)
{
- type = "pickup";
+ action = AUTO_PICKUP;
}
while (TRUE)
@@ -534,20 +3076,496 @@ void automatizer_add_rule(object_type *o_ptr, bool_ destroy)
if (ch == 'T' || ch == 't')
{
- call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tsval", do_status, o_ptr);
+ easy_add_rule(action, "tsval", do_status, o_ptr);
break;
}
if (ch == 'F' || ch == 'f')
{
- call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tval", do_status, o_ptr);
+ easy_add_rule(action, "tval", do_status, o_ptr);
break;
}
if (ch == 'N' || ch == 'n')
{
- call_lua("easy_add_rule", "(s,s,d,O)", "", type, "name", do_status, o_ptr);
+ easy_add_rule(action, "name", do_status, o_ptr);
break;
}
}
}
+
+static condition_type *parse_condition(json_t *condition_json)
+{
+ cptr type_s = NULL;
+ match_type match;
+
+ if ((condition_json == NULL) || json_is_null(condition_json))
+ {
+ return NULL;
+ }
+
+ if (json_unpack(condition_json,
+ "{s:s}",
+ "type", &type_s) < 0)
+ {
+ msg_print("Missing/invalid 'type' in condition");
+ return NULL;
+ }
+
+ if (!match_type_from_string(type_s, &match))
+ {
+ msg_format("Invalid 'type' in condition: %s", type_s);
+ return NULL;
+ }
+
+ switch (match)
+ {
+ case M_AND:
+ case M_OR:
+ {
+ json_t *conditions_j = json_object_get(condition_json,
+ "conditions");
+
+ if ((conditions_j == NULL) ||
+ (json_is_null(conditions_j)))
+ {
+ return NULL;
+ }
+ else if (json_is_array(conditions_j))
+ {
+ int i;
+ json_t *subcondition_j = NULL;
+ condition_type *condition = condition_new(match);
+ condition_type *subcondition = NULL;
+
+ for (i = 0; i < json_array_size(conditions_j); i++)
+ {
+ subcondition_j =
+ json_array_get(conditions_j, i);
+ subcondition =
+ parse_condition(subcondition_j);
+
+ if (subcondition != NULL)
+ {
+ condition_and_add(condition, subcondition);
+ }
+ }
+
+ return condition;
+ }
+ else
+ {
+ msg_print("'conditions' property has invalid type");
+ return NULL;
+ }
+
+ break;
+ }
+
+ case M_NOT:
+ case M_INVENTORY:
+ case M_EQUIPMENT:
+ {
+ json_t *condition_j = json_object_get(condition_json,
+ "condition");
+
+ if ((condition_j == NULL) ||
+ (json_is_null(condition_j)))
+ {
+ return NULL;
+ }
+ else if (json_is_object(condition_j))
+ {
+ condition_type *condition =
+ condition_new(match);
+ condition->subcondition =
+ parse_condition(condition_j);
+ return condition;
+ }
+ else
+ {
+ msg_print("Invlalid 'condition' property");
+ return NULL;
+ }
+ }
+
+ case M_NAME:
+ {
+ cptr s = NULL;
+ if (json_unpack(condition_json,
+ "{s:s}",
+ "name", &s) < 0)
+ {
+ msg_print("Missing/invalid 'name' property");
+ return NULL;
+ }
+
+ return condition_new_name(s);
+ }
+
+ case M_CONTAIN:
+ {
+ cptr s = NULL;
+ if (json_unpack(condition_json,
+ "{s:s}",
+ "contain", &s) < 0)
+ {
+ msg_print("Missing/invalid 'contain' property");
+ return NULL;
+ }
+
+ return condition_new_contain(s);
+ }
+
+ case M_SYMBOL:
+ {
+ cptr s = NULL;
+ int sl;
+ if (json_unpack(condition_json, "{s:s}", "symbol", &s) < 0)
+ {
+ msg_print("Missing/invalid 'symbol' property");
+ return NULL;
+ }
+
+ sl = strlen(s);
+ if (sl == 0)
+ {
+ msg_print("Invalid 'symbol' property: Too short");
+ return NULL;
+ }
+ if (sl > 1)
+ {
+ msg_print("Invalid 'symbol' property: Too long");
+ return NULL;
+ }
+
+ return condition_new_symbol(s[0]);
+ }
+
+ case M_INSCRIBED:
+ {
+ cptr s = NULL;
+ if (json_unpack(condition_json, "{s:s}", "inscription", &s) < 0)
+ {
+ msg_print("Missing/invalid 'inscription' property");
+ return NULL;
+ }
+
+ return condition_new_inscribed(s);
+ }
+
+ case M_DISCOUNT:
+ {
+ int min, max;
+
+ if (json_unpack(condition_json, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return NULL;
+ }
+
+ return condition_new_discount(min, max);
+ }
+
+ case M_TVAL:
+ {
+ int tval;
+
+ if (json_unpack(condition_json, "{s:i}", "tval", &tval) < 0)
+ {
+ msg_print("Missing/invalid 'tval' property");
+ return NULL;
+ }
+
+ return condition_new_tval(tval);
+ }
+
+ case M_SVAL:
+ {
+ int min, max;
+
+ if (json_unpack(condition_json, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return NULL;
+ }
+
+ return condition_new_sval(min, max);
+ }
+
+ case M_STATUS:
+ {
+ cptr s;
+ status_type status;
+
+ if (json_unpack(condition_json, "{s:s}", "status", &s) < 0)
+ {
+ msg_print("Missing/invalid 'status' property");
+ return NULL;
+ }
+
+ if (!status_from_string(s, &status))
+ {
+ msg_format("Invalid 'status' property: %s", s);
+ return NULL;
+ }
+
+ return condition_new_status(status);
+ }
+
+ case M_STATE:
+ {
+ cptr s;
+ identification_state state;
+
+ if (json_unpack(condition_json, "{s:s}", "state", &s) < 0)
+ {
+ msg_print("Missing/invalid 'state' property");
+ return NULL;
+ }
+
+ if (!identification_state_from_string(s, &state))
+ {
+ msg_format("Invalid 'state' property: %s", s);
+ return NULL;
+ }
+
+ return condition_new_state(state);
+ }
+
+ case M_RACE:
+ {
+ cptr s;
+
+ if (json_unpack(condition_json, "{s:s}", "race", &s) < 0)
+ {
+ msg_print("Missing/invalid 'race' property");
+ return NULL;
+ }
+
+ return condition_new_race(s);
+ }
+
+ case M_SUBRACE:
+ {
+ cptr s;
+
+ if (json_unpack(condition_json, "{s:s}", "subrace", &s) < 0)
+ {
+ msg_print("Missing/invalid 'subrace' property");
+ return NULL;
+ }
+
+ return condition_new_subrace(s);
+ }
+
+ case M_CLASS:
+ {
+ cptr s;
+
+ if (json_unpack(condition_json, "{s:s}", "class", &s) < 0)
+ {
+ msg_print("Missing/invalid 'class' property");
+ return NULL;
+ }
+
+ return condition_new_class(s);
+ }
+
+ case M_LEVEL:
+ {
+ int min, max;
+
+ if (json_unpack(condition_json, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return NULL;
+ }
+
+ return condition_new_level(min, max);
+ }
+
+ case M_SKILL:
+ {
+ cptr s;
+ s16b si;
+ int min, max;
+
+ if (json_unpack(condition_json, "{s:i,s:i,s:s}",
+ "min", &min,
+ "max", &max,
+ "name", &s) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max'/'name' properties");
+ return NULL;
+ }
+
+ si = find_skill_i(s);
+ if (si < 0)
+ {
+ msg_print("Invalid 'name' property");
+ return NULL;
+ }
+
+ return condition_new_skill(min, max, si);
+ }
+
+ case M_ABILITY:
+ {
+ cptr a;
+ s16b ai;
+
+ if (json_unpack(condition_json, "{s:s}",
+ "ability", &a) < 0)
+ {
+ msg_print("Missing/invalid 'ability' property");
+ return NULL;
+ }
+
+ ai = find_ability(a);
+ if (ai < 0)
+ {
+ msg_print("Invalid 'ability' property");
+ return NULL;
+ }
+
+ return condition_new_ability(ai);
+ }
+
+ }
+
+ /* Could not parse */
+ return NULL;
+}
+
+static void parse_rule(json_t *rule_json)
+{
+ char *rule_name_s = NULL;
+ char *rule_action_s = NULL;
+ char *rule_module_s = NULL;
+ json_t *rule_inscription_j = NULL;
+ arule_type *rule = NULL;
+ action_type action;
+ int module_idx;
+
+ if (!json_is_object(rule_json))
+ {
+ msg_print("Rule is not an object");
+ return;
+ }
+
+ /* Retrieve the attributes */
+ if (json_unpack(rule_json,
+ "{s:s,s:s,s:s}",
+ "name", &rule_name_s,
+ "action", &rule_action_s,
+ "module", &rule_module_s) < 0)
+ {
+ msg_print("Rule missing required field(s)");
+ return;
+ }
+
+ /* Get the optional inscription */
+ rule_inscription_j = json_object_get(rule_json, "inscription");
+
+ /* Convert attributes */
+ if (!action_from_string((cptr) rule_action_s, &action))
+ {
+ msg_format("Invalid rule action '%s'", rule_action_s);
+ return;
+ }
+
+ module_idx = find_module((cptr) rule_module_s);
+ if (module_idx < 0)
+ {
+ msg_format("Skipping rule for unrecognized module '%s'",
+ (cptr) rule_module_s);
+ return;
+ }
+
+ /* Sanity check: Inscription */
+ if (action == AUTO_INSCRIBE)
+ {
+ if (rule_inscription_j == NULL)
+ {
+ msg_print("Inscription rule missing 'inscription' attribute");
+ return;
+ }
+ if (!json_is_string(rule_inscription_j))
+ {
+ msg_print("Inscription rule 'inscription' attribute wrong type");
+ return;
+ }
+ }
+
+ /* Create rule */
+ rule = rule_new(rule_name_s,
+ action,
+ module_idx,
+ NULL,
+ json_string_value(rule_inscription_j));
+ rules_append(rule);
+
+ /* Parse the conditions */
+ rule->condition = parse_condition(json_object_get(rule_json, "condition"));
+}
+
+static void parse_rules(json_t *rules)
+{
+ int i;
+
+ if (!json_is_array(rules))
+ {
+ msg_format("Error 'rules' is not an array");
+ return;
+ }
+
+ for (i = 0; i < json_array_size(rules); i++)
+ {
+ json_t *rule = json_array_get(rules, i);
+ parse_rule(rule);
+ }
+}
+
+/**
+ * Initialize the automatizer. This function may be called multiple
+ * times with different file names -- it should NOT clear any
+ * automatizer state (including loaded rules).
+ */
+void automatizer_init(cptr file_path)
+{
+ json_t *rules_json = NULL;
+ json_error_t error;
+
+ /* Does the file exist? */
+ if (!file_exist(file_path))
+ {
+ /* No big deal, we'll just skip */
+ goto out;
+ }
+
+ /* Parse file */
+ rules_json = json_load_file(file_path, 0, &error);
+ if (rules_json == NULL)
+ {
+ msg_format("Error parsing automatizer rules from '%s'.", file_path);
+ msg_format("Line %d, Column %d", error.line, error.column);
+ msg_print(NULL);
+ goto out;
+ }
+
+ /* Go through all the found rules */
+ parse_rules(rules_json);
+
+out:
+ if (rules_json == NULL)
+ {
+ json_decref(rules_json);
+ }
+}