diff --git a/game/engines/default/engine/generator/actor/Random.lua b/game/engines/default/engine/generator/actor/Random.lua index 248d941b628f9df0922f755b22f6b557eb227875..d76ac1a0c317b1513bf256591d15fbac1988846c 100644 --- a/game/engines/default/engine/generator/actor/Random.lua +++ b/game/engines/default/engine/generator/actor/Random.lua @@ -47,22 +47,26 @@ function _M:generate() if self.guardian_level then glevel = self.guardian_level end if self.guardian and self.level.level == glevel then - local m = self.zone:makeEntityByName(self.level, "actor", self.guardian) - if m then - local x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2) - local tries = 0 - while (not m:canMove(x, y) or self.map.room_map[x][y].special) and tries < 100 do - x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2) - tries = tries + 1 - end - if tries < 100 then - self.spots[#self.spots+1] = {x=x, y=y, guardian=true, check_connectivity=(not self.guardian_no_connectivity) and "entrance" or nil} - self.zone:addEntity(self.level, m, "actor", x, y) - print("Guardian allocated: ", self.guardian, m.uid, m.name) - end - else - print("WARNING: Guardian not found: ", self.guardian) + self:generateGuardian(self.guardian) + end +end + +function _M:generateGuardian(guardian) + local m = self.zone:makeEntityByName(self.level, "actor", guardian) + if m then + local x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2) + local tries = 0 + while (not m:canMove(x, y) or self.map.room_map[x][y].special) and tries < 100 do + x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2) + tries = tries + 1 + end + if tries < 100 then + self.spots[#self.spots+1] = {x=x, y=y, guardian=true, check_connectivity=(not self.guardian_no_connectivity) and "entrance" or nil} + self.zone:addEntity(self.level, m, "actor", x, y) + print("Guardian allocated: ", self.guardian, m.uid, m.name) end + else + print("WARNING: Guardian not found: ", self.guardian) end end diff --git a/game/modules/tome/data/chats/temporal-rift-end.lua b/game/modules/tome/data/chats/temporal-rift-end.lua new file mode 100644 index 0000000000000000000000000000000000000000..7b08b4c3edffb868ea427be73ad1caecbc9812e8 --- /dev/null +++ b/game/modules/tome/data/chats/temporal-rift-end.lua @@ -0,0 +1,44 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newChat{ id="welcome", + text = [[#LIGHT_GREEN#*A tall man, glowing like a star appears out of nowhere.*#WHITE# +You destroyed *it* both? I am sorry for my harsh tone when we first met, but repairing time threads is stressful. +I can not stay, I still have much to do, but take this it should help you. +#LIGHT_GREEN#*He disappears again before you can even reply. A rift opens, to Maj'Eyal you hope.*#WHITE#]], + answers = { + {"Ok...", action = function(npc, player) + local o = game.zone:makeEntityByName(game.level, "object", "RUNE_RIFT") + o:identify(true) + game.zone:addEntity(game.level, o, "object") + player:addObject(player.INVEN_INVEN, o) + game.log("The temporal warden gives you: %s.", o:getName{do_color=true}) + + game:setAllowedBuild("chronomancer") + game:setAllowedBuild("chronomancer_temporal_warden", true) + + local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT") + g.change_level = 4 + g.change_zone = "daikara" + game.zone:addEntity(game.level, g, "terrain", player.x, player.y) + end}, + } +} + +return "welcome" diff --git a/game/modules/tome/data/chats/temporal-rift-start.lua b/game/modules/tome/data/chats/temporal-rift-start.lua new file mode 100644 index 0000000000000000000000000000000000000000..0989f87c44c5cd493c6473bf1e1f10920ca61894 --- /dev/null +++ b/game/modules/tome/data/chats/temporal-rift-start.lua @@ -0,0 +1,31 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newChat{ id="welcome", + text = [[#LIGHT_GREEN#*A tall man, glowing like a star appears out of nowhere.*#WHITE# +Oh no, an other *adventurer*! You should not mess with things beyond your comprehension! +Do not meddle in the affairs of Time, for it passes swiftly and easily disrupted! +#LIGHT_GREEN#*He looks at you more closely.*#WHITE# +You seem capable. Help me, fight the abominations while I repair the timeline, it is your only way out!]], + answers = { + {"But what is all th...", action = function(npc, player) game:changeLevel(2) game.player:grantQuest("temporal-rift") end}, + } +} + +return "welcome" diff --git a/game/modules/tome/data/general/grids/void.lua b/game/modules/tome/data/general/grids/void.lua index 314a6206fa2719cd379cccafdfa16033ce7f9a79..26346ba40537e14db843ab458561a857ad039c4e 100644 --- a/game/modules/tome/data/general/grids/void.lua +++ b/game/modules/tome/data/general/grids/void.lua @@ -29,9 +29,18 @@ newEntity{ } newEntity{ - define_as = "SPACE_TURBULENCE", + define_as = "SPACE_TURBULENCE1", name = "space turbulence", - display = '#', color=colors.WHITE, image="terrain/temporal_instability_yellow.png", + display = '#', color=colors.YELLOW, image="terrain/temporal_instability_yellow.png", + always_remember = true, + does_block_move = true, + _noalpha = false, +} + +newEntity{ + define_as = "SPACE_TURBULENCE2", + name = "space turbulence", + display = '#', color=colors.BLUE, image="terrain/temporal_instability_blue.png", always_remember = true, does_block_move = true, _noalpha = false, diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua index bc55e07bea0858aee80319210bc7e429b824376c..9ceb338897897578a1c0e8a3477ba6c3b4bdd821 100644 --- a/game/modules/tome/data/general/npcs/horror.lua +++ b/game/modules/tome/data/general/npcs/horror.lua @@ -674,79 +674,3 @@ newEntity{ base="BASE_NPC_HORROR", define_as = "GRGGLCK_TENTACLE", end end, } - -newEntity{ base="BASE_NPC_HORROR", - name = "Chronolith Twin", color=colors.VIOLET, unique = true, - subtype = "temporal", - desc = [[A six armed creature with black insect-like eyes dressed in robes.]], - level_range = {20, nil}, exp_worth = 2, - max_life = 150, life_rating = 15, fixed_rating = true, - rank = 4, - size_category = 3, - stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 }, - - instakill_immune = 1, - blind_immune = 0.5, - silence_immune = 0.5, - - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - equipment = resolvers.equip{ - {type="weapon", subtype="staff", ego_chance=100, autoreq=true}, - {type="armor", subtype="cloth", ego_chance=100, autoreq=true}, - }, - resolvers.drops{chance=100, nb=4, {ego_chance=100} }, - resolvers.drops{chance=100, nb=1, {unique=true} }, - - resists = { [DamageType.TEMPORAL] = 50, }, - - resolvers.talents{ - [Talents.T_RETHREAD]=3, - [Talents.T_ECHOES_FROM_THE_PAST]=3, - [Talents.T_TURN_BACK_THE_CLOCK]=3, - [Talents.T_HASTE]=3, - [Talents.T_STATIC_HISTORY]=5, - [Talents.T_DIMENSIONAL_STEP]=5, - [Talents.T_FORESIGHT]=5, - }, - - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" }, -} - -newEntity{ base="BASE_NPC_HORROR", - name = "Chronolith Clone", color=colors.VIOLET, unique = true, - subtype = "temporal", - desc = [[A six armed creature with black insect-like eyes dressed in robes.]], - level_range = {20, nil}, exp_worth = 2, - max_life = 150, life_rating = 15, fixed_rating = true, - rank = 4, - size_category = 3, - stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 }, - - instakill_immune = 1, - blind_immune = 0.5, - silence_immune = 0.5, - - body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, - equipment = resolvers.equip{ - {type="weapon", subtype="staff", ego_chance=100, autoreq=true}, - {type="armor", subtype="cloth", ego_chance=100, autoreq=true}, - }, - resolvers.drops{chance=100, nb=4, {ego_chance=100} }, - resolvers.drops{chance=100, nb=1, {unique=true} }, - - resists = { [DamageType.TEMPORAL] = 50, }, - - resolvers.talents{ - [Talents.T_TEMPORAL_WAKE]=3, - [Talents.T_TIME_SKIP]=3, - [Talents.T_TEMPORAL_FUGUE]=3, - [Talents.T_BORROWED_TIME]=3, - [Talents.T_DIMENSIONAL_STEP]=5, - [Talents.T_STATIC_HISTORY]=5, - [Talents.T_FORESIGHT]=5, - }, - - autolevel = "warriormage", - ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" }, -} \ No newline at end of file diff --git a/game/modules/tome/data/general/npcs/telugoroth.lua b/game/modules/tome/data/general/npcs/telugoroth.lua index cac43e1a691995bc991df254c22ceccbd858698e..fa97f6434e2de89444fd268f3aa9c5cdbb0dbed1 100644 --- a/game/modules/tome/data/general/npcs/telugoroth.lua +++ b/game/modules/tome/data/general/npcs/telugoroth.lua @@ -160,7 +160,7 @@ newEntity{ base = "BASE_NPC_TELUGOROTH", resolvers.talents{ [Talents.T_ANOMALY_TEMPORAL_STORM]=1, - [Talents.T_TIME_SKIP]=5, + [Talents.T_QUANTUM_SPIKE]=5, [Talents.T_DIMENSIONAL_STEP]=5, [Talents.T_ANOMALY_REARRANGE]=1, [Talents.T_TEMPORAL_WAKE]=4, diff --git a/game/modules/tome/data/gfx/particles/weather_storm.lua b/game/modules/tome/data/gfx/particles/weather_storm.lua index 366fbd5260b534adb6a1a2c17656972033e60864..7db48595b21e4b92f456a08d45d0b1d2c6f084b7 100644 --- a/game/modules/tome/data/gfx/particles/weather_storm.lua +++ b/game/modules/tome/data/gfx/particles/weather_storm.lua @@ -19,6 +19,9 @@ rdir = math.rad(dir or 110) h_per_tick = math.sin(rdir) +r = r or 1 +g = g or 1 +b = b or 1 local first = true @@ -38,9 +41,9 @@ return { generator = function() dir = rdir, dirv = blur, dira = 0, vel = vel, velv = 0, vela = 0, - r = 1, rv = 0, ra = 0, - g = 1, gv = 0, ga = 0, - b = 1, bv = 0, ba = 0, + r = r, rv = 0, ra = 0, + g = g, gv = 0, ga = 0, + b = b, bv = 0, ba = 0, a = rng.float(alpha[1], alpha[2]), av = 0, aa = 0, } end, }, diff --git a/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png b/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png index 1edb0e1fe26aaeef2d92d16f9bff5918ed54c885..37e57bde9844f4ad0b0e16298776a94f64efcee3 100644 Binary files a/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png and b/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png b/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0d48d554931f105d9aa93fed62654fcc0c8554 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png differ diff --git a/game/modules/tome/data/gfx/terrain/demon_portal2.png b/game/modules/tome/data/gfx/terrain/demon_portal2.png new file mode 100644 index 0000000000000000000000000000000000000000..1e3dd2d690bdd01afa7cdb015aca8d6761ff8f4c Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/demon_portal2.png differ diff --git a/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png b/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..47fd2c3ec74a1a89d699c5e06bb696ef7b105b0c Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png differ diff --git a/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png b/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..fa030de630991a29255fb7c0bfd498413d896641 Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png differ diff --git a/game/modules/tome/data/quests/temporal-rift.lua b/game/modules/tome/data/quests/temporal-rift.lua new file mode 100644 index 0000000000000000000000000000000000000000..84c436f04805014f6f28740af9c15fc579adccf8 --- /dev/null +++ b/game/modules/tome/data/quests/temporal-rift.lua @@ -0,0 +1,36 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +name = "Back and Back and Back to the Future" +desc = function(self, who) + local desc = {} + desc[#desc+1] = "After passing through some kind of time anomaly you met a temporal warden who told you to destroy to abominations of this alternate timeline.\n" + return table.concat(desc, "\n") +end + +on_status_change = function(self, who, status, sub) + if sub then + if self:isCompleted("twin") and self:isCompleted("clone") then + who:setQuestStatus(self.id, engine.Quest.DONE) + local Chat = require "engine.Chat" + local chat = Chat.new("temporal-rift-end", {name="Temporal Warden"}, who) + chat:invoke() + end + end +end diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua index f2d999fd3dabf3d50205fa34759e5a16011d0586..65e221d944e1ef084a6c2fbe977633d391875001 100644 --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua @@ -96,7 +96,7 @@ newTalent{ if tx then target = game.level.map(tx, ty, engine.Map.ACTOR) end - if target then + if target and not target.player then local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) if not hit then game.logSeen(target, "The spell fizzles!") diff --git a/game/modules/tome/data/talents/misc/inscriptions.lua b/game/modules/tome/data/talents/misc/inscriptions.lua index 9487dfbc132d1d9a62c5518670c7705180c89472..f2353377760f1fe7566070d56feac6d039819589 100644 --- a/game/modules/tome/data/talents/misc/inscriptions.lua +++ b/game/modules/tome/data/talents/misc/inscriptions.lua @@ -573,3 +573,107 @@ newInscription{ return ([[%d%% regen over %d turns; %d instant mana]]):format(data.mana + data.inc_stat, data.dur, (data.mana + data.inc_stat) / 20) end, } + +-- This is mostly a copy of Time Skip .. uuuglly +newInscription{ + name = "Rune of the Rift", + type = {"inscriptions/runes", 1}, + points = 1, + is_spell = true, + tactical = { DISABLE = 2, ATTACK = 1 }, + direct_hit = true, + reflectable = true, + requires_target = true, + getDamage = function(self, t) return 150 + self:getWil() * 4 end, + getDuration = function(self, t) return 4 end, + action = function(self, t) + -- Find the target and check hit + local tg = {type="hit", self:getTalentRange(t), talent=t} + local tx, ty, target = self:getTarget(tg) + if not tx or not ty then return nil end + local _ _, tx, ty = self:canProject(tg, tx, ty) + tx, ty = checkBackfire(self, tx, ty) + if tx then + target = game.level.map(tx, ty, engine.Map.ACTOR) + end + if target and not target.player then + local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) + if not hit then + game.logSeen(target, "The spell fizzles!") + return true + end + else + return + end + + -- Keep the Actor from leveling on return + target.forceLevelup = false + -- Create an object to time the effect and store the creature + -- First, clone the terrain that we are replacing + local terrain = game.level.map(game.player.x, game.player.y, engine.Map.TERRAIN) + local temporal_instability = mod.class.Object.new{ + old_feat = game.level.map(target.x, target.y, engine.Map.TERRAIN), + name = "temporal instability", type="temporal", subtype="anomaly", + display = '&', color=colors.LIGHT_BLUE, + temporary = t.getDuration(self, t), + canAct = false, + target = target, + act = function(self) + self:useEnergy() + self.temporary = self.temporary - 1 + if self.temporary <= 0 then + game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat) + game.level:removeEntity(self) + local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true}) + game.zone:addEntity(game.level, self.target, "actor", mx, my) + game.level.map:redisplay() + end + end, + summoner_gain_exp = true, + summoner = self, + } + -- Mixin the old terrain + table.update(temporal_instability, terrain) + -- Now update the display overlay + local overlay = engine.Entity.new{ + -- image = "terrain/wormhole.png", + display = '&', color=colors.LIGHT_BLUE, image="object/temporal_instability.png", + display_on_seen = true, + display_on_remember = true, + } + if not temporal_instability.add_displays then + temporal_instability.add_displays = {overlay} + else + table.append(temporal_instability.add_displays, overlay) + end + + self:project(tg, tx, ty, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) + game.level.map:particleEmitter(tx, ty, 1, "temporal_thrust") + game:playSoundNear(self, "talents/arcane") + -- Remove the target and place the temporal placeholder + if not target.dead then + if target ~= self then + target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)}) + end + game.logSeen(target, "%s has moved forward in time!", target.name:capitalize()) + game.level:removeEntity(target) + game.level:addEntity(temporal_instability) + game.level.map(target.x, target.y, engine.Map.TERRAIN, temporal_instability) + else + game.logSeen(target, "%s has been killed by the temporal energy!", target.name:capitalize()) + end + + self:incParadox(-120) + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local duration = t.getDuration(self, t) + return ([[Inflicts %0.2f temporal damage. If your target survives it will be sent %d turns into the future. + It will also lower your paradox by 120 (if you have any).]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration) + end, + short_info = function(self, t) + return ("%0.2f temporal damage, removed from time %d turns"):format(t.getDamage(self, t), t.getDuration(self, t)) + end, +} diff --git a/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua b/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua new file mode 100644 index 0000000000000000000000000000000000000000..eab621e146f1b6797d16ae630591217aea774131 --- /dev/null +++ b/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua @@ -0,0 +1,35 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +return "New Class: #LIGHT_GREEN#Temporal Warden (Chronomancer)", +[[Time isn't a linear thing, it only appears to be. In truth it's a fabric, the very fabric that holds the universe together. +Like all great powers though there are those who seek to manipulate it and pervert it, but there are also those who seek to uphold rather then destroy. +Those who have learned to bend rather then break the fabric so they can keep the balance against dark or power hungry forces. + +You have been taught the ways of chronomancy by a Temporal Warden and can now create new characters with the #LIGHT_GREEN#Temporal Warden class#WHITE#. + +Temporal Wardens are warriors proficient in both ranged and hand-to-hand combat. +Class features:#YELLOW# +- Duel-wield a medium sized and a small weapon or attack from afar with your ranged weapon skills +- Manipulate space and time to speed yourself up, create wormholes, or even throw an opponent into the future +- See into the future or even travel into the past#WHITE# + +Temporal Warden's use Paradox for their powers. It represents how much damage they've done to the spacetime continuum. +The higher their paradox the greater the effect of their powers but the harder they'll be to control. +]] \ No newline at end of file diff --git a/game/modules/tome/data/zones/daikara/grids.lua b/game/modules/tome/data/zones/daikara/grids.lua index 99b1e654130bea91a26a49d8544b090749e28304..76d090673d9fe7fe332cda3e6087a86446012434 100644 --- a/game/modules/tome/data/zones/daikara/grids.lua +++ b/game/modules/tome/data/zones/daikara/grids.lua @@ -19,3 +19,14 @@ load("/data/general/grids/basic.lua") load("/data/general/grids/mountain.lua") + +newEntity{ + define_as = "RIFT", + name = "Temporal Rift", image = "terrain/rocky_ground.png", add_displays = {class.new{image="terrain/temporal_instability_yellow.png"}}, + display = '&', color_r=255, color_g=0, color_b=220, back_color=colors.VIOLET, + notice = true, + always_remember = true, + show_tooltip = true, + desc = [[The rift leads somewhere ..]], + change_level = 1, change_zone = "temporal-rift", +} diff --git a/game/modules/tome/data/zones/daikara/zone.lua b/game/modules/tome/data/zones/daikara/zone.lua index 9166613ff294f6c2e5510fc5366a320785e4ba5d..e30affffa5b8d40a524e94ac417b9748b90784a0 100644 --- a/game/modules/tome/data/zones/daikara/zone.lua +++ b/game/modules/tome/data/zones/daikara/zone.lua @@ -71,6 +71,20 @@ return { -- Place a lore note on each level game:placeRandomLoreObject("NOTE"..level.level) + -- Temporal rift on level 4 + if level.level == 4 then + local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT") + local x, y = rng.range(0, game.level.map.w-1), rng.range(0, game.level.map.h-1) + local tries = 0 + while game.level.map:checkEntity(x, y, engine.Map.TERRAIN, "block_move") and tries < 100 do + x, y = rng.range(0, game.level.map.w-1), rng.range(0, game.level.map.h-1) + tries = tries + 1 + end + if tries < 100 then + game.zone:addEntity(game.level, g, "terrain", x, y) + end + end + game.state:makeWeather(level, 6, {max_nb=7, chance=1, dir=120, speed={0.1, 0.9}, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"}) end, } diff --git a/game/modules/tome/data/zones/temporal-rift/grids.lua b/game/modules/tome/data/zones/temporal-rift/grids.lua index 31bdb805e7ff8074c1f904b4be52d993c1a3174a..186df6ee5574afad20029a00e1c5c992e5ebba86 100644 --- a/game/modules/tome/data/zones/temporal-rift/grids.lua +++ b/game/modules/tome/data/zones/temporal-rift/grids.lua @@ -18,4 +18,32 @@ -- darkgod@te4.org load("/data/general/grids/basic.lua") +load("/data/general/grids/forest.lua") +load("/data/general/grids/water.lua") +load("/data/general/grids/mountain.lua") +load("/data/general/grids/sand.lua") load("/data/general/grids/void.lua") + +-- Override exits +newEntity { base = "GRASS", define_as = "GRASS_UP_WILDERNESS" } +newEntity { base = "DEEP_OCEAN_WATER", define_as = "WATER_DOWN" } +newEntity { base = "GRASS", define_as = "OLD_FOREST" } + +newEntity{ + define_as = "RIFT", + name = "Temporal Rift", image="terrain/demon_portal2.png", + display = '&', color_r=255, color_g=0, color_b=220, back_color=colors.VIOLET, + notice = true, + always_remember = true, + show_tooltip = true, + desc = [[The rift leads somewhere ..]], + change_level = 1, + change_level_check = function() + if game.level.level > 1 then return end + local p = game.party:findMember{main=true} + local Chat = require "engine.Chat" + local chat = Chat.new("temporal-rift-start", {name="Temporal Warden"}, p) + chat:invoke() + return true + end, +} diff --git a/game/modules/tome/data/zones/temporal-rift/npcs.lua b/game/modules/tome/data/zones/temporal-rift/npcs.lua index f11c4d416a5211f715d8f634821970347da13cac..ae6ab647d8242e3a14e11674791a7ba68bd5e821 100644 --- a/game/modules/tome/data/zones/temporal-rift/npcs.lua +++ b/game/modules/tome/data/zones/temporal-rift/npcs.lua @@ -18,3 +18,189 @@ -- darkgod@te4.org load("/data/general/npcs/telugoroth.lua", rarity(0)) +load("/data/general/npcs/horror.lua", function(e) if e.rarity then e.horror_rarity, e.rarity = e.rarity, nil end end) + +local Talents = require("engine.interface.ActorTalents") + +newEntity{ define_as = "BEN_CRUTHDAR", + type = "humanoid", subtype = "temporal", unique = true, + name = "Ben Cruthdar, the Abomination", + display = "p", color=colors.VIOLET, + desc = [[This crazed madman seems twisted and corrupted by temporal energy, his body shifting and phasing in and out of reality.]], + level_range = {15, nil}, exp_worth = 2, + max_life = 270, life_rating = 17, fixed_rating = true, + max_stamina = 85, + stats = { str=20, dex=15, wil=18, con=20 }, + rank = 3.5, + size_category = 2, + infravision = 20, + instakill_immune = 1, + move_others=true, + + resists = { [DamageType.COLD] = 25 }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, + resolvers.equip{ {type="weapon", subtype="battleaxe", ego_chance=100, autoreq=true}, }, + resolvers.drops{chance=100, nb=1, {ego_chance=100} }, + + resolvers.talents{ + [Talents.T_GLOOM]=3, + [Talents.T_WEAKNESS]=3, + [Talents.T_TORMENT]=3, + [Talents.T_UNNATURAL_BODY]=4, + [Talents.T_DOMINATE]=1, + [Talents.T_BLINDSIDE]=3, + [Talents.T_SLASH]=3, + [Talents.T_RECKLESS_CHARGE]=1, + + [Talents.T_DAMAGE_SMEARING]=5, + [Talents.T_HASTE]=3, + [Talents.T_BORROWED_TIME]=3, + }, + resolvers.sustains_at_birth(), + + autolevel = "warriorwill", + ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", }, + ai_tactic = resolvers.tactic"melee", + resolvers.inscriptions(1, "healing infusion"), + + -- On die needs to make stairs back to the Rift + on_die = function(self, who) + game.level.data.portal_next(self) + end, +} + +newEntity{ define_as = "ABOMINATION_RANTHA", + type = "dragon", subtype = "temporal", unique = true, + name = "Rantha the Abomination", + display = "D", color=colors.VIOLET, + desc = [[Claws and teeth. Ice and death. Dragons are not all extinct it seems... and this one seems to have been corrupted by the time rift.]], + level_range = {15, nil}, exp_worth = 2, + max_life = 220, life_rating = 15, fixed_rating = true, + max_stamina = 85, + max_mana = 200, + stats = { str=25, dex=10, cun=8, mag=20, wil=20, con=20 }, + rank = 3.5, + size_category = 5, + combat_armor = 17, combat_def = 14, + infravision = 20, + instakill_immune = 1, + move_others=true, + + resists = { [DamageType.FIRE] = -20, [DamageType.COLD] = 100, [DamageType.TEMPORAL] = 25, }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, + + -- Frost Treads drop should be changed. + resolvers.drops{chance=100, nb=5, {type="gem"} }, + resolvers.drops{chance=100, nb=10, {type="money"} }, + + resolvers.talents{ + [Talents.T_KNOCKBACK]=3, + + [Talents.T_ICE_STORM]=2, + [Talents.T_FREEZE]=3, + + [Talents.T_ICE_CLAW]=4, + [Talents.T_ICY_SKIN]=3, + [Talents.T_ICE_BREATH]=4, + + [Talents.T_BODY_REVERSION]=4, + [Talents.T_ECHOES_FROM_THE_PAST]=4, + [Talents.T_QUANTUM_SPIKE]=4, + }, + resolvers.sustains_at_birth(), + + autolevel = "warriormage", + ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", }, + resolvers.inscriptions(1, "infusion"), + + -- On die needs to make stairs back to the Rift + on_die = function(self, who) + game.level.data.portal_next(self) + end, +} + +newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_TWIN", + name = "Chronolith Twin", color=colors.VIOLET, unique = true, + subtype = "temporal", + desc = [[A six armed creature with black insect-like eyes dressed in robes.]], + level_range = {20, nil}, exp_worth = 2, + max_life = 150, life_rating = 15, fixed_rating = true, + rank = 4, + size_category = 3, + stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 }, + + instakill_immune = 1, + blind_immune = 0.5, + silence_immune = 0.5, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, + equipment = resolvers.equip{ + {type="weapon", subtype="staff", ego_chance=100, autoreq=true}, + {type="armor", subtype="cloth", ego_chance=100, autoreq=true}, + }, + resolvers.drops{chance=100, nb=4, {ego_chance=100} }, + resolvers.drops{chance=100, nb=1, {unique=true} }, + + resists = { [DamageType.TEMPORAL] = 50, }, + + resolvers.talents{ + [Talents.T_RETHREAD]=3, + [Talents.T_ECHOES_FROM_THE_PAST]=3, + [Talents.T_TURN_BACK_THE_CLOCK]=3, + [Talents.T_HASTE]=3, + [Talents.T_STATIC_HISTORY]=5, + [Talents.T_DIMENSIONAL_STEP]=5, + [Talents.T_FORESIGHT]=5, + }, + + autolevel = "warriormage", + ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" }, + + on_die = function(self, who) + game.player:resolveSource():setQuestStatus("temporal-rift", engine.Quest.COMPLETED, "twin") + end, +} + +newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_CLONE", + name = "Chronolith Clone", color=colors.VIOLET, unique = true, + subtype = "temporal", + desc = [[A six armed creature with black insect-like eyes dressed in robes.]], + level_range = {20, nil}, exp_worth = 2, + max_life = 150, life_rating = 15, fixed_rating = true, + rank = 4, + size_category = 3, + stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 }, + + instakill_immune = 1, + blind_immune = 0.5, + silence_immune = 0.5, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, + equipment = resolvers.equip{ + {type="weapon", subtype="staff", ego_chance=100, autoreq=true}, + {type="armor", subtype="cloth", ego_chance=100, autoreq=true}, + }, + resolvers.drops{chance=100, nb=4, {ego_chance=100} }, + resolvers.drops{chance=100, nb=1, {unique=true} }, + + resists = { [DamageType.TEMPORAL] = 50, }, + + resolvers.talents{ + [Talents.T_TEMPORAL_WAKE]=3, + [Talents.T_QUANTUM_SPIKE]=3, + [Talents.T_TEMPORAL_FUGUE]=3, + [Talents.T_BORROWED_TIME]=3, + [Talents.T_DIMENSIONAL_STEP]=5, + [Talents.T_STATIC_HISTORY]=5, + [Talents.T_FORESIGHT]=5, + }, + + autolevel = "warriormage", + ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" }, + + on_die = function(self, who) + game.player:resolveSource():setQuestStatus("temporal-rift", engine.Quest.COMPLETED, "clone") + end, +} diff --git a/game/modules/tome/data/zones/temporal-rift/objects.lua b/game/modules/tome/data/zones/temporal-rift/objects.lua index 4e7b8c6c6405bb629806f2279b08ee9e45700cf7..ed7b218554658f16d1aaff3a47094b7bb8680414 100644 --- a/game/modules/tome/data/zones/temporal-rift/objects.lua +++ b/game/modules/tome/data/zones/temporal-rift/objects.lua @@ -18,3 +18,14 @@ -- darkgod@te4.org load("/data/general/objects/objects.lua") + +newEntity{ base = "BASE_RUNE", define_as = "RUNE_RIFT", + name = "Rune of the Rift", unique = true, identified = true, + cost = 100, + material_level = 3, + + inscription_data = { + cooldown = 14, + }, + inscription_talent = "RUNE_OF_THE_RIFT", +} diff --git a/game/modules/tome/data/zones/temporal-rift/zone.lua b/game/modules/tome/data/zones/temporal-rift/zone.lua index d61c4d0f5883715e36c9e3ec21fff3eef524401f..01959b45e3b50c4275e0383a7ace214099012a86 100644 --- a/game/modules/tome/data/zones/temporal-rift/zone.lua +++ b/game/modules/tome/data/zones/temporal-rift/zone.lua @@ -19,44 +19,131 @@ return { name = "Temporal Rift", + display_name = function(x, y) + if game.level.level == 2 then return "Temporal Rift: Lumberjack village" + elseif game.level.level == 3 then return "Temporal Rift: Daikara" + elseif game.level.level == 2 then return "Temporal Rift: Lake of Nur" + end + return "Temporal Rift" + end, + variable_zone_name = true, level_range = {16, 30}, level_scheme = "player", max_level = 4, decay = {300, 800}, actor_adjust_level = function(zone, level, e) return zone.base_level + e:getRankLevelAdjust() + level.level-1 + rng.range(-1,2) end, - width = 90, height = 90, - all_remembered = true, + width = 60, height = 25, +-- all_remembered = true, all_lited = true, persistent = "zone", generator = { - map = { - class = "engine.generator.map.Forest", - floor = "VOID", - wall = "SPACE_TURBULENCE", - up = "VOID", - down = "VOID", - }, - actor = { - class = "engine.generator.actor.Random", - nb_npc = {10, 20}, - }, ---[[ - object = { - class = "engine.generator.object.Random", - nb_object = {12, 16}, - }, - trap = { - class = "engine.generator.trap.Random", - nb_trap = {20, 30}, - }, -]] }, + levels = + { + [1] = { generator = { + map = { + class = "engine.generator.map.Forest", + floor = "VOID", + wall = {"SPACE_TURBULENCE1","SPACE_TURBULENCE2"}, + up = "VOID", + down = "RIFT", + edge_entrances = {4,6}, + }, + actor = { + class = "engine.generator.actor.Random", + nb_npc = {15, 25}, + }, + } }, + [2] = { + width = 25, height = 25, + generator = { + map = { + class = "engine.generator.map.Static", + map = "towns/lumberjack-village", + }, + actor = { + class = "engine.generator.actor.Random", + nb_npc = {3, 3}, + }, + } }, + [3] = { + width = 50, height = 50, + generator = { + map = { + class = "engine.generator.map.Roomer", + nb_rooms = 10, + edge_entrances = {2,8}, + rooms = {"forest_clearing","rocky_snowy_trees"}, + ['.'] = "ROCKY_GROUND", + ['T'] = "ROCKY_SNOWY_TREE", + ['#'] = "MOUNTAIN_WALL", + up = "ROCKY_GROUND", + down = "ROCKY_GROUND", + door = "ROCKY_GROUND", + }, + actor = { + class = "engine.generator.actor.Random", + nb_npc = {15, 25}, + }, + } }, + [4] = { + width = 50, height = 50, + generator = { + map = { + class = "engine.generator.map.Static", + map = "zones/lake-nur", + }, + actor = { + class = "engine.generator.actor.Random", + nb_npc = {0, 0}, + }, + } }, + }, + post_process = function(level) - local Map = require "engine.Map" - level.background_particle = require("engine.Particles").new("starfield", 1, {width=Map.viewport.width, height=Map.viewport.height}) + if level.level == 1 then + local Map = require "engine.Map" + level.background_particle = require("engine.Particles").new("starfield", 1, {width=Map.viewport.width, height=Map.viewport.height}) + end + + if level.level <= 2 then + game.state:makeWeather(level, 6, {max_nb=7, chance=1, dir=120, speed={0.1, 0.9}, r=0.2, g=0.4, b=1, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"}) + else + game.state:makeWeather(level, 6, {max_nb=12, chance=1, dir=120, speed={0.1, 0.9}, r=0.2, g=0.4, b=1, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"}) + end + end, + + on_enter = function(lev, old_lev, newzone) + local Dialog = require("engine.ui.Dialog") + if lev == 1 and not game.level.shown_warning then + Dialog:simplePopup("Temporal Rift", "Space and time distort and lose meaning as you pass through the rift. This place is alien.") + game.level.shown_warning = true + elseif lev == 2 and not game.level.shown_warning then + Dialog:simplePopup("Temporal Rift", "This looks like Maj'Eyal's forest but it looks strangely distorbed, beware...") + game.level.shown_warning = true + elseif lev == 3 and not game.level.shown_warning then + Dialog:simplePopup("Temporal Rift", "As you pass the rift you see what seems to be the Daikara mountains, yet they are not.") + game.level.shown_warning = true + require("engine.generator.actor.Random").new(game.zone, game.level.map, game.level, {}):generateGuardian("ABOMINATION_RANTHA") + elseif lev == 4 and not game.level.shown_warning then + Dialog:simplePopup("Temporal Rift", "The peace of this place has been disturbed.") + game.level.shown_warning = true + + local m = game.zone:makeEntityByName(game.level, "actor", "CHRONOLITH_TWIN") + game.zone:addEntity(game.level, m, "actor", 26, 8) + local m = game.zone:makeEntityByName(game.level, "actor", "CHRONOLITH_CLONE") + game.zone:addEntity(game.level, m, "actor", 29, 8) + end + end, + + portal_next = function(npc) + local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT") + game.zone:addEntity(game.level, g, "terrain", npc.x, npc.y) end, background = function(level, x, y, nb_keyframes) + if level.level ~= 1 then return end + local Map = require "engine.Map" for i = 1, nb_keyframes do