diff --git a/game/engines/default/engine/interface/ActorInventory.lua b/game/engines/default/engine/interface/ActorInventory.lua index a151efe2ddf67c7c722a9bf581015a2c727f837e..c8c236b174c3b66b6a8e7018cd11f9f2514f1678 100644 --- a/game/engines/default/engine/interface/ActorInventory.lua +++ b/game/engines/default/engine/interface/ActorInventory.lua @@ -98,7 +98,6 @@ function _M:addObject(inven_id, o) -- Do whatever is needed when wearing this object if inven.worn then self:onWear(o) - o:check("on_wear", self) end self:onAddObject(o) @@ -169,7 +168,6 @@ function _M:removeObject(inven, item, no_unstack) -- Do whatever is needed when taking off this object if inven.worn then self:onTakeoff(o) - o:check("on_takeoff", self) end self:onRemoveObject(o) @@ -377,6 +375,7 @@ end function _M:onWear(o) -- Apply wielder properties o.wielded = {} + o:check("on_wear", self) if o.wielder then for k, e in pairs(o.wielder) do o.wielded[k] = self:addTemporaryValue(k, e) @@ -395,6 +394,7 @@ function _M:onTakeoff(o) end end end + o:check("on_takeoff", self) o.wielded = nil end diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 5f89423bbbcf37ac2f700e5f3a4aed3b052db938..262b204eda21032230d246c40ed76e0d24a2faf5 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -1791,8 +1791,36 @@ function _M:updateModdableTile() end --- Call when an object is worn -function _M:onWear(o) - engine.interface.ActorInventory.onWear(self, o) +-- This doesnt call the base interface onWear, it copies the code because we need some tricky stuff +function _M:onWear(o, bypass_set) + o.wielded = {} + + if o.set_list and not bypass_set then + local list = {} + for i, d in ipairs(o.set_list) do + local po, item, inven_id = self:findInAllInventoriesBy(d[1], d[2]) + if po and self:getInven(inven_id).worn then + list[#list+1] = po + end + end + if #list == #o.set_list then + for i, po in ipairs(list) do + self:onTakeoff(po, true) + po:check("on_set_complete", self) + self:onWear(po, true) + po.set_complete = true + end + o:check("on_set_complete", self) + o.set_complete = true + end + end + + o:check("on_wear", self) + if o.wielder then + for k, e in pairs(o.wielder) do + o.wielded[k] = self:addTemporaryValue(k, e) + end + end if o.talent_on_spell then self.talent_on_spell = self.talent_on_spell or {} @@ -1807,9 +1835,39 @@ function _M:onWear(o) end --- Call when an object is taken off -function _M:onTakeoff(o) +function _M:onTakeoff(o, bypass_set) engine.interface.ActorInventory.onTakeoff(self, o) + if o.set_list and o.set_complete and not bypass_set then + local list = {} + for i, d in ipairs(o.set_list) do + local po, item, inven_id = self:findInAllInventoriesBy(d[1], d[2]) + if po then + self:onTakeoff(po, true) + po:check("on_set_broken", self) + if po._special_set then + for k, id in pairs(po._special_set) do + po:removeTemporaryValue(k, id) + end + po._special_set = nil + end + self:onWear(po, true) + po.set_complete = nil + end + end + o:check("on_set_broken", self) + if o._special_set then + for k, id in pairs(o._special_set) do o:removeTemporaryValue(k, id) end + o._special_set = nil + end + o.set_complete = nil + end + + if o._special_wear then + for k, id in pairs(o._special_wear) do o:removeTemporaryValue(k, id) end + o._special_wear = nil + end + if o.talent_on_spell then self.talent_on_spell = self.talent_on_spell or {} for i = 1, #o.talent_on_spell do diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index b2f37d8107ac438ee659ef1e5c4f4c3999146dfc..d233cd24f7fc5810744a697167cfe49b531e5c7e 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -238,6 +238,11 @@ function _M:getTextualDesc(compare_with) if self.slot_forbid == "OFFHAND" then desc:add("It must be held with both hands.", true) end desc:add(true) + if self.set_list then + desc:add({"color","GREEN"}, "It is part of a set of items.", {"color","LAST"}, true) + if self.set_complete then desc:add({"color","LIGHT_GREEN"}, "The set is complete.", {"color","LAST"}, true) end + end + -- Stop here if unided if not self:isIdentified() then return desc end @@ -867,7 +872,7 @@ function _M:getTextualDesc(compare_with) for tid, data in pairs(talents) do desc:add(talents[tid][3] and {"color","GREEN"} or {"color","WHITE"}, ("Talent on hit(spell): %s (%d%% chance level %d)."):format(self:getTalentFromId(tid).name, talents[tid][1], talents[tid][2]), {"color","LAST"}, true) end - + if self.extra_description then desc:add({"color", "LIGHT_UMBER"}, self.extra_description, {"color","LAST"}, true) end @@ -1111,3 +1116,15 @@ function _M:on_identify() game.player:learnLore(self.unique) end end + +--- Add some special properties right before wearing it +function _M:specialWearAdd(prop, value) + self._special_wear = self._special_wear or {} + self._special_wear[prop] = self:addTemporaryValue(prop, value) +end + +--- Add some special properties right when completting a set +function _M:specialSetAdd(prop, value) + self._special_set = self._special_set or {} + self._special_set[prop] = self:addTemporaryValue(prop, value) +end diff --git a/game/modules/tome/data/general/objects/boss-artifacts.lua b/game/modules/tome/data/general/objects/boss-artifacts.lua index 67375df36f97be6d33b6b502dd2e65072f23b88b..2f707cd170f42617b9194f68b00d034242861255 100644 --- a/game/modules/tome/data/general/objects/boss-artifacts.lua +++ b/game/modules/tome/data/general/objects/boss-artifacts.lua @@ -776,6 +776,34 @@ newEntity{ base = "BASE_GLOVES", define_as = "FLAMEWROUGHT", use_talent = { id = Talents.T_RITCH_FLAMESPITTER_BOLT, level = 2, power = 6 }, } +-- The crystal set +local crystal_activate_pair = function(w, a, who) + local DamageType = require "engine.DamageType" + w.paired = {} + a.paired = {} + + -- The weapon's bonuses + w.talent_on_spell = { {chance=10, talent="T_MANATHRUST", level=3} } + w.combat.talent_on_hit = { T_MANATHRUST = {level=3, chance=10} } + w.paired._special1 = {who, "combat_spellcrit", who:addTemporaryValue("combat_spellcrit", 10)} + w.paired._special2 = {who, "combat_physcrit", who:addTemporaryValue("combat_physcrit", 10)} + w.paired._special3 = {who, "resists_pen", who:addTemporaryValue("resists_pen", {[DamageType.ARCANE]=20})} + -- The armor's bonuses + a.paired._special1 = {who, "stun_immune", who:addTemporaryValue("stun_immune", 0.5)} + a.paired._special2 = {who, "blind_immune", who:addTemporaryValue("blind_immune", 0.5)} + game.logPlayer(who, "#GOLD#As the crystalline weapon and armour are brought together, they begin to emit a constant humming.") +end +local crystal_deactivate_pair = function(w, a, who) + -- Remove the paired bonusese + for k, id in pairs(w) do id[1]:removeTemporaryValue(id[2], id[3]) end + for k, id in pairs(a) do id[1]:removeTemporaryValue(id[2], id[3]) end + w.talent_on_spell = nil + w.combat.talent_on_hit = nil + w.paired = nil + a.paired = nil + game.logPlayer(who, "#GOLD#The humming from the crystalline artifacts fades as they are separated.") +end + newEntity{ base = "BASE_GEM", define_as = "CRYSTAL_FOCUS", power_source = {arcane=true}, unique = true, @@ -805,6 +833,7 @@ newEntity{ base = "BASE_GEM", define_as = "CRYSTAL_FOCUS", o.combat.dam = o.combat.dam * 1.25 o.combat.damtype = engine.DamageType.ARCANE end + o.is_crystalline_weapon = true o.wielder = o.wielder or {} o.wielder.combat_spellpower = 12 o.wielder.combat_dam = 12 @@ -813,11 +842,83 @@ newEntity{ base = "BASE_GEM", define_as = "CRYSTAL_FOCUS", o.wielder.inc_stats[engine.interface.ActorStats.STAT_CON] = 3 o.wielder.inc_damage = o.wielder.inc_damage or {} o.wielder.inc_damage[engine.DamageType.ARCANE] = 10 + + o.set_list = { {"is_crystalline_armor", true} } + o.on_set_complete = function(self, who) + self.talent_on_spell = { {chance=10, talent="T_MANATHRUST", level=3} } + self.combat.talent_on_hit = { T_MANATHRUST = {level=3, chance=10} } + self:specialSetAdd({"wielder","combat_spellcrit"}, 10) + self:specialSetAdd({"wielder","combat_physcrit"}, 10) + self:specialSetAdd({"wielder","resists_pen"}, {[engine.DamageType.ARCANE]=20, [engine.DamageType.PHYSICAL]=15}) + game.logPlayer(who, "#GOLD#As the crystalline weapon and armour are brought together, they begin to emit a constant humming.") + end + o.on_set_broken = function(self, who) + self.talent_on_spell = nil + self.combat.talent_on_hit = nil + game.logPlayer(who, "#GOLD#The humming from the crystalline artifacts fades as they are separated.") + end + + who:sortInven() + who.changed = true + + game.logPlayer(who, "You fix the crystal on the %s and create the %s.", oldname, o:getName{do_color=true}) + end) + end }, +} + +newEntity{ base = "BASE_GEM", define_as = "CRYSTAL_HEART", + power_source = {arcane=true}, + unique = true, + unided_name = "coruscating crystal", + name = "Crystal Heart", subtype = "multi-hued", + color = colors.RED, image="object/ruby.png", + level_range = {35, 42}, + desc = [[This crystal is huge, easily the size of your head. It sparkles brilliantly almost of its own accord.]], + rarity = 250, + cost = 200, + material_level = 5, + + max_power = 1, power_regen = 1, + use_power = { name = "combine with a suit of body armor", power = 1, use = function(self, who, gem_inven, gem_item) + -- Body armour only, can be cloth, light, heavy, or massive though. No clue if o.slot works for this. + who:showInventory("Fuse with which armor?", who:getInven("INVEN"), function(o) return o.type == "armor" and o.slot == "BODY" and not o.egoed and not o.unique end, function(o, item) + local oldname = o:getName{do_color=true} + + -- Remove the gem + who:removeObject(gem_inven, gem_item) + who:sortInven(gem_inven) + + -- Change the weapon... err, armour. No, I'm not copy/pasting here, honest! + o.name = "Crystalline "..o.name:capitalize() + o.unique = o.name + o.no_unique_lore = true + o.is_crystalline_armor = true + + o.wielder = o.wielder or {} + -- This is supposed to add 1 def for crap cloth robes if for some reason you choose it instead of better robes, and then multiply by 1.25. + o.wielder.combat_def = ((o.wielder.combat_def or 0) + 2) * 1.7 + -- Same for armour. Yay crap cloth! + o.wielder.combat_armor = ((o.wielder.combat_armor or 0) + 3) * 1.7 + o.wielder.combat_spellresist = 35 + o.wielder.combat_physresist = 25 + o.wielder.inc_stats = o.wielder.inc_stats or {} + o.wielder.inc_stats[engine.interface.ActorStats.STAT_MAG] = 8 + o.wielder.inc_stats[engine.interface.ActorStats.STAT_CON] = 8 + o.wielder.inc_stats[engine.interface.ActorStats.STAT_LCK] = 12 + o.wielder.resists = o.wielder.resists or {} + o.wielder.resists = { [engine.DamageType.ARCANE] = 35, [engine.DamageType.PHYSICAL] = 15 } + o.wielder.poison_immune = 1 + o.wielder.disease_immune = 1 + + o.set_list = { {"is_crystalline_weapon", true} } + o.on_set_complete = function(self, who) + self:specialSetAdd({"wielder","stun_immune"}, 0.5) + self:specialSetAdd({"wielder","blind_immune"}, 0.5) + end who:sortInven() who.changed = true game.logPlayer(who, "You fix the crystal on the %s and create the %s.", oldname, o:getName{do_color=true}) --- return true end) end }, } diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index 754c2384ab505b74a7e7abd86eaf9c2e8c1345d5..abe489b7f997cbe9692ca0368e05761e220a9beb 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -858,36 +858,6 @@ newEntity{ base = "BASE_KNIFE", }, } --- The Twin blades Moon and Star -local activate_pair = function(moon, star, who) - local DamageType = require "engine.DamageType" - moon.paired = {} - star.paired = {} - - -- The Moon knife's bonuses - moon.paired._special1 = {who, "lite", who:addTemporaryValue("lite", 1)} - moon.paired._special2 = {moon, "combat", moon:addTemporaryValue("combat", {melee_project={[DamageType.RANDOM_CONFUSION]=3}})} - moon.paired._special3 = {who, {"inc_damage", DamageType.DARKNESS}, who:addTemporaryValue({"inc_damage", DamageType.DARKNESS}, 10)} - -- The Star knife's bonuses - star.paired._special1 = {who, "lite", who:addTemporaryValue("lite", 1)} - star.paired._special2 = {star, "combat", star:addTemporaryValue("combat", {melee_project={[DamageType.RANDOM_BLIND]=3}})} - star.paired._special3 = {who, "inc_damage", who:addTemporaryValue("inc_damage", {[DamageType.LIGHT]=10}) } - game.logPlayer(who, "The two blades glow brightly as they are brought close together.") -end - -local deactivate_pair = function(moon, star, who) - -- Remove the paired bonusese - for k, id in pairs(moon.paired) do - id[1]:removeTemporaryValue(id[2], id[3]) - end - for k, id in pairs(star.paired) do - id[1]:removeTemporaryValue(id[2], id[3]) - end - moon.paired = nil - star.paired = nil - game.logPlayer(who, "The light from the two blades fades as they are separated.") -end - newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_MOON", power_source = {arcane=true}, unique = true, @@ -912,22 +882,15 @@ newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_MOON", [DamageType.DARKNESS] = 10, }, }, - activate_pair = activate_pair, - deactivate_pair = deactivate_pair, - on_wear = function(self, who) - -- Look for the Moon knife - local o, item, inven_id = who:findInAllInventoriesBy("define_as", "ART_PAIR_STAR") - if o and who:getInven(inven_id).worn then - self.activate_pair(o, self, who) - end + set_list = { {"define_as","ART_PAIR_STAR"} }, + on_set_complete = function(self, who) + self:specialSetAdd({"wielder","lite"}, 1) + self:specialSetAdd({"combat","melee_project"}, {[engine.DamageType.RANDOM_CONFUSION]=3}) + self:specialSetAdd({"wielder","inc_damage"}, {[engine.DamageType.DARKNESS]=10}) + game.logSeen(who, "#ANTIQUE_WHITE#The two blades glow brightly as they are brought close together.") end, - on_takeoff = function(self, who) - if self.paired then - local o, item, inven_id = who:findInAllInventoriesBy("define_as", "ART_PAIR_STAR") - if o and who:getInven(inven_id).worn then - self.deactivate_pair(o, self, who) - end - end + on_set_broken = function(self, who) + game.logPlayer(who, "#ANTIQUE_WHITE#The light from the two blades fades as they are separated.") end, } @@ -955,23 +918,13 @@ newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_STAR", [DamageType.LIGHT] = 10, }, }, - activate_pair = activate_pair, - deactivate_pair = deactivate_pair, - on_wear = function(self, who) - -- Look for the Moon knife - local o, item, inven_id = who:findInAllInventoriesBy("define_as", "ART_PAIR_MOON") - if o and who:getInven(inven_id).worn then - self.activate_pair(o, self, who) - end - end, - on_takeoff = function(self, who) - if self.paired then - local o, item, inven_id = who:findInAllInventoriesBy("define_as", "ART_PAIR_MOON") - if o and who:getInven(inven_id).worn then - self.deactivate_pair(o, self, who) - end - end + set_list = { {"define_as","ART_PAIR_MOON"} }, + on_set_complete = function(self, who) + self:specialSetAdd({"wielder","lite"}, 1) + self:specialSetAdd({"combat","melee_project"}, {[engine.DamageType.RANDOM_BLIND]=3}) + self:specialSetAdd({"wielder","inc_damage"}, {[engine.DamageType.LIGHT]=10}) end, + } newEntity{ base = "BASE_RING", @@ -1346,9 +1299,9 @@ It was made by Humans for Humans; only they can harness the true power of the ro local Stats = require "engine.interface.ActorStats" local DamageType = require "engine.DamageType" - self.wielded._special1 = {"inc_stats", who:addTemporaryValue("inc_stats", { [Stats.STAT_MAG] = 3, [Stats.STAT_CUN] = 9, }) } - self.wielded._special2 = {"inc_damage", who:addTemporaryValue("inc_damage", {[DamageType.ARCANE]=7}) } - self.wielded._special3 = {"combat_spellcrit", who:addTemporaryValue("combat_spellcrit", 2) } + self:specialWearAdd({"wielder","inc_stats"}, { [Stats.STAT_MAG] = 3, [Stats.STAT_CUN] = 9, }) + self:specialWearAdd({"wielder","inc_damage"}, {[DamageType.ARCANE]=7}) + self:specialWearAdd({"wielder","combat_spellcrit"}, 2) game.logPlayer(who, "#LIGHT_BLUE#You feel as surge of power as you wear the vestments of the old Human Conclave!") end end, @@ -1556,9 +1509,9 @@ newEntity{ base = "BASE_BATTLEAXE", if who.descriptor and who.descriptor.race == "Dwarf" then local Stats = require "engine.interface.ActorStats" - self.wielded._special1 = {"inc_stats", who:addTemporaryValue("inc_stats", { [Stats.STAT_CON] = 4, [Stats.STAT_DEX] = 4, }) } - self.wielded._special2 = {"stun_immune", who:addTemporaryValue("stun_immune", 0.5) } - self.wielded._special3 = {"knockback_immune", who:addTemporaryValue("knockback_immune", 0.5) } + self:specialWearAdd({"wielder","inc_stats"}, { [Stats.STAT_CON] = 7, [Stats.STAT_DEX] = 7, }) + self:specialWearAdd({"wielder","stun_immune"}, 0.5) + self:specialWearAdd({"wielder","knockback_immune"}, 0.5) game.logPlayer(who, "#LIGHT_BLUE#You feel as surge of power as you wield the axe of your ancestors!") end end, diff --git a/game/modules/tome/data/zones/scintillating-caves/npcs.lua b/game/modules/tome/data/zones/scintillating-caves/npcs.lua index f1cf2b1fb06e3e599b6605211ab54eb5a62aa197..01faa622ca7c8b0aed2c3b7b156098f1700517b6 100644 --- a/game/modules/tome/data/zones/scintillating-caves/npcs.lua +++ b/game/modules/tome/data/zones/scintillating-caves/npcs.lua @@ -74,7 +74,8 @@ newEntity{ base="BASE_NPC_CRYSTAL", define_as = "SPELLBLAZE_SIMULACRUM", desc = [[A formation of purple crystal, but where the others could only be described as polyhedral, this construct seems to strangely resemble... you, if you were several orders of magnitude larger.]], killer_message = "and vaporised into nothingness", level_range = {35, nil}, exp_worth = 3, - max_life = 300, life_rating = 15, fixed_rating = true, + max_life = 300, life_rating = 25, fixed_rating = true, + life_regen = 0, mana_regen = 20, stats = { str=15, dex=15, cun=20, mag=35, con=15 }, rank = 4, @@ -82,12 +83,15 @@ newEntity{ base="BASE_NPC_CRYSTAL", define_as = "SPELLBLAZE_SIMULACRUM", infravision = 10, see_invisible = 10, blind_immune = 1, + poison_immune = 1, + disease_immune = 1, instakill_immune = 1, body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - resolvers.drops{chance=100, nb=1, {defined="GOLEM_HEART", random_art_replace={chance=75}} }, + resolvers.drops{chance=100, nb=1, {defined="CRYSTAL_HEART", random_art_replace={chance=75}} }, resolvers.drops{chance=100, nb=3, {tome_drops="boss"} }, + inc_damage = { all=70 }, resolvers.talents{ [Talents.T_ARCANE_POWER]=5, [Talents.T_FLAME]={base=3, every=7, max=5}, @@ -98,7 +102,7 @@ newEntity{ base="BASE_NPC_CRYSTAL", define_as = "SPELLBLAZE_SIMULACRUM", [Talents.T_ICE_STORM]={base=3, every=7, max=5}, }, resolvers.sustains_at_birth(), - resolvers.inscriptions(4, {}), + resolvers.inscriptions(4, "rune"), autolevel = "caster", ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", },