From 6aa48afdd57d03314fdf4be6c9da911c32277c84 Mon Sep 17 00:00:00 2001 From: Bardur Arantsson Date: Fri, 8 Jan 2010 20:28:34 +0100 Subject: Import tome-2.3.5. --- lib/core/auto.lua | 803 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/core/building.lua | 15 + lib/core/crpt_aux.lua | 243 +++++++++++++++ lib/core/dungeon.lua | 106 +++++++ lib/core/gen_idx.lua | 261 ++++++++++++++++ lib/core/gods.lua | 40 +++ lib/core/help.lua | 141 +++++++++ lib/core/init.lua | 84 ++++++ lib/core/load.lua | 37 +++ lib/core/load2.lua | 63 ++++ lib/core/mimc_aux.lua | 95 ++++++ lib/core/monsters.lua | 16 + lib/core/objects.lua | 45 +++ lib/core/player.lua | 140 +++++++++ lib/core/powers.lua | 105 +++++++ lib/core/quests.lua | 57 ++++ lib/core/s_aux.lua | 742 ++++++++++++++++++++++++++++++++++++++++++++++ lib/core/stores.lua | 32 ++ lib/core/util.lua | 257 ++++++++++++++++ lib/core/xml.lua | 347 ++++++++++++++++++++++ 20 files changed, 3629 insertions(+) create mode 100644 lib/core/auto.lua create mode 100644 lib/core/building.lua create mode 100644 lib/core/crpt_aux.lua create mode 100644 lib/core/dungeon.lua create mode 100644 lib/core/gen_idx.lua create mode 100644 lib/core/gods.lua create mode 100644 lib/core/help.lua create mode 100644 lib/core/init.lua create mode 100644 lib/core/load.lua create mode 100644 lib/core/load2.lua create mode 100644 lib/core/mimc_aux.lua create mode 100644 lib/core/monsters.lua create mode 100644 lib/core/objects.lua create mode 100644 lib/core/player.lua create mode 100644 lib/core/powers.lua create mode 100644 lib/core/quests.lua create mode 100644 lib/core/s_aux.lua create mode 100644 lib/core/stores.lua create mode 100644 lib/core/util.lua create mode 100644 lib/core/xml.lua (limited to 'lib/core') diff --git a/lib/core/auto.lua b/lib/core/auto.lua new file mode 100644 index 00000000..fa2457ff --- /dev/null +++ b/lib/core/auto.lua @@ -0,0 +1,803 @@ +-- This file is the core of the Automatizer +-- Please do not touch unless you know what you are doing + +__rules = {} +__rules_max = 0 + +rule_aux = {} + +-- Rule apply function, does .. nothing +function auto_nothing(obj, item) + return +end + +function auto_inscribe(obj, item, note) + if obj.note ~= 0 then return end + msg_print("") + obj.note = quark_add(note) + return TRUE +end + +-- Rule apply function, pickup object +function auto_pickup(obj, item) + if item >= 0 then return end + if inven_carry_okay(obj) == FALSE then return end + msg_print("") + object_pickup(-item) + return TRUE +end + +-- Rule apply function, destroy item +function auto_destroy(obj, item) + -- be carefull to what we can destroy + -- Unaware things won't be destroyed. + if is_aware(obj) == FALSE then return end + + -- Inscribed things won't be destroyed! + if obj.note ~= 0 then return end + + -- Keep Artifacts -- they cannot be destroyed anyway + if is_artifact(obj) == TRUE then return end + + -- Cannot destroy CURSE_NO_DROP objects + local f1, f2, f3, f4, f5, esp = object_flags(obj); + if band(f4, TR4_CURSE_NO_DROP) ~= 0 and band(obj.ident, IDENT_CURSED) then return end + + msg_print("") + + -- Eliminate the item (from the pack) + if item >= 0 then + inven_item_increase(item, -obj.number) + inven_item_describe(item) + inven_item_optimize(item) + -- Eliminate the item (from the floor) + else + floor_item_increase(0 - item, -obj.number) + floor_item_describe(0 - item) + floor_item_optimize(0 - item) + end + return TRUE +end + +-- Report the status of an object +function object_status(obj) + local sense = + { + [SENSE_CURSED] = "bad", + [SENSE_WORTHLESS] = "very bad", + [SENSE_AVERAGE] = "average", + [SENSE_GOOD_LIGHT] = "good", + [SENSE_GOOD_HEAVY] = "good", + [SENSE_EXCELLENT] = "very good", + [SENSE_SPECIAL] = "special", + [SENSE_TERRIBLE] = "terrible", + } + + if is_known(obj) == FALSE then + if sense[obj.sense] then + return sense[obj.sense] + else + return "" + end + else +if nil then -- test + local osense = -1 + local type = select_sense(obj, TRUE, TRUE) + if type == 1 then + osense = value_check_aux1(obj) + elseif type == 2 then + osense = value_check_aux1_magic(obj) + end +print("type : "..type) + if sense[osense] then + print("sense: "..sense[osense]) + return sense[osense] + else + print("sense: ") + return "" + end + +else -- the real one + + local slot = wield_slot_ideal(obj, TRUE) + + -- Arts items + if is_artifact(obj) == TRUE then + if band(obj.ident, IDENT_CURSED) == 0 then return "special" + else return "terrible" end + -- Ego items + elseif (obj.name2 > 0 or obj.name2b > 0) then + if band(obj.ident, IDENT_CURSED) == 0 then return "very good" + else return "very bad" end + -- weapon + elseif (slot == INVEN_WIELD) or (slot == INVEN_BOW) or (slot == INVEN_AMMO) or (slot == INVEN_TOOL) then + if obj.to_h + obj.to_d < 0 then + return "bad" + elseif obj.to_h + obj.to_d > 0 then + return "good" + else + return "average" + end + -- armor + elseif (slot >= INVEN_BODY) and (slot <= INVEN_FEET) then + if obj.to_a < 0 then + return "bad" + elseif obj.to_a > 0 then + return "good" + else + return "average" + end + -- ring + elseif slot == INVEN_RING then + if (obj.to_d + obj.to_h < 0) or (obj.to_a < 0) or (obj.pval < 0) then + return "bad" + else + return "average" + end + -- amulet + elseif slot == INVEN_NECK then + if (obj.pval < 0) then + return "bad" + else + return "average" + end + -- chests + elseif obj.tval == TV_CHEST then + if obj.pval == 0 then + return "empty" + elseif obj.pval < 0 then + return "disarmed" + else + return "average" + end + else + return "average" + end +end + end +end + +-- Recursive function to generate a rule function tree +function gen_rule_fct(r) + -- It is a test rule (or, and, ...) + if r.label == "and" or r.label == "or" then + local i + local fct_tbl = {} + for i = 1, getn(r) do + if r[i].label ~= "comment" then + tinsert(fct_tbl, gen_rule_fct(r[i])) + end + end + if r.label == "and" then + return function(object) + local fcts = %fct_tbl + local i + for i = 1, getn(fcts) do + if not fcts[i](object) then return end + end + return TRUE + end + elseif r.label == "or" then + return function(object) + local fcts = %fct_tbl + local i + for i = 1, getn(fcts) do + if fcts[i](object) then return TRUE end + end + end + end + -- It is a condition rule (name, type, level, ...) + else + if r.label == "not" then + local f + if not r[1] then + f = function (object) return TRUE end + else + f = gen_rule_fct(r[1]) + end + return function(object) return not %f(object) end + elseif r.label == "name" then + return function(object) if strlower(object_desc(object, -1, 0)) == strlower(%r[1]) then return TRUE end end + elseif r.label == "contain" then + return function(object) if strfind(strlower(object_desc(object, -1, 0)), strlower(%r[1])) then return TRUE end end + elseif r.label == "symbol" then + return function(object) if strchar(get_kind(object).d_char) == %r[1] then return TRUE end end + elseif r.label == "inscribed" then + return function(object) if object.note ~= 0 and strfind(strlower(quark_str(object.note)), strlower(%r[1])) then return TRUE end end + elseif r.label == "discount" then + local d1 = r.args.min + local d2 = r.args.max + if tonumber(d1) == nil then d1 = getglobal(d1) else d1 = tonumber(d1) end + if tonumber(d2) == nil then d2 = getglobal(d2) else d2 = tonumber(d2) end + return function(object) if is_aware(object) == TRUE and object.discount >= %d1 and object.discount <= %d2 then return TRUE end end + elseif r.label == "tval" then + local tv = r[1] + if tonumber(tv) == nil then tv = getglobal(tv) else tv = tonumber(tv) end + return function(object) if object.tval == %tv then return TRUE end end + elseif r.label == "sval" then + assert(r.args.min and r.args.max, "sval rule lacks min or max") + local sv1 = r.args.min + local sv2 = r.args.max + if tonumber(sv1) == nil then sv1 = getglobal(sv1) else sv1 = tonumber(sv1) end + if tonumber(sv2) == nil then sv2 = getglobal(sv2) else sv2 = tonumber(sv2) end + return function(object) if is_aware(object) == TRUE and object.sval >= %sv1 and object.sval <= %sv2 then return TRUE end end + elseif r.label == "status" then + return function(object) if object_status(object) == strlower(%r[1]) then return TRUE end end + elseif r.label == "state" then + if r[1] == "identified" then + return function(object) if is_known(object) == TRUE then return TRUE end end + else + return function(object) if is_known(object) == FALSE then return TRUE end end + end + elseif r.label == "race" then + return function(object) if strlower(get_race_name()) == strlower(%r[1]) then return TRUE end end + elseif r.label == "subrace" then + return function(object) if strlower(get_subrace_name()) == strlower(%r[1]) then return TRUE end end + elseif r.label == "class" then + return function(object) if strlower(get_class_name()) == strlower(%r[1]) then return TRUE end end + elseif r.label == "level" then + assert(r.args.min and r.args.max, "level rule lacks min or max") + return function(object) if player.lev >= tonumber(%r.args.min) and player.lev <= tonumber(%r.args.max) then return TRUE end end + elseif r.label == "skill" then + assert(r.args.min and r.args.max, "skill rule lacks min or max") + local s = find_skill_i(r[1]) + assert(s ~= -1, "no skill "..r[1]) + return function(object) if get_skill(%s) >= tonumber(%r.args.min) and get_skill(%s) <= tonumber(%r.args.max) then return TRUE end end + elseif r.label == "ability" then + local s = find_ability(r[1]) + assert(s ~= -1, "no ability "..r[1]) + return function(object) if has_ability(%s) == TRUE then return TRUE end end + end + end +end + +function auto_inscribe_maker(inscription) + return function(...) + arg.n = arg.n + 1 + arg[getn(arg)] = %inscription + return call(auto_inscribe, arg) + end +end + +-- Generate a rule from a table +function gen_full_rule(t) + -- only honor rules for this module + if not t.args.module then + t.args.module = "ToME" + end + + if not ((t.args.module == "all") or (t.args.module == game_module)) then + return function() end + end + + -- Check for which action to do + local apply_fct = auto_nothing + if t.args.type == "destroy" then apply_fct = auto_destroy + elseif t.args.type == "pickup" then apply_fct = auto_pickup + elseif t.args.type == "inscribe" then apply_fct = auto_inscribe_maker(t.args.inscription) + end + + -- create the function tree + local rf + if t[1] then + rf = gen_rule_fct(t[1]) + else + rf = function (object) end + end + + -- create the final function + return function(...) + local rf = %rf + if rf(arg[1]) then + if call(%apply_fct, arg) == TRUE then return TRUE end + end + end +end + +-- Create a function that checks for the rules(passed in xml form) +function add_ruleset(s) + local tbl = xml:collect(s) + local i + + -- Add all rules + for i = 1, getn(tbl) do + local t = tbl[i] + + if t.label == "rule" then + -- Create the function tree + local fct = gen_full_rule(t) + + -- Create the test function + __rules[__rules_max] = + { + ["table"] = t, + ["fct"] = fct + } + __rules_max = __rules_max + 1 + end + end +end + +-- Apply the current rules to an object +-- call with at least (object, idx) +function apply_rules(...) + local i + for i = 0, __rules_max - 1 do + if call(__rules[i].fct, arg) then return TRUE end + end + return FALSE +end + +-- Clear the current rules +function clean_ruleset() + __rules_max = 0 + __rules = {} +end + +------ helper fonctions for the GUI + +auto_aux = {} +auto_aux.stack = { n = 0 } +auto_aux.idx = 1 +auto_aux.rule = 1 +function auto_aux:go_right() + if auto_aux.rule[1] and type(auto_aux.rule[1]) == "table" then + tinsert(auto_aux.stack, auto_aux.idx) + tinsert(auto_aux.stack, auto_aux.rule) + auto_aux.rule = auto_aux.rule[1] + auto_aux.idx = 1 + end +end + +function auto_aux:go_left(sel) + local n = getn(auto_aux.stack) + + if n > 0 then + auto_aux.idx = auto_aux.stack[n - 1] + auto_aux.rule = auto_aux.stack[n] + tremove(auto_aux.stack) + tremove(auto_aux.stack) + end +end + +function auto_aux:go_down() + if getn(auto_aux.stack) > 1 then + if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx + 1] then + auto_aux.idx = auto_aux.idx + 1 + auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx] + end + end +end + +function auto_aux:go_up() + if getn(auto_aux.stack) > 1 then + if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx - 1] then + auto_aux.idx = auto_aux.idx - 1 + auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx] + end + end +end + +function auto_aux:scroll_up() + xml.write_off_y = xml.write_off_y - 1 +end + +function auto_aux:scroll_down() + xml.write_off_y = xml.write_off_y + 1 +end + +function auto_aux:scroll_left() + xml.write_off_x = xml.write_off_x + 1 +end + +function auto_aux:scroll_right() + xml.write_off_x = xml.write_off_x - 1 +end + +function auto_aux:adjust_current(sel) + if __rules_max == 0 then return end + + xml.write_off_y = 0 + xml.write_off_x = 0 + auto_aux.idx = 1 + auto_aux.stack = { n = 0 } + auto_aux.rule = __rules[sel].table +end + +function auto_aux:move_up(sel) + if sel > 0 then + local u = __rules[sel - 1] + local d = __rules[sel] + __rules[sel - 1] = d + __rules[sel] = u + return sel - 1 + end + return sel +end + +function auto_aux:move_down(sel) + if sel < __rules_max - 1 then + local u = __rules[sel] + local d = __rules[sel + 1] + __rules[sel + 1] = u + __rules[sel] = d + return sel + 1 + end + return sel +end + +function auto_aux:new_rule(sel, nam, typ, arg) + local r + + + -- nam can also directly be the table itself + if type(nam) == "table" then + r = + { + ["table"] = nam, + ["fct"] = function (object) end + } + elseif typ == "inscribe" then + if arg == "" then + arg = input_box("Inscription?", 79) + end + r = + { + ["table"] = + { + label = "rule", + args = { name = nam, type = typ, inscription = arg, module = game_module }, + }, + ["fct"] = function (object) end + } + else + r = + { + ["table"] = + { + label = "rule", + args = { name = nam, type = typ, module = game_module }, + }, + ["fct"] = function (object) end + } + end + tinsert(__rules, sel, r) + __rules_max = __rules_max + 1 +end + +function auto_aux:rename_rule(sel, nam) + if sel >= 0 and sel < __rules_max then + __rules[sel].table.args.name = nam + end +end + +function auto_aux:save_ruleset() + xml.write = xml.write_file + + print_hook("clean_ruleset()\nadd_ruleset\n[[\n") + local i + for i = 0, __rules_max - 1 do + xml:print_xml(__rules[i].table, '') + end + print_hook("]]\n") + + xml.write = xml.write_screen +end + +function auto_aux:del_self(sel) + if auto_aux.rule.label == "rule" then + tremove(__rules, sel) + __rules_max = __rules_max - 1 + return sel - 1 + else + local idx = auto_aux.idx + auto_aux:go_left(sel) + tremove(auto_aux.rule, idx) + return sel + end +end + +auto_aux.types_desc = +{ + ["and"] = + { + "Check is true if all rules within it are true", + xml:collect([[.........]]), + function () + return xml:collect("") + end, + }, + ["or"] = + { + "Check is true if at least one rule within it is true", + xml:collect([[.........]]), + function () + return xml:collect("") + end, + }, + ["not"] = + { + "Invert the result of its child rule", + xml:collect([[...]]), + function () + return xml:collect("") + end, + }, + ["comment"] = + { + "Comments are meaningless", + xml:collect([[Comment explaining something]]), + function () + local n = input_box("Comment?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["name"] = + { + "Check is true if object name matches name", + xml:collect([[potion of healing]]), + function () + local n = input_box("Object name to match?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["contain"] = + { + "Check is true if object name contains word", + xml:collect([[healing]]), + function () + local n = input_box("Word to find in object name?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["inscribed"] = + { + "Check is true if object inscription contains word", + xml:collect([[=g]]), + function () + local n = input_box("Word to find in object inscription?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["discount"] = + { + "Check is true if object discount is between 2 values", + xml:collect([[]]), + function () + local s = "" + return xml:collect(s) + end, + }, + ["symbol"] = + { + "Check is true if object symbol is ok", + xml:collect([[!]]), + function () + local n = input_box("Symbol to match?", 1) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["status"] = + { + "Check is true if object status is ok", + xml:collect([[good]]), + function () + local n = msg_box("[t]errible, [v]ery bad, [b]ad, [a]verage, [G]ood, [V]ery good, [S]pecial?") + local t = + { + ["t"] = "terrible", + ["v"] = "very bad", + ["b"] = "bad", + ["a"] = "average", + ["G"] = "good", + ["V"] = "very good", + ["S"] = "special", + } + if not t[strchar(n)] then return end + return xml:collect(""..t[strchar(n)].."") + end, + }, + ["state"] = + { + "Check is true if object is identified/unidentified", + xml:collect([[identified]]), + function () + local n = msg_box("[i]dentified, [n]on identified?") + local t = + { + ["i"] = "identified", + ["n"] = "not identified", + } + if not t[strchar(n)] then return end + return xml:collect(""..t[strchar(n)].."") + end, + }, + ["tval"] = + { + "Check is true if object tval(from k_info.txt) is ok", + xml:collect([[55]]), + function () + local n = input_box("Tval to match?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["sval"] = + { + { + "Check is true if object sval(from k_info.txt) is between", + "2 values", + }, + xml:collect([[]]), + function () + local s = "" + return xml:collect(s) + end, + }, + ["race"] = + { + "Check is true if player race is ok", + xml:collect([[dunadan]]), + function () + local n = input_box("Player race to match?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["subrace"] = + { + "Check is true if player subrace is ok", + xml:collect([[vampire]]), + function () + local n = input_box("Player subrace to match?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["class"] = + { + "Check is true if player class is ok", + xml:collect([[sorceror]]), + function () + local n = input_box("Player class to match?", 79) + if n == "" then return end + return xml:collect(""..n.."") + end, + }, + ["level"] = + { + "Check is true if player level is between 2 values", + xml:collect([[]]), + function () + local s = "" + + return xml:collect(s) + end, + }, + ["skill"] = + { + "Check is true if player skill level is between 2 values", + xml:collect([[Divination]]), + function () + local s = "" + + n = input_box("Skill name?", 79) + if n == "" then return end + if find_skill_i(n) == -1 then return end + s = s..n.."" + + return xml:collect(s) + end, + }, + ["ability"] = + { + "Check is true if player has the ability", + xml:collect([[Ammo creation]]), + function() + local n = input_box("Ability name?", 79) + if n == "" then return end + if find_ability(n) == -1 then return end + return xml:collect(""..n.."") + end, + }, +} + +function auto_aux:display_desc(sel) + local d = auto_aux.types_desc[sel][1] + if type(d) == "string" then + c_prt(TERM_WHITE, d, 1, 17) + else + local k, e, i + i = 0 + for k, e in d do + c_prt(TERM_WHITE, e, 1 + i, 17) + i = i + 1 + end + end +end + +function auto_aux:add_child(sel) + -- and contain only one match + if (auto_aux.rule.label == "rule" or auto_aux.rule.label == "not") and auto_aux.rule[1] then return end + + -- Only and can contain + if auto_aux.rule.label ~= "rule" and auto_aux.rule.label ~= "and" and auto_aux.rule.label ~= "or" and auto_aux.rule.label ~= "not" then return end + + -- get it + local r = auto_aux.types_desc[sel][3]() + if not r then return end + + -- Ok add it + tinsert(auto_aux.rule, r[1]) +end + +function auto_aux.regen_ruleset() + local i + for i = 0, __rules_max - 1 do + __rules[i].fct = gen_full_rule(__rules[i].table) + end +end + + +-- Easily add new rules +function easy_add_rule(typ, mode, do_status, obj) + local detect_rule + + if mode == "tval" then + detect_rule = ""..obj.tval.."" + elseif mode == "tsval" then + detect_rule = ""..obj.tval.."" + elseif mode == "name" then + detect_rule = ""..strlower(object_desc(obj, -1, 0)).."" + end + + if do_status == TRUE then + local status = object_status(obj) + if status and not (status == "") then + detect_rule = ""..detect_rule..""..status.."" + end + end + + local rule = ""..detect_rule.."" + auto_aux:new_rule(0, xml:collect(rule)[1], '') + auto_aux.regen_ruleset() + msg_print("Rule added. Please go to the Automatizer screen (press = then T)") + msg_print("to save the modified ruleset.") +end diff --git a/lib/core/building.lua b/lib/core/building.lua new file mode 100644 index 00000000..8e88888a --- /dev/null +++ b/lib/core/building.lua @@ -0,0 +1,15 @@ +__building_actions = {} + +function add_building_action(a) + assert(a.index, "No building action index") + assert(a.action, "No building action action") + __building_actions[a.index] = a.action +end + +function __bact_activate(bact) + if __building_actions[bact] then + return __building_actions[bact]() + end +end + +add_hook_script(HOOK_BUILDING_ACTION, "__bact_activate", "__bact_activate") diff --git a/lib/core/crpt_aux.lua b/lib/core/crpt_aux.lua new file mode 100644 index 00000000..e4f16e2a --- /dev/null +++ b/lib/core/crpt_aux.lua @@ -0,0 +1,243 @@ +-- Core functions for corruptions + +__corruptions = {} +__corruptions_max = 0 +__corruptions_callbacks_max = 0 + +-- Get the corruption +function player.corruption(c, set) + if set then + player.corruptions_aux[c + 1] = set + player.redraw = bor(player.redraw, PR_BASIC) + player.update = bor(player.update, PU_BONUS, PU_TORCH, PU_BODY, PU_POWERS) + if (set == TRUE) and (__corruptions[c].gain) then + __corruptions[c].gain() + end + if (set == FALSE) and (__corruptions[c].lose) then + __corruptions[c].lose() + end + else + return player.corruptions_aux[c + 1] + end +end + +-- Test if we have that corruption +-- We must: +-- 1) have it or be willing to get it +-- 2) have all its dependancies +-- 3) have none of its opposing corruptions +-- 4) pass the possible tests +function test_depend_corrupt(corrupt, can_gain) + local i, c + + if not can_gain then can_gain = FALSE end + + if can_gain == TRUE then + if (player.corruption(corrupt) ~= FALSE) then + return FALSE + end + else + if (player.corruption(corrupt) ~= TRUE) then + return FALSE + end + end + + for c, i in __corruptions[corrupt].depends do + if test_depend_corrupt(c) ~= TRUE then + return FALSE + end + end + + for c, i in __corruptions[corrupt].oppose do + if test_depend_corrupt(c) ~= FALSE then + return FALSE + end + end + + -- are we even allowed to get it? + if __corruptions[corrupt].can_gain and (not __corruptions[corrupt].can_gain()) then + return FALSE + end + + return TRUE +end + +-- Gain a new corruption +function gain_corruption(group) + local i, max + local pos = {} + + -- Get the list of all possible ones + max = 0 + for i = 0, __corruptions_max - 1 do + if __corruptions[i].group == group and test_depend_corrupt(i, TRUE) == TRUE and __corruptions[i].random == TRUE and __corruptions[i].allow() then + pos[max] = i + max = max + 1 + end + end + + -- Ok now get one of them + if (max > 0) then + local ret = rand_int(max) + + player.corruption(pos[ret], TRUE) + cmsg_print(TERM_L_RED, __corruptions[pos[ret]].get_text) + + return pos[ret] + else + return -1 + end +end + +-- Lose an existing corruption +function lose_corruption() + local i, max + local pos = {} + + -- Get the list of all possible ones + max = 0 + for i = 0, __corruptions_max - 1 do + if test_depend_corrupt(i) == TRUE and __corruptions[i].removable == TRUE then + pos[max] = i + max = max + 1 + end + end + + -- Ok now get one of them + if (max > 0) then + local ret = rand_int(max) + + player.corruption(pos[ret], FALSE) + cmsg_print(TERM_L_RED, __corruptions[pos[ret]].lose_text) + + -- Ok now lets see if it broke some dependancies + for i = 0, max - 1 do + if player.corruption(pos[i]) ~= test_depend_corrupt(pos[i]) then + player.corruption(pos[i], FALSE) + cmsg_print(TERM_L_RED, __corruptions[pos[i]].lose_text) + end + end + + return pos[ret] + else + return -1 + end +end + +-- Lose all corruptions (for e.g. Potion of New Life) +function lose_all_corruptions() + local i; + for i = 0, __corruptions_max - 1 do + lose_corruption() + end + return -1 +end + +-- Creates a new corruption +function add_corruption(c) + assert(c.color, "No corruption color") + assert(c.name, "No corruption name") + assert(c.get_text, "No corruption get_text") + assert(c.lose_text, "No corruption lose_text") + assert(c.desc, "No corruption desc") + assert(c.hooks, "Nothing to do for corruption") + if not c.random then c.random = TRUE end + if not c.removable then c.removable = TRUE end + if not c.allow then c.allow = function() return not nil end end + + if c.depends == nil then c.depends = {} end + if c.oppose == nil then c.oppose = {} end + + -- We must make sure the other ones opposes too + local o, i + for o, i in c.oppose do + __corruptions[o].oppose[__corruptions_max] = TRUE + end + + local index, h + for index, h in c.hooks do + add_hook_script(index, "__lua__corrupt_callback"..__corruptions_callbacks_max, "__lua__corrupt_callback"..__corruptions_callbacks_max) + setglobal("__lua__corrupt_callback"..__corruptions_callbacks_max, + function (...) + if test_depend_corrupt(%__corruptions_max) == TRUE then + return call(%h, arg) + end + end + ) + __corruptions_callbacks_max = __corruptions_callbacks_max + 1 + end + + if type(c.desc) == "table" then + local new_desc = "" + for index, h in c.desc do + new_desc = new_desc..h.."\n" + end + c.desc = new_desc + end + + __corruptions[__corruptions_max] = c + __corruptions_max = __corruptions_max + 1 + return (__corruptions_max - 1) +end + + + +---------- Corruption spoiler generator ----------- +function corruption_spoiler_generate() + make_temp_file() + print_hook( +[[~~~~~01|Corruptions (Spoiler) +~~~~~02|Spoilers|Corruptions +#####R=== ToME Corruptions Spoiler === + +Sometimes adventurers become exposed to the dark powers of Morgoth. If they +are unable to resist these powers, they become corrupted. Corruptions can +change their physical or mental abilities, some of which can be good, and +some bad. Most corruptions will affect you permanently, although some only +operate when they are activated (whether by player choice or as a random +event). You can check which corruptions you have in the knowledge screen 6 +(accessed through the '~' menu) or in a character dump. + +#####GGaining and (not) losing corruptions +There are several ways that you can become corrupted. + +You can become corrupted by quaffing a Potion of Corruption or by drinking +from a Fountain of Corruption. Also some strange items can be activated +for corruption. + +Corruptions are permanent. Once you have one, you have it for life. + +]]) + local i, e + for i = 0, __corruptions_max - 1 do + print_hook("[[[[[B"..__corruptions[i].name.."]\n") + print_hook(__corruptions[i].desc) + print_hook("[[[[[GGain message: "..__corruptions[i].get_text.."]\n") + if __corruptions[i].removable == TRUE then + print_hook("[[[[[RLose message: "..__corruptions[i].lose_text.."]\n") + else + print_hook("It is not removable.\n") + end + + local ok + ok = nil + for e, _ in __corruptions[i].depends do ok = not nil end + if ok then + print_hook("It depends on:\n") + for e, _ in __corruptions[i].depends do + print_hook(" "..__corruptions[e].name.."\n") + end + end + ok = nil + for e, _ in __corruptions[i].oppose do ok = not nil end + if ok then + print_hook("It is opposed to:\n") + for e, _ in __corruptions[i].oppose do + print_hook(" "..__corruptions[e].name.."\n") + end + end + print_hook("\n\n") + end + close_temp_file() + msg_print("File created as: "..get_temp_name()) +end diff --git a/lib/core/dungeon.lua b/lib/core/dungeon.lua new file mode 100644 index 00000000..2877838d --- /dev/null +++ b/lib/core/dungeon.lua @@ -0,0 +1,106 @@ +-- Internal lua file in charge of dungeon stuff + +function place_dungeon(y, x, d_idx) + if d_idx then + wild_map(y, x).entrance = 1000 + d_idx + else + wild_map(y, x).entrance = 0 + end +end + +function dungeon(d_idx) + return d_info[1 + d_idx] +end + +function wild_feat(wild) + return wf_info[1 + wild.feat] +end + +function explode_dir(dir) + return ddy[dir + 1], ddx[dir + 1] +end + +function rotate_dir(dir, mov) + if mov > 0 then + if dir == 7 then dir = 8 + elseif dir == 8 then dir = 9 + elseif dir == 9 then dir = 6 + elseif dir == 6 then dir = 3 + elseif dir == 3 then dir = 2 + elseif dir == 2 then dir = 1 + elseif dir == 1 then dir = 4 + elseif dir == 4 then dir = 7 + end + elseif mov < 0 then + if dir == 7 then dir = 4 + elseif dir == 4 then dir = 1 + elseif dir == 1 then dir = 2 + elseif dir == 2 then dir = 3 + elseif dir == 3 then dir = 6 + elseif dir == 6 then dir = 9 + elseif dir == 9 then dir = 8 + elseif dir == 8 then dir = 7 + end + end + + return dir +end + +-- Check if the map is a filename or directly a map +function load_map(map, y, x) + if strsub(map, 1, 5) == "#!map" then + %load_map(TRUE, map, y, x) + else + %load_map(FALSE, map, y, x) + end +end +function get_map_size(map) + if strsub(map, 1, 5) == "#!map" then + return %get_map_size(TRUE, map) + else + return %get_map_size(FALSE, map) + end +end + +-- Place a trap for a specific level +function place_trap(y, x, level) + local old_dun = dun_level + dun_level = level + %place_trap(y, x) + dun_level = old_dun +end + +-- Level generators processing +__level_generators = {} + +function level_generator(t) + assert(t.name, "no generator name") + assert(t.gen, "no generator function") + + if not t.stairs then t.stairs = TRUE end + if not t.monsters then t.monsters = TRUE end + if not t.objects then t.objects = TRUE end + if not t.miscs then t.miscs = TRUE end + + __level_generators[t.name] = t.gen + add_scripted_generator(t.name, t.stairs, t.monsters, t.objects, t.miscs) +end + +function level_generate(name) + assert(__level_generators[name], "Unknown level generator '"..name.."'") + return __level_generators[name]() +end + +--[[ Example +level_generator +{ + ["name"] = "test", + ["gen"] = function() + print("zog") + for i = 1, 30 do + cave(i, 2).feat = 1 + end + return new_player_spot(get_branch()) + end, +} +]] diff --git a/lib/core/gen_idx.lua b/lib/core/gen_idx.lua new file mode 100644 index 00000000..5f3af435 --- /dev/null +++ b/lib/core/gen_idx.lua @@ -0,0 +1,261 @@ +-- Place here the list of files to parse +files = +{ + "birth.txt", + "experien.hlp", + "gods.txt", + "explore.hlp", + "newbie.hlp", + "advanced.hlp", + "help.hlp", + "general.txt", + "whattome.txt", + "dungeon.txt", + "spoiler.hlp", + "g_melkor.txt", + "skills.txt", + "c_bard.txt", + "c_druid.txt", + "c_lorema.txt", + "c_mage.txt", + "c_mimic.txt", + "c_mindcr.txt", + "c_monk.txt", + "c_palad.txt", + "c_posses.txt", + "c_pr_drk.txt", + "c_pr_eru.txt", + "c_pr_man.txt", + "c_symbia.txt", + "c_alchem.txt", + "c_archer.txt", + "c_assass.txt", + "c_axemas.txt", + "c_demono.txt", + "c_geoman.txt", + "c_hafted.txt", + "c_necro.txt", + "c_polear.txt", + "c_ranger.txt", + "c_rogue.txt", + "c_runecr.txt", + "c_sorcer.txt", + "c_swordm.txt", + "c_thaum.txt", + "c_unbel.txt", + "c_warper.txt", + "c_warrio.txt", + "m_meta.txt", + "rm_skel.txt", + "rm_zomb.txt", + "luckspoi.txt", + "m_air.txt", + "dunspoil.txt", + "g_eru.txt", + "g_manwe.txt", + "g_tulkas.txt", + "m_divin.txt", + "m_mimic.txt", + "m_water.txt", + "magic.txt", + "r_drkelf.txt", + "r_dwarf.txt", + "r_elf.txt", + "r_hielf.txt", + "r_hobbit.txt", + "r_pettyd.txt", + "r_wodelf.txt", + "rm_spec.txt", + "tome_faq.txt", + "ability.txt", + "automat.txt", + "c_summon.txt", + "command.txt", + "corspoil.txt", + "debug.txt", + "m_music.txt", + "rm_barb.txt", + "macrofaq.txt", + "m_necrom.txt", + "m_mindcr.txt", + "m_symbio.txt", + "m_thaum.txt", + "magic.hlp", + "m_convey.txt", + "m_fire.txt", + "m_mana.txt", + "m_mind.txt", + "m_nature.txt", + "m_tempo.txt", + "m_udun.txt", + "m_geoman.txt", + "essences.txt", + "r_ent.txt", + "g_yavann.txt", + "defines.txt", + "rm_vamp.txt", + "inscrip.txt", + "m_earth.txt", + "option.txt", + "attack.txt", + "version.txt", + "m_demono.txt", + "r_beorn.txt", + "r_deathm.txt", + "r_rohank.txt", + "r_hafogr.txt", + "r_human.txt", + "r_kobold.txt", + "r_maia.txt", + "r_orc.txt", + "r_thlord.txt", + "r_troll.txt", + "r_yeek.txt", + "rm_class.txt", + "rm_herm.txt", + "rm_lsoul.txt", + "wishing.txt", + "c_priest.txt", + "fatespoi.txt", + "gambling.txt", + "r_dunad.txt", + "r_gnome.txt", + "r_hafelf.txt", + "c_merch.txt", + "spoil_faq.txt", +} + +out_file = "index.txt" + +index = {} + +function parse_file(file) + local fff = openfile(path_build(ANGBAND_DIR_HELP, file), "r") + local line + + line = read(fff, "*l") + while line do + local i, j, anchor, name, subname = strfind(line, "~~~~~(%d+)|([%d%a -]+)|([%d%a -]+)") + if not i then + i, j, anchor, name = strfind(line, "~~~~~(%d+)|([%d%a -]+)") + + subname = nil + end + + if i then + if not index[name] then + index[name] = {} + end + if subname then + tinsert(index[name], { __name__ = subname, __file__ = file, __anchor__ = anchor}) + else + tinsert(index[name], { __name__ = "__primary__", __file__ = file, __anchor__ = anchor}) + end + end + + line = read(fff, "*l") + end + + closefile(fff) +end + +function sort_fct(a, b) + local i, len + + a = a.__name__ + b = b.__name__ + + if strlen(a) > strlen(b) then len = strlen(b) else len = strlen(a) end + + for i = 1, len do + local ac = strbyte(a, i) + local bc = strbyte(b, i) + + if ac < bc then + return not nil + elseif ac > bc then + return nil + end + end + if strlen(a) > strlen(b) then return nil else return not nil end +end + +function generate_index() + local k, e, index_list + for _, e in files do + parse_file(e) + end + + index_list = {} + for k, e in index do + -- Ok either my sort function or lua sort function sucks ass .. + sort(e, sort_fct) + sort(e, sort_fct) + sort(e, sort_fct) + sort(e, sort_fct) + sort(e, sort_fct) + tinsert(index_list, {__name__= k, __table__ = e}) + end + + -- Ok either my sort function or lua sort function sucks ass .. + sort(index_list, sort_fct) + sort(index_list, sort_fct) + sort(index_list, sort_fct) + sort(index_list, sort_fct) + sort(index_list, sort_fct) + index = index_list +end + +function out_link(fff, space, name, file, anchor) + write(fff, space.."*****"..file.."*"..anchor.."["..name.."]\n") +end + +function print_index() + local i, j, c, new_c + local fff = openfile(path_build(ANGBAND_DIR_HELP, out_file), "w") + + write(fff, +[[|||||oy +#####R /----------------------------------------\ +#####R < Help Index > +#####R \----------------------------------------/ + +This is the index of everything in the T.o.M.E. documentation. + +#####BHit a letter key to jump to the entries for that letter. + +Some entries in the index link to the same place as other entries. This is +intentional, so that the information you want is easy to find. + +Don't forget you can browse the help from the *****help.hlp*02[Main menu]. + +#####sSpotted a problem with the help files, or some content thats missing? +#####sContact fearoffours@t-o-m-e.net . + +]]) + + c = ' ' + for i = 1, getn(index) do + new_c = strbyte(index[i].__name__, 1) + if c ~= new_c then + c = new_c + write(fff, "~~~~~"..c.."\n") + write(fff, "*****/"..strchar(c)..out_file.."*"..c.."["..strchar(c).."]\n") + end + for j = 1, getn(index[i].__table__) do + if index[i].__table__[j].__name__ == "__primary__" then + out_link(fff, " ", index[i].__name__, index[i].__table__[j].__file__, index[i].__table__[j].__anchor__) + end + end + for j = 1, getn(index[i].__table__) do + if index[i].__table__[j].__name__ ~= "__primary__" then + out_link(fff, " ", index[i].__table__[j].__name__, index[i].__table__[j].__file__, index[i].__table__[j].__anchor__) + end + end + end + closefile(fff) +end + +generate_index() + +print_index() diff --git a/lib/core/gods.lua b/lib/core/gods.lua new file mode 100644 index 00000000..77e0aad5 --- /dev/null +++ b/lib/core/gods.lua @@ -0,0 +1,40 @@ +-- Gods helper files + +-- Gods structs + +__gods_hook = {} +__gods_callbacks = {} +__gods_callbacks_max = 0 + +function add_god(q) + local i, index, d, z, qq + + assert(q.name, "No god name") + assert(q.desc, "No god desc") + assert(q.hooks, "No god hooks") + + i = add_new_gods(q.name); + + z = 0 + for index, d in q.desc do + desc_god(i, z, d); + z = z + 1 + end + + __gods_hook[i] = q.hooks + for index, d in q.hooks do + add_hook_script(index, "__lua__gods_callback"..__gods_callbacks_max, "__lua__gods_callback"..__gods_callbacks_max) + setglobal("__lua__gods_callback"..__gods_callbacks_max, d) + __gods_callbacks_max = __gods_callbacks_max + 1 + end + if q.data then + for index, d in q.data do + -- Besure it exists + setglobal(index, d) + + -- Make it save & load + add_loadsave(index, d) + end + end + return i +end diff --git a/lib/core/help.lua b/lib/core/help.lua new file mode 100644 index 00000000..a581fe63 --- /dev/null +++ b/lib/core/help.lua @@ -0,0 +1,141 @@ +-- Ingame contextual help + +-- We use our own hook list as to not overburn the hook proccessor +-- with many hooks that would slow down things +-- It would be very meaningless if the option is not even on +__ingame_hooks = {} + +__ingame_help_max = 0 + +function ingame_help(t, ...) + -- This function can also be used to call the callbacks + if type(t) == "string" then + local f = getglobal("__ingame_help_fct_"..t) + call(f, arg) + return + end + + assert(t.desc or t.fct, "no ingame help desc/fct") + assert(t.hook or t.callback, "no ingame help hook/callback") + if t.hook then assert(t.event, "no ingame hepl event needed by hook") end + + -- Set it to only trigger once + setglobal("__ingame_help_activated_"..__ingame_help_max, FALSE) + -- Save/load it + add_loadsave("__ingame_help_activated_"..__ingame_help_max, FALSE) + + if t.hook then + -- If the hok list didnt exist yet, add it + if not __ingame_hooks[t.hook] then + -- Set it to empty, we'll fill it later + __ingame_hooks[t.hook] = {} + -- Add the global hook + add_hooks + { + [t.hook] = function (...) + if option_ingame_help ~= TRUE then return end + local k, e + for k, e in __ingame_hooks[%t.hook] do + if k ~= "n" then + call(e, arg) + end + end + end + } + end + if t.desc then + tinsert(__ingame_hooks[t.hook], + function (...) + local tbl = %t + if getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE then + if call(tbl.event, arg) == TRUE then + local k, e + for k, e in tbl.desc do + msg_print(TERM_YELLOW, e) + end + setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE) + end + end + end + ) + elseif t.fct then + tinsert(__ingame_hooks[t.hook], + function (...) + local tbl = %t + if getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE then + if call(tbl.event, arg) == TRUE then + if tbl.fct() == TRUE then + setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE) + end + end + end + end + ) + end + else + local no_test = FALSE + if t.no_test == TRUE then no_test = TRUE end + if t.desc then + setglobal + ( + "__ingame_help_fct_"..(t.callback), + function (...) + local tbl = %t + if ((option_ingame_help == TRUE) or (%no_test == TRUE)) and (getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE) then + local k, e + for k, e in tbl.desc do + msg_print(TERM_YELLOW, e) + end + setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE) + end + end + ) + elseif t.fct then + setglobal + ( + "__ingame_help_fct_"..(t.callback), + function (...) + local tbl = %t + if ((option_ingame_help == TRUE) or (%no_test == TRUE)) and (getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE) then + if call(tbl.fct, arg) == TRUE then + setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE) + end + end + end + ) + end + end + + __ingame_help_max = __ingame_help_max + 1 +end + +-- Clean up the ingame help seen at birth +add_hooks +{ + [HOOK_BIRTH_OBJECTS] = function() + local i + for i = 0, __ingame_help_max - 1 do + setglobal("__ingame_help_activated_"..i, FALSE) + end + end +} + +function ingame_clean() + local i + for i = 0, __ingame_help_max - 1 do + setglobal("__ingame_help_activated_"..i, FALSE) + end +end + +-- helper function, brings up a doc +function ingame_help_doc(name, anchor) + -- Save screen + screen_save(); + + -- Peruse the help file + if not anchor then anchor = 0 end + show_file(name, 0, -anchor, 0) + + -- Load screen + screen_load() +end diff --git a/lib/core/init.lua b/lib/core/init.lua new file mode 100644 index 00000000..9a9ec1ee --- /dev/null +++ b/lib/core/init.lua @@ -0,0 +1,84 @@ +-- +-- This file is loaded at the initialisation of ToME +-- Load the system functions +-- + +-- Name of globals to save +tome_dofile_anywhere(ANGBAND_DIR_CORE, "load.lua") + +-- 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") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "objects.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "monsters.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "powers.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "building.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "dungeon.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "s_aux.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "crpt_aux.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "mimc_aux.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "quests.lua") +tome_dofile_anywhere(ANGBAND_DIR_CORE, "gods.lua") + +-- Load the ingame contextual help +tome_dofile_anywhere(ANGBAND_DIR_CORE, "help.lua") + +-- let the store specific stuff happen! +tome_dofile_anywhere(ANGBAND_DIR_CORE, "stores.lua") + +-------------------------------------------------------------- +-------------------------------------------------------------- +-------------------------------------------------------------- +-------------Here we load the non vital scripts--------------- +-----------------------from lib/scpt-------------------------- +-------------------------------------------------------------- +-------------------------------------------------------------- +tome_dofile("init.lua") + +-- The dofile functions for each patch +patch_dofile = {} + +-- Now load patches +function load_patches() + scansubdir(ANGBAND_DIR_PATCH) + for i = 0, scansubdir_max - 1 do + if (scansubdir_result[i + 1] ~= ".") and (scansubdir_result[i + 1] ~= "..") then + local dir = path_build(ANGBAND_DIR_PATCH, scansubdir_result[i + 1]) + local file = path_build(dir, "patch.lua") + if file_exist(file) == TRUE then + patch_init = nil + tome_dofile_anywhere(dir, "patch.lua", TRUE) + unset_safe_globals() + if patch_init == nil then + set_safe_globals() + quit("Patch in "..file.." did not include a patch_init() function") + else + set_safe_globals() + + -- create the dofile function + patch_dofile[scansubdir_result[i + 1]] = function(f) + tome_dofile_anywhere(%dir, f, TRUE) + end + + local name, version = patch_init() + if name == nil or version == nil then + quit("Patch in "..file.." did not return valid name or version.\nIt must return name, version") + end + patch_version(name, version) + end + end + end + end +end +load_patches() + +-------------------------------------------------------------- +-------------------------------------------------------------- +-------------------------------------------------------------- +-- +-- Do not thouch after this line +-- +tome_dofile_anywhere(ANGBAND_DIR_CORE, "load2.lua") diff --git a/lib/core/load.lua b/lib/core/load.lua new file mode 100644 index 00000000..9522ec91 --- /dev/null +++ b/lib/core/load.lua @@ -0,0 +1,37 @@ +-- Savefile stuff +-- Do not meddle in the affairs of savefiles for they are subtle and quick to be become incompatible + +__loadsave_name = {} +__loadsave_max = 0 +__loadsave_tmp = 0 + +function add_loadsave(name, default) + assert(name, "No variable name to save") + assert(default, "No default value") + + -- if it is a table we must create many entries + if type(default) == "table" then + for k, e in default do + add_loadsave(name.."."..k, e) + end + else + __loadsave_name[__loadsave_max] = { name = name, default = default } + __loadsave_max = __loadsave_max + 1 + end +end + +-- Example of how to save a table +-- NOTE: { 1, 2, 3 } will NOT work, the key MUST be a string +--[[ +add_loadsave("t", +{ + foo = 7, + tab = { + a = 1, + b = 2, + tab = { + a=1, b=2, c=3, + }, + }, +}) +]] diff --git a/lib/core/load2.lua b/lib/core/load2.lua new file mode 100644 index 00000000..7ea432e9 --- /dev/null +++ b/lib/core/load2.lua @@ -0,0 +1,63 @@ +-- Savefile helpers + +-- function called when a key in the variable part ofthe savefile is read +-- if the key matches what we need, we use it, otehrwise just ignore it +function __savefile_load(key, val) + local index, elem + + for index, elem in __loadsave_name do + if (key == elem.name) then + dostring(elem.name.." = "..val) + end + end +end + +function dump_loadsave() + local k, e + for k, e in __loadsave_name do + msg_print(k.." :: ".. e.name.." ["..e.default.."]") + end +end + +-- called when the game is saved, can only save numbers +-- assosiate a key with them to allow the loading code to recognize them +function __savefile_save() + local index, elem + for index, elem in __loadsave_name do + dostring("__loadsave_tmp = "..elem.name) + save_number_key(elem.name, __loadsave_tmp); + end +end + +register_savefile(__loadsave_max) +add_hook_script(HOOK_LOAD_GAME, "__savefile_load", "__hook_load") +add_hook_script(HOOK_SAVE_GAME, "__savefile_save", "__hook_save") + +-- Parse a flattened(i.e: foo.bar.zog) table path and recrate tables +function reconstruct_table(name) + for i = 1, strlen(name) - 1 do + if strsub(name, i, i) == "." then + local tbl = strsub(name, 1, i - 1) + + if dostring("return "..tbl) == nil then + dostring(tbl.."={}") + end + end + end +end + +-- Automagically set unkown variables, otherwise the savefile code +-- might get VERY upset +do + local k, e + -- We need to be able to check for unknown globals + unset_safe_globals() + for k, e in __loadsave_name do + reconstruct_table(e.name) + if dostring("return "..(e.name)) == nil then + dostring((e.name).." = "..(e.default)) + end + end + -- Now taht we did, we set it back, for it is usefull ;) + set_safe_globals() +end diff --git a/lib/core/mimc_aux.lua b/lib/core/mimc_aux.lua new file mode 100644 index 00000000..c37a869b --- /dev/null +++ b/lib/core/mimc_aux.lua @@ -0,0 +1,95 @@ +-- Mimic shapes helper file + +__mimics = {} +__mimics_max = 1 +__mimics_names = {} + +function add_mimic_shape(t) + assert(t.name, "no mimic name") + assert(t.desc, "no mimic desc") + assert(t.calc, "no mimic calc") + assert(t.level, "no mimic level") + assert(t.duration, "no mimic duration") + + if not t.limit then t.limit = 0 end + + if not t.obj_name then + t.obj_name = t.name + end + + t.show_name = '['..t.name..']' + + -- if it needs hooks, add them + if t.hooks then + add_hooks(t.hooks) + end + + -- Add it in a name to index hash table + __mimics_names[t.name] = __mimics_max + + __mimics[__mimics_max] = t + __mimics_max = __mimics_max + 1 +end + +function resolve_mimic_name(name) + if __mimics_names[name] then + return __mimics_names[name] + else + return -1 + end +end + +function find_random_mimic_shape(level, limit, realm) + local mimic, tries + + tries = 1000 + while tries > 0 do + tries = tries - 1 + mimic = rand_range(1, __mimics_max - 1) + if (not realm) or (__mimics[mimic].realm == realm) then + if limit >= __mimics[mimic].limit then + if (rand_int(__mimics[mimic].level * 3) < level) and (__mimics[mimic].rarity < 100) and (magik(100 - __mimics[mimic].rarity) == TRUE) then + break + end + end + end + end + if tries > 0 then + return mimic + else + return resolve_mimic_name("Abomination") + end +end + +function get_mimic_info(mimic, info) + if not __mimics[mimic] then return 0 end + return __mimics[mimic][info] +end + +function get_mimic_rand_dur(mimic) + return rand_range(__mimics[mimic].duration[1], __mimics[mimic].duration[2]) +end + +function calc_mimic(mimic) + return __mimics[mimic].calc() +end + +function calc_mimic_power(mimic) + if __mimics[mimic].power then __mimics[mimic].power() end +end + +--- Here comes the only vital shape + +add_mimic_shape +{ + ["name"] = "Abomination", + ["obj_name"] = "Abominable Cloak", + ["desc"] = "Abominations are failed experiments of powerful wizards.", + ["realm"] = nil, + ["level"] = 1, + ["rarity"] = 101, + ["duration"] = {20, 100}, + ["calc"] = function () + apply_flags(TR1_SPEED + TR1_STR + TR1_INT + TR1_WIS + TR1_DEX + TR1_CON + TR1_CHR, 0, TR3_AGGRAVATE, 0, 0, 0, -10) + end, +} diff --git a/lib/core/monsters.lua b/lib/core/monsters.lua new file mode 100644 index 00000000..ca2851a0 --- /dev/null +++ b/lib/core/monsters.lua @@ -0,0 +1,16 @@ +-- SYSTEM FILE +-- +-- Monster stuff, do not touch +-- + +function summon_monster(y, x, lev, friend, typ) + if type(typ) == "number" then + if friend == TRUE then + return summon_specific_friendly(y, x, lev, typ, FALSE) + else + return summon_specific(y, x, lev, typ) + end + else + return summon_monster_aux(y, x, lev, friend, typ) + end +end diff --git a/lib/core/objects.lua b/lib/core/objects.lua new file mode 100644 index 00000000..97320b82 --- /dev/null +++ b/lib/core/objects.lua @@ -0,0 +1,45 @@ +-- SYSTEM FILE +-- +-- Lua object funtions +-- + +function create_object(tval, sval) + local obj = new_object() + object_prep(obj, lookup_kind(tval, sval)) + return (obj) +end + +function set_item_tester(tester) + if tolua.type(tester) == "number" then + lua_set_item_tester(tester, "") + end + if tolua.type(tester) == "string" then + lua_set_item_tester(0, tester) + end + if tolua.type(tester) == "function" then + __get_item_hook_default = tester + lua_set_item_tester(0, "__get_item_hook_default") + end +end + +function create_artifact(a_idx) + local obj + local tval, sval + + tval = a_info[a_idx + 1].tval + sval = a_info[a_idx + 1].sval + obj = create_object(tval, sval) + obj.name1 = a_idx + apply_magic(obj, -1, TRUE, TRUE, TRUE) + + return (obj) +end + +function get_kind(obj) + return k_info[obj.k_idx + 1] +end + +function get_item(ask, deny, flags, mask) + set_item_tester(mask) + return get_item_aux(0, ask, deny, flags) +end diff --git a/lib/core/player.lua b/lib/core/player.lua new file mode 100644 index 00000000..3017d94f --- /dev/null +++ b/lib/core/player.lua @@ -0,0 +1,140 @@ +-- SYSTEM FILE +-- +-- Lua player funtions +-- + +-- Gods +function deity(i) + return deity_info[1 + i] +end + +-------- skill stuff --------- + +-- Easy skill access +function skill(i) + return s_info[i + 1] +end + +-- Sart a lasting spell +function player.start_lasting_spell(spl) + player.music_extra = -spl +end + +-- stat mods +function player.modify_stat(stat, inc) + player.stat_add[1 + stat] = player.stat_add[1 + stat] + inc +end + +-- powers mods +function player.add_power(pow) + player.powers[1 + pow] = TRUE +end + +-- easier inventory access +function player.inventory(i) + return player.inventory_real[i + 1] +end + +-- modify mana +-- returns TRUE if there is a pb +function increase_mana(amt) + player.csp = player.csp + amt + player.redraw = bor(player.redraw, PR_MANA) + if (player.csp < 0) then + player.csp = 0 + return TRUE + end + if (player.csp > player.msp) then + player.csp = player.msp + end + return FALSE +end + + +-- Return the coordinates of the player whether in wild or not +function player.get_wild_coord() + if player.wild_mode == TRUE then + return player.py, player.px + else + return player.wilderness_y, player.wilderness_x + end +end + +-- Create a new power +__power_fct = {} +function add_power(p) + local i + + assert(p.name, "No power name!") + assert(p.desc, "No power desc!") + assert(p.desc_get, "No power desc get!") + assert(p.desc_lose, "No power desc lose!") + assert(p.stat, "No power stat!") + assert(p.level, "No power level!") + assert(p.cost, "No power cost!") + assert(p.fail, "No power fail!") + assert(p.power, "No power power!") + + i = add_new_power(p.name, p.desc, p.desc_get, p.desc_lose, p.level, p.cost, p.stat, p.fail) + __power_fct[i] = p.power + return i +end + +function __power_fct_activate(power) + if __power_fct[power] then + __power_fct[power]() + return TRUE + else + return FALSE + end +end + +-- Register in the hook list +add_hook_script(HOOK_ACTIVATE_POWER, "__power_fct_activate", "__power_fct_activate") + + +--- Mkeys + +-- Create a new power +__mkey_fct = {} +function add_mkey(p) + local i + + assert(p.mkey, "No mkey mkey!") + assert(p.fct, "No mkeey fct!") + + __mkey_fct[p.mkey] = p.fct +end + +function __mkey_fct_activate(power) + if __mkey_fct[power] then + __mkey_fct[power]() + return TRUE + else + return FALSE + end +end + +-- Register in the hook list +add_hook_script(HOOK_MKEY, "__mkey_fct_activate", "__mkey_fct_activate") + + +-- Subraces +function subrace(racem) + return race_mod_info[racem + 1] +end + +function subrace_add_power(subrace, power) + for i = 1, 4 do + if subrace.powers[i] == -1 then + subrace.powers[i] = power + return not nil + end + end + return nil +end + +-- Body parts +function player.add_body_part(part, nb) + player.extra_body_parts[part + 1] = player.extra_body_parts[part + 1] + nb +end diff --git a/lib/core/powers.lua b/lib/core/powers.lua new file mode 100644 index 00000000..839a921d --- /dev/null +++ b/lib/core/powers.lua @@ -0,0 +1,105 @@ +-- +-- Helper functions for magic powers +-- + +__magic_powers = {} + +function add_magic(m) + local i, ret + + if type(m.spell_list) ~= "table" then + error("add_magic called without a table") + end + + -- Ok iterate over all the powers to add + local index, p, max + + -- First, count them + max = 0 + for index, p in m.spell_list do + max = max + 1 + end + + -- Now register it + ret = {} + i = new_magic_power(max) + ret.spells = i + ret.max = max + ret.fail_fct = m.fail + if m.stat then + ret.stat = m.stat + else + ret.stat = A_INT + end + if m.get_level then + ret.get_current_level = m.get_level + else + ret.get_current_level = function() + return player.lev + end + end + + -- And add each spells + max = 0 + ret.info = {} + ret.spell = {} + for index, p in m.spell_list do + assert(p.name, "No name for the spell!") + assert(p.desc, "No desc for the spell!") + assert(p.mana, "No mana for the spell!") + assert(p.level, "No level for the spell!") + assert(p.fail, "No fail for the spell!") + assert(p.info, "No info for the spell!") + assert(p.spell, "No spell for the spell!") + + get_magic_power(i, max).name = p.name + get_magic_power(i, max).desc = p.desc + get_magic_power(i, max).mana_cost = p.mana + get_magic_power(i, max).min_lev = p.level + get_magic_power(i, max).fail = p.fail + ret.info[max] = p.info + ret.spell[max] = p.spell + + max = max + 1 + end + + return ret +end + +function __get_magic_info(power) + return __current_magic_power_info[power]() +end + +function execute_magic(m) + local sn, ret + + -- Ask for a spell + __current_magic_power_info = m.info + ret, sn = select_magic_power(0, m.spells, m.max, "__get_magic_info", m.get_current_level(), m.stat) + if (ret == FALSE) then return end + + -- Verify mana needs + if (get_magic_power(m.spells, sn).mana_cost > player.csp) then msg_print("Not enough mana!") return end + + -- Verify failure(second parameter is optional) + if m.fail then + __current_magic_power_fail = m.fail_fct + if (magic_power_sucess(get_magic_power(m.spells, sn), m.stat, "__current_magic_power_fail") == FALSE) then return end + else + if (magic_power_sucess(get_magic_power(m.spells, sn), m.stat) == FALSE) then return end + end + + -- Actually cast the spells + m.spell[sn]() + + -- use up some mana + increase_mana(-get_magic_power(m.spells, sn).mana_cost) +end + +-- Get the level of a power +function get_level_power(s, max, min) + if not max then max = 50 end + if not min then min = 1 end + + return value_scale(s.get_current_level(), 50, max, min) +end diff --git a/lib/core/quests.lua b/lib/core/quests.lua new file mode 100644 index 00000000..dfe9db51 --- /dev/null +++ b/lib/core/quests.lua @@ -0,0 +1,57 @@ +-- Quest helper files + +-- Quest structs + +__quest_hook = {} +__quest_callbacks = {} +__quest_callbacks_max = 0 +__quest_dynamic_desc = {} + +function add_quest(q) + local i, index, d, z, qq + + assert(q.global, "No quest global name") + assert(q.name, "No quest name") + assert(q.desc, "No quest desc") + assert(q.level, "No quest level") + assert(q.hooks, "No quest hooks") + + i = new_quest(q.name); + setglobal(q.global, i) + + -- Make it save & load + add_loadsave("quest("..q.global..").status", QUEST_STATUS_UNTAKEN) + + if type(q.desc) == "table" then + z = 0 + for index, d in q.desc do + quest_desc(i, z, d); + z = z + 1 + end + else + __quest_dynamic_desc[i] = q.desc + quest(i).dynamic_desc = TRUE + end + quest(i).level = q.level + if not q.silent then + quest(i).silent = FALSE + else + quest(i).silent = q.silent + end + __quest_hook[i] = q.hooks + for index, d in q.hooks do + add_hook_script(index, "__lua__quest_callback"..__quest_callbacks_max, "__lua__quest_callback"..__quest_callbacks_max) + setglobal("__lua__quest_callback"..__quest_callbacks_max, d) + __quest_callbacks_max = __quest_callbacks_max + 1 + end + if q.data then + for index, d in q.data do + -- Besure it exists + setglobal(index, d) + + -- Make it save & load + add_loadsave(index, d) + end + end + return i +end diff --git a/lib/core/s_aux.lua b/lib/core/s_aux.lua new file mode 100644 index 00000000..abd1269d --- /dev/null +++ b/lib/core/s_aux.lua @@ -0,0 +1,742 @@ +-- Functions to help with spells, do not touch + +__schools = {} +__schools_num = 0 + +__tmp_spells = {} +__tmp_spells_num = 0 + +function add_school(s) + __schools[__schools_num] = s + + __schools_num = __schools_num + 1 + return (__schools_num - 1) +end + +function finish_school(i) + local s + + s = __schools[i] + assert(s.name, "No school name!") + assert(s.skill, "No school skill!") + + -- Need hooks? + if s.hooks then + add_hooks(s.hooks) + end + + new_school(i, s.name, s.skill) +end + +function add_spell(s) + __tmp_spells[__tmp_spells_num] = s + + __tmp_spells_num = __tmp_spells_num + 1 + return (__tmp_spells_num - 1) +end + +function finish_spell(must_i) + local i, s + + s = __tmp_spells[must_i] + assert(s.name, "No spell name!") + assert(s.school, "No spell school!") + assert(s.level, "No spell level!") + assert(s.mana, "No spell mana!") + if not s.mana_max then s.mana_max = s.mana end + assert(s.fail, "No spell failure rate!") + assert(s.spell, "No spell function!") + if not s.info then s.info = function() return "" end end + assert(s.desc, "No spell desc!") + if not s.random then s.random = SKILL_MAGIC end + if s.lasting then + assert(type(s.lasting) == "function", "Spell lasting is not function") + end + if s.stick then + local k, e + for k, e in s.stick do + if type(k) == "table" then + assert(e.base_level, "Arg no stick base level") + assert(e.max_level, "Arg no stick max level") + end + end + end + + i = new_spell(must_i, s.name) + assert(i == must_i, "ACK ! i != must_i ! please contact the maintainer") + if type(s.school) == "number" then __spell_school[i] = {s.school} + else __spell_school[i] = s.school end + spell(i).mana = s.mana + spell(i).mana_max = s.mana_max + spell(i).fail = s.fail + spell(i).skill_level = s.level + __spell_spell[i] = s.spell + __spell_info[i] = s.info + __spell_desc[i] = s.desc + return i +end + +-- Creates the school books array +__spell_spell = {} +__spell_info = {} +__spell_desc = {} +__spell_school = {} +school_book = {} + +-- Find a spell by name +function find_spell(name) + local i + + i = 0 + while (i < __tmp_spells_num) do + if __tmp_spells[i].name == name then return i end + i = i + 1 + end + return -1 +end + +-- Find if the school is under the influence of a god, returns nil or the level +function get_god_level(sch) + if __schools[sch].gods[player.pgod] then + return (s_info[__schools[sch].gods[player.pgod].skill + 1].value * __schools[sch].gods[player.pgod].mul) / __schools[sch].gods[player.pgod].div + else + return nil + end +end + +-- Change this fct if I want to switch to learnable spells +function get_level_school(s, max, min) + local lvl, sch, index, num, bonus + local allow_spell_power = TRUE + + lvl = 0 + num = 0 + bonus = 0 + + -- No max specified ? assume 50 + if not max then + max = 50 + end + if not min then + min = 1 + end + + -- Do we pass tests? + if __tmp_spells[s].depend then + if __tmp_spells[s].depend() ~= TRUE then + return min, "n/a" + end + end + + for index, sch in __spell_school[s] do + local r, s, p, ok = 0, 0, 0, 0 + + -- Does it require we worship a specific god? + if __schools[sch].god then + if __schools[sch].god ~= player.pgod then + if min then return min, "n/a" + else return 1, "n/a" end + end + end + + -- Take the basic skill value + r = s_info[(school(sch).skill) + 1].value + + -- Do we pass tests? + if __schools[sch].depend then + if __schools[sch].depend() ~= TRUE then + return min, "n/a" + end + end + + -- Are we under sorcery effect ? + if __schools[sch].sorcery then + s = s_info[SKILL_SORCERY + 1].value + end + + -- Are we affected by spell power ? + -- All teh schools must allow it for it to work + if not __schools[sch].spell_power then + allow_spell_power = nil + end + + -- Are we under a god effect ? + if __schools[sch].gods then + p = get_god_level(sch) + if not p then p = 0 end + end + + -- Find the higher + ok = r + if ok < s then ok = s end + if ok < p then ok = p end + + -- Do we need to add a special bonus ? + if __schools[sch].bonus_level then + bonus = bonus + (__schools[sch].bonus_level() * (SKILL_STEP / 10)) + end + + -- All schools must be non zero to be able to use it + if ok == 0 then return min, "n/a" end + + -- Apply it + lvl = lvl + ok + num = num + 1 + end + + -- Add the Spellpower skill as a bonus + if allow_spell_power then + bonus = bonus + (get_skill_scale(SKILL_SPELL, 20) * (SKILL_STEP / 10)) + end + + -- Add bonus from objects + bonus = bonus + (player.to_s * (SKILL_STEP / 10)) + + -- / 10 because otherwise we can overflow a s32b and we can use a u32b because the value can be negative + -- The loss of information should be negligible since 1 skill = 1000 internally + lvl = (lvl / num) / 10 + lvl = lua_get_level(s, lvl, max, min, bonus) + + return lvl, nil +end + +-- This is the function to use when casting through a stick +function get_level_device(s, max, min) + local lvl + + -- No max specified ? assume 50 + if not max then + max = 50 + end + + lvl = s_info[SKILL_DEVICE + 1].value + lvl = lvl + (get_level_use_stick * SKILL_STEP) + + -- Sticks are limited + if lvl - ((spell(s).skill_level + 1) * SKILL_STEP) >= get_level_max_stick * SKILL_STEP then + lvl = (get_level_max_stick + spell(s).skill_level - 1) * SKILL_STEP + end + + -- / 10 because otherwise we can overflow a s32b and we can use a u32b because the value can be negative + -- The loss of information should be negligible since 1 skill = 1000 internally + lvl = lvl / 10 + if not min then + lvl = lua_get_level(s, lvl, max, 1, 0) + else + lvl = lua_get_level(s, lvl, max, min, 0) + end + + return lvl +end + +-- The real get_level, works for schooled magic and for innate powers +get_level_use_stick = -1 +get_level_max_stick = -1 +function get_level(s, max, min) + if type(s) == "number" then + -- Ahah shall we use Magic device instead ? + if get_level_use_stick > -1 then + return get_level_device(s, max, min) + else + local lvl, na = get_level_school(s, max, min) + return lvl + end + else + return get_level_power(s, max, min) + end +end + +-- Can we cast the spell ? +function is_ok_spell(s, obj) + if get_level(s, 50, 0) == 0 then return nil end + if __tmp_spells[s].pval and obj.pval < __tmp_spells[s].pval then return nil end + return 1 +end + +-- Get the amount of mana(or power) needed +function get_mana(s) + return spell(s).mana + get_level(s, spell(s).mana_max - spell(s).mana, 0) +end + +-- Return the amount of power(mana, piety, whatever) for the spell +function get_power(s) + if check_affect(s, "piety", FALSE) then + return player.grace + else + return player.csp + end +end + +-- Return the amount of power(mana, piety, whatever) for the spell +function get_power_name(s) + if check_affect(s, "piety", FALSE) then + return "piety" + else + return "mana" + end +end + +-- Changes the amount of power(mana, piety, whatever) for the spell +function adjust_power(s, x) + if check_affect(s, "piety", FALSE) then + inc_piety(GOD_ALL, x) + else + increase_mana(x) + end +end + +-- Print the book and the spells +function print_book(book, spl, obj) + local x, y, index, sch, size, s + + x = 0 + y = 2 + size = 0 + + -- Hack if the book is 255 it is a random book + if book == 255 then + school_book[book] = {spl} + end + + -- Parse all spells + for index, s in school_book[book] do + local color = TERM_L_DARK + local lvl, na = get_level_school(s, 50, -50) + local xx, sch_str + + if is_ok_spell(s, obj) then + if get_mana(s) > get_power(s) then color = TERM_ORANGE + else color = TERM_L_GREEN end + end + + xx = nil + sch_str = "" + for index, sch in __spell_school[s] do + if xx then + sch_str = sch_str.."/"..school(sch).name + else + xx = 1 + sch_str = sch_str..school(sch).name + end + end + + if na then + c_prt(color, format("%c) %-20s%-16s %3s %4s %3d%s %s", size + strbyte("a"), spell(s).name, sch_str, na, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x) + else + c_prt(color, format("%c) %-20s%-16s %3d %4s %3d%s %s", size + strbyte("a"), spell(s).name, sch_str, lvl, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x) + end + y = y + 1 + size = size + 1 + end + prt(format(" %-20s%-16s Level Cost Fail Info", "Name", "School"), 1, x) + return y +end + +-- Output the describtion when it is used as a spell +function print_spell_desc(s, y) + local index, desc, x + + x = 0 + + if type(__spell_desc[s]) == "string" then c_prt(TERM_L_BLUE, __spell_desc[s], y, x) + else + for index, desc in __spell_desc[s] do + c_prt(TERM_L_BLUE, desc, y, x) + y = y + 1 + end + end + if check_affect(s, "piety", FALSE) then + c_prt(TERM_L_WHITE, "It uses piety to cast.", y, x) + y = y + 1 + end + if not check_affect(s, "blind") then + c_prt(TERM_ORANGE, "It is castable even while blinded.", y, x) + y = y + 1 + end + if not check_affect(s, "confusion") then + c_prt(TERM_ORANGE, "It is castable even while confused.", y, x) + y = y + 1 + end +end + +-- Output the desc when sued as a device +function print_device_desc(s) + local index, desc + + if type(__spell_desc[s]) == "string" then text_out(__spell_desc[s]) + else + for index, desc in __spell_desc[s] do + text_out("\n" .. desc) + end + end +end + +function book_spells_num(book) + local size, index, sch + + size = 0 + + -- Hack if the book is 255 it is a random book + if book == 255 then + return 1 + end + + -- Parse all spells + for index, s in school_book[book] do + size = size + 1 + end + return size +end + +function spell_x(book, spl, s) + if book == 255 then + return spl + else + local i, x, val + + i, val = next(school_book[book], nil) + x = 0 + while x < s do + i, val = next(school_book[book], i) + x = x + 1 + end + return val + end +end + +function spell_in_book(book, spell) + local i, s + + for i, s in school_book[book] do + if s == spell then return TRUE end + end + return FALSE +end + +-- Returns spell chance of failure for spell +function spell_chance(s) + local chance, s_ptr + + s_ptr = spell(s) + + -- Extract the base spell failure rate + if get_level_use_stick > -1 then + chance = lua_spell_device_chance(s_ptr.fail, get_level(s, 50), s_ptr.skill_level) + else + chance = lua_spell_chance(s_ptr.fail, get_level(s, 50), s_ptr.skill_level, get_mana(s), get_power(s), get_spell_stat(s)) + end + + -- Return the chance + return chance +end + +function check_affect(s, name, default) + local s_ptr = __tmp_spells[s] + local a + + if type(s_ptr[name]) == "number" then + a = s_ptr[name] + else + a = default + end + if a == FALSE then + return nil + else + return TRUE + end +end + +-- Returns the stat to use for the spell, INT by default +function get_spell_stat(s) + if not __tmp_spells[s].stat then return A_INT + else return __tmp_spells[s].stat end +end + +function cast_school_spell(s, s_ptr, no_cost) + local use = FALSE + + -- No magic + if (player.antimagic > 0) then + msg_print("Your anti-magic field disrupts any magic attempts.") + return + end + + -- No magic + if (player.anti_magic == TRUE) then + msg_print("Your anti-magic shell disrupts any magic attempts.") + return + end + + -- if it costs something then some condition must be met + if not no_cost then + -- Require lite + if (check_affect(s, "blind")) and ((player.blind > 0) or (no_lite() == TRUE)) then + msg_print("You cannot see!") + return + end + + -- Not when confused + if (check_affect(s, "confusion")) and (player.confused > 0) then + msg_print("You are too confused!") + return + end + + -- Enough mana + if (get_mana(s) > get_power(s)) then + if (get_check("You do not have enough "..get_power_name(s)..", do you want to try anyway?") == FALSE) then return end + end + + -- Invoke the spell effect + if (magik(spell_chance(s)) == FALSE) then + if (__spell_spell[s]() ~= nil) then + use = TRUE + end + else + local index, sch + + -- added because this is *extremely* important --pelpel + if (flush_failure) then flush() end + + msg_print("You failed to get the spell off!") + for index, sch in __spell_school[s] do + if __schools[sch].fail then + __schools[sch].fail(spell_chance(s)) + end + end + use = TRUE + end + else + __spell_spell[s]() + end + + if use == TRUE then + -- Reduce mana + adjust_power(s, -get_mana(s)) + + -- Take a turn + if is_magestaff() == TRUE then energy_use = 80 + else energy_use = 100 end + end + + player.redraw = bor(player.redraw, PR_MANA) + player.window = bor(player.window, PW_PLAYER) +end + + +-- Helper functions +HAVE_ARTIFACT = 0 +HAVE_OBJECT = 1 +HAVE_EGO = 2 +function have_object(mode, type, find, find2) + local o, i, min, max + + max = 0 + min = 0 + if band(mode, USE_EQUIP) == USE_EQUIP then + min = INVEN_WIELD + max = INVEN_TOTAL + end + if band(mode, USE_INVEN) == USE_INVEN then + min = 0 + if max == 0 then max = INVEN_WIELD end + end + + i = min + while i < max do + o = get_object(i) + if o.k_idx ~= 0 then + if type == HAVE_ARTIFACT then + if find == o.name1 then return i end + end + if type == HAVE_OBJECT then + if find2 == nil then + if find == o.k_idx then return i end + else + if (find == o.tval) and (find2 == o.sval) then return i end + end + end + if type == HAVE_EGO then + if (find == o.name2) or (find == o.name2b) then return i end + end + end + i = i + 1 + end + return -1 +end + +-- Can the spell be randomly found(in random books) +function can_spell_random(i) + return __tmp_spells[i].random +end + +-- Find a random spell +function get_random_spell(typ, level) + local spl, tries + + tries = 1000 + while tries > 0 do + tries = tries - 1 + spl = rand_int(__tmp_spells_num) + if (can_spell_random(spl) == typ) and (rand_int(spell(spl).skill_level * 3) < level) then + break + end + end + if tries > 0 then + return spl + else + return -1 + end +end + +-- Execute a lasting spell +function exec_lasting_spell(spl) + assert(__tmp_spells[spl].lasting, "No lasting effect for spell "..__tmp_spells[spl].name.." but called as such") + return __tmp_spells[spl].lasting() +end + +-- Helper function for spell effect to know if they are or not obvious +function is_obvious(effect, old) + if old then + if old == TRUE or effect == TRUE then + return TRUE + else + return FALSE + end + else + return effect + end +end + +-------------------------Sticks------------------------- + +-- Fire off the spell +function activate_stick(spl) + local ret = __spell_spell[spl]() + local charge, obvious + if not ret then + charge = FALSE + obvious = FALSE + else + charge = TRUE + obvious = ret + end + return obvious, charge +end + +----------------------------------- Wand, Staves, Rods specific functions ---------------------------- + +-- Get a spell for a given stick(wand, staff, rod) +function get_random_stick(stick, level) + local spl, tries + + tries = 1000 + while tries > 0 do + tries = tries - 1 + spl = rand_int(__tmp_spells_num) + if __tmp_spells[spl].stick and (type(__tmp_spells[spl].stick[stick]) == "table") then + if (rand_int(spell(spl).skill_level * 3) < level) and (magik(100 - __tmp_spells[spl].stick[stick].rarity) == TRUE) then + break + end + end + end + if tries > 0 then + return spl + else + return -1 + end +end + +-- Get a random base level +function get_stick_base_level(stick, level, spl) + -- Paranoia + if spl < 0 or spl >= __tmp_spells_num or not __tmp_spells[spl].stick[stick] then return 0 end + + local min, max = __tmp_spells[spl].stick[stick].base_level[1], __tmp_spells[spl].stick[stick].base_level[2] + local range = max - min; + + -- Ok the basic idea is to have a max possible level of half the dungeon level + if range * 2 > level then range = level / 2 end + + -- Randomize a bit + range = m_bonus(range, dun_level) + + -- And get the result + return min + range +end + +-- Get a random max level +function get_stick_max_level(stick, level, spl) + -- Paranoia + if spl < 0 or spl >= __tmp_spells_num or not __tmp_spells[spl].stick[stick] then return 0 end + + local min, max = __tmp_spells[spl].stick[stick].max_level[1], __tmp_spells[spl].stick[stick].max_level[2] + local range = max - min; + + -- Ok the basic idea is to have a max possible level of half the dungeon level + if range * 2 > level then range = level / 2 end + + -- Randomize a bit + range = m_bonus(range, dun_level) + + -- And get the result + return min + range +end + +-- Get the number of desired charges +function get_stick_charges(spl) + return __tmp_spells[spl].stick.charge[1] + randint(__tmp_spells[spl].stick.charge[2]); +end + +-- Get activation desc +function get_activation_desc(spl) + local turns + if type(__tmp_spells[spl].activate) == 'number' then + turns = __tmp_spells[spl].activate + else + turns = __tmp_spells[spl].activate[1] .. '+d' .. __tmp_spells[spl].activate[2] + end + return __tmp_spells[spl].desc[1] .. ' every ' .. turns .. ' turns' +end + +-- Compute the timeout of an activation +function get_activation_timeout(spl) + if type(__tmp_spells[spl].activate) == 'number' then + return __tmp_spells[spl].activate + else + return __tmp_spells[spl].activate[1] + randint(__tmp_spells[spl].activate[2]) + end +end + +-- Fire off the spell +function activate_activation(spl, item) + __spell_spell[spl](item) +end + + +------- Add new GF type ---------- +max_gf = MAX_GF +function add_spell_type(t) + t.index = max_gf + max_gf = max_gf + 1 + assert(t.color, "No GF color") + if not t.monster then t.monster = function() end end + if not t.angry then t.angry = function() end end + if not t.object then t.object = function() end end + if not t.player then t.player = function() end end + if not t.grid then t.grid = function() end end + + add_hooks + { + [HOOK_GF_COLOR] = function (gf, new_gfx) + local t = %t + if gf == t.index then return TRUE, t.color[new_gfx + 1] end + end, + [HOOK_GF_EXEC] = function (action, who, gf, dam, rad, y, x, extra) + local t = %t + if t.index == gf then + return t[action](who, dam, rad, y, x, extra) + end + end, + } + return t.index +end diff --git a/lib/core/stores.lua b/lib/core/stores.lua new file mode 100644 index 00000000..d4a63168 --- /dev/null +++ b/lib/core/stores.lua @@ -0,0 +1,32 @@ +-- Take care of all concerning stores +function store_buy_list(t) + assert(type(t) == "table", "store_buy_list got no table") + add_hooks + { + [HOOK_STORE_BUY] = function (index, name, obj) + local tbl = %t + local elt = tbl[index] + if not elt then + elt = tbl[name] + end + if elt then + if elt then + if type(elt) == "function" then + return TRUE, elt(obj) + elseif type(elt) == "table" then + local k, e + for k, e in elt do + if type(e) == "number" then + if obj.tval == e then return TRUE, TRUE end + else + if (obj.tval == e[1]) and (obj.sval >= e[2]) and (obj.sval <= e[3]) then return TRUE, TRUE end + end + end + elseif elt == -1 then + return TRUE, FALSE + end + end + end + end, + } +end diff --git a/lib/core/util.lua b/lib/core/util.lua new file mode 100644 index 00000000..997f1eba --- /dev/null +++ b/lib/core/util.lua @@ -0,0 +1,257 @@ +-- various stuff to make scripters life easier + +-- Beware of the scary undefined globals +function safe_getglobal(x) + local v = rawget(globals(), x) + + if v then + return v + else + error("undefined global variable '"..x.."'") + end +end + +function set_safe_globals() + settagmethod(tag(nil), "getglobal", safe_getglobal) +end +function unset_safe_globals() + settagmethod(tag(nil), "getglobal", nil) +end + +set_safe_globals() + +-- Patch modules +__patch_modules = {} + +function patch_version(name, version) + assert(not __patch_modules[name], "Patch " .. name .. " already loaded!!!") + __patch_modules[name] = version +end + +function patchs_list() + local k, e, first + first = FALSE + for k, e in __patch_modules do + if first == FALSE then print_hook("\n\n [Patch modules]\n") first = TRUE end + print_hook("\n "..k.." version "..e) + end + if first == TRUE then print_hook("\n") end +end + +function patchs_display() + local k, e + for k, e in __patch_modules do + msg_print("Patch: "..k.." version "..e) + end +end + + +-- Better hook interface +__hooks_list_callback = {} +__hooks_list_callback_max = 0 + +function add_hooks(h_table, name_prefix) + local k, e + + if not name_prefix then name_prefix = "" end + for k, e in h_table do + add_hook_script(k, "__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max, "__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max) + setglobal("__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max, e) + __hooks_list_callback_max = __hooks_list_callback_max + 1 + end +end + +-- Wrapper for the real msg_print and cmsg_print +-- it understands if we want color or not +function msg_print(c, m) + if type(c) == "number" then + cmsg_print(c, m) + else + call(%msg_print, { c }) + end +end + +-- Returns the direction of the compass that y2, x2 is from y, x +-- the return value will be one of the following: north, south, +-- east, west, north-east, south-east, south-west, north-west, +-- or "close" if it is within 2 tiles. +function compass(y, x, y2, x2) + local y_axis, x_axis, y_diff, x_diff, compass_dir + + -- is it close to the north/south meridian? + y_diff = y2 - y + + -- determine if y2, x2 is to the north or south of y, x + if (y_diff > -3) and (y_diff < 3) then + y_axis = nil + elseif y2 > y then + y_axis = "south" + else + y_axis = "north" + end + + -- is it close to the east/west meridian? + x_diff = x2 - x + + -- determine if y2, x2 is to the east or west of y, x + if (x_diff > -3) and (x_diff < 3) then + x_axis = nil + elseif x2 > x then + x_axis = "east" + else + x_axis = "west" + end + + -- Maybe it is very close + if ((not x_axis) and (not y_axis)) then compass_dir = "close" + -- Maybe it is (almost) due N/S + elseif not x_axis then compass_dir = y_axis + -- Maybe it is (almost) due E/W + elseif not y_axis then compass_dir = x_axis + -- or if it is neither + else compass_dir = y_axis.."-"..x_axis + end + + return compass_dir +end + +-- Returns a relative approximation of the 'distance' of y2, x2 from y, x. +function approximate_distance(y, x, y2, x2) + local y_diff, x_diff, most_dist + + -- how far to away to the north/south + y_diff = y2 - y + + -- make sure it's a positive integer + if y_diff < 0 then + y_diff = 0 - y_diff + end + + -- how far to away to the east/west + x_diff = x2 - x + + -- make sure it's a positive integer + if x_diff < 0 then + x_diff = 0 - x_diff + end + + -- find which one is the larger distance + if x_diff > y_diff then + most_dist = x_diff + else + most_dist = y_diff + end + + -- how far away then? + if most_dist >= 41 then + how_far = "a very long way" + elseif most_dist >= 25 then + how_far = "a long way" + elseif most_dist >= 8 then + how_far = "quite some way" + else + how_far = "not very far" + end + + return how_far + +end + +-- better timer add function +__timers_callback_max = 0 + +function new_timer(t) + assert(t.delay > 0, "no timer delay") + assert(t.enabled, "no timer enabled state") + assert(t.callback, "no timer callback") + + local timer + if type(t.callback) == "function" then + setglobal("__timers_callback_"..__timers_callback_max, t.callback) + timer = %new_timer("__timers_callback_"..__timers_callback_max, t.delay) + __timers_callback_max = __timers_callback_max + 1 + else + timer = %new_timer(t.callback, t.delay) + end + + timer.enabled = t.enabled + + return timer +end + +-- saves all timer values +function save_timer(name) + add_loadsave(name..".enabled", FALSE) + add_loadsave(name..".delay", 1) + add_loadsave(name..".countdown", 1) +end + + +-- displays a scrolling list +function display_list(y, x, h, w, title, list, begin, sel, sel_color) + local l = create_list(getn(list)) + + for i = 1, getn(list) do + add_to_list(l, i - 1, list[i]) + end + + %display_list(y, x, h, w, title, l, getn(list), begin - 1, sel - 1, sel_color) + + delete_list(l, getn(list)) +end + +-- Easier access to special gene stuff +function set_monster_generation(monster, state) + if type(monster) == "string" then + m_allow_special[test_monster_name(monster) + 1] = state + else + m_allow_special[monster + 1] = state + end +end +function set_object_generation(obj, state) + if type(obj) == "string" then + m_allow_special[test_item_name(obj) + 1] = state + else + m_allow_special[obj + 1] = state + end +end +function set_artifact_generation(obj, state) + m_allow_special[obj + 1] = state +end + +-- Strings +function strcap(str) + if strlen(str) > 1 then + return strupper(strsub(str, 1, 1))..strsub(str, 2) + elseif strlen(str) == 1 then + return strupper(str) + else + return str + end +end + +function msg_format(...) + msg_print(call(format, arg)) +end + +-- Stacks +function stack_push(stack, val) + tinsert(stack, val) +end +function stack_pop(stack) + if getn(stack) >= 1 then + return tremove(stack) + else + error("Tried to unstack an empty stack") + return nil + end +end + +-- A way to check if the game is now running(as opposed to initialization/character gen) +game = {} +add_hooks +{ + [HOOK_GAME_START] = function () + game.started = TRUE + end +} diff --git a/lib/core/xml.lua b/lib/core/xml.lua new file mode 100644 index 00000000..f1188d51 --- /dev/null +++ b/lib/core/xml.lua @@ -0,0 +1,347 @@ +-- 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 == "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, "\n") + else + xml.write(ecol, tab.."\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 -- cgit v1.2.3