diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index e05c241175ef50a84f9ae0a174893b7e20d52483..d618387a17ef3c4c45de7cc0ba967abb63284066 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -1121,6 +1121,7 @@ function _M:countBirthUnlocks() local max = 0 local list = { campaign_infinite_dungeon = true, + campaign_arena = true, undead_ghoul = true, undead_skeleton = true, diff --git a/game/modules/tome/class/PlayerDisplay.lua b/game/modules/tome/class/PlayerDisplay.lua index 48d4a05ecb5a6a9f9830b1296d794ee7372f8559..de163b38884224231f0fb46ffe4bd88f72c33e1b 100644 --- a/game/modules/tome/class/PlayerDisplay.lua +++ b/game/modules/tome/class/PlayerDisplay.lua @@ -265,6 +265,49 @@ function _M:display() h = h + self.font_h end + if game.level and game.level.arena then + h = h + self.font_h + local arena = game.level.arena + self:makeTexture(("-Arena mode-"), x, h, 255, 255, 255) h = h + self.font_h + + if arena.score > world.arena.scores[1].score then + self:makeTexture(("Score(TOP): %d"):format(arena.score), x, h, 255, 255, 100) h = h + self.font_h + else + self:makeTexture(("Score: %d"):format(arena.score), x, h, 255, 255, 255) h = h + self.font_h + end + if arena.currentWave > world.arena.bestWave then + self:makeTexture(("Wave(TOP) %d"):format(arena.currentWave), x, h, 255, 255, 100) + elseif arena.currentWave > world.arena.lastScore.wave then + self:makeTexture(("Wave %d"):format(arena.currentWave), x, h, 100, 100, 255) + else + self:makeTexture(("Wave %d"):format(arena.currentWave), x, h, 255, 255, 255) + end + if arena.event > 0 then + if arena.event == 1 then + self:makeTexture((" [MiniBoss]"), x + (self.font_w * 13), h, 255, 255, 100) + elseif arena.event == 2 then + self:makeTexture((" [Boss]"), x + (self.font_w * 13), h, 255, 0, 255) + elseif arena.event == 3 then + self:makeTexture((" [Final]"), x + (self.font_w * 13), h, 255, 10, 15) + end + end + h = h + self.font_h + if arena.pinch == true then + self:makeTexture(("Bonus: %d (x%.1f)"):format(arena.bonus, arena.bonusMultiplier), x, h, 255, 50, 50) h = h + self.font_h + else + self:makeTexture(("Bonus: %d (x%.1f)"):format(arena.bonus, arena.bonusMultiplier), x, h, 255, 255, 255) h = h + self.font_h + end + if arena.display then + h = h + self.font_h + self:makeTexture(arena.display[1], x, h, 255, 0, 255) h = h + self.font_h + self:makeTexture(" VS", x, h, 255, 0, 255) h = h + self.font_h + self:makeTexture(arena.display[2], x, h, 255, 0, 255) h = h + self.font_h + else + self:makeTexture("Rank: "..arena.printRank(arena.rank, arena.ranks), x, h, 255, 255, 255) h = h + self.font_h + end + h = h + self.font_h + end + h = h + self.font_h for tid, act in pairs(player.sustain_talents) do if act then @@ -285,6 +328,7 @@ function _M:display() self:mouseTooltip(desc, self:makeTexture(("#LIGHT_GREEN#%s(%d)"):format(e.desc,dur), x, h, 255, 255, 255)) h = h + self.font_h end end + end function _M:toScreen() diff --git a/game/modules/tome/class/generator/actor/Arena.lua b/game/modules/tome/class/generator/actor/Arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..f54f5e977855bd34b39cf9a883febcc50cf1f47f --- /dev/null +++ b/game/modules/tome/class/generator/actor/Arena.lua @@ -0,0 +1,484 @@ +-- 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 + +require "engine.class" +local Map = require "engine.Map" +require "engine.Generator" + +module(..., package.seeall, class.inherit(engine.Generator)) + +function _M:init(zone, map, level, spots) + engine.Generator.init(self, zone, map, level, spots) + self.data = level.data.generator.actor + self.level = level + self.rate = self.data.rate + self.max_rate = 5 + self.turn_scale = game.energy_per_tick / game.energy_to_act +end + +function _M:tick() + if game.level.arena.initEvent == true and game.level.arena.lockEvent == false then + game.level.arena.lockEvent = true + if game.level.arena.event == 1 then + local mboss = math.floor(game.level.arena.currentWave / game.level.arena.eventWave) + self:summonMiniboss(mboss) + return + elseif game.level.arena.event == 2 then + local boss = math.floor(game.level.arena.currentWave / (game.level.arena.eventWave * 3)) + self:generateBoss(boss) + return + elseif game.level.arena.event == 3 then + self:generateMaster() + end + end + + if game.level.arena.pinch == true or game.level.turn_counter or game.level.arena.delay > 0 then return + else --Get entity data. + local dangerMin = 1 + math.floor(game.level.arena.currentWave * 0.1) + local dangerMax = dangerMin + (game.level.arena.currentWave ^ game.level.arena.dangerMod) + local en = rng.range(dangerMin, dangerMax) + local t = self:calculateWave() + if t.special then t = t.special(t) end + for i = 1, t.wave do + self:generateOne(t) + end + if t.entitySub then t.entity = t.entitySub self:generateOne(t) end + game.level.arena.delay = self:mitigateDelay(t.delay, en) + end +end + +function _M:mitigateDelay(val, l) + local wave = game.level.arena.currentWave + if wave > 10 and l + 1 < wave then + local reduction = math.floor(wave * 0.1) + if reduction > 3 then reduction = 3 end + end + return val +end + +function _M:summonMiniboss(val) + local miniboss = { + { name = "SKELERAT", wave = 4, entry = 2, display = "Skeletal rats", score = 100, power = 5 }, + { name = "GLADIATOR", wave = 2, entry = 1, display = "Gladiators", score = 150, power = 10 }, + { nil }, + { name = "GOLDCRYSTAL", wave = 4, entry = 3, display = "Golden crystals", score = 250, power = 15 }, + { name = "MASTERSLINGER", wave = 3, entry = 2, display = "Master slingers", score = 350, power = 20 }, + { nil }, + { name = "MASTERALCHEMIST", wave = 1, entry = 1, display = "Master Alchemist", score = 400, power = 25 }, + { name = "MULTIHUEWYRMIC", wave = 1, entry = 1, display = "Multi-hued Wyrmic", score = 400, power = 30 }, + { nil }, + { name = "REAVER", wave = 2, entry = 2, display = "Reaver", score = 800, power = 40 }, + { name = "HEADLESSHORROR", wave = 1, entry = 1, display = "Headless horror", score = 1000, power = 50 }, + } + local e = miniboss[val] or miniboss[1] + for i = 1, e.wave do + self:generateMiniboss(e) + end + game.level.arena.display = {game.player.name.." the "..game.level.arena.printRank(game.level.arena.rank, game.level.arena.ranks), e.display} + local verb = "" + game:playSoundNear(game.player, "talents/teleport") + if e.wave > 1 then verb = " appear!!" else verb = " appears!!" end + game.log("#LIGHT_RED#"..e.display..verb) +end + +function _M:getEntrance(val) + if val == 1 then return game.level.arena.entry.main + elseif val == 3 then return game.level.arena.entry.crystal + else return game.level.arena.entry.corner + end +end + +function _M:generateMiniboss(e) + local m = self.zone:makeEntityByName(self.level, "actor", e.name) + if m then + local entry = self:getEntrance(e.entry) + m.arenaPower = e.power + m.arenaScore = e.score + if m.on_added then m.on_added_orig = m.on_added end + m.on_added = function (self) + if m.on_added_orig then m.on_added_orig(self) end + game.level.arena.danger = game.level.arena.danger + self.arenaPower + game.level.map:particleEmitter(self.x, self.y, 1, "teleport") + game.level.arena.pinchValue = game.level.arena.pinchValue + self.arenaPower + end + if m.on_die then m.on_die_orig = m.on_die end + m.on_die = function (self) + if m.on_die_orig then m.on_die_orig(self) end + game.level.arena.danger = game.level.arena.danger - self.arenaPower + game.level.arena.bonus = game.level.arena.bonus + self.arenaScore + game.level.map:particleEmitter(self.x, self.y, 1, "ball_fire", {radius = 1}) + end + self:place(m, entry, true) + else print("[ARENA] Miniboss error ("..e.display..")") + end +end + +function _M:place(m, entry, elite) + local place = rng.range(1, entry.max) + local x, y = entry[place]() + local tries = 0 + while (not m:canMove(x, y)) and tries < entry.max + 2 do + x, y = entry[place]() + place = rng.range(1, entry.max) + tries = tries + 1 + end + if elite == true then + self.zone:addEntity(self.level, m, "actor", x, y) + elseif tries < entry.max + 2 then + self.zone:addEntity(self.level, m, "actor", x, y) + end +end + + +function _M:generateBoss(val) + local bosses = { + { name = "ARENA_BOSS_RYAL", display = "Ryal the Towering", chat = "arena_boss_ryal", + score = 1500, power = 35, + start = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Boss fight!"}, game.player) + chat:invoke("ryal-entry") + end, + finish = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Victory!!"}, game.player) + chat:invoke("ryal-defeat") + end + }, + { name = "ARENA_BOSS_FRYJIA", display = "Fryjia the Hailstorm", chat= "arena_boss_fryjia", + score = 2500, power = 55, + start = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Boss fight!"}, game.player) + chat:invoke("fryjia-entry") + end, + finish = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Victory!!"}, game.player) + chat:invoke("fryjia-defeat") + end + }, + { name = "ARENA_BOSS_RIALA", display = "Riala the Crimson", chat = "arena_boss_riala", + score = 3500, power = 85, + start = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Boss fight!"}, game.player) + chat:invoke("riala-entry") + end, + finish = function () + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Victory!!"}, game.player) + chat:invoke("riala-defeat") + end + }, + { name = "ARENA_BOSS_VALFREN", display = "Valfren the Rampage", chat = "arena_boss_valfren", + score = 4500, power = 125, + start = function () + game.level.map:setShown(0.3, 0.3, 0.3, 1) + game.level.map:setObscure(0.3*0.6, 0.3*0.6, 0.3*0.6, 1) + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Boss fight!"}, game.player) + chat:invoke("valfren-entry") + end, + finish = function () + game.level.map:setShown(1, 1, 1, 1) + game.level.map:setObscure(1*0.6, 1*0.6, 1*0.6, 1) + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Victory!!"}, game.player) + chat:invoke("valfren-defeat") + end + }, + } + local e = bosses[val] or bosses[1] + local m = self.zone:makeEntityByName(self.level, "actor", e.name) + if m then + m.arenaPower = e.power + m.arenaScore = e.score + m.on_added = function (self) + game.level.arena.danger = game.level.arena.danger + self.arenaPower + game.level.map:particleEmitter(self.x, self.y, 1, "teleport") + game:playSoundNear(game.player, "talents/teleport") + game.level.arena.pinchValue = game.level.arena.pinchValue + self.arenaPower + end + m.on_die = function (self) + game.level.arena.danger = game.level.arena.danger - self.arenaPower + game.level.arena.bonus = game.level.arena.bonus + self.arenaScore + game.level.map:particleEmitter(self.x, self.y, 1, "ball_fire", {radius = 2}) + self.arenaDefeat() + end + e.start() + m.arenaDefeat = e.finish + self.zone:addEntity(self.level, m, "actor", 7, 1) + game.level.arena.display = {game.player.name.." the "..game.level.arena.printRank(game.level.arena.rank, game.level.arena.ranks), e.display} + game.log("#LIGHT_RED#WARNING! "..e.display.." appears!!!") + else print("[ARENA] - Boss error #1! ("..e.display..")") + end +end + +function _M:generateMaster() + local defmaster = true + local master + if game.level.arena.finalWave < 60 then + master = world.arena.master30 or nil + else + master = world.arena.master60 or nil + end + if master then + if master.version[1] == game.__mod_info.version[1] and master.version[2] == game.__mod_info.version[2] and master.version[3] == game.__mod_info.version[3] then + defmaster = false + else + defmaster = true + end + else defmaster = true + end + + if defmaster == true then + local m = self.zone:makeEntityByName(self.level, "actor", "ARENA_BOSS_MASTER_DEFAULT") + if m then + m.on_added = function (self) + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="The final fight!"}, game.player) + chat:invoke("master-entry") + game.level.arena.danger = game.level.arena.danger + 1000 + game.level.map:particleEmitter(self.x, self.y, 3, "teleport") + game:playSoundNear(game.player, "talents/teleport") + game.level.arena.pinchValue = game.level.arena.pinchValue + 100 + end + m.on_die = function (self) + game.level.arena.danger = 0 + game.level.arena.bonus = 100 + game.level.map:particleEmitter(self.x, self.y, 1, "ball_fire", {radius = 5}) + game.level.arena.clear() + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Congratulations!"}, game.player) + chat:invoke("master-defeat") + end + self.zone:addEntity(self.level, m, "actor", 7, 1) + local rank = math.floor(game.level.arena.rank) + game.level.arena.display = {game.player.name.." the "..game.level.arena.printRank(game.level.arena.rank, game.level.arena.ranks), "Rej the Master of Arena"} + game.log("#LIGHT_RED#WARNING! Rej Arkatis, the master of the arena, appears!!!") + else print("[ARENA] - Finale error #1! (Default master error)") + end + else + local m = master + mod.class.NPC.castAs(m) + engine.interface.ActorAI.init(m, m) + m.on_added = function (self) + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="The final fight!"}, game.player) + chat:invoke("master-entry") + game.level.arena.danger = game.level.arena.danger + 1000 + game.level.map:particleEmitter(self.x, self.y, 3, "teleport") + game:playSoundNear(game.player, "talents/teleport") + game.level.arena.pinchValue = game.level.arena.pinchValue + 100 + end + m.on_die = function (self) + game.level.arena.danger = 0 + game.level.arena.bonus = 100 + game.level.map:particleEmitter(self.x, self.y, 1, "ball_fire", {radius = 3}) + game.level.arena.clear() + local Chat = require "engine.Chat" + local chat = Chat.new("arena", {name="Congratulations!"}, game.player) + chat:invoke("master-defeat") + + end + self.zone:addEntity(self.level, m, "actor", 7, 1) + game.level.arena.display = {game.player.name.." the "..game.level.arena.printRank(game.level.arena.rank, game.level.arena.ranks), m.name.." the Master of Arena"} + game.log("#LIGHT_RED#WARNING! "..m.name..", the master of the arena, appears!!!") + end +end + +function _M:calculateWave() +--TODO:Apply some reduction in delay based on current wave. + local foe = { +--Relative cannon fodder. + { entity = { type = "vermin", subtype = "rodent" }, --Wave 1 + wave = 4, power = 2, delay = 3, bonus = 0.1, score = 5, entry = 2 }, + { entity = { type = "insect", subtype = "ant" }, + wave = 2, power = 2, delay = 2, bonus = 0.1, score = 15 , entry = 2 }, + { entity = { type = "animal", subtype = "canine" }, + wave = 2, power = 3, delay = 3, bonus = 0.1 , score = 25, entry = 2 }, + { entity = { name = "white ooze" }, + wave = 1, power = 3, delay = 3, bonus = 0.1, score = 15, entry = 2, + special = function (self) if game.level.arena.currentWave > 10 then self.wave = 2 end if game.level.arena.currentWave > 15 then self.entity = { name = "green ooze" } end return self end }, + { entity = { name = "cutpurse" }, --Wave ~5 + wave = 1, power = 8, delay = 3, bonus = 0.1, score = 40, entry = 1 }, + { entity = { name = "white crystal" }, + wave = 1, power = 5, delay = 4, bonus = 0.5 , score = 100, entry = 3}, + { entity = { name = "slinger" }, + wave = 1, power = 7, delay = 4, bonus = 0.2, score = 100, entry = 1, + special = function (self) if game.level.arena.currentWave > 35 then self.entity = { name = "high slinger" } end return self end }, + { entity = { name = "brown bear" }, + wave = 1, power = 4, delay = 3, bonus = 0.2, score = 100 , entry = 2}, + { entity = { type = "animal", subtype = "snake" }, + wave = 2, power = 4, delay = 3, bonus = 0.1, score = 100 , entry = 2}, + { entity = { type = "humanoid", subtype = "human", name = "rogue" }, --Wave ~10 + wave = 1, power = 6, delay = 3, bonus = 0.2, score = 100 , entry = 1, + special = function (self) if game.level.arena.currentWave > 30 then self.wave = 2 end return self end }, + { entity = { type = "animal", subtype = "bear" }, + wave = 2, power = 4, delay = 5, bonus = 0.3, score = 80, entry = 2 }, + { entity = { type = "vermin", subtype = "ooze" }, + wave = 4, power = 3, delay = 2, bonus = 0.1, score = 15, entry = 2 }, + { entity = { type = "undead", subtype = "skeleton" }, + wave = 1, power = 8, delay = 4, bonus = 0.2, score = 120, entry = 2, + special = function (self) if game.level.arena.currentWave > 15 then self.wave = 2 end return self end }, + { entity = { name = "ghoul" }, + wave = 1, power = 7, delay = 5, bonus = 0.2, score = 150, entry = 1, + special = function (self) if game.level.arena.currentWave > 20 then self.wave, self.entry = 2, 2 end return self end }, + { entity = { type = "humanoid", subtype = "human", name = "alchemist" }, + wave = 1, power = 9, delay = 3, bonus = 0.2, score = 150, entry = 1, + special = function (self) if game.level.arena.currentWave > 25 then self.wave = 2 end return self end }, + { entity = { type = "immovable", subtype = "crystal" }, + wave = 2, power = 5, delay = 3, bonus = 0.5 , score = 100, entry = 3, + special = function (self) if game.level.arena.currentWave > 20 then self.wave = 3 end if game.level.arena.currentWave > 45 then self.wave = 4 end return self end }, + { entity = { type = "giant", subtype = "troll" }, + wave = 1, power = 4, delay = 2, bonus = 0.2 , score = 150, entry = 1 }, + { entity = { name = "blood mage" }, + wave = 1, power = 10, delay = 2, bonus = 0.5 , score = 200, entry = 1 }, + { entity = { name = "honey tree" }, + wave = 1, power = 4, delay = 3, bonus = 0.3 , score = 120, entry = 3, + special = function (self) if game.level.arena.currentWave > 35 then self.wave, self.score = 2, 150 end return self end }, + { entity = { name = "skeleton mage" }, + wave = 1, power = 10, delay = 4, bonus = 0.3, score = 150 , entry = 1, + special = function (self) if game.player.level > 10 then self.wave = 2 end return self end }, + { entity = { name = "high gladiator" }, + wave = 1, power = 6, delay = 1, bonus = 0.3, score = 200, entry = 1, + special = function (self) if game.level.arena.currentWave > 30 then self.wave = 2 end return self end }, + { entity = { name = "wisp" }, + wave = 4, power = 1, delay = 1, bonus = 0.3, score = 50 , entry = 3 }, + { entity = { name = "minotaur" }, + wave = 1, power = 12, delay = 3, bonus = 0.3, score = 150 , entry = 2 }, + { entity = { name = "shadowblade" }, + wave = 1, power = 13, delay = 1, bonus = 0.4, score = 200 , entry = 1 }, + { entity = { name = "orc soldier" }, + wave = 1, power = 10, delay = 4, bonus = 0.3, score = 150 , entry = 1, + special = function (self) if game.level.arena.currentWave > 25 then self.entity = {type = "humanoid", subtype = "orc"} end return self end }, + { entity = { name = "bone giant" }, + wave = 1, power = 11, delay = 3, bonus = 0.3, score = 150 , entry = 2, + special = function (self) if game.player.level > 30 then self.wave, self.power = 2, 8 end return self end }, + { entity = { name = "ziguranth warrior" }, + wave = 2, power = 8, delay = 2, bonus = 0.3, score = 130 , entry = 1 }, + { entity = { name = "orc archer" }, + wave = 2, power = 10, delay = 5, bonus = 0.2, score = 100, entry = 2, + special = function (self) if game.player.level > 25 then self.wave, self.score = 3, 125 end if game.player.level > 35 then self.wave, self.score = 4, 150 end return self end }, + { entity = { name = "hexer" }, + wave = 1, power = 13, delay = 1, bonus = 0.5 , score = 250, entry = 1 }, + { entity = { name = "wisp" }, + wave = 6, power = 1, delay = 1, bonus = 0.3, score = 50 , entry = 3 }, + { entity = { name = "orc assassin" }, + wave = 2, power = 10, delay = 4, bonus = 0.2, score = 200, entry = 2 }, + { entity = { name = "fire wyrmic" }, entitySub = { name = "ice wyrmic"}, + wave = 1, power = 11, delay = 3, bonus = 0.3, score = 250, entry = 1 }, + { entity = { name = "sand wyrmic" }, entitySub = { name = "storm wyrmic" }, + wave = 1, power = 11, delay = 3, bonus = 0.3, score = 250, entry = 1 }, + { entity = { name = "martyr" }, + wave = 1, power = 13, delay = 1, bonus = 0.5 , score = 250, entry = 1 }, + { entity = { type = "vermin", subtype = "sandworm" }, + wave = 1, power = 10, delay = 3, bonus = 0.2, score = 200, entry = 2 }, + { entity = { name = "ritch larva" }, + wave = 2, power = 4, delay = 3, bonus = 0.1, score = 150, entry = 2 }, + { entity = { type = "giant", subtype = "ice" }, + wave = 1, power = 8, delay = 2, bonus = 0.5, score = 300, entry = 1 }, + { entity = { name = "naga myrmidon" }, + wave = 1, power = 9, delay = 2, bonus = 0.3, score = 300, entry = 1, + special = function (self) if game.level.arena.currentWave > 40 then self.wave = 2 end return self end }, + { entity = { name = "quasit" }, + wave = 1, power = 9, delay = 2, bonus = 0.2, score = 300, entry = 2 }, + { entity = { name = "wretchling" }, + wave = 1, power = 10, delay = 3, bonus = 0.3, score = 250, entry = 2, + special = function (self) if game.player.level > 25 then self.wave = 3 end if game.player.level > 35 then self.wave = 4 end return self end }, + { entity = { name = "great gladiator" }, + wave = 2, power = 9, delay = 3, bonus = 0.3, score = 300, entry = 1 }, + { entity = { name = "bloated horror" }, + wave = 2, power = 11, delay = 2, bonus = 0.4, score = 300, entry = 3 }, + { entity = { name = "umber hulk" }, + wave = 1, power = 10, delay = 3, bonus = 0.3, score = 350, entry = 1 }, + { entity = { name = "anorithil" }, + wave = 1, power = 10, delay = 2, bonus = 0.5, score = 450, entry = 1 }, + { entity = { name = "naga tide huntress" }, + wave = 1, power = 10, delay = 2, bonus = 0.3, score = 300, entry = 2 }, + { entity = { name = "ritch hunter" }, + wave = 4, power = 6, delay = 5, bonus = 0.4, score = 250, entry = 2 }, + { entity = { type = "undead", subtype = "wight" }, + wave = 1, power = 11, delay = 2, bonus = 0.5, score = 400, entry = 1 }, + { entity = { name = "sun paladin" }, + wave = 1, power = 12, delay = 2, bonus = 1, score = 450, entry = 1 }, + { entity = { name = "dread" }, + wave = 1, power = 13, delay = 2, bonus = 1, score = 450, entry = 2 }, + { entity = { name = "naga psyren" }, + wave = 1, power = 10, delay = 2, bonus = 0.3, score = 400, entry = 2 }, + { entity = { name = "star crusader" }, + wave = 1, power = 14, delay = 2, bonus = 1, score = 450, entry = 2 }, + + } + local dangerMin = 1 + math.floor(game.level.arena.currentWave * 0.1) + local dangerMax = dangerMin + (game.level.arena.currentWave ^ game.level.arena.dangerMod) + if dangerMax > #foe then dangerMax = #foe end + local val = rng.range(dangerMin, dangerMax) + return foe[val] +end + +function _M:generateOne(e) + local m = self.zone:makeEntity(self.level, "actor", e.entity, nil, true) + if m then + local escort = m.make_escort + m.make_escort = nil + local entry = self:getEntrance(e.entry) + m.arenaPower = e.power * m.level + m.arenaBonusMult = e.bonus + m.arenaScore = e.score + if m.on_added then m.on_added_orig = m.on_added end + if e.entry == 3 then + m.on_added = function (self) + if self.on_added_orig then self.on_added_orig(self) end + game.level.arena.danger = game.level.arena.danger + self.arenaPower + game:playSoundNear(game.player, "talents/teleport") + game.level.map:particleEmitter(self.x, self.y, 0.5, "teleport") + end + else + m.on_added = function (self) + if m.on_added_orig then m.on_added_orig(self) end + game.level.arena.danger = game.level.arena.danger + self.arenaPower + end + end + if m.on_die then m.on_die_orig = m.on_die end + m.on_die = function (self) + if self.on_die_orig then self.on_die_orig(self) end + game.level.arena.danger = game.level.arena.danger - self.arenaPower + if game.level.arena.pinch == false then + game.log("#LIGHT_GREEN#Your score multiplier increases by #WHITE#"..self.arenaBonusMult.."#LIGHT_GREEN#!") + game.level.arena.bonusMultiplier = game.level.arena.bonusMultiplier + self.arenaBonusMult + else + game.level.arena.bonus = game.level.arena.bonus + self.arenaScore + end + game.level.arena.kills = game.level.arena.kills + 1 + if game.level.arena.kills > 5 then + game.level.arena.bonusMultiplier = game.level.arena.bonusMultiplier + 0.1 + game.log("#LIGHT_GREEN#Your score multiplier increases by #WHITE#0.1#LIGHT_GREEN#!") + end + if self.level > game.player.level + 3 then + game.log("#YELLOW#You defeat an experienced enemy!") + local raise = (self.level - game.player.level) + 0.03 + if raise > 0.5 then raise = 0.5 end + game.level.arena.raiseRank(raise) + end + end + self:place(m, entry, false) + end +end diff --git a/game/modules/tome/data/achievements/arena.lua b/game/modules/tome/data/achievements/arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..19a7232c25db8e8ecd436a87c0ceeaa2c1ce98f1 --- /dev/null +++ b/game/modules/tome/data/achievements/arena.lua @@ -0,0 +1,58 @@ +-- 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 + +newAchievement{ + name = "The Arena", + desc = [[Unlocked Arena mode.]], +} + +newAchievement{ + name = "Arena Battler 20", + desc = [[Got to wave 20 in the arena.]], +} + +newAchievement{ + name = "Arena Battler 50", + desc = [[Got to wave 50 in the arena.]], +} + +newAchievement{ + name = "Almost Master of Arena", + desc = [[Became the new master of the arena in 30-wave mode.]], +} + +newAchievement{ + name = "Master of Arena", + desc = [[Became the new master of the arena in 60-wave mode.]], +} + +newAchievement{ + name = "XXX the Destroyer", + desc = [[Earned the rank of Destroyer in the arena.]], +} + +newAchievement{ + name = "Grand Master", + desc = [[Earned the rank of Grand Master in the arena.]], +} + +newAchievement{ + name = "Ten at one blow", + desc = [[Killed ten enemies in a single turn, in the arena.]], +} diff --git a/game/modules/tome/data/birth/descriptors.lua b/game/modules/tome/data/birth/descriptors.lua index 0a0a5674cc47763b44bc5f8fa20c37cf44dfdae7..71422632420bd8c03721dd7e334e644add0942f4 100644 --- a/game/modules/tome/data/birth/descriptors.lua +++ b/game/modules/tome/data/birth/descriptors.lua @@ -40,6 +40,7 @@ newBirthDescriptor{ Tutorial = "disallow", ["Maj'Eyal"] = "allow", Infinite = function() return profile.mod.allow_build.campaign_infinite_dungeon and "allow" or "disallow" end, + Arena = function() return profile.mod.allow_build.campaign_arena and "allow" or "disallow" end, Ents = "disallow", Spydre = "disallow", Orcs = "disallow", diff --git a/game/modules/tome/data/birth/worlds.lua b/game/modules/tome/data/birth/worlds.lua index e6937caf44bff986b9345aeeb7685ca44654bb65..cb512cd2224e6e3d77c7366ecaef98651b3b5f31 100644 --- a/game/modules/tome/data/birth/worlds.lua +++ b/game/modules/tome/data/birth/worlds.lua @@ -53,6 +53,38 @@ newBirthDescriptor{ }, } +local default_eyal_descriptors = { + race = + { + __ALL__ = "disallow", + Human = "allow", + Elf = "allow", + Dwarf = "allow", + Halfling = "allow", + Undead = function() return profile.mod.allow_build.undead and "allow" or "disallow" end, + }, + + class = + { + __ALL__ = "disallow", + -- currently psionics consist only of mindslayers + Psionic = "allow", + Warrior = "allow", + Archer = "allow", + Rogue = "allow", + Mage = "allow", + Divine = function() return profile.mod.allow_build.divine and "allow" or "disallow" end, + Wilder = function() return ( + profile.mod.allow_build.wilder_summoner or + profile.mod.allow_build.wilder_wyrmic + ) and "allow" or "disallow" + end, + Corrupter = function() return profile.mod.allow_build.corrupter and "allow" or "disallow" end, + Afflicted = function() return profile.mod.allow_build.afflicted and "allow" or "disallow" end, + Chronomancer = function() return profile.mod.allow_build.chronomancer and "allow" or "disallow" end, + Psionic = function() return profile.mod.allow_build.psionic and "allow" or "disallow" end, + }, +} -- Player worlds/campaigns newBirthDescriptor{ @@ -66,39 +98,7 @@ newBirthDescriptor{ "You are an adventurer, setting out to find lost treasure and glory.", "But what lurks in the shadows of the world?", }, - descriptor_choices = - { - race = - { - __ALL__ = "disallow", - Human = "allow", - Elf = "allow", - Dwarf = "allow", - Halfling = "allow", - Undead = function() return profile.mod.allow_build.undead and "allow" or "disallow" end, - }, - - class = - { - __ALL__ = "disallow", - -- currently psionics consist only of mindslayers - Psionic = "allow", - Warrior = "allow", - Archer = "allow", - Rogue = "allow", - Mage = "allow", - Divine = function() return profile.mod.allow_build.divine and "allow" or "disallow" end, - Wilder = function() return ( - profile.mod.allow_build.wilder_summoner or - profile.mod.allow_build.wilder_wyrmic - ) and "allow" or "disallow" - end, - Corrupter = function() return profile.mod.allow_build.corrupter and "allow" or "disallow" end, - Afflicted = function() return profile.mod.allow_build.afflicted and "allow" or "disallow" end, - Chronomancer = function() return profile.mod.allow_build.chronomancer and "allow" or "disallow" end, - Psionic = function() return profile.mod.allow_build.psionic and "allow" or "disallow" end, - }, - }, + descriptor_choices = default_eyal_descriptors, } newBirthDescriptor{ @@ -110,39 +110,7 @@ newBirthDescriptor{ "Play as your favorite race and class and venture into the infinite dungeon.", "The only limit to how far you can go is your own skill!", }, - descriptor_choices = - { - race = - { - __ALL__ = "disallow", - Human = "allow", - Elf = "allow", - Dwarf = "allow", - Halfling = "allow", - Undead = function() return profile.mod.allow_build.undead and "allow" or "disallow" end, - }, - - class = - { - __ALL__ = "disallow", - -- currently psionics consist only of mindslayers - Psionic = "allow", - Warrior = "allow", - Archer = "allow", - Rogue = "allow", - Mage = "allow", - Divine = function() return profile.mod.allow_build.divine and "allow" or "disallow" end, - Wilder = function() return ( - profile.mod.allow_build.wilder_summoner or - profile.mod.allow_build.wilder_wyrmic - ) and "allow" or "disallow" - end, - Corrupter = function() return profile.mod.allow_build.corrupter and "allow" or "disallow" end, - Afflicted = function() return profile.mod.allow_build.afflicted and "allow" or "disallow" end, - Chronomancer = function() return profile.mod.allow_build.chronomancer and "allow" or "disallow" end, - Psionic = function() return profile.mod.allow_build.psionic and "allow" or "disallow" end, - }, - }, + descriptor_choices = default_eyal_descriptors, copy = { -- Give the orb of knowledge resolvers.inventory{ id=true, {defined="ORB_KNOWLEDGE"}}, @@ -159,6 +127,36 @@ newBirthDescriptor{ }, } +newBirthDescriptor{ + type = "world", + name = "Arena", + display_name = "The Arena: Challenge of the Master", + desc = + { + "Play as a lone warrior setting foot in the Arena's challenge!", + "You can use any class and race into it.", + "Check out how far you can go! Can you become the new Master of the Arena?", + "If so, you will battle your own champion next time!", + }, + descriptor_choices = default_eyal_descriptors, + copy = { + death_dialog = "ArenaFinish", + + -- Give the orb of knowledge + resolvers.inventory{ id=true, {defined="ORB_KNOWLEDGE"}}, + resolvers.generic(function(e) e.hotkey[12] = {"inventory", "Orb of Knowledge"} end), + + -- Override normal stuff + before_starting_zone = function(self) + self.starting_level = 1 + self.starting_level_force_down = nil + self.starting_zone = "arena" + self.starting_quest = "arena" + self.starting_intro = "arena" + end, + }, +} + newBirthDescriptor{ type = "world", name = "Orcs", diff --git a/game/modules/tome/data/chats/arena-start.lua b/game/modules/tome/data/chats/arena-start.lua new file mode 100644 index 0000000000000000000000000000000000000000..412ef834977251294e3056c2fe5a95f7602773e7 --- /dev/null +++ b/game/modules/tome/data/chats/arena-start.lua @@ -0,0 +1,194 @@ +-- 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 + +local give_boots_rush = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_BOOTS_RUSH") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Rush"..game.level.arena.modeString + end +end + +local give_boots_phas = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_BOOTS_PHAS") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Phasing"..game.level.arena.modeString + end +end + +local give_boots_dise = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_BOOTS_DISE") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Disengage"..game.level.arena.modeString + end +end + +local give_boots_lspeed = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_BOOTS_LSPEED") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "LSpeed"..game.level.arena.modeString + end +end + +local give_bow = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_BOW") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Bow"..game.level.arena.modeString + end +end + +local give_sling = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_SLING") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Sling"..game.level.arena.modeString + end +end + +local give_healinfu = function(self, player) + local o = game.zone:makeEntity(game.level, "object", {name="healing infusion"}, 1, true) + if o then + o:identify(true) + o.inscription_data.heal = 350 + o.inscription_data.cooldown = o.inscription_data.cooldown + 5 + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + game.level.arena.perk = "Heal"..game.level.arena.modeString + end +end + +local give_imbue = function(self, player) + player:learnTalent(player.T_IMBUE_ITEM, true, 4) + player.changed = true + game.level.arena.perk = "Imbue"..game.level.arena.modeString +end + +local give_debugarms = function(self, player) + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_DEBUG_CANNON") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + end + o = nil + o = game.zone:makeEntityByName(game.level, "object", "ARENA_DEBUG_ARMOR") + if o then + o:identify(true) + player:addObject(player.INVEN_INVEN, o) + game.zone:addEntity(game.level, o, "object") + end + game.level.arena.perk = "Debug"..game.level.arena.modeString +end + +local arena_3 = function(self, player) + game.level.arena.eventWave = 2 + game.level.arena.finalWave = 3 + game.level.arena.modeString = "3" +end + +local arena_30 = function(self, player) + game.level.arena.eventWave = 5 + game.level.arena.finalWave = 31 + game.level.arena.modeString = "30" +end + +local arena_60 = function(self, player) + game.level.arena.eventWave = 5 + game.level.arena.finalWave = 61 + game.level.arena.modeString = "60" +end + +local give_bonus = function(self, player) + game.level.arena.bonusMin = 1.1 + game.level.arena.bonusMultiplier = 1.1 + game.level.arena.perk = "None"..game.level.arena.modeString +end + +local save_clear = function(self, player) + world.arena = nil + if not world.arena then + local emptyScore = {name = nil, score = 0, perk = nil, wave = 1, sex = nil, race = nil, class = nil} + world.arena = { + master30 = nil, + master60 = nil, + lastScore = emptyScore, + bestWave = 1, + ver = 1 + } + world.arena.scores = {[1] = emptyScore} + end + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_SCORING") + if o then game.zone:addEntity(game.level, o, "object", 7, 3) end +end + + +newChat{ id="perks", + text = [[#LIGHT_GREEN#Preparing for the arena, you brought with you...]], + answers = { + {"A pair of boots of disengagement...", action=give_boots_dise}, + {"A pair of boots of rushing...", action=give_boots_rush}, + {"A pair of boots of phasing...", action=give_boots_phas}, + --{"#DARK_BLUE#A pair of boots of lightning speed...", action=give_boots_lspeed}, + {"A healing infusion...", action=give_healinfu}, + {"A bow of steady shots...", action=give_bow}, + {"A sling of flare...", action=give_sling}, + {"The talent to imbue gems...", action=give_imbue}, + --{"[DEBUG] Debug arms!", action=give_debugarms}, + {"Nothing!", action=give_bonus}, + } +} + +newChat{ id="welcome", + text = "#LIGHT_GREEN#You take a look at the ranking boards before entering.\n"..text, + answers = { + --{"Enter the arena for 3 rounds[DEBUG]", action=arena_3, jump="perks"}, + {"Enter the arena for 30 rounds", action=arena_30, jump="perks"}, + {"Enter the arena for 60 rounds", action=arena_60, jump="perks"}, + --{"Enter the arena for as much as you can last", action=arena_inf, jump="perks"}, + {"#LIGHT_RED#[Reset all arena save data]", action=save_clear, jump="welcome2"}, + } +} + +newChat{ id="welcome2", + text = "What will you do now?", + answers = { + --{"Enter the arena for 3 rounds[DEBUG]", action=arena_3, jump="perks"}, + {"Enter the arena for 30 rounds", action=arena_30, jump="perks"}, + {"Enter the arena for 60 rounds", action=arena_60, jump="perks"}, + --{"Enter the arena for as much as you can last", action=arena_inf, jump="perks"}, + } +} + +return "welcome" diff --git a/game/modules/tome/data/chats/arena-unlock.lua b/game/modules/tome/data/chats/arena-unlock.lua new file mode 100644 index 0000000000000000000000000000000000000000..9e5bb57f57f4f6326abc669d1a50dc9578d93bfd --- /dev/null +++ b/game/modules/tome/data/chats/arena-unlock.lua @@ -0,0 +1,97 @@ +newChat{ id="welcome", + text = [[#LIGHT_GREEN#*A tall, hooded man stares at you*#WHITE# +I am waiting for a great fighter. Go away. +#LIGHT_GREEN#*The man ignores you for a few uncomfortable seconds, then asks:*#WHITE# +What is it? Are you the fighter I am waiting for? +]], + answers = { + {"No", jump="no"}, + {"Yes", jump="yes"}, + } +} + +newChat{ id="no", + text = [[#LIGHT_GREEN#*The man stares back at you*#WHITE# +That's unfortunate. You look like a fighter. +#LIGHT_GREEN#*The hooded man draws a smile in his face. You can't tell if it's an arrogant +#LIGHT_GREEN#smirk, or a sign of satisfaction. Concerned, you draw your hand closer to your +#LIGHT_GREEN#weapon*#WHITE# +Heheheh...Indeed, you are. +I am looking for strong fighters for battling in the arena. A tiny entertainment for the +masses...but also a good source of good gold, and battles. +Listen, here is the deal. If you can prove your worth as fighter, I will give you +an invitation to the arena. +Fight three combatants, if you win, you get a pass. If you can survive, that is... +]], + answers = { + {"Interesting, I am up to the challenge.", jump="yes"}, + {"I am not interested.", jump="ok"}, + } +} + +newChat{ id="yes", + text = [[#LIGHT_GREEN#*The man shows a devious smile*#WHITE# +Ah, excellent. A great fighter is always willing to head into battle. +Heheh...very well then, follow me, @playerdescriptor.race@. +#LIGHT_GREEN#*The man starts walking away, but suddenly turns to you*#WHITE# +Just remember, you might lose your life if you are weak... +But that is not the case, is it? Heheh... +]], + answers = { + {"Let's go", jump="go"}, + {"Wait, I am not ready yet.", jump="ok"}, + } +} + +newChat{ id="go", + text = "#LIGHT_GREEN#*The man quietly walks away, after making you a gesture to follow him*", + answers = { + {"#LIGHT_GREEN#*You follow the man*", + action = function (self, player) + self:die() + player:grantQuest("arena-unlock") + game:changeLevel(1, "arena-unlock") + require("engine.ui.Dialog"):simpleLongPopup("Get ready!", "Defeat all three enemies!", 400) + end + }, + } +} + + +newChat{ id="win", + text = [[#LIGHT_GREEN#*The cornac rogue comes back from the shadows*#WHITE# +Hehehe, well done, @playerdescriptor.race@! I knew you had potential. +#LIGHT_GREEN#*The rogue takes out his hood, showing a fairly young, but unmistakingly +#LIGHT_GREEN#battle-hardened man. He is smiling widely, but this time you can clearly +#LIGHT_GREEN#recognize satisfaction in his smile.#WHITE# +The name's Rej. I work for the arena to recruit great fighters who can give a good +show...and not die in two blows. +A promise is a promise. This envelope contains an invitation to the arena, and a map +with its location. +When you are done adventuring, I'll like to see you there. +#LIGHT_GREEN#*As you travel back to Derth in company of the rogue, you both talk about past +#LIGHT_GREEN#battles and techniques. His experience in combat inspires you greatly. (#WHITE#+2 generic talent points#LIGHT_GREEN#) +#WHITE#Very well, @playername@. I must go now. I need to meet with some fighters at +Zigur, not too far from here. +Come to the arena when you are done with your adventures, will you?. +]], + answers = { + { "I will. Farewell for now.", action = function (self, player) + game:changeLevel(1, "town-derth") + player.unused_generics = player.unused_generics + 2 + game:setAllowedBuild("campaign_arena", true) + game.player:setQuestStatus("arena-unlock", engine.Quest.COMPLETED) + world:gainAchievement("THE_ARENA", game.player) + end + }, + } +} + +newChat{ id="ok", + text = "#WHITE#I see... come back if you change your mind...", + answers = { + { "See you."}, + } +} + +return "welcome" diff --git a/game/modules/tome/data/chats/arena.lua b/game/modules/tome/data/chats/arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..8f46ce8a8b53b15d4a0650f3ccbab1595eaad126 --- /dev/null +++ b/game/modules/tome/data/chats/arena.lua @@ -0,0 +1,176 @@ +newChat{ id="ryal-entry", +text = [[#LIGHT_GREEN#*A gigantic bone giant walks through the main gate. +#LIGHT_GREEN#Its shape is intricate and sharp, resembling a drake, but with countless +#LIGHT_GREEN#spikes instead of wings. +#LIGHT_GREEN#The massive undead stares at you with unusual...intellect. +#LIGHT_GREEN#You have heard of him. Ryal the Towering, your first obstacle! +#LIGHT_GREEN#As an eerie blue glow fills where its eyes should be, the undead giant +#LIGHT_GREEN#roars and multiple bones fly in your general direction!* +]], + answers = { + {"Have at you!!"}, + } +} + +newChat{ id="ryal-defeat", +text = [[#LIGHT_GREEN#*After taking several hits, the undead giant finally succumbs +#LIGHT_GREEN#to your attacks* +#LIGHT_GREEN#Suddenly, Ryal's body starts to regenerate! +#LIGHT_GREEN#Standing tall again, you can almost feel its emotionless skull staring +#LIGHT_GREEN#at you with...satisfaction. +#WHITE#Hehehe...well done, @playerdescriptor.race@. +#LIGHT_GREEN#*Ryal quietly turns towards the gate and leaves, seemingly unharmed* +]], + answers = { + {"It was fun, bone giant!"}, + {"...what? unharmed?"} + } +} + +newChat{ id="fryjia-entry", +text = [[#LIGHT_GREEN#*The wind chills as a young girl walks calmly through the gate. +#LIGHT_GREEN#She looks surprisingly young, with extremely pale skin and contrasting +#LIGHT_GREEN#long black hair. She examines you with eerie calmness*#WHITE# +I am known as Fryjia the Hailstorm. That's all you need to know, @playerdescriptor.race@. Let us begin. +#LIGHT_GREEN#*The whole arena starts to get colder as she speaks, and the audience +#LIGHT_GREEN#starts wearing their finest cloaks*]], + answers = { + {"Bring it!"}, + } +} + +newChat{ id="fryjia-defeat", +text = [[#LIGHT_GREEN#*With your final blow, Fryjia falls, unable to continue* +#LIGHT_GREEN#*She awkwardly stands up, but doesn't seem critically injured* +#WHITE# I...I admit defeat. +#LIGHT_GREEN#*The audience "oooohs" in awe. Fryjia has turned her back to you* +#WHITE# @playerdescriptor.race@. You are not the person I am looking for... +#LIGHT_GREEN#*Leaving you wondering what she was talking about, the young girl walks +#LIGHT_GREEN#towards the gate. As it closes, you realize her eyes are wet with tears. +]], + answers = { + {"..."}, + {"w...what was that about?"} + } +} + +newChat{ id="riala-entry", +text = [[#LIGHT_GREEN#*The gate opens, revealing a mature human woman in crimson robes. +#LIGHT_GREEN#She looks at you with a wide smile* +#WHITE# My, my, what a fine @playerdescriptor.race@ you are. What was your name again, @playername@? I am soo delighted to be your rival today. +#LIGHT_GREEN#*She speaks quietly as if telling a secret* #WHITE#You know, so few get past the little one as of late, it's such a bore.#LIGHT_GREEN#*She giggles*#WHITE# +So! I am Reala, the Crimson. I came directly from Angolwen. Despite, you know, the whole thing with the Spellblaze, people still enjoys a few magic tricks! +#LIGHT_GREEN#*She snaps her fingers, and then flames start dancing around her!*#WHITE# +Fryjia told me about you, the poor thing, so I will not underestimate such a promising aspirant #LIGHT_GREEN#*She smiles warmly* #WHITE#So, let's make haste my dear! +There is a battle to fight here!]], + answers = { + {"Let's go!"}, + } +} + +newChat{ id="riala-defeat", +text = [[#LIGHT_GREEN#*With the final blow, Reala falls...to suddenly burst in flames!! +#LIGHT_GREEN#You stare at the blazing inferno with understandable confusion, +#LIGHT_GREEN#until you hear her voice from behind*#WHITE# +Oh, my dear! That was quite the fight wasn't it? I concede you the honor of victory. +#LIGHT_GREEN#*She bows politely* +Fryjia was right about you, you seem to be a champion on the works! +Oh, and, please forgive her behavior. You will understand when you meet her father. +And, if you keep fighting like this, it will be really soon. +So, it's been my pleasure, @playername@. #LIGHT_GREEN#*She vanishes in a spiral of flame*]], + answers = { + {"I am pumped up! What's next?"}, + {"Am I the only person with a name that can die here?"} + } +} + +newChat{ id="valfren-entry", +text = [[#LIGHT_GREEN#*You suddenly realize everything has turned dark. +#LIGHT_GREEN#You look around searching for your rival. And then you notice it. Standing +#LIGHT_GREEN#right before you, a massive battle armor with an equally massive battle axe. +#LIGHT_GREEN#It wasn't there just a second ago. You step back and examine him better, +#LIGHT_GREEN#realizing it's actually a human inside that hulking, worn armor. You can't see +#LIGHT_GREEN#his eyes, but you know he's piercing your soul with his stare* +f...t...ma....ll... +#LIGHT_GREEN#*You hear a devilish voice, coming from everywhere at once!! But...you are +#LIGHT_GREEN#unable to understand anything! It doesn't seem like any language used in +#LIGHT_GREEN#Maj'Eyal! +#LIGHT_GREEN#And then...a piercing, demonic roar...you are overwhelmed by extreme +#LIGHT_GREEN#emotions invading your very soul!!* +]], + answers = { + {"#LIGHT_GREEN#*You valiantly stand against the darkness*"}, + } +} + +newChat{ id="valfren-defeat", +text = [[#LIGHT_GREEN#*You valiantly deliver the finishing blow!* +#LIGHT_GREEN#*Valfren collapses as the light returns to this world. +#LIGHT_GREEN#You close your eyes for a brief instant, Fryjia is there when you open them* +Father... #LIGHT_GREEN#*She stands silent for a few seconds*#WHITE# You win, @playerdescriptor.race@. +You have done well. Prepare for your final battle...if you win, we will be at your service. +Good luck... +#LIGHT_GREEN#*After a few uncomfortable seconds, Valfren starts to move again. +#LIGHT_GREEN#He stands up and walks away with Fryjia. At the gates, Valfren turns his +#LIGHT_GREEN#head in your direction. You look at him, and then, he looks above +#LIGHT_GREEN#the arena's walls. You follow his guide...to meet the Master of the Arena* + +#LIGHT_GREEN#*There it is. Your goal. Your heart beats fast, as the time has come* +#LIGHT_GREEN#*The Master of the Arena smiles proudly* +#RED#The final battle begins when the gate closes, just this final time!! +]], + answers = { + {"I will defeat you, Master of the Arena!!!"}, + {"I will become Master of the Arena instead of the Master of the Arena!!"}, + {"Wealth and glory! Wealth and glory!"}, + } +} + +newChat{ id="master-entry", +text = [[#LIGHT_GREEN#*Finally, the master of the arena comes into the gates! +#LIGHT_GREEN#The public roars with excitement as he faces you with confidence!* +I applaud you, @playerdescriptor.race@! You have fought with might and courage! +And now...the time for the final showdown! +#LIGHT_GREEN#*The master assumes a fighting stance. The audience cheers!* +Like you, I started from nowhere. I won't underestimate someone with such potential. +#LIGHT_GREEN#*The master smirks, you assume your fighting stance as well, and the +#LIGHT_GREEN#audience cheers you as well, making the excitement grow inside you* +Can you hear it, the public cheering? That's what this is about. +Pursue glory with all your might, @playerdescriptor.race@!! +#LIGHT_GREEN#*The master steps forward into the sand* +]], + answers = { + {"Wealth and glory!!!"}, + } +} + +newChat{ id="master-defeat", +text = [[#LIGHT_GREEN#*After a glorious battle, the Master falls!* +Hah...haha. You did it, @playerdescriptor.race@... +#LIGHT_GREEN#*The master of the arena, defeated, stands up with a wide smile. +#LIGHT_GREEN#Feeling the master's approval, you pick up its weapon, now lying +#LIGHT_GREEN#in the blood-stained sand.* +Everyone! We got a champion today!! +#LIGHT_GREEN#*The audience rages and shouts your name repeatedly* +Congratulations, @playerdescriptor.race@. You are the Master now. +Now you shall take your rightful place as the champion. +Just remember...like me, you shall fall one day... +But meanwhile, this is your place! Welcome to paradise, @playerdescriptor.race@! +#LIGHT_GREEN#*You see several sponsors and military recruiters approach the +#LIGHT_GREEN#now defeated master, offering deals and good positions in the army. +#LIGHT_GREEN#You smile, victorious, knowing your life will be glorious from now on. +#LIGHT_GREEN#Because even if you are defeated in the future... +#LIGHT_GREEN#You can always sell your image and live large. + +#YELLOW#CONGRATULATIONS! +#YELLOW#You are the new master of the arena! You are great and epic! +#YELLOW#You shall remain as the new master until someone challenges you! +#YELLOW#Next time you play, you shall battle this new champion instead! +]], + answers = { + {"WEALTH!! AND!! GLORYYYYY!!"}, + {"I won't need to save chicks from cults anymore!", cond=function(npc, player) if player.female == true then return false else return true end end }, + {"I hereby stand victorious, awaiting future challenges!"}, + {"#LIGHT_GREEN#*dance*"}, + } +} diff --git a/game/modules/tome/data/general/grids/basic.lua b/game/modules/tome/data/general/grids/basic.lua index 88ea2b887c8f52d0170c654b2f52f46c809b477e..686d629ba081e093a6b7515720bf051798d60e26 100644 --- a/game/modules/tome/data/general/grids/basic.lua +++ b/game/modules/tome/data/general/grids/basic.lua @@ -153,17 +153,17 @@ newEntity{ dig = "FLOOR", } for i = 1, 5 do - newEntity{ base = "WALL", define_as = "WALL"..i, image = "terrain/granite_wall1_"..i..".png", nice_tiler = false} - newEntity{ base = "WALL", define_as = "WALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} - newEntity{ base = "WALL", define_as = "WALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}, nice_tiler = false} + newEntity{ base = "WALL", define_as = "WALL"..i, image = "terrain/granite_wall1_"..i..".png"} + newEntity{ base = "WALL", define_as = "WALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} + newEntity{ base = "WALL", define_as = "WALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}} end -newEntity{ base = "WALL", define_as = "WALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "WALL", define_as = "WALL_SOUTH", image = "terrain/granite_wall2.png", nice_tiler = false} -for i = 1, 17 do newEntity{ base = "WALL", define_as = "WALL_SOUTH"..i, image = "terrain/granite_wall2_"..i..".png", nice_tiler = false} end -newEntity{ base = "WALL", define_as = "WALL_SMALL_PILLAR", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "WALL", define_as = "WALL_PILLAR_6", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "WALL", define_as = "WALL_PILLAR_4", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "WALL", define_as = "WALL_PILLAR_2", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}, nice_tiler = false} +newEntity{ base = "WALL", define_as = "WALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} +newEntity{ base = "WALL", define_as = "WALL_SOUTH", image = "terrain/granite_wall2.png"} +for i = 1, 17 do newEntity{ base = "WALL", define_as = "WALL_SOUTH"..i, image = "terrain/granite_wall2_"..i..".png"} end +newEntity{ base = "WALL", define_as = "WALL_SMALL_PILLAR", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}} +newEntity{ base = "WALL", define_as = "WALL_PILLAR_6", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}} +newEntity{ base = "WALL", define_as = "WALL_PILLAR_4", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}} +newEntity{ base = "WALL", define_as = "WALL_PILLAR_2", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}} ----------------------------------------- -- Big Walls @@ -196,17 +196,17 @@ newEntity{ air_level = -20, } for i = 1, 5 do - newEntity{ base = "HARDWALL", define_as = "HARDWALL"..i, image = "terrain/granite_wall1_"..i..".png", nice_tiler = false} - newEntity{ base = "HARDWALL", define_as = "HARDWALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} - newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}, nice_tiler = false} + newEntity{ base = "HARDWALL", define_as = "HARDWALL"..i, image = "terrain/granite_wall1_"..i..".png"} + newEntity{ base = "HARDWALL", define_as = "HARDWALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} + newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}} end -newEntity{ base = "HARDWALL", define_as = "HARDWALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "HARDWALL", define_as = "HARDWALL_SOUTH", image = "terrain/granite_wall2.png", nice_tiler = false} -for i = 1, 17 do newEntity{ base = "HARDWALL", define_as = "HARDWALL_SOUTH"..i, image = "terrain/granite_wall2_"..i..".png", nice_tiler = false} end -newEntity{ base = "HARDWALL", define_as = "HARDWALL_SMALL_PILLAR", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_6", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_4", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_2", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}, nice_tiler = false} +newEntity{ base = "HARDWALL", define_as = "HARDWALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} +newEntity{ base = "HARDWALL", define_as = "HARDWALL_SOUTH", image = "terrain/granite_wall2.png"} +for i = 1, 17 do newEntity{ base = "HARDWALL", define_as = "HARDWALL_SOUTH"..i, image = "terrain/granite_wall2_"..i..".png"} end +newEntity{ base = "HARDWALL", define_as = "HARDWALL_SMALL_PILLAR", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}} +newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_6", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}} +newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_4", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}} +newEntity{ base = "HARDWALL", define_as = "HARDWALL_PILLAR_2", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}} ----------------------------------------- @@ -230,10 +230,10 @@ newEntity{ always_remember = true, door_closed = "DOOR", } -newEntity{ base = "DOOR", define_as = "DOOR_HORIZ", image = "terrain/granite_door1.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false, door_opened = "DOOR_HORIZ_OPEN"} -newEntity{ base = "DOOR_OPEN", define_as = "DOOR_HORIZ_OPEN", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_open.png", z=17}, class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false, door_closed = "DOOR_HORIZ"} -newEntity{ base = "DOOR", define_as = "DOOR_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_vert.png", z=17}, class.new{image="terrain/granite_door1_vert_north.png", z=18, display_y=-1}}, door_opened = "DOOR_OPEN_VERT", nice_tiler = false, dig = "DOOR_OPEN_VERT"} -newEntity{ base = "DOOR_OPEN", define_as = "DOOR_OPEN_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_open_vert.png", z=17}, class.new{image="terrain/granite_door1_open_vert_north.png", z=18, display_y=-1}}, nice_tiler = false, door_closed = "DOOR_VERT"} +newEntity{ base = "DOOR", define_as = "DOOR_HORIZ", image = "terrain/granite_door1.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, door_opened = "DOOR_HORIZ_OPEN"} +newEntity{ base = "DOOR_OPEN", define_as = "DOOR_HORIZ_OPEN", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_open.png", z=17}, class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, door_closed = "DOOR_HORIZ"} +newEntity{ base = "DOOR", define_as = "DOOR_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_vert.png", z=17}, class.new{image="terrain/granite_door1_vert_north.png", z=18, display_y=-1}}, door_opened = "DOOR_OPEN_VERT", dig = "DOOR_OPEN_VERT"} +newEntity{ base = "DOOR_OPEN", define_as = "DOOR_OPEN_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_open_vert.png", z=17}, class.new{image="terrain/granite_door1_open_vert_north.png", z=18, display_y=-1}}, door_closed = "DOOR_VERT"} newEntity{ define_as = "DOOR_VAULT", @@ -248,8 +248,8 @@ newEntity{ door_player_check = "This door seems to have been sealed off, you think you can open it.", door_opened = "DOOR_OPEN", } -newEntity{ base = "DOOR_VAULT", define_as = "DOOR_VAULT_HORIZ", image = "terrain/granite_door1.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false, door_opened = "DOOR_HORIZ_OPEN"} -newEntity{ base = "DOOR_VAULT", define_as = "DOOR_VAULT_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_vert.png", z=17}, class.new{image="terrain/granite_door1_vert_north.png", z=18, display_y=-1}}, door_opened = "DOOR_OPEN_VERT", nice_tiler = false} +newEntity{ base = "DOOR_VAULT", define_as = "DOOR_VAULT_HORIZ", image = "terrain/granite_door1.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, door_opened = "DOOR_HORIZ_OPEN"} +newEntity{ base = "DOOR_VAULT", define_as = "DOOR_VAULT_VERT", image = "terrain/marble_floor.png", add_displays = {class.new{image="terrain/granite_door1_vert.png", z=17}, class.new{image="terrain/granite_door1_vert_north.png", z=18, display_y=-1}}, door_opened = "DOOR_OPEN_VERT"} ----------------------------------------- -- Old walls @@ -260,7 +260,7 @@ newEntity{ display = '.', color_r=255, color_g=255, color_b=255, back_color=colors.DARK_GREY, -- nice_tiler = { method="replace", base={"OLD_FLOOR", 100, 1, 4}}, } ---for i = 1, 4 do newEntity{ base = "OLD_FLOOR", define_as = "OLD_FLOOR"..i, image = "terrain/oldstone_floor"..(i > 1 and "_"..i or "")..".png", nice_tiler = false} end +--for i = 1, 4 do newEntity{ base = "OLD_FLOOR", define_as = "OLD_FLOOR"..i, image = "terrain/oldstone_floor"..(i > 1 and "_"..i or "")..".png"} end newEntity{ define_as = "OLD_WALL", @@ -273,15 +273,15 @@ newEntity{ air_level = -20, } for i = 1, 5 do - newEntity{ base = "OLD_WALL", define_as = "OLD_WALL"..i, image = "terrain/granite_wall1_"..i..".png", nice_tiler = false} - newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} - newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}, nice_tiler = false} + newEntity{ base = "OLD_WALL", define_as = "OLD_WALL"..i, image = "terrain/granite_wall1_"..i..".png"} + newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} + newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_8"..i, image = "terrain/granite_wall1_"..i..".png", add_displays = {class.new{image="terrain/granite_wall_pillar_8.png", z=18, display_y=-1}}} end -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} -for i = 1, 3 do newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH_SOUTH"..i, image = "terrain/granite_wall_lichen_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}, nice_tiler = false} end -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SOUTH", image = "terrain/granite_wall2.png", nice_tiler = false} -for i = 1, 3 do newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SOUTH"..i, image = "terrain/granite_wall_lichen_"..i..".png", nice_tiler = false} end -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SMALL_PILLAR", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_6", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_4", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}, nice_tiler = false} -newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_2", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}, nice_tiler = false} +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH_SOUTH", image = "terrain/granite_wall2.png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} +for i = 1, 3 do newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_NORTH_SOUTH"..i, image = "terrain/granite_wall_lichen_"..i..".png", add_displays = {class.new{image="terrain/granite_wall3.png", z=18, display_y=-1}}} end +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SOUTH", image = "terrain/granite_wall2.png"} +for i = 1, 3 do newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SOUTH"..i, image = "terrain/granite_wall_lichen_"..i..".png"} end +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_SMALL_PILLAR", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_small.png"}, class.new{image="terrain/granite_wall_pillar_small_top.png", z=18, display_y=-1}}} +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_6", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_3.png"}, class.new{image="terrain/granite_wall_pillar_9.png", z=18, display_y=-1}}} +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_4", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_1.png"}, class.new{image="terrain/granite_wall_pillar_7.png", z=18, display_y=-1}}} +newEntity{ base = "OLD_WALL", define_as = "OLD_WALL_PILLAR_2", image = "terrain/oldstone_floor.png", add_displays = {class.new{image="terrain/granite_wall_pillar_2.png"}}} diff --git a/game/modules/tome/data/general/npcs/crystal.lua b/game/modules/tome/data/general/npcs/crystal.lua new file mode 100644 index 0000000000000000000000000000000000000000..513b4fe69b3017ae9ab8c860239e39ba82fc13f0 --- /dev/null +++ b/game/modules/tome/data/general/npcs/crystal.lua @@ -0,0 +1,116 @@ +local Talents = require("engine.interface.ActorTalents") + +newEntity{ + define_as = "BASE_NPC_CRYSTAL", + type = "immovable", subtype = "crystal", + display = "%", color=colors.WHITE, + desc = "A shining crystal formation charged with magical energies.", + body = { INVEN = 10 }, + autolevel = "caster", + ai = "dumb_talented_simple", ai_state = { talent_in=1, }, + resolvers.drops{chance=25, nb=1, {type="jewelry", ego_chance = 100}}, + max_life = resolvers.rngavg(12,34), + stats = { str=1, dex=5, mag=20, con=1 }, + energy = { mod=0.5 }, + infravision = 20, + combat_armor = 10, combat_def = 1, + never_move = 1, + blind_immune = 1, + fear_immune = 1, + rank = 2, + size_category = 2, + poison_immune = 1, + disease_immune = 1, + no_breath = 1, + confusion_immune = 1, + disease_immune = 1, + poison_immune = 1, + see_invisible = 25, + resolvers.talents{ + [Talents.T_PHASE_DOOR]=1, + } +} + + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "red crystal", color=colors.RED, + desc = "A formation of red crystal. It emits bright red, scorching light.", + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + resists = { [DamageType.FIRE] = 100, [DamageType.ICE] = -100 }, + resolvers.talents{ + [Talents.T_FLAME]=1, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "white crystal", color=colors.WHITE, + desc = "A formation of white crystal. It emits bright white, chilling light.", + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + resists = { [DamageType.ICE] = 100, [DamageType.FIRE] = -100 }, + resolvers.talents{ + [Talents.T_ICE_SHARDS]=1, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "black crystal", color=colors.BLACK, + desc = "A formation of black crystal. It absorbs all light around it.", + level_range = {1, nil}, exp_worth = 1, + rarity = 2, + resists = { [DamageType.LIGHT] = 100 ,[DamageType.DARKNESS] = -100 }, + resolvers.talents{ + [Talents.T_SOUL_ROT]=1, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "crimson crystal", color=colors.DARK_RED, + desc = "A formation of crimson crystal. It emits a crimson light reminiscent of blood.", + level_range = {1, nil}, exp_worth = 1, + rarity = 3, + resists = { [DamageType.LIGHT] = -100 }, + resolvers.talents{ + [Talents.T_BLOOD_GRASP]=1, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "blue crystal", color=colors.BLUE, + desc = "A formation of blue crystal. Its light shines like the ocean's waves.", + level_range = {1, nil}, exp_worth = 1, + rarity = 4, + resists = { [DamageType.ICE] = -100 }, + resolvers.talents{ + [Talents.T_WATER_BOLT]=3, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "multi-hued crystal", color=colors.VIOLET, + shader = "quad_hue", + desc = "A formation of multi-hued crystal. It shines with all the colors of the rainbow.", + level_range = {1, nil}, exp_worth = 1, + rarity = 4, + resists = { [DamageType.LIGHT] = 100 }, + resolvers.talents{ + [Talents.T_ELEMENTAL_BOLT]=1, + }, + talent_cd_reduction={ + [Talents.T_ELEMENTAL_BOLT]=2, + } +} + +newEntity{ base = "BASE_NPC_CRYSTAL", + name = "shimmering crystal", color=colors.GREEN, + shader = "quad_hue", + desc = "A formation of shimmering crystal. Orbs of light circle around it.", + level_range = {1, nil}, exp_worth = 1, + rarity = 5, + resists = { [DamageType.LIGHT] = 100 }, + summon = {{name = "wisp", number=3, hasxp=false}}, + resolvers.talents{ + [Talents.T_SUMMON]=1, + } +} diff --git a/game/modules/tome/data/gfx/particles/arena_gate.lua b/game/modules/tome/data/gfx/particles/arena_gate.lua new file mode 100644 index 0000000000000000000000000000000000000000..828bb0b6b0cbea6ed6b066b4d9a1671b854fb92c --- /dev/null +++ b/game/modules/tome/data/gfx/particles/arena_gate.lua @@ -0,0 +1,21 @@ +base_size = 32 + +return { + base = 2000, + + angle = { 0, 360 }, anglev = { 4000, 5000 }, anglea = { 20, 60 }, + + life = { 20, 30 }, + size = { 7, 8 }, sizev = {0, 0}, sizea = {0, 0}, + + r = {240, 255}, rv = {0, 10}, ra = {0, 0}, + g = {200, 214}, gv = {0, 0}, ga = {0, 0}, + b = {150, 163}, bv = {0, 10}, ba = {0, 0}, + a = {25, 120}, av = {0, 0}, aa = {0, 0}, + +}, function(self) + self.nb = (self.nb or 0) + 1 + if self.nb < 3 then + self.ps:emit(20) + end +end \ No newline at end of file diff --git a/game/modules/tome/data/lore/arena.lua b/game/modules/tome/data/lore/arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..1962aac0adb8a1f643d46ba9a1fdec00c2902e7d --- /dev/null +++ b/game/modules/tome/data/lore/arena.lua @@ -0,0 +1,22 @@ +newLore{ + id = "arena-scoring", + category = "arena", + name = "Arena for dummies", + lore = [[#{italic}#You find a note with some highlighted words... +#{bold}#SCORING IN THE ARENA + +#{normal}#The rules of the arena are simple. +Every wave, the gates will open, releasing enemies. When the gates close, you will be given a bonus score. +The bonus will #YELLOW#decrease every turn #WHITE#, but #YELLOW#enemies you kill at this point#WHITE#, will +raise your bonus. When you defeat all enemies, you will be granted your bonus score, multiplied by +your bonus multiplier. + +Every few rounds, a miniboss round will come. After two miniboss rounds, one out of four bosses will appear. +#YELLOW#Kill enemies before the gates close #WHITE#to raise your score multiplier!. +#YELLOW#Kill several enemies at once or very strong enemies #WHITE#to please the audience and increase your rank!. +#YELLOW#Collect gold #WHITE#to increase your score and resources at the end of each round. +#YELLOW#Kill enemies fast to get better scores! +#YELLOW#Take advantage of the trap at the center of the arena! +Good luck! +]], +} \ No newline at end of file diff --git a/game/modules/tome/data/lore/lore.lua b/game/modules/tome/data/lore/lore.lua index 926e5a5a1e200c6f9fafdd22159d71a1bfe75f36..fa65d7442799c530fc5c97da6a06c3b3dcbaabfd 100644 --- a/game/modules/tome/data/lore/lore.lua +++ b/game/modules/tome/data/lore/lore.lua @@ -33,3 +33,4 @@ load("/data/lore/spellblaze.lua") load("/data/lore/orc-prides.lua") load("/data/lore/age-allure.lua") load("/data/lore/misc.lua") +load("/data/lore/arena.lua") diff --git a/game/modules/tome/data/maps/towns/derth.lua b/game/modules/tome/data/maps/towns/derth.lua index 23fc4a21f201ce35df69ed460aee1df976a4374a..bffabdcf1d7e419644c4b332445a6899d3316878 100644 --- a/game/modules/tome/data/maps/towns/derth.lua +++ b/game/modules/tome/data/maps/towns/derth.lua @@ -49,6 +49,21 @@ quickEntity('c', {show_tooltip=true, name="Closed store", display='*', color=col quickEntity('d', {show_tooltip=true, name="Closed store", display='*', color=colors.LIGHT_UMBER, block_move=true, block_sight=true, image="terrain/wood_store_closed.png"}) quickEntity('e', {show_tooltip=true, name="Closed store", display='*', color=colors.LIGHT_UMBER, block_move=true, block_sight=true, image="terrain/wood_store_closed.png"}) +defineTile('r', "GRASS", nil, mod.class.NPC.new{ + type = "humanoid", subtype = "human", + display = "p", + faction = "allied-kingdoms", + name = "Shady cornac man", color=colors.DARK_BLUE, unique = true, + instakill_immune = 1, + teleport_immune = 1, + move_others = true, + open_door = true, + size_category = 3, rank = 2, + ai = "simple", + can_talk = "arena-unlock", + can_quest = true, +}) + startx = 76 starty = 36 @@ -60,7 +75,7 @@ return { [[ ,,----TTTTTT--------------TTTTTTTTTT-----,,,,, ]], [[ ,,---TTTT..................----------TTTTT------,, ]], [[ ,---TTT....--ssssssss-..---..........-----TTTTTT---,,, ]], -[[ ,--TTT...-----SSSSSSSS--.----------,,.....------TTT----,, ]], +[[ ,--TTT...----rSSSSSSSS--.----------,,.....------TTT----,, ]], [[ ,,--TT...-,,,,--ssssssss-..-sssssssssss,---......---TTTT--T,, ]], [[ ,,---TT..-------,,##9#####-.--sssssssssss-,-------...----TT--TT, ]], [[ ,--- T..--sssSsss,,,,------.--StSStSSSStS--,------.-....--TT--TT,^]], diff --git a/game/modules/tome/data/maps/zones/arena-unlock.lua b/game/modules/tome/data/maps/zones/arena-unlock.lua new file mode 100644 index 0000000000000000000000000000000000000000..9d7faa6676e2ac4a56cd939bb1c49b6ea21023b0 --- /dev/null +++ b/game/modules/tome/data/maps/zones/arena-unlock.lua @@ -0,0 +1,47 @@ +-- 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 + +defineTile("#", "WALL") +defineTile("+", "LOCK") +quickEntity(':', {name='grass', display='.', color=colors.LIGHT_GREEN, image="terrain/grass.png"}) +defineTile(".", "SAND") +defineTile("-", "FLOOR") +quickEntity('T', {name='tree', display='#', color=colors.LIGHT_GREEN, block_move=true, block_sight=true, image="terrain/grass.png", add_displays = {mod.class.Grid.new{image="terrain/tree_alpha1.png"}}}) + +startx = 8 +starty = 12 + +-- ASCII map section +return [[ +:.:::T:::.::::T:T +:#######+#######: +:#...#.....#...#: +:#.............#. +:#....#...#....#: +.##...........##T +:#.............#: +T#...#.....#...#: +.#.............#: +:##...........##: +:#....#...#....#: +:#.............#. +:#...#..:..#...#: +:#######+#######: +:::TT:T:::T:.::.: +]] diff --git a/game/modules/tome/data/maps/zones/arena.lua b/game/modules/tome/data/maps/zones/arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..ff2ac3c5dd9ec12ae25f4851b05c3e0a1ffea58e --- /dev/null +++ b/game/modules/tome/data/maps/zones/arena.lua @@ -0,0 +1,45 @@ +-- 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 + +defineTile("#", "WALL") +defineTile("+", "LOCK") +defineTile("1", "WALL_SEE") +defineTile(".", "SAND") +defineTile("-", "FLOOR") + +startx = 7 +starty = 1 + +-- ASCII map section +return [[ ++######-######+ +#--.1.---.1.--# +#-...........-# +#....1...1....# +#1...........1# +#.............# +#.............# +#...1..-..1...# +#.............# +#.............# +#1...........1# +#....1...1....# +#-...........-# +#--.1.....1.--# ++#############+]] diff --git a/game/modules/tome/data/quests/arena-unlock.lua b/game/modules/tome/data/quests/arena-unlock.lua new file mode 100644 index 0000000000000000000000000000000000000000..d59e8ced0c71484b372312862c6f55d6c9f0a0d7 --- /dev/null +++ b/game/modules/tome/data/quests/arena-unlock.lua @@ -0,0 +1,9 @@ +name = "The agent of the arena" +desc = function(self, who) + local desc = {} + desc[#desc+1] = "You were asked to prove your worth as a fighter by a rogue, in order to participate in the arena" + if self:isCompleted() then + desc[#desc+1] = "You succesfully defeated your adversaries and gained access to the arena!" + end + return table.concat(desc, "\n") +end diff --git a/game/modules/tome/data/quests/arena.lua b/game/modules/tome/data/quests/arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..c987f169aebf57e8f6e55a9f92cd786a42ef8358 --- /dev/null +++ b/game/modules/tome/data/quests/arena.lua @@ -0,0 +1,26 @@ +-- 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 = "The Arena" +desc = function(self, who) + local desc = {} + desc[#desc+1] = "Seeking wealth, glory, and a great fight, you challenge the Arena!" + desc[#desc+1] = "Can you defeat your foes and become Master of Arena?" + return table.concat(desc, "\n") +end diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua index 66991b8fdf86578b8774351c653e960d1dc1e050..271b2872be334da6e64423f656f2d806388b75b5 100644 --- a/game/modules/tome/data/talents/misc/npcs.lua +++ b/game/modules/tome/data/talents/misc/npcs.lua @@ -968,3 +968,61 @@ newTalent{ return ([[Invoke your tentacles on your victim.]]) end, } + +newTalent{ + short_name = "EXPLODE", + name = "Explode", + type = {"technique/other", 1}, + points = 5, + message = "@Source@ explodes against @target@.", + cooldown = 1, + range = 1, + requires_target = true, + action = function(self, t) + local tg = {type="bolt", range=1} + local x, y, target = self:getTarget(tg) + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + self:project(tg, x, y, DamageType.LIGHT, math.floor(self:combatSpellpower(0.25) * self:getTalentLevel(t)), {type="light"}) + self:die(self) + game:playSoundNear(self, "talents/arcane") + return true + end, + info = function(self, t) + return ([[Explodes in a blinding light.]]) + end, +} + +newTalent{ + short_name = "ELEMENTAL_BOLT", + name = "Elemental bolt", + type = {"spell/other", 1}, + points = 5, + mana = 10, + message = "@Source@ casts Elemental Bolt!", + cooldown = 3, + range = 20, + proj_speed = 2, + requires_target = true, + action = function(self, t) + local tg = {type = "bolt", range = 20, talent = t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local elem = rng.table{ + {DamageType.FIRE, "flame", "bolt_fire"}, + {DamageType.COLD, "freeze", "bolt_ice"}, + {DamageType.LIGHTNING, "lightning_explosion", "bolt_lightning"}, + {DamageType.ACID, "acid", "bolt_acid"}, + {DamageType.NATURE, "slime", "bolt_slime"}, + {DamageType.BLIGHT, "blood", "bolt_blood"}, + {DamageType.LIGHT, "light", "bolt_light"}, + {DamageType.ARCANE, "manathrust", "bolt_arcane"}, + } + tg.display={particle=elem[3]} + self:projectile(tg, x, y, elem[1], math.floor(self:getMag(100) * self:getTalentLevel(t)), {type=elem[2]}) + game:playSoundNear(self, "talents/arcane") + return true + end, + info = function(self, t) + return ([[Fire a slow bolt of a random element. Damage raises with magic stat.]]) + end, +} diff --git a/game/modules/tome/data/texts/intro-arena.lua b/game/modules/tome/data/texts/intro-arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..fba18bb2188ca1ff889855104f2f9ab10607cc53 --- /dev/null +++ b/game/modules/tome/data/texts/intro-arena.lua @@ -0,0 +1,28 @@ +-- 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 [[Welcome #LIGHT_GREEN#@name@#WHITE#. +As you walk forward, you hear the increasingly loud cheers coming from outside. +As you step into the arena, the scorching sun blinds your eyes temporarily. +You look up, and meet your target eye to eye. The Master of the Arena, carefully examining +the newcomer. He smirks, and starts looking at the other fighters with interest. +Whoever defeats the Master of Arena will achieve utmost glory. +Determined to win, you draw your weapons and advance defiantly. +The public roars in anticipation.... +]] diff --git a/game/modules/tome/data/texts/unlock-campaign_arena.lua b/game/modules/tome/data/texts/unlock-campaign_arena.lua new file mode 100644 index 0000000000000000000000000000000000000000..4e298587ab4ff170f4c309b6b88b1a597d02a29d --- /dev/null +++ b/game/modules/tome/data/texts/unlock-campaign_arena.lua @@ -0,0 +1,34 @@ +-- 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 game campaign: #LIGHT_GREEN#The Arena: Challenge of the Master", +[[ +The arena, a way of violent entertainment. +A delight for the audience, a source of wealth and glory. A place where aspiring fighters, former adventurers and those cursed to fight +eternally gather to hack away at each other. + +You have unlocked the Arena and can now create new characters in a new campaign: #LIGHT_GREEN#The Arena#WHITE#. + +The arena pits you against multiple enemies in an open field, making your battle tactics important for survival. +Campaign features:#YELLOW# +- No quests, plots, friendly creatures or ways out: only you against all odds. +- Exclusive scoring system where the faster you kill, the more you earn. Scores are kept for bragging rights! +- Pure hack and slash MAYHEM! +- Your champion becomes the new master of the arena, allowing you to challenge your own champions! +]] diff --git a/game/modules/tome/data/zones/arena-unlock/grids.lua b/game/modules/tome/data/zones/arena-unlock/grids.lua new file mode 100644 index 0000000000000000000000000000000000000000..915aa9b4b1246f6b72c4d3e0d9735938d5a8b920 --- /dev/null +++ b/game/modules/tome/data/zones/arena-unlock/grids.lua @@ -0,0 +1,43 @@ +-- 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 + +load("/data/general/grids/basic.lua") +load("/data/general/grids/sand.lua") + +newEntity{ + define_as = "WALL_SEE", + name = "wall", image = "terrain/granite_wall1.png", + display = '#', color_r=255, color_g=255, color_b=255, back_color=colors.GREY, + always_remember = true, + does_block_move = true, + can_pass = {pass_wall=1}, + block_sight = false, + air_level = -20, + dig = "FLOOR", +} + +newEntity{ + define_as = "LOCK", + name = "closed gate", image = "terrain/sealed_door.png", + display = '+', color=colors.WHITE, back_color=colors.DARK_UMBER, + notice = true, + always_remember = true, + block_sight = true, + does_block_move = true, +} diff --git a/game/modules/tome/data/zones/arena-unlock/npcs.lua b/game/modules/tome/data/zones/arena-unlock/npcs.lua new file mode 100644 index 0000000000000000000000000000000000000000..1491af412bc10cc35a4c6ddc784eae2b845243ae --- /dev/null +++ b/game/modules/tome/data/zones/arena-unlock/npcs.lua @@ -0,0 +1,162 @@ +-- 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 + +local Talents = require("engine.interface.ActorTalents") + +newEntity{ name = "gladiator", + define_as = "GLADIATOR", + type = "humanoid", subtype = "human", + color=colors.GOLD, display = "p", + + combat = { dam=resolvers.rngavg(5,12), atk=2, apr=6, physspeed=2 }, + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, + resolvers.drops{chance=25, nb=1, {} }, + resolvers.drops{chance=100, nb=1, {type="money"} }, + + life_rating = 15, + rank = 3, + infravision = 10, + lite = 2, + open_door = true, + autolevel = "warrior", + ai = "dumb_talented_simple", ai_state = { ai_move = "move_dmap", talent_in = 1 }, + energy = { mod = 1 }, + + stats = { str=15, dex=15, mag=1, con=15 }, + desc = [[A menacing man in heavy armor, wielding a mace. He looks battle-hardened.]], + level_range = {5, 19}, exp_worth = 2, + rarity = false, + max_life = resolvers.rngavg(100,125), + resolvers.equip{ + {type="weapon", subtype="mace", autoreq=true}, + {type="armor", subtype="shield", autoreq=true}, + {type="armor", subtype="heavy", autoreq=true}, + {type="armor", subtype="feet", autoreq=true}, + }, + resolvers.talents{ + [Talents.T_SHIELD_PUMMEL]=2, + [Talents.T_RUSH]=3, + [Talents.T_REPULSION]=2, + [Talents.T_HEAVY_ARMOUR_TRAINING] = 1, + [Talents.T_WEAPON_COMBAT] = 1, + }, + on_die = function (self) + local m = game.zone:makeEntityByName(game.level, "actor", "SLINGER") + if m then + game.zone:addEntity(game.level, m, "actor", 8, 2) + end + end +} + +newEntity{ name = "slinger", + define_as = "SLINGER", + type = "humanoid", subtype = "halfling", + color=colors.GOLD, display = "p", + + combat = { dam=resolvers.rngavg(5,12), atk=2, apr=6, physspeed=2 }, + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, + resolvers.drops{chance=25, nb=1, {} }, + resolvers.drops{chance=100, nb=1, {type="money"} }, + + life_rating = 8, + rank = 3, + infravision = 10, + lite = 2, + open_door = true, + autolevel = "slinger", + ai = "dumb_talented_simple", ai_state = { ai_move = "move_dmap", talent_in = 1 }, + energy = { mod = 1 }, + + stats = { str=10, dex=15, cun=15, con=8 }, + desc = [[A halfling slinger. He seems adept at combat.]], + level_range = {6, 20}, exp_worth = 1, + rarity = false, + max_life = resolvers.rngavg(100,110), + resolvers.equip{ + {type="weapon", subtype="sling", autoreq=true}, + {type="ammo", subtype="shot", autoreq=true}, + {type="ammo", subtype="shot"}, + {type="armor", subtype="shield", autoreq=true}, + }, + resolvers.talents{ + [Talents.T_SHOOT]=1, + [Talents.T_DISENGAGE]=2, + [Talents.T_RAPID_SHOT]=3, + [Talents.T_INERTIAL_SHOT]=2, + [Talents.T_HEAVE]=3, + [Talents.T_WEAPON_COMBAT] = 2, + }, + on_added = function (self) + game.level.map:particleEmitter(self.x, self.y, 1, "teleport") + game:playSoundNear(game.player, "talents/teleport") + end, + on_die = function (self) + local m = game.zone:makeEntityByName(game.level, "actor", "ARCANEBLADE") + if m then + game.zone:addEntity(game.level, m, "actor", 8, 2) + end + end +} + +newEntity{ name = "arcane blade", + define_as = "ARCANEBLADE", + type = "humanoid", subtype = "human", + color=colors.GOLD, display = "p", + + combat = { dam=resolvers.rngavg(6,12), atk=3, apr=6, physspeed=2 }, + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, + resolvers.drops{chance=100, nb=1, {type="weapon", ego_chance=100}}, + resolvers.drops{chance=100, nb=2, {type="money"} }, + + life_rating = 15, + rank = 3, + infravision = 10, + lite = 2, + open_door = true, + autolevel = "warrior", + ai = "dumb_talented_simple", ai_state = { ai_move = "move_dmap", talent_in = 1 }, + energy = { mod = 1 }, + + stats = { str=20, dex=20, mag=8, con=16 }, + desc = [[A human Arcane Blade. His body shows multiple scars from battle.]], + level_range = {6, 21}, exp_worth = 2, + rarity = false, + max_life = resolvers.rngavg(100,130), + resolvers.equip{ + {type="weapon", subtype="greatsword", autoreq=true}, + {type="armor", subtype="heavy", autoreq=true}, + {type="armor", subtype="feet", autoreq=true}, + }, + resolvers.talents{ + [Talents.T_ARCANE_COMBAT]=2, + [Talents.T_ARCANE_FEED]=2, + [Talents.T_FLAME]=1, + [Talents.T_HEAVY_ARMOUR_TRAINING] = 2, + [Talents.T_WEAPON_COMBAT] = 2, + }, + on_added = function (self) + game.level.map:particleEmitter(self.x, self.y, 1, "teleport") + game:playSoundNear(game.player, "talents/teleport") + end, + on_die = function (self) + local Chat = require "engine.Chat" + local chat = Chat.new("arena-unlock", {name="Cornac rogue"}, game.player) + chat:invoke("win") + end +} diff --git a/game/modules/tome/data/zones/arena-unlock/objects.lua b/game/modules/tome/data/zones/arena-unlock/objects.lua new file mode 100644 index 0000000000000000000000000000000000000000..4e7b8c6c6405bb629806f2279b08ee9e45700cf7 --- /dev/null +++ b/game/modules/tome/data/zones/arena-unlock/objects.lua @@ -0,0 +1,20 @@ +-- 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 + +load("/data/general/objects/objects.lua") diff --git a/game/modules/tome/data/zones/arena-unlock/traps.lua b/game/modules/tome/data/zones/arena-unlock/traps.lua new file mode 100644 index 0000000000000000000000000000000000000000..f8f844f583d30b1999a9bf7a520221cdebdfa557 --- /dev/null +++ b/game/modules/tome/data/zones/arena-unlock/traps.lua @@ -0,0 +1,20 @@ +-- 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 + +load("/data/general/traps/elemental.lua") diff --git a/game/modules/tome/data/zones/arena-unlock/zone.lua b/game/modules/tome/data/zones/arena-unlock/zone.lua new file mode 100644 index 0000000000000000000000000000000000000000..c7c848351cf2541d111b96b979994c6901308e63 --- /dev/null +++ b/game/modules/tome/data/zones/arena-unlock/zone.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 + +return { + name = "Derth (Southeast)", + level_range = {5, 12}, + level_scheme = "player", + max_level = 1, + actor_adjust_level = function(zone, level, e) return zone.base_level + e:getRankLevelAdjust() + level.level-1 + rng.range(-1,2) end, + width = 17, height = 16, + all_remembered = true, + all_lited = true, + persistant = "zone", + ambiant_music = "a_lomos_del_dragon_blanco.ogg", + no_level_connectivity = true, + generator = { + map = { + class = "engine.generator.map.Static", + map = "zones/arena-unlock", + }, + }, + post_process = function(level) + local m = game.zone:makeEntityByName(game.level, "actor", "GLADIATOR") + if m then + game.zone:addEntity(game.level, m, "actor", 8, 2) + end + end +} diff --git a/game/modules/tome/data/zones/arena/grids.lua b/game/modules/tome/data/zones/arena/grids.lua new file mode 100644 index 0000000000000000000000000000000000000000..915aa9b4b1246f6b72c4d3e0d9735938d5a8b920 --- /dev/null +++ b/game/modules/tome/data/zones/arena/grids.lua @@ -0,0 +1,43 @@ +-- 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 + +load("/data/general/grids/basic.lua") +load("/data/general/grids/sand.lua") + +newEntity{ + define_as = "WALL_SEE", + name = "wall", image = "terrain/granite_wall1.png", + display = '#', color_r=255, color_g=255, color_b=255, back_color=colors.GREY, + always_remember = true, + does_block_move = true, + can_pass = {pass_wall=1}, + block_sight = false, + air_level = -20, + dig = "FLOOR", +} + +newEntity{ + define_as = "LOCK", + name = "closed gate", image = "terrain/sealed_door.png", + display = '+', color=colors.WHITE, back_color=colors.DARK_UMBER, + notice = true, + always_remember = true, + block_sight = true, + does_block_move = true, +} diff --git a/game/modules/tome/data/zones/arena/npcs.lua b/game/modules/tome/data/zones/arena/npcs.lua new file mode 100644 index 0000000000000000000000000000000000000000..4dcf86abda0ea64df62b5f0381b4181553fb9938 --- /dev/null +++ b/game/modules/tome/data/zones/arena/npcs.lua @@ -0,0 +1,1048 @@ +-- 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 + +-- Load all others +load("/data/general/npcs/all.lua") +load("/data/general/npcs/crystal.lua") +load("/data/general/npcs/bone-giant.lua") +load("/data/general/npcs/ritch.lua") +load("/data/general/npcs/ziguranth.lua") +local Talents = require("engine.interface.ActorTalents") + +--Base arena human +newEntity{ define_as = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + display = "@", color=colors.UMBER, + + combat = { dam=resolvers.rngavg(5,12), atk=2, apr=6, physspeed=2 }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, + resolvers.drops{chance=25, nb=1, {} }, + resolvers.drops{chance=100, nb=1, {type="money"} }, + infravision = 10, + lite = 2, + life_rating = 10, rank = 2, size_category = 3, + open_door = true, + autolevel = "warrior", + stun_immune = 0.2, + confusion_immune = 0.1, + fear_immune = 0.1, + ai = "dumb_talented_simple", ai_state = { ai_move = "move_dmap", talent_in = 1 }, + energy = { mod = 1 }, + stats = { str = 10, dex = 10, mag = 10, con = 10 }, + resolvers.talents{ [Talents.T_HEAVY_ARMOUR_TRAINING] = 2, }, + resolvers.equip{ + {type="armor", subtype="feet", autoreq=true}, + }, +} + +newEntity{ name = "wisp", + type = "elemental", subtype = "light", + display = "*", color=colors.YELLOW, + desc = [[A floating orb of magical energy. It shines with a radiant light. They explode Pupon contact.]], + combat = { dam=10, atk=5, apr=10, physspeed=1 }, + level_range = {1, nil}, + exp_worth = 1, + max_life = 10, + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, QUIVER=1 }, + no_drops = true, open_door = false, + infravision = 20, + rarity = 1, + lite = 4, + life_rating = 1, rank = 1, size_category = 1, + autolevel = "caster", + ai = "dumb_talented_simple", ai_state = { ai_move = "move_dmap", talent_in = 1 }, + energy = { mod = 1 }, + stats = { str = 9, dex = 20, mag = 20 }, + resolvers.talents{ + [Talents.T_EXPLODE] = 3, + }, + no_breath = 1, + blind_immune = 1, + fear_immune = 1, + rank = 2, + size_category = 1, + poison_immune = 1, + disease_immune = 1, + poison_immune = 1, + stun_immune = 1, + resolvers.sustains_at_birth(), +} + +--Minibosses + +newEntity{ name = "skeletal rat", + base = "BASE_NPC_RODENT", + define_as = "SKELERAT", + desc = [[The diminutive skeleton of a giant rat, charged with evil energies. Nobody understands the usefulness of undead rodents until several of them come after you]], + color = colors.GOLD, + level_range = {3, 4}, + exp_worth = 2, + rank = 3, + max_life = resolvers.rngavg(30, 45), + combat = { dam = 7, atk = 15, apr = 10 }, + stats = { str=8, dex=15, mag=1, con=5 }, + combat_def = 2, + resolvers.talents{ + [Talents.T_SOUL_ROT]=1, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "golden crystal", + base = "BASE_NPC_CRYSTAL", + define_as = "GOLDCRYSTAL", + color=colors.GOLD, + desc = "A formation of golden crystal. It emits a golden radiance equalling the sun itself.", + level_range = {1, 99}, + exp_worth = 2, + rank = 3, + resolvers.drops{chance=100, nb=1, {type="jewelry", ego_chance=100}}, + max_life = resolvers.rngavg(12,34), + resists = { [DamageType.LIGHT] = 100 }, + resolvers.talents{ + [Talents.T_SEARING_LIGHT]=1, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "master alchemist", + define_as = "MASTERALCHEMIST", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.ORANGE, + + life_rating = 8, + + stats = { str = 12, dex = 20, cun = 5, mag = 20, con = 10 }, + desc = [[Deadly fighters using explosive gems to attack.]], + level_range = {15, nil}, exp_worth = 2, + rarity = 10, + rank = 3, + max_life = 120, + 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=1, {type="wand", ego_chance=100}}, + autolevel = "dexmage", + combat_def = 3, + resolvers.talents{ + [Talents.T_SHOCKWAVE_BOMB]=3, + [Talents.T_EXPLOSION_EXPERT]=3, + [Talents.T_ALCHEMIST_PROTECTION]=5, + [Talents.T_STAFF_MASTERY]=4, + [Talents.T_FIRE_INFUSION]=3, + [Talents.T_ACID_INFUSION]=3, + [Talents.T_GEM_PORTAL]=2, + [Talents.T_ELEMENTAL_BOLT]=2, + }, + resolvers.generic(function(self) + local t = self:getTalentFromId(self.T_CREATE_ALCHEMIST_GEMS) + local gem = t.make_gem(self, t, "GEM_AGATE") + self:wearObject(gem, true, false) + end), +} + +newEntity{ name = "multihued wyrmic", + define_as = "MULTIHUEWYRMIC", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GOLD, + female = true, + shader = "quad_hue", + resolvers.drops{chance=100, nb=3, {type="scroll"} }, + stats = { str = 20, dex = 20, mag = 20, con = 9, wil = 15 }, + desc = [[A powerful wyrmic with mastery over several elements.]], + level_range = {20, nil}, exp_worth = 2, + rarity = 10, + rank = 3, + instakill_immune = 1, + stun_immune = 0.8, + confusion_immune = 0.7, + max_life = 180, + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + }, + combat_armor = 0, combat_def = 6, + resolvers.talents{ + [Talents.T_LIGHTNING_SPEED] = 5, + [Talents.T_ICE_CLAW] = 4, + [Talents.T_WING_BUFFET] = 3, + [Talents.T_ICY_SKIN] = 2, + [Talents.T_TORNADO] = 1, + [Talents.T_DEATH_DANCE] = 3, + [Talents.T_RUSH] = 3, + [Talents.T_WEAPON_COMBAT]=2, + [Talents.T_WEAPONS_MASTERY]=2, + }, +} + +newEntity{ name = "master slinger", + define_as = "MASTERSLINGER", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GOLD, + + life_rating = 11, + rank = 3, + autolevel = "slinger", + stats = { str=11, dex=15, cun = 15, mag=1, con=13 }, + desc = [[Expert slingers on hire by the arena. They are great at their jobs.]], + level_range = {12, nil}, exp_worth = 2, + rarity = 10, + max_life = resolvers.rngavg(100,110), + resolvers.equip{ + {type="weapon", subtype="sling", autoreq=true}, + {type="ammo", subtype="shot", autoreq=true}, + {type="armor", subtype="shield", autoreq=true}, + }, + combat_armor = 1, combat_def = 4, + resolvers.talents{ + [Talents.T_SHOOT]=1, + [Talents.T_DISENGAGE]=2, + [Talents.T_RAPID_SHOT]=3, + [Talents.T_INERTIAL_SHOT]=3, + [Talents.T_HEAVE]=3, + [Talents.T_SHIELD_EXPERTISE]=1, + [Talents.T_SLING_MASTERY]=1, + }, +} + +newEntity{ name = "gladiator", + define_as = "GLADIATOR", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GOLD, + + life_rating = 15, + rank = 3, + + stats = { str=20, dex=10, mag=1, con=16 }, + desc = [[Mercenaries hired by the arena for the sole purpose of entertainment. They make a living beating people up.]], + level_range = {7, nil}, exp_worth = 2, + rarity = 10, + max_life = resolvers.rngavg(80,100), + resolvers.equip{ + {type="weapon", subtype="mace", autoreq=true}, + {type="armor", subtype="shield", autoreq=true}, + {type="armor", subtype="heavy", autoreq=true}, + }, + resolvers.drops{chance=100, nb=1, {type="weapon", ego_chance=100}}, + combat_def = 4, + resolvers.talents{ + [Talents.T_SHIELD_PUMMEL]=2, + [Talents.T_RUSH]=1, + [Talents.T_REPULSION]=2, + }, +} + +newEntity{ name = "reaver", + define_as = "REAVER", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GOLD, + stats = { str = 20, dex = 20, mag = 20, con = 9, wil = 15 }, + desc = [[A warrior of death.]], + level_range = {25, nil}, exp_worth = 2, + rarity = 10, + rank = 3, + max_life = 150, + resolvers.equip{ + {type="weapon", subtype="longsword", autoreq=true}, + {type="weapon", subtype="longsword", autoreq=true}, + {type="armor", subtype="heavy", autoreq=true}, + }, + autolevel = "warriormage", + combat_def = 4, + resolvers.talents{ + [Talents.T_DRAIN] = 4, + [Talents.T_BONE_SPEAR] = 3, + [Talents.T_BONE_GRAB] = 2, + [Talents.T_BONE_SHIELD] = 2, + [Talents.T_VIRULENT_DISEASE] = 2, + [Talents.T_CORRUPTED_STRENGTH] = 2, + [Talents.T_REND] = 2, + [Talents.T_RUIN] = 2, + [Talents.T_ACID_BLOOD] = 2, + [Talents.T_WEAPON_COMBAT] = 2, + + }, +} + +newEntity{ name = "headless horror", + base = "BASE_NPC_HORROR", define_as = "HEADLESSHORROR", + color=colors.GOLD, + desc ="A headless gangly humanoid with a large distended stomach. Was captured and tamed by the first master of the arena.", + level_range = {30, nil}, exp_worth = 1, + rarity = 3, + rank = 3, + autolevel = "warrior", + ai = "dumb_talented_simple", ai_state = { ai_move="move_dmap", talent_in=1, }, + combat = { dam=18, atk=18, apr=10, dammod={str=1} }, + combat = {damtype=DamageType.PHYSICAL}, + no_auto_resists = true, + + resolvers.talents{ + [Talents.T_MANA_CLASH]=4, + [Talents.T_GRAB]=4, + }, + + on_added = function(self) + local eyes = {} + for i = 1, 3 do + local x, y = util.findFreeGrid(self.x, self.y, 15, true, {[engine.Map.ACTOR]=true}) + if x and y then + local m = game.zone:makeEntity(game.level, "actor", {properties={"is_eldritch_eye"}, special_rarity="_eldritch_eye_rarity"}, nil, true) + if m then + m.summoner = self + game.zone:addEntity(game.level, m, "actor", x, y) + eyes[m] = true + + local damtype = next(m.resists) + self.resists[damtype] = 100 + self.resists.all = (self.resists.all or 0) + 30 + end + end + end + self.eyes = eyes + end, + on_die = function(self, src) + local nb = 0 + for eye, _ in pairs(self.eyes) do + if not eye.dead then eye:die(src) nb = nb + 1 end + end + if nb > 0 then + game.logSeen(self, "#AQUAMARINE#As %s falls all its eyes fall to the ground!", self.name) + end + end, +} + + +--Bosses + +newEntity{ name = "Ryal", + base = "BASE_NPC_BONE_GIANT", + color=colors.VIOLET, + define_as = "ARENA_BOSS_RYAL", + desc = "A gargantuan bone giant resembling a wingless wyrm. He is fully sentient and surprisingly fast.", + rank = 4, unique = true, + resolvers.equip{ + {type="weapon", subtype="trident", autoreq=true, special_rarity="trident_rarity"}, + }, + instakill_immune = 1, + level_range = {9, nil}, exp_worth = 3, + max_life = resolvers.rngavg(120,150), + combat_armor = 0, combat_def = 10, + melee_project = {[DamageType.BLIGHT]=resolvers.mbonus(15, 1)}, + autolevel = "warriormage", + talent_cd_reduction={ + [Talents.T_KNOCKBACK] = 2, + }, + resolvers.talents { + [Talents.T_EXOTIC_WEAPONS_MASTERY]=2, + [Talents.T_BONE_SHIELD]=2, + [Talents.T_THROW_BONES]=1, + [Talents.T_KNOCKBACK]=3, + [Talents.T_BLINDING_SPEED]=1, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "Fryjia Loren", + define_as = "ARENA_BOSS_FRYJIA", + type = "humanoid", subtype = "human", + display = "@", + color=colors.VIOLET, + desc = [[A young girl with skin pale as snow. She is small, but deadly in battle, unleashing a constant barrage of ice shards in battle.]], + level_range = {12, nil}, exp_worth = 3, + rank = 4, + size_category = 2, + female = true, + max_life = 75, life_rating = 11, + infravision = 20, + stats = { str=10, dex=20, cun=10, mag=15, con=10, wil = 15 }, + resists={[DamageType.FIRE] = -100, [DamageType.ICE] = 60}, + + instakill_immune = 1, + stun_immune = 0.5, + fear_immune = 1, + + open_door = true, + + autolevel = "dexmage", + ai = "dumb_talented_simple", ai_state = { talent_in=1, ai_move="move_astar", }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, HEAD=1, FEET=1 }, + + resolvers.equip{ + {type="weapon", subtype="dagger", autoreq=true}, + {type="weapon", subtype="dagger", autoreq=true}, + {type="armor", subtype="light", autoreq=true}, + {type="armor", subtype="light", autoreq=true} + }, + talent_cd_reduction={ + [Talents.T_ICE_SHARDS] = 3, + [Talents.T_LIGHTNING_SPEED] = 4, + [Talents.T_DISENGAGE] = 5, + [Talents.T_RUSH] = 3, + }, + resolvers.talents{ + [Talents.T_DUAL_WEAPON_TRAINING] = 2, + [Talents.T_DUAL_WEAPON_DEFENSE] = 1, + [Talents.T_DUAL_STRIKE] = 1, + [Talents.T_RUSH] = 4, + [Talents.T_SUPPRESSION] = 3, + [Talents.T_PITY] = 1, + [Talents.T_MANAFLOW] = 3, + [Talents.T_QUICK_RECOVERY] = 5, + [Talents.T_DISENGAGE] = 5, + [Talents.T_ICE_CLAW] = 3, + [Talents.T_LIGHTNING_SPEED] = 2, + [Talents.T_UTTERCOLD]=2, + [Talents.T_TIDAL_WAVE]=2, + [Talents.T_ICE_SHARDS]=3, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "Riala Shalarak", + define_as = "ARENA_BOSS_RIALA", + type = "humanoid", subtype = "human", + display = "@", + color=colors.VIOLET, + desc = [[A strong female magician. Years of experience make her a dangerous combatant.]], + level_range = {25, nil}, exp_worth = 3, + rank = 4, + size_category = 3, + female = true, + max_life = 150, life_rating = 30, + infravision = 20, + stats = { str=15, dex=15, cun=20, mag=30, con=15, wil=15 }, + resists={[DamageType.FIRE] = 100, [DamageType.ICE] = -20}, + + instakill_immune = 1, + stun_immune = 0.5, + fear_immune = 0.5, + open_door = true, + + autolevel = "caster", + ai = "dumb_talented_simple", ai_state = { talent_in=1, ai_move="move_astar", }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, HEAD=1, FEET=1 }, + + summon = {{name = "wisp", number=3, hasxp=false}}, + + resolvers.equip{ + {type="weapon", subtype="staff", autoreq=true}, + {type="armor", subtype="cloth", autoreq=true} + }, + resolvers.talents{ + [Talents.T_FIREFLASH] = 2, + [Talents.T_ILLUMINATE] = 5, + [Talents.T_STRIKE] = 2, + [Talents.T_SUMMON] = 1, + [Talents.T_ELEMENTAL_BOLT] = 3, + [Talents.T_PHASE_DOOR] = 3, + [Talents.T_DISRUPTION_SHIELD] = 2, + [Talents.T_STAFF_MASTERY] = 2, + [Talents.T_DEFENSIVE_POSTURE] = 2, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "Valfren Loren", + define_as = "ARENA_BOSS_VALFREN", + type = "humanoid", subtype = "human", + display = "@", + color=colors.VIOLET, + desc = [[A massive armor with a massive axe containing a massively cursed person. He is cursed to fight eternally.]], + level_range = {16, nil}, exp_worth = 3, + rank = 4, + size_category = 4, + female = false, + max_life = 160, life_rating = 15, + infravision = 0, + stats = { str=25, dex=25, cun=5, mag=10, con=15, wil=35 }, + resists={[DamageType.DARKNESS] = 100, [DamageType.LIGHT] = -70}, + + instakill_immune = 1, + + open_door = true, + + autolevel = "warrior", + ai = "dumb_talented_simple", ai_state = { talent_in=1, ai_move="move_astar", }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, HEAD=1, FEET=1 }, + + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true, ego_chance=100}, + {type="armor", subtype="massive", autoreq=true, ego_chance=100} + }, + resolvers.talents{ + [Talents.T_GLOOM] = 5, + [Talents.T_BLINDSIDE] = 3, + [Talents.T_SLASH] = 4, + [Talents.T_SEETHE] = 5, + [Talents.T_RAMPAGE] = 8, + [Talents.T_BRUTALITY] = 3, + [Talents.T_INSTINCTS] = 3, + [Talents.T_RECKLESS_CHARGE] = 5, + [Talents.T_MASSIVE_ARMOUR_TRAINING] = 5, + [Talents.T_WEAPON_COMBAT] = 2, + }, + resolvers.sustains_at_birth(), +} + +newEntity{ name = "Rej Arkatis", + define_as = "ARENA_BOSS_MASTER_DEFAULT", + type = "humanoid", subtype = "human", + display = "@", + color=colors.VIOLET, + desc = [[A cornac fighter of respectable talent. He emerged from nowhere and became the true master of fighting.]], + level_range = {1, nil}, exp_worth = 3, + rank = 5, + size_category = 3, + female = false, + max_life = 180, life_rating = 20, + infravision = 20, + stats = { str=15, dex=20, cun=25, mag=20, con=15, wil=35 }, + resists={[DamageType.DARKNESS] = -20}, + no_drops = true, + + instakill_immune = 1, + stun_immune = 0.7, + fear_immune = 0.6, + open_door = true, + + ai = "dumb_talented_simple", ai_state = { talent_in=1, ai_move="move_astar", }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, HEAD=1, FEET=1 }, + + resolvers.equip{ + {type="weapon", subtype="dagger", autoreq=true, ego_chance=100}, + {type="weapon", subtype="dagger", autoreq=true, ego_chance=100}, + {type="armor", subtype="cloak", autoreq=true, ego_chance=100}, + }, + autolevel = "rogue", + combat_def = 2, + resolvers.talents{ + [Talents.T_STEALTH]=5, + [Talents.T_SHADOW_CUNNING]=4, + [Talents.T_SHADOWSTEP]=3, + [Talents.T_SHADOW_COMBAT]=3, + [Talents.T_SHADOW_FEED]=2, + [Talents.T_DEADLY_STRIKES]=2, + [Talents.T_ILLUMINATE]=4, + [Talents.T_PHASE_DOOR]=4, + [Talents.T_HIDE_IN_PLAIN_SIGHT]=5, + [Talents.T_STICKY_SMOKE]=3, + [Talents.T_DISENGAGE]=5, + [Talents.T_RUSH]=5, + [Talents.T_WILLFUL_COMBAT]=3, + [Talents.T_DUAL_WEAPON_TRAINING] = 5, + }, + resolvers.sustains_at_birth(), +} + +--Regular + +newEntity{ name = "slinger", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.UMBER, + life_rating = 8, stats = { str = 7, dex = 10, cun = 10, mag = 1, con = 7 }, + desc = [[Ranged fighters coming to the arena looking for wealth and glory, just like you.]], + level_range = {4, nil}, exp_worth = 1, + rarity = 1, + max_life = resolvers.rngavg(80,95), + resolvers.equip{ + {type="weapon", subtype="sling", autoreq=true}, + {type="ammo", subtype="shot", autoreq=true}, + }, + autolevel = "slinger", + combat_def = 2, + resolvers.talents{ + [Talents.T_SHOOT]=1, + [Talents.T_DISENGAGE]=2, + [Talents.T_RAPID_SHOT]=2 + }, +} + +newEntity{ name = "high slinger", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.DARK_UMBER, + life_rating = 8, stats = { str = 7, dex = 10, cun = 10, mag = 1, con = 7 }, + desc = [[Ranged fighters coming to the arena looking for wealth and glory, just like you.]], + level_range = {4, nil}, exp_worth = 1, + rarity = 1, + max_life = resolvers.rngavg(80,95), + resolvers.equip{ + {type="weapon", subtype="sling", autoreq=true}, + {type="ammo", subtype="shot", autoreq=true}, + }, + autolevel = "slinger", + combat_def = 2, + resolvers.talents{ + [Talents.T_SHOOT]=1, + [Talents.T_DISENGAGE]=3, + [Talents.T_HEAVE]=1, + [Talents.T_RAPID_SHOT]=4, + [Talents.T_INERTIAL_SHOT]=2, + }, +} + +newEntity{ name = "alchemist", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.ORANGE, + + life_rating = 8, + + stats = { str = 7, dex = 11, cun = 1, mag = 11, con = 6 }, + desc = [[Deadly fighters using explosive gems to attack.]], + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + max_life = 60, + equipment = resolvers.equip{ + {type="weapon", subtype="staff", autoreq=true}, + {type="armor", subtype="cloth", autoreq=true}, + }, + resolvers.drops{chance=40, {type="wand", ego_chance=100}}, + autolevel = "dexmage", + combat_def = 2, + resolvers.talents{ + [Talents.T_THROW_BOMB]=2, + [Talents.T_ALCHEMIST_PROTECTION]=4, + [Talents.T_LIGHTNING_INFUSION]=2, + [Talents.T_ACID_INFUSION]=2, + [Talents.T_FIRE_INFUSION]=2, + [Talents.T_ELEMENTAL_BOLT]=1, + }, + resolvers.generic(function(self) + local t = self:getTalentFromId(self.T_CREATE_ALCHEMIST_GEMS) + local gem = t.make_gem(self, t, "GEM_AGATE") + self:wearObject(gem, true, false) + end), +} + +newEntity{ name = "blood mage", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.CRIMSON, + + life_rating = 4, + + stats = { str = 7, dex = 11, cun = 1, mag = 11, con = 6 }, + desc = [[A man dressed in black robes. You feel weak as you hear their ominous chanting.]], + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + max_life = 40, + equipment = resolvers.equip{ + {type="weapon", subtype="staff", autoreq=true}, + {type="armor", subtype="cloth", autoreq=true}, + }, + resolvers.drops{chance=40, {type="wand", ego_chance=100}}, + autolevel = "caster", + resolvers.talents{ + [Talents.T_BLOOD_GRASP] = 4, + [Talents.T_CURSE_OF_VULNERABILITY] = 2, + [Talents.T_PHASE_DOOR] = 1, + }, +} + +newEntity{ name = "hexer", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.DARK_GREY, + + life_rating = 1, + + stats = { str = 7, dex = 11, cun = 1, mag = 15, con = 6 }, + desc = [[A man dressed in black robes. You feel a thousand curses being put on you.]], + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + max_life = 10, + equipment = resolvers.equip{ + {type="weapon", subtype="staff", autoreq=true}, + {type="armor", subtype="cloth", autoreq=true}, + }, + resolvers.drops{chance=40, {type="wand", ego_chance=100}}, + autolevel = "caster", + resolvers.talents{ + [Talents.T_BURNING_HEX]=3, + [Talents.T_EMPATHIC_HEX]=1, + [Talents.T_HYMN_OF_SHADOWS]=3, + [Talents.T_SOUL_ROT]=3, + }, +} + +newEntity{ name = "rogue", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.DARK_BLUE, + + life_rating = 8, + + stats = { str = 7, dex = 10, cun = 28, mag = 1, con = 7 }, + desc = [[Stealthy fighters trying to achieve victory with trickery. Be careful or they will steal your sight!]], + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + max_life = 60, + resolvers.equip{ + {type="weapon", subtype="dagger", autoreq=true}, + {type="weapon", subtype="dagger", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + {name="rough leather gloves", autoreq=true}, + }, + autolevel = "rogue", + combat_def = 2, + resolvers.talents{ + [Talents.T_STEALTH]=3, + [Talents.T_HIDE_IN_PLAIN_SIGHT]=5, + [Talents.T_STICKY_SMOKE]=2, + [Talents.T_DISENGAGE]=1, + [Talents.T_DUAL_WEAPON_TRAINING] = 1, + }, +} + +newEntity{ name = "shadowblade", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.BLACK, + + life_rating = 10, + + stats = { str = 7, dex = 10, cun = 28, mag = 1, con = 7 }, + desc = [[Stealthy fighters trying to achieve victory with trickery. Be careful or they will steal your life!]], + level_range = {1, nil}, exp_worth = 1, + rarity = 1, + max_life = 100, + resolvers.equip{ + {type="weapon", subtype="dagger", autoreq=true}, + {type="weapon", subtype="dagger", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + {name="rough leather gloves", autoreq=true}, + }, + autolevel = "rogue", + combat_def = 2, + resolvers.talents{ + [Talents.T_STEALTH]=4, + [Talents.T_SHADOW_CUNNING]=3, + [Talents.T_SHADOWSTEP]=1, + [Talents.T_SHADOW_COMBAT]=2, + [Talents.T_ILLUMINATE]=3, + [Talents.T_HIDE_IN_PLAIN_SIGHT]=5, + [Talents.T_DISENGAGE]=2, + [Talents.T_DUAL_WEAPON_TRAINING] = 3, + }, +} + +newEntity{ name = "fire wyrmic", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.RED, + resolvers.drops{chance=30, nb=1, {type="scroll"}}, + stats = { str = 10, dex = 10, mag = 10, con = 9, wil = 15}, + desc = [[A fire wyrmic aspiring to win the tournament. He comes paired with an ice wyrmic.]], + level_range = {1, nil}, exp_worth = 1, autolevel = "wyrmic", + rarity = 1, + max_life = resolvers.rngavg(90,105), + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + }, + combat_armor = 1, combat_def = 4, + resolvers.talents{ + [Talents.T_BELLOWING_ROAR] = 1, + [Talents.T_WING_BUFFET] = 2, + [Talents.T_DEVOURING_FLAME] = 1, + [Talents.T_DEATH_DANCE] = 2, + [Talents.T_WEAPONS_MASTERY]=2, + }, +} + +newEntity{ name = "ice wyrmic", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.BLUE, + resolvers.drops{chance=30, nb=1, {type="scroll"}}, + stats = { str = 10, dex = 10, mag = 10, con = 9, wil = 15}, + desc = [[An ice wyrmic aspiring to win the tournament. He comes paired with a fire wyrmic.]], + level_range = {1, nil}, exp_worth = 1, autolevel = "wyrmic", + rarity = 1, + female = true, + max_life = resolvers.rngavg(90,105), + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + }, + combat_armor = 1, combat_def = 4, + resolvers.talents{ + [Talents.T_ICE_CLAW] = 3, + [Talents.T_ICY_SKIN] = 3, + [Talents.T_ICE_WALL] = 2, + [Talents.T_RUSH] = 2, + [Talents.T_WEAPON_COMBAT]=2, + }, +} + +newEntity{ name = "sand wyrmic", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color = {r = 204, g = 255, b = 95}, + resolvers.drops{chance=30, nb=1, {type="scroll"}}, + stats = { str = 10, dex = 10, mag = 10, con = 9, wil = 15}, + desc = [[A sand wyrmic aspiring to win the tournament. He comes paired with a storm wyrmic.]], + level_range = {1, nil}, exp_worth = 1, autolevel = "wyrmic", + rarity = 1, + max_life = resolvers.rngavg(90,105), + + make_escort = { + {name="storm wyrmic", number=1}, + }, + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + }, + combat_armor = 1, combat_def = 4, + resolvers.talents{ + [Talents.T_EARTHEN_MISSILES] = 3, + [Talents.T_SAND_BREATH] = 2, + [Talents.T_NATURE_TOUCH] = 2, + [Talents.T_DEATH_DANCE] = 2, + [Talents.T_WEAPONS_MASTERY]=2, + }, +} + +newEntity{ name = "storm wyrmic", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.WHITE, + resolvers.drops{chance=30, nb=1, {type="scroll"}}, + stats = { str = 10, dex = 10, mag = 10, con = 9, wil = 15}, + desc = [[A storm wyrmic aspiring to win the tournament. He comes paired with a sand wyrmic.]], + level_range = {1, nil}, exp_worth = 1, autolevel = "wyrmic", + rarity = 1, + female = true, + max_life = resolvers.rngavg(90,105), + + resolvers.equip{ + {type="weapon", subtype="battleaxe", autoreq=true}, + {type="armor", subtype="cloak", autoreq=true}, + }, + combat_armor = 1, combat_def = 4, + resolvers.talents{ + [Talents.T_LIGHTNING_SPEED] = 4, + [Talents.T_STATIC_FIELD] = 2, + [Talents.T_TORNADO] = 1, + [Talents.T_DISENGAGE] = 3, + [Talents.T_WEAPON_COMBAT]=2, + }, +} + +newEntity{ name = "high gladiator", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.DARK_RED, + + life_rating = 11, + + stats = { str=15, dex=15, mag=1, con=15, wil=20 }, + desc = [[Mercenaries hired by the arena for the sole purpose of entertainment. They make a living beating people up.]], + level_range = {14, nil}, exp_worth = 1, + rarity = 10, + max_life = 90, + resolvers.equip{ + {type="weapon", subtype="mace", autoreq=true}, + {type="armor", subtype="shield", autoreq=true}, + {type="armor", subtype="heavy", autoreq=true}, + }, + autolevel = "warrior", + resolvers.drops{chance=50, nb=1, {type="weapon", ego_chance=20}}, + combat_def = 3, + resolvers.talents{ + [Talents.T_SHIELD_PUMMEL]=2, + [Talents.T_RUSH]=2, + [Talents.T_DISENGAGE]=2, + [Talents.T_REPULSION]=2, + [Talents.T_OVERPOWER]=2, + [Talents.T_WEAPONS_MASTERY]=1, + }, +} + +newEntity{ name = "great gladiator", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.VERY_DARK_RED, + + life_rating = 12, + + stats = { str=20, dex=20, mag=5, con=15, wil=25 }, + desc = [[Mercenaries hired by the arena for the sole purpose of entertainment. They make a living beating people up.]], + level_range = {19, nil}, exp_worth = 2, + rarity = 10, + max_life = 120, + resolvers.equip{ + {type="weapon", subtype="trident", autoreq=true, special_rarity="trident_rarity"}, + {type="armor", subtype="heavy", autoreq=true}, + }, + autolevel = "warrior", + resolvers.drops{chance=75, nb=1, {type="weapon", ego_chance=40}}, + combat_def = 4, + resolvers.talents{ + [Talents.T_RUSH]=5, + [Talents.T_EXOTIC_WEAPONS_MASTERY]=2, + [Talents.T_JUGGERNAUT]=3, + [Talents.T_DEATH_DANCE]=3, + [Talents.T_BATTLE_CRY]=2, + [Talents.T_WEAPONS_MASTERY]=2, + [Talents.T_WEAPON_COMBAT]=2, + }, +} + +newEntity{ name = "martyr", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.SALMON, + life_rating = 12, + + stats = { str=15, dex=15, mag=20, con=10, wil=15 }, + desc = [[A devout soldier.]], + level_range = {15, nil}, exp_worth = 2, + rarity = 10, + max_life = 200, + resolvers.equip{ + {type="weapon", subtype="longsword", ego_chance=10, autoreq=true}, + {type="armor", subtype="shield", ego_chance=30, autoreq=true}, + }, + autolevel = "caster", + combat_def = 4, + resolvers.talents{ + [Talents.T_MARTYRDOM]=2, + [Talents.T_RUSH]=2, + [Talents.T_CHANT_OF_FORTRESS]=3, + [Talents.T_RETRIBUTION]=1, + [Talents.T_WEAPONS_MASTERY]=2, + [Talents.T_SHIELD_EXPERTISE]=2, + }, +} + +newEntity{ name = "anorithil", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GREY, + life_rating = 12, + + stats = { str=10, dex=10, mag=15, con=10, wil=15 }, + desc = [[Divine warriors from afar. They use the power of light and darkness against you!]], + level_range = {15, nil}, exp_worth = 2, + rarity = 10, + max_life = 120, + resolvers.equip{ + {type="weapon", subtype="staff", ego_chance=30, autoreq=true}, + {type="armor", subtype="cloth", ego_chance=30, autoreq=true}, + }, + autolevel = "caster", + resolvers.drops{chance=50, nb=1, {type="weapon", subtype="staff", ego_chance=100}}, + combat_def = 4, + resolvers.talents{ + [Talents.T_CIRCLE_OF_SHIFTING_SHADOWS]=2, + [Talents.T_CIRCLE_OF_BLAZING_LIGHT]=2, + [Talents.T_TWILIGHT]=2, + [Talents.T_SEARING_LIGHT]=2, + [Talents.T_MOONLIGHT_RAY]=1, + [Talents.T_HYMN_OF_SHADOWS]=2, + [Talents.T_CHANT_OF_FORTRESS]=2, + }, +} + +newEntity{ name = "sun paladin", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.LIGHT_UMBER, + life_rating = 12, + + stats = { str=20, dex=20, mag=15, con=10, wil=15 }, + desc = [[Divine warriors from afar. They wield the power of light, and a mean sword.]], + level_range = {15, nil}, exp_worth = 2, + rarity = 10, + max_life = 150, + resolvers.equip{ + {type="weapon", subtype="longsword", ego_chance=30, autoreq=true}, + {type="armor", subtype="massive", autoreq=true}, + {type="armor", subtype="shield", ego_chance=30, autoreq=true}, + }, + resolvers.drops{chance=50, nb=1, {type="weapon", subtype="staff", ego_chance=100}}, + combat_def = 4, + autolevel = "warriormage", + resolvers.talents{ + [Talents.T_CHANT_OF_FORTITUDE]=3, + [Talents.T_WEAPON_OF_LIGHT]=2, + [Talents.T_WAVE_OF_POWER]=2, + [Talents.T_CRUSADE]=3, + [Talents.T_SHIELD_OF_LIGHT]=1, + [Talents.T_BRANDISH]=1, + [Talents.T_MASSIVE_ARMOUR_TRAINING]=2, + [Talents.T_WEAPON_COMBAT]=2, + }, +} + +newEntity{ name = "star crusader", + base = "BASE_NPC_ARENA1", + type = "humanoid", subtype = "human", + color=colors.GOLD, + life_rating = 10, + + stats = { str=25, dex=25, mag=25, con=8, wil=25 }, + desc = [[Divine warriors from afar. They wield the power of light, and a mean sword. Darkness too.]], + level_range = {20, nil}, exp_worth = 2, + rarity = 10, + max_life = 150, + stun_immune = 0.9, + fear_immune = 1, + stun_immune = 0.2, + confusion_immune = 0.4, + resolvers.equip{ + {type="weapon", subtype="longsword", ego_chance=30, autoreq=true}, + {type="armor", subtype="massive", autoreq=true}, + {type="armor", subtype="shield", ego_chance=50, autoreq=true}, + }, + resolvers.drops{chance=50, nb=1, {type="weapon", subtype="staff", ego_chance=100}}, + combat_def = 4, + autolevel = "warriormage", + resolvers.talents{ + [Talents.T_CIRCLE_OF_SHIFTING_SHADOWS]=2, + [Talents.T_CIRCLE_OF_BLAZING_LIGHT]=2, + [Talents.T_TWILIGHT]=2, + [Talents.T_SEARING_LIGHT]=2, + [Talents.T_MOONLIGHT_RAY]=1, + [Talents.T_HYMN_OF_SHADOWS]=2, + [Talents.T_CHANT_OF_FORTRESS]=2, + [Talents.T_WEAPON_OF_LIGHT]=2, + [Talents.T_CRUSADE]=3, + [Talents.T_BRANDISH]=1, + [Talents.T_RETRIBUTION]=1, + [Talents.T_MASSIVE_ARMOUR_TRAINING]=1, + [Talents.T_WEAPON_COMBAT]=2, + [Talents.T_WEAPONS_MASTERY]=2, + }, +} diff --git a/game/modules/tome/data/zones/arena/objects.lua b/game/modules/tome/data/zones/arena/objects.lua new file mode 100644 index 0000000000000000000000000000000000000000..c6a394919724600f0b9e67f6f6ac17c2c8fdee69 --- /dev/null +++ b/game/modules/tome/data/zones/arena/objects.lua @@ -0,0 +1,301 @@ +-- 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 + +load("/data/general/objects/objects.lua") +local Talents = require "engine.interface.ActorTalents" + +newEntity{ base = "BASE_LORE", + define_as = "ARENA_SCORING", + name = "Arena for dummies", lore="arena-scoring", + desc = [[A note explaining the arena's scoring rules. Someone must have dropped it.]], + rarity = false, + is_magic_device = false, + encumberance = 0, +} + + +-- Id stuff +newEntity{ define_as = "ORB_KNOWLEDGE", + unique = true, quest=true, + type = "jewelry", subtype="orb", + unided_name = "orb", no_unique_lore = true, + name = "Orb of Knowledge", identified = true, + display = "*", color=colors.VIOLET, image = "object/ruby.png", + encumber = 1, + desc = [[This orb was given to you by Elisa the halfling scryer, it will automatically identify normal and rare items for you and can be activated to identify all others.]], + + on_drop = function(self, who) + if who == game.player then + game.logPlayer(who, "You cannot bring yourself to drop the %s", self:getName()) + return true + end + end, + + max_power = 1, power_regen = 1, + use_power = { name = "use the orb", power = 1, + use = function(self, who) + for inven_id, inven in pairs(who.inven) do + for item, o in ipairs(inven) do + if not o:isIdentified() then + o:identify(true) + game.logPlayer(who, "You have: %s", o:getName{do_colour=true}) + end + end + end + end + }, + + carrier = { + auto_id_mundane = 1, + }, +} + +newEntity{ + define_as = "ARENA_BOOTS_DISE", + slot = "FEET", + type = "armor", subtype="feet", + add_name = " (#ARMOR#)", + display = "]", color=colors.UMBER, image = resolvers.image_material("boots", "leather"), + encumber = 2, + desc = [[A pair of boots made of leather. They seem to be of exceptional quality.]], + name = "a pair of leather boots of disengagement", suffix=true, instant_resolve=true, + egoed = true, + greater_ego = true, + identified = true, + rarity = false, + cost = 0, + material_level = 1, + wielder = { + combat_armor = 2, + fatigue = 1, + }, + max_power = 20, power_regen = 1, + use_talent = { id = Talents.T_DISENGAGE, level = 2, power = 10 }, +} + +newEntity{ + define_as = "ARENA_BOOTS_PHAS", + slot = "FEET", + type = "armor", subtype="feet", + add_name = " (#ARMOR#)", + display = "]", color=colors.UMBER, image = resolvers.image_material("boots", "leather"), + encumber = 2, + desc = [[A pair of boots made of leather. They seem to be of exceptional quality.]], + name = "a pair of leather boots of phasing", suffix=true, instant_resolve=true, + egoed = true, + greater_ego = true, + identified = true, + cost = 0, + rarity = false, + material_level = 1, + wielder = { + combat_armor = 2, + fatigue = 1, + }, + max_power = 25, power_regen = 1, + use_power = { name = "blink to a nearby random location", power = 15, use = function(self, who) + game.level.map:particleEmitter(who.x, who.y, 1, "teleport") + who:teleportRandom(who.x, who.y, 10 + who:getMag(5)) + game.level.map:particleEmitter(who.x, who.y, 1, "teleport") + game:playSoundNear(who, "talents/teleport") + game.logSeen(who, "%s uses %s!", who.name:capitalize(), self:getName{no_count=true}) + return nil, true + end} +} + +newEntity{ + define_as = "ARENA_BOOTS_RUSH", + slot = "FEET", + type = "armor", subtype="feet", + add_name = " (#ARMOR#)", + display = "]", color=colors.UMBER, image = resolvers.image_material("boots", "leather"), + encumber = 2, + desc = [[A pair of boots made of leather. They seem to be of exceptional quality.]], + name = "a pair of leather boots of rushing", suffix=true, instant_resolve=true, + egoed = true, + rarity = false, + greater_ego = true, + identified = true, + cost = 0, + material_level = 1, + wielder = { + combat_armor = 2, + fatigue = 1, + }, + max_power = 20, power_regen = 1, + use_talent = { id = Talents.T_RUSH, level = 2, power = 10 }, +} + +newEntity{ + define_as = "ARENA_DEBUG_CANNON", + slot = "MAINHAND", + slot_forbid = "OFFHAND", + type = "weapon", subtype="cannon", + name = "debug cannon", + display = "}", color=colors.BLUE, + encumber = 4, + egoed = true, + unique = true, + greater_ego = true, + identified = true, + max_power = 20, power_regen = 1, + cost = 0, + material_level = 1, + rarity = 9999999999999, + metallic = true, + twohanded = true, + combat = { talented = "bow", sound = "actions/arrow", sound_miss = "actions/arrow",}, + archery = "bow", + combat = { + range = 16, + physspeed = 1, + talented = "mace", + dam = resolvers.rngavg(22,30), + apr = 2, + physcrit = 1, + dammod = {str=1.2}, + damrange = 1.5, + sound = "actions/melee", + sound_miss = "actions/melee_miss", + }, + basic_ammo = { + dam = 3000, + apr = 5000, + physcrit = 10, + dammod = {wil = 2}, + }, + wielder = { + ranged_project={ + [DamageType.LIGHTNING] = 3000, + [DamageType.LIGHT] = 3000, + }, + fatigue = 1, + }, + desc = [[A powerful weapon from another world. It operates on a graviton engine.]], + use_talent = { id = Talents.T_GRAVITY_SPIKE, level = 6, power = 10 }, +} + +newEntity{ + define_as = "ARENA_DEBUG_ARMOR", + slot = "BODY", + type = "armor", subtype="mechanic", + add_name = " (#ARMOR#)", + display = "[", color=colors.SLATE, image = resolvers.image_material("plate", "metal"), + unique = true, + name = "Full frame", + unided_name = "Strange armor", + desc = [[A powered armor from another world. Worn by fighters facing unstable dimensional fields.]], + color = colors.BLACK, + metallic = true, + rarity = 99999999999, + cost = 250, + material_level = 3, + max_power = 20, power_regen = 1, + wielder = { + combat_armor = 120, + combat_def = 120, + combat_def_ranged = 120, + max_encumber = 300, + life_regen = 1000, + stamina_regen = 20, + fatigue = 0, + max_stamina = 500, + max_life = 3000, + knockback_immune = 1, + stun_immune = 1, + size_category = 2, + }, + use_talent = { id = Talents.T_TWILIGHT_SURGE, level = 99, power = 10 }, +} + + +newEntity{ + define_as = "ARENA_BOOTS_LSPEED", + slot = "FEET", + type = "armor", subtype="feet", + add_name = " (#ARMOR#)", + display = "]", color=colors.UMBER, image = resolvers.image_material("boots", "leather"), + encumber = 2, + desc = [[A pair of boots made of leather. They seem to be of exceptional quality.]], + name = "a pair of leather boots of lightning speed", suffix=true, instant_resolve=true, + egoed = true, + greater_ego = true, + identified = true, + rarity = false, + cost = 0, + material_level = 1, + wielder = { + combat_armor = 2, + fatigue = 1, + }, + max_power = 20, power_regen = 1, + use_talent = { id = Talents.T_LIGHTNING_SPEED, level = 2, power = 10 }, +} + +newEntity{ + define_as = "ARENA_BOW", + base = "BASE_LONGBOW", + name = "elm longbow of steady shot", + level_range = {1, 10}, + require = { stat = { dex=11 }, }, + rarity = false, + egoed = true, + greater_ego = true, + identified = true, + cost = 0, + material_level = 1, + use_talent = { id = Talents.T_STEADY_SHOT, level = 2, power = 10 }, + max_power = 15, power_regen = 1, + combat = { + range = 8, + physspeed = 0.8, + }, + basic_ammo = { + dam = 12, + apr = 5, + physcrit = 1, + dammod = {dex=0.7, str=0.5}, + }, +} + +newEntity{ + define_as = "ARENA_SLING", + base = "BASE_SLING", + name = "rough leather sling of flare", + level_range = {1, 10}, + require = { stat = { dex=11 }, }, + rarity = false, + egoed = true, + greater_ego = true, + identified = true, + cost = 0, + material_level = 1, + use_talent = { id = Talents.T_FLARE, level = 2, power = 15 }, + max_power = 15, power_regen = 1, + combat = { + range = 8, + physspeed = 0.8, + }, + basic_ammo = { + dam = 12, + apr = 1, + physcrit = 4, + dammod = {dex=0.7, cun=0.5}, + }, +} diff --git a/game/modules/tome/data/zones/arena/traps.lua b/game/modules/tome/data/zones/arena/traps.lua new file mode 100644 index 0000000000000000000000000000000000000000..f8f844f583d30b1999a9bf7a520221cdebdfa557 --- /dev/null +++ b/game/modules/tome/data/zones/arena/traps.lua @@ -0,0 +1,20 @@ +-- 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 + +load("/data/general/traps/elemental.lua") diff --git a/game/modules/tome/data/zones/arena/zone.lua b/game/modules/tome/data/zones/arena/zone.lua new file mode 100644 index 0000000000000000000000000000000000000000..0fc62a0fb9d8cf90b1c30b724dc871484212c32a --- /dev/null +++ b/game/modules/tome/data/zones/arena/zone.lua @@ -0,0 +1,436 @@ +-- 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 { + name = "The Arena", + level_range = {1, 50}, + level_scheme = "player", + max_level = 1, + actor_adjust_level = function(zone, level, e) return game.player.level + rng.range(-2, 2) end, + width = 15, height = 15, + all_remembered = true, + all_lited = true, + no_worldport = true, + --ambiant_music = "a_lomos_del_dragon_blanco.ogg", + + generator = { + map = { + class = "engine.generator.map.Static", + map = "zones/arena", + zoom = 4, + }, + actor = { }, + object = { }, + trap = { }, + + }, + + + on_turn = function(self) + if game.turn % 10 ~= 0 or game.level.arena.event == 4 then return end + game.level.arena.checkPinch() + require("mod.class.generator.actor.Arena").new(self, game.level.map, game.level, {}):tick() + if game.level.turn_counter then + if game.level.turn_counter > 0 then game.level.turn_counter = game.level.turn_counter - 10 + else + --Clear up items and do gold bonus if applicable. + --The wave starts at this point. + game.level.turn_counter = nil + if game.level.arena.event == 1 then game.log("#GOLD#Miniboss round starts!!") + elseif game.level.arena.event == 2 then game.log("#VIOLET#Boss round starts!!!") + elseif game.level.arena.event == 3 then game.log("#LIGHT_RED#Final round starts!!!!") + end + game.level.arena.removeStuff() + if game.player.money > 0 then + game.level.arena.goldToScore() + end + game.level.arena.openGates() + end + end + if game.level.arena.bonus > 0 then game.level.arena.bonus = game.level.arena.bonus - 10 end + if game.level.arena.delay > 0 then game.level.arena.delay = game.level.arena.delay - 1 end + --Only raise danger level while you can raise bonus multiplier. + if game.level.arena.dangerMod < 1.5 and game.level.arena.pinch == false + and game.level.arena.delay <= 0 and not game.level.turn_counter then + game.level.arena.dangerMod = game.level.arena.dangerMod + 0.02 + end + --Reset kill counter + if game.level.arena.kills > 0 then + game.level.arena.checkCombo(game.level.arena.kills) + game.level.arena.totalKills = game.level.arena.totalKills + game.level.arena.kills + end + game.level.arena.kills = 0 + end, + + + post_process = function(level) + game.player.money = 0 + game.player.no_resurrect = true + game.player.on_die = function (self, src) + local rank = math.floor(game.level.arena.rank) + local drank + if rank < 0 then drank = "Master of Arena" else drank = game.level.arena.ranks[rank] or "nobody" end + local lastScore = { + name = game.player.name.." the "..drank, + score = game.level.arena.score, + perk = game.level.arena.perk, + wave = game.level.arena.currentWave, + sex = game.player.descriptor.sex, + race = game.player.descriptor.subrace, + class = game.player.descriptor.subclass, + } + game.level.arena.updateScores(lastScore) + end + + --Allow players to shoot bows and stuff by default. Move it back to perks if too powerful. + game.player:learnTalent(game.player.T_SHOOT, true) + game.player.changed = true + level.turn_counter = 50 --5 turns before action starts. + --world.arena = nil + if not world.arena or not world.arena.ver then + local emptyScore = {name = nil, score = 0, perk = nil, wave = 1, sex = nil, race = nil, class = nil} + world.arena = { + master30 = nil, + master60 = nil, + lastScore = emptyScore, + bestWave = 1, + ver = 1 + } + world.arena.scores = {[1] = emptyScore} + local o = game.zone:makeEntityByName(game.level, "object", "ARENA_SCORING") + if o then game.zone:addEntity(game.level, o, "object", 7, 3) end + end + level.arena = { + ranks = { "nobody", "rat stomper", "aspirant", "fighter", "brave", "powerful", "promise", "rising star", "destroyer", "obliterator", "annihilator", "grandious", "glorious", "victorious", "ultimate", "ultimate", "ultimate", "ultimate", "ultimate", "grand master" }, + rank = 1, + perk = nil, + event = 0, + initEvent = false, + lockEvent = false, + display = nil, + kills = 0, + totalKills = 0, + currentWave = 1, + eventWave = 5, + finalWave = 61, + modeString = "60", + danger = 0, + dangerTop = 12, + dangerMod = 0, + score = 0, + delay = 0, + pinch = false, + pinchValue = 0, + bonus = 0, + clearItems = false, + bonusMultiplier = 1, + bonusMin = 1, + entry = { + --The physical doors + door = { + max = 5, + function () return 0, 0 end, + function () return 0, 14 end, + function () return 14, 0 end, + function () return 14, 14 end, + function () return 7, 0 end + }, + --Main gate + main = { + max = 4, + function () return 7, 0 end, + function () return 7, 1 end, + function () return 8, 1 end, + function () return 6, 1 end + }, + --Corner gates + corner = { + max = 12, + function () return 1, 1 end, + function () return 2, 1 end, + function () return 1, 2 end, + + function () return 13, 13 end, + function () return 12, 13 end, + function () return 13, 12 end, + + function () return 1, 13 end, + function () return 2, 13 end, + function () return 1, 12 end, + + function () return 13, 1 end, + function () return 12, 1 end, + function () return 13, 2 end, + }, + --Crystal gates + crystal = { + max = 8, + function () return 4, 2 end, + function () return 10, 2 end, + function () return 10, 12 end, + function () return 4, 12 end, + + function () return 1, 4 end, + function () return 12, 4 end, + function () return 1, 10 end, + function () return 12, 10 end, + } + }, + clear = function() + game.player:setQuestStatus("arena", engine.Quest.COMPLETED) + local master = game.player:cloneFull() + game.level.arena.rank = -1 + game.player:die(game.player) + master.version = game.__mod_info.version + master.no_drops = true + master.energy.value = 0 + master.player = nil + master.rank = 5 + master.color_r = 255 + master.color_g = 0 + master.color_b = 255 + master._mo:invalidate() + master.ai = "dumb_talented_simple" + master.ai_state = {talent_in=1} + master.faction="enemies" + master.life = master.max_life + -- Remove some talents + local tids = {} + for tid, _ in pairs(master.talents) do + local t = master:getTalentFromId(tid) + if t.no_npc_use then tids[#tids+1] = t end + end + game.level.arena.event = 4 + if game.level.arena.finalWave > 60 then + world:gainAchievement("MASTER_OF_ARENA", game.player) + world.arena.master60 = master + else + world:gainAchievement("ALMOST_MASTER_OF_ARENA", game.player) + world.arena.master30 = master + end + end, + printRankings = function (val) + local scores = world.arena.scores + if not scores[1].name then return "#LIGHT_GREEN#...but it's been wiped out recently." + else + local text = "" + local tmp = "" + local line = function (txt, col) return " "..col..txt.."\n" end + local stri = "%s (%s %s %s)\n Score %d[%s]) - Wave: %d" + local i = 1 + while(scores[i] and scores[i].name) do + p = scores[i] + tmp = stri:format(p.name:capitalize(), p.sex, p.race, p.class, p.score, p.perk, p.wave) + text = text..line(tmp, "#LIGHT_BLUE#") + i = i + 1 + end + p = world.arena.lastScore + tmp = "\n#YELLOW#LAST:"..stri:format(p.name:capitalize(), p.sex, p.race, p.class, p.score, p.perk, p.wave) + return text..line(tmp, "#YELLOW#") + end + end, + printRank = function (r, ranks) + local rank = math.floor(r) + if rank > #ranks then rank = #ranks end + return ranks[rank] + end, + updateScores = function(l) + local scores = world.arena.scores + table.insert(scores, l) + table.sort(scores, function(a,b) return a.score > b.score end) + if #scores > 10 then table.remove(scores) end + world.arena.scores = scores + if l.wave > world.arena.bestWave then world.arena.bestWave = l.wave end + world.arena.lastScore = l + end, + openGates = function() + local gates = game.level.arena.entry.door + local g = game.zone:makeEntityByName(game.level, "terrain", "FLOOR") + local x, y = 0, 0 + for i = 1, gates.max do + x, y = gates[i]() + game.zone:addEntity(game.level, g, "terrain", x, y) + end + game:playSoundNear(game.player, "talents/earth") + game.log("#LIGHT_GREEN#The gates open!") + end, + closeGates = function() + local gates = game.level.arena.entry.door + local g = game.zone:makeEntityByName(game.level, "terrain", "LOCK") + local x, y = 0, 0 + for i = 1, gates.max do + x, y = gates[i]() + game.zone:addEntity(game.level, g, "terrain", x, y) + game.level.map:particleEmitter(x, y, 0.5, "arena_gate") + end + game:playSoundNear(game.player, "talents/earth") + game.log("#LIGHT_RED#The gates close!") + end, + raiseRank = function (val) + if game.level.arena.rank >= 20 then return end + local currentRank = math.floor(game.level.arena.rank) + game.level.arena.rank = game.level.arena.rank + val + local newRank = math.floor(game.level.arena.rank) + if currentRank < newRank then --Player's rank increases! + local x, y = game.level.map:getTileToScreen(game.player.x, game.player.y) + if rank == 9 then world:gainAchievement("XXX_THE_DESTROYER", game.player) + elseif rank == 20 then world:gainAchievement("GRAND_MASTER", game.player) + end + game.flyers:add(x, y, 90, 0, -0.5, "RANK UP!!", { 2, 57, 185 }, false) + game.log("#LIGHT_GREEN#The public is pleased by your performance! You now have the rank of #WHITE#"..game.level.arena.ranks[newRank].."!") + end + end, + checkCombo = function (k) + if k >= 10 then world:gainAchievement("TEN_AT_ONE_BLOW", game.player) end + if k > 2 then + local x, y = game.level.map:getTileToScreen(game.player.x, game.player.y) + local b = k * 0.035 + game.level.arena.raiseRank(b) + game.flyers:add(x, y, 90, 0, -0.5, k.." kills!", { 2, 57, 185 }, false) + game.log("#YELLOW#You killed "..k.." enemies in a single turn! The public is excited!") + else return + end + end, + addTrap = function () + local g = game.zone:makeEntity(game.level, "trap", nil, nil, true) + local d = game.level.arena.currentWave + g.dam = 5 + (d * 2) + rng.range(d, d * 1.5) + game.zone:addEntity(game.level, g, "trap", 7, 7) + g:setKnown(game.player, true) + game.level.map:updateMap(7, 7) + game.level.map:particleEmitter(7, 7, 0.3, "demon_teleport") + if d > 1 then game.log("#YELLOW#The trap changes...") end + end, + goldToScore = function () + local x, y = game.level.map:getTileToScreen(game.player.x, game.player.y) + local goldAward = game.player.money * 100 + local healthAward = game.player.money * 10 + local regStamina = goldAward * 0.5 + game.level.arena.score = game.level.arena.score + goldAward + game.flyers:add(x, y, 90, 0, -0.6, "GOLD BONUS! +"..goldAward.." SCORE!", { 2, 57, 185 }, false) + game.log("#ROYAL_BLUE#Gold bonus! Score increased by #WHITE#"..goldAward.."#ROYAL_BLUE#! #LIGHT_GREEN#You also recover some health(#WHITE#+"..healthAward.."#LIGHT_GREEN#!)#LAST#") + if game.player:knowTalent(game.player.T_STAMINA_POOL) then + game.player:incStamina(regStamina) + game.log("#LIGHT_GREEN#Stamina(#WHITE#+"..regStamina.."#LIGHT_GREEN#)") + end + game.player.money = 0 + game.player:heal(healthAward) + game.player.changed = true + end, + initWave = function (val) --Clean up and start a new wave. + if val > 20 then --If the player has more than 20 turns of rest, clean up all items lying around. + game.level.arena.clearItems = true + game.log("#YELLOW#Items lying around will disappear in #WHITE#"..val.."#YELLOW# turns!#LAST#") + end + game.level.arena.dangerTop = game.level.arena.dangerTop + (2 + math.floor(game.level.arena.currentWave * 0.05)) + game.level.arena.currentWave = game.level.arena.currentWave + 1 + game.level.arena.dangerMod = 0.7 + (game.level.arena.currentWave * 0.005) + game.level.arena.bonus = 0 + game.level.level = game.level.arena.currentWave + game.level.arena.bonusMultiplier = game.level.arena.bonusMin + game.level.arena.pinchValue = 0 + game.level.arena.pinch = false + if game.level.arena.display then game.level.arena.display = nil end + if game.level.arena.currentWave % game.level.arena.eventWave == 0 then + if game.level.arena.currentWave % (game.level.arena.eventWave * 3) == 0 then --Boss round! + game.log("#VIOLET#Boss round!!!") + game.level.arena.event = 2 + else --Miniboss round! + game.log("#GOLD#Miniboss round!") + game.level.arena.event = 1 + end + elseif game.level.arena.currentWave == game.level.arena.finalWave then --Final round! + game.level.arena.event = 3 + game.log("#LIGHT_RED#Final round!!!") + else --Regular stuff. + game.level.arena.event = 0 + end + game.level.arena.initEvent = false + game.level.arena.lockEvent = false + game.level.arena.addTrap() + if game.level.arena.currentWave == 21 then world:gainAchievement("ARENA_BATTLER_20", game.player) + elseif game.level.arena.currentWave == 51 then world:gainAchievement("ARENA_BATTLER_50", game.player) + end + end, + removeStuff = function () + for i = 0, game.level.map.w - 1 do + for j = 0, game.level.map.h - 1 do + local nb = game.level.map:getObjectTotal(i, j) + for z = nb, 1, -1 do game.level.map:removeObject(i, j, z) end + end + end + end, + doReward = function (val) + local col = "#ROYAL_BLUE#" + local hgh = "#WHITE#" + local dangerBonus = val * 0.5 + local scoreBonus = game.level.arena.bonus * 0.2 + local clearBonus = math.ceil(game.level.arena.currentWave ^ 1.85) + local rankBonus = math.floor(game.level.arena.rank) * 20 + local expAward = (dangerBonus + scoreBonus + clearBonus + rankBonus) * game.level.arena.bonusMultiplier + local x, y = game.level.map:getTileToScreen(game.player.x, game.player.y) + game.player:gainExp(expAward) + game.level.arena.score = game.level.arena.score + game.level.arena.bonus + game.flyers:add(x, y, 90, 0, -1, "Round Clear! +"..expAward.." EXP!", { 2, 57, 185 }, true) + game.log(col.."Wave clear!") + game.log(col.."Clear bonus: "..hgh..clearBonus..col.."! Score bonus: "..hgh..scoreBonus..col.."! Danger bonus: "..hgh..dangerBonus..col.."! Rank bonus: "..hgh..rankBonus..col.."!") + game.log(col.."Your experience increases by"..hgh..expAward..col.."!") + game.player.changed = true + end, + clearRound = function () --Relax and give rewards. + --Do rewarding. + local val = game.level.arena.pinchValue + local plvl = game.player.level + game.level.arena.doReward(val) + --Set rest time. + local rest_time = val + if not plvl == game.player.level then --If bonuses made the player level up, give minimal time. + if rest_time > 30 then rest_time = 30 end + else + if rest_time < 10 then rest_time = 10 + elseif rest_time > 80 then rest_time = 80 + end + end + game.level.turn_counter = rest_time * 10 + game.level.arena.initWave(val) + end, + checkPinch = function () + if game.level.arena.danger > game.level.arena.dangerTop and game.level.arena.pinch == false then --The player is in a pinch! + if game.level.arena.danger - game.level.arena.dangerTop < 10 then return end --Ignore minimal excess of power. + game.level.arena.pinch = true + game.level.arena.pinchValue = game.level.arena.danger - game.level.arena.dangerTop + game.level.arena.bonus = (game.level.arena.pinchValue * 20) + 200 + game.level.arena.closeGates() + elseif game.level.arena.danger <= 0 and game.level.arena.pinch == true then --The player cleared the round. + if game.level.arena.event == 0 then + game.level.arena.clearRound() + elseif game.level.arena.lockEvent == false then --Call minibosses or boss next turn. + game.level.arena.initEvent = true + else --Round is clear + game.level.arena.clearRound() + end + end + end, + } + local Chat = require "engine.Chat" + local chat = Chat.new("arena-start", {name="Arena mode"}, game.player, {text = level.arena.printRankings()}) + chat:invoke() + game.level.arena.addTrap() + end +} diff --git a/game/modules/tome/data/zones/town-derth/grids.lua b/game/modules/tome/data/zones/town-derth/grids.lua index 54f3736d46e0bba482e647af43c7aaa074715a66..20a179e9e456b10acbf6950202f771c7d599c075 100644 --- a/game/modules/tome/data/zones/town-derth/grids.lua +++ b/game/modules/tome/data/zones/town-derth/grids.lua @@ -17,3 +17,9 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +newEntity{ + define_as = "GRASS", + name = "grass", image = "terrain/grass.png", + display = '.', color=colors.LIGHT_GREEN, back_color={r=44,g=95,b=43}, + grow = "TREE", +} \ No newline at end of file diff --git a/game/modules/tome/dialogs/ArenaFinish.lua b/game/modules/tome/dialogs/ArenaFinish.lua new file mode 100644 index 0000000000000000000000000000000000000000..8b9ff8d2ff0cc202adc685b413218d53316fda29 --- /dev/null +++ b/game/modules/tome/dialogs/ArenaFinish.lua @@ -0,0 +1,162 @@ +-- 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 + +require "engine.class" +local Dialog = require "engine.ui.Dialog" +local Textzone = require "engine.ui.Textzone" +local Separator = require "engine.ui.Separator" +local List = require "engine.ui.List" +local Savefile = require "engine.Savefile" +local Map = require "engine.Map" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init(actor) + self.actor = actor + Dialog.init(self, "Arena mode", 500, 300) + + actor:saveUUID() + + self:generateList() + + self.c_desc = Textzone.new{width=self.iw, auto_height=true, text=self:printRanking() } + self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) self:use(item) end} + + self:loadUI{ + {left=0, top=0, ui=self.c_desc}, + {left=5, top=self.c_desc.h, padding_h=10, ui=Separator.new{dir="vertical", size=self.iw - 10}}, + {left=0, bottom=0, ui=self.c_list}, + } + self:setFocus(self.c_list) + self:setupUI(false, true) +end + +function _M:printRanking() + local scores = world.arena.scores + if not scores[1].name then return "#LIGHT_GREEN#No high scores. This should not happen." + else + local text = "" + local tmp = "" + local line = function (txt, col) return " "..col..txt.."\n" end + local stri = "%s (%s %s %s)\n Score %d[%s]) - Wave: %d" + local i = 1 + while(scores[i] and scores[i].name) do + p = scores[i] + tmp = stri:format(p.name:capitalize(), p.sex, p.race, p.class, p.score, p.perk, p.wave) + if p.name == world.arena.lastScore.name and p.score == world.arena.lastScore.score and + p.wave == world.arena.lastScore.wave and p.perk == world.arena.lastScore.perk then + text = text..line(tmp, "#YELLOW#") + else + text = text..line(tmp, "#LIGHT_BLUE#") + end + i = i + 1 + end + return text + end +end + +--- Clean the actor from debuffs/buffs +function _M:cleanActor(actor) + local effs = {} + + -- Go through all spell effects + for eff_id, p in pairs(actor.tmp) do + local e = actor.tempeffect_def[eff_id] + effs[#effs+1] = {"effect", eff_id} + end + + -- Go through all sustained spells + for tid, act in pairs(actor.sustain_talents) do + if act then + effs[#effs+1] = {"talent", tid} + end + end + + while #effs > 0 do + local eff = rng.tableRemove(effs) + + if eff[1] == "effect" then + actor:removeEffect(eff[2]) + else + actor:forceUseTalent(eff[2], {ignore_energy=true}) + end + end +end + +--- Restore resources +function _M:restoreResources(actor) + if actor.resetToFull then + actor:resetToFull() + actor.energy.value = game.energy_to_act + end +end + +--- Basic resurrection +function _M:resurrectBasic(actor) + actor.dead = false + actor.died = (actor.died or 0) + 1 + + local x, y = util.findFreeGrid(actor.x, actor.y, 20, true, {[Map.ACTOR]=true}) + if not x then x, y = actor.x, actor.y end + actor.x, actor.y = nil, nil + + actor:move(x, y, true) + game.level:addEntity(actor) + game:unregisterDialog(self) + game.level.map:redisplay() + actor.energy.value = game.energy_to_act + actor.changed = true + game.paused = true +end + +function _M:use(item) + if not item then return end + local act = item.action + + if act == "exit" then + local save = Savefile.new(game.save_name) + save:delete() + save:close() + world:saveWorld() + if item.subaction == "none" then + util.showMainMenu() + elseif item.subaction == "restart" then + util.showMainMenu(false, engine.version[4], engine.version[1].."."..engine.version[2].."."..engine.version[3], game.__mod_info.short_name, game.save_name, true, "auto_quickbirth=true") + end + elseif act == "cheat" then + game.logPlayer(self.actor, "#LIGHT_BLUE#You resurrect! CHEATER !") + + self:cleanActor(self.actor) + self:restoreResources(self.actor) + self:resurrectBasic(self.actor) + elseif act == "dump" then + game:registerDialog(require("mod.dialogs.CharacterSheet").new(self.actor)) + end +end + +function _M:generateList() + local list = {} + + if config.settings.cheat then list[#list+1] = {name="Resurrect by cheating", action="cheat"} end + list[#list+1] = {name="Character dump", action="dump"} + list[#list+1] = {name="Restart the same character", action="exit", subaction="restart"} + list[#list+1] = {name="Exit to main menu", action="exit", subaction="none"} + + self.list = list +end