From f2991857a94cd5ce865beeaf7f8ba020f1ccd987 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Tue, 5 Oct 2010 16:26:55 +0000 Subject: [PATCH] The main menu interface is now a module like any others, called "boot" The main menu will now have a random animated background with an AI-controlled player running around in a forest, blasting things git-svn-id: http://svn.net-core.org/repos/t-engine4@1423 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/engines/default/engine/Game.lua | 8 +- game/engines/default/engine/Module.lua | 49 +- .../default/engine/dialogs/VideoOptions.lua | 9 + game/engines/default/engine/utils.lua | 13 +- game/engines/default/engine/version.lua | 2 +- game/loader/init.lua | 5 + game/modules/boot/ai/player.lua | 58 +++ game/modules/boot/class/Actor.lua | 179 +++++++ game/modules/boot/class/Game.lua | 469 ++++++++++++++++++ game/modules/boot/class/Grid.lua | 107 ++++ game/modules/boot/class/NPC.lua | 53 ++ game/modules/boot/class/Player.lua | 108 ++++ game/modules/boot/class/interface/Combat.lua | 56 +++ game/modules/boot/data/birth/descriptors.lua | 56 +++ game/modules/boot/data/damage_types.lua | 61 +++ .../modules/boot/data/general/grids/basic.lua | 83 ++++ .../boot/data/general/grids/forest.lua | 46 ++ .../modules/boot/data/general/grids/water.lua | 40 ++ .../modules/boot/data/general/npcs/canine.lua | 86 ++++ .../boot/data/general/npcs/skeleton.lua | 72 +++ game/modules/boot/data/general/npcs/troll.lua | 77 +++ .../data/gfx/npc/armored_skeleton_warrior.png | Bin 0 -> 1582 bytes game/modules/boot/data/gfx/npc/canine_dw.png | Bin 0 -> 3466 bytes game/modules/boot/data/gfx/npc/canine_fox.png | Bin 0 -> 3178 bytes game/modules/boot/data/gfx/npc/canine_gw.png | Bin 0 -> 3640 bytes game/modules/boot/data/gfx/npc/canine_w.png | Bin 0 -> 3319 bytes .../modules/boot/data/gfx/npc/canine_warg.png | Bin 0 -> 3694 bytes game/modules/boot/data/gfx/npc/canine_ww.png | Bin 0 -> 3847 bytes .../gfx/npc/degenerated_skeleton_warrior.png | Bin 0 -> 994 bytes .../boot/data/gfx/npc/skeleton_mage.png | Bin 0 -> 1388 bytes .../boot/data/gfx/npc/skeleton_warrior.png | Bin 0 -> 1153 bytes game/modules/boot/data/gfx/npc/troll_c.png | Bin 0 -> 2725 bytes game/modules/boot/data/gfx/npc/troll_f.png | Bin 0 -> 2213 bytes game/modules/boot/data/gfx/npc/troll_m.png | Bin 0 -> 2331 bytes game/modules/boot/data/gfx/npc/troll_mt.png | Bin 0 -> 2938 bytes game/modules/boot/data/gfx/npc/troll_s.png | Bin 0 -> 2419 bytes game/modules/boot/data/gfx/particles/acid.lua | 38 ++ .../boot/data/gfx/particles/bolt_fire.lua | 51 ++ .../boot/data/gfx/particles/fireflash.lua | 96 ++++ .../boot/data/gfx/particles/firetrail.lua | 56 +++ .../modules/boot/data/gfx/particles/flame.lua | 38 ++ .../boot/data/gfx/particles/golden_shield.lua | 46 ++ .../boot/data/gfx/particles/lightning.lua | 83 ++++ .../boot/data/gfx/particles/mana_beam.lua | 58 +++ .../boot/data/gfx/particles/sunburst.lua | 132 +++++ .../boot/data/gfx/shaders/main_fbo.frag | 172 +++++++ .../boot/data/gfx/shaders/main_fbo.lua | 28 ++ game/modules/boot/data/gfx/shaders/water.frag | 21 + game/modules/boot/data/gfx/shaders/water.lua | 29 ++ game/modules/boot/data/gfx/terrain/grass.png | Bin 0 -> 684 bytes .../boot/data/gfx/terrain/grass_flower3.png | Bin 0 -> 1096 bytes .../boot/data/gfx/terrain/tree_alpha1.png | Bin 0 -> 829 bytes .../boot/data/gfx/terrain/tree_alpha2.png | Bin 0 -> 777 bytes .../boot/data/gfx/terrain/tree_alpha3.png | Bin 0 -> 1016 bytes .../boot/data/gfx/terrain/tree_alpha4.png | Bin 0 -> 4267 bytes .../boot/data/gfx/terrain/tree_alpha5.png | Bin 0 -> 4267 bytes .../boot/data/gfx/terrain/water_floor.png | Bin 0 -> 154 bytes .../data/gfx/terrain/water_floor_alpha.png | Bin 0 -> 177 bytes game/modules/boot/data/rooms/pilar.lua | 26 + game/modules/boot/data/rooms/simple.lua | 37 ++ game/modules/boot/data/talents.lua | 168 +++++++ game/modules/boot/data/timed_effects.lua | 47 ++ .../modules/boot/data/zones/dungeon/grids.lua | 22 + game/modules/boot/data/zones/dungeon/npcs.lua | 22 + .../boot/data/zones/dungeon/objects.lua | 18 + .../modules/boot/data/zones/dungeon/traps.lua | 18 + game/modules/boot/data/zones/dungeon/zone.lua | 52 ++ game/modules/boot/dialogs/LoadGame.lua | 111 +++++ game/modules/boot/dialogs/MainMenu.lua | 59 +++ game/modules/boot/dialogs/NewGame.lua | 69 +++ game/modules/boot/dialogs/Profile.lua | 86 ++++ game/modules/boot/dialogs/ProfileLogin.lua | 85 ++++ game/modules/boot/init.lua | 30 ++ game/modules/boot/load.lua | 64 +++ .../data/general/objects/world-artifacts.lua | 2 +- src/core_lua.c | 19 +- src/main.c | 26 +- 77 files changed, 3412 insertions(+), 34 deletions(-) create mode 100644 game/modules/boot/ai/player.lua create mode 100644 game/modules/boot/class/Actor.lua create mode 100644 game/modules/boot/class/Game.lua create mode 100644 game/modules/boot/class/Grid.lua create mode 100644 game/modules/boot/class/NPC.lua create mode 100644 game/modules/boot/class/Player.lua create mode 100644 game/modules/boot/class/interface/Combat.lua create mode 100644 game/modules/boot/data/birth/descriptors.lua create mode 100644 game/modules/boot/data/damage_types.lua create mode 100644 game/modules/boot/data/general/grids/basic.lua create mode 100644 game/modules/boot/data/general/grids/forest.lua create mode 100644 game/modules/boot/data/general/grids/water.lua create mode 100644 game/modules/boot/data/general/npcs/canine.lua create mode 100644 game/modules/boot/data/general/npcs/skeleton.lua create mode 100644 game/modules/boot/data/general/npcs/troll.lua create mode 100644 game/modules/boot/data/gfx/npc/armored_skeleton_warrior.png create mode 100644 game/modules/boot/data/gfx/npc/canine_dw.png create mode 100644 game/modules/boot/data/gfx/npc/canine_fox.png create mode 100644 game/modules/boot/data/gfx/npc/canine_gw.png create mode 100644 game/modules/boot/data/gfx/npc/canine_w.png create mode 100644 game/modules/boot/data/gfx/npc/canine_warg.png create mode 100644 game/modules/boot/data/gfx/npc/canine_ww.png create mode 100644 game/modules/boot/data/gfx/npc/degenerated_skeleton_warrior.png create mode 100644 game/modules/boot/data/gfx/npc/skeleton_mage.png create mode 100644 game/modules/boot/data/gfx/npc/skeleton_warrior.png create mode 100644 game/modules/boot/data/gfx/npc/troll_c.png create mode 100644 game/modules/boot/data/gfx/npc/troll_f.png create mode 100644 game/modules/boot/data/gfx/npc/troll_m.png create mode 100644 game/modules/boot/data/gfx/npc/troll_mt.png create mode 100644 game/modules/boot/data/gfx/npc/troll_s.png create mode 100644 game/modules/boot/data/gfx/particles/acid.lua create mode 100644 game/modules/boot/data/gfx/particles/bolt_fire.lua create mode 100644 game/modules/boot/data/gfx/particles/fireflash.lua create mode 100644 game/modules/boot/data/gfx/particles/firetrail.lua create mode 100644 game/modules/boot/data/gfx/particles/flame.lua create mode 100644 game/modules/boot/data/gfx/particles/golden_shield.lua create mode 100644 game/modules/boot/data/gfx/particles/lightning.lua create mode 100644 game/modules/boot/data/gfx/particles/mana_beam.lua create mode 100644 game/modules/boot/data/gfx/particles/sunburst.lua create mode 100644 game/modules/boot/data/gfx/shaders/main_fbo.frag create mode 100644 game/modules/boot/data/gfx/shaders/main_fbo.lua create mode 100644 game/modules/boot/data/gfx/shaders/water.frag create mode 100644 game/modules/boot/data/gfx/shaders/water.lua create mode 100644 game/modules/boot/data/gfx/terrain/grass.png create mode 100644 game/modules/boot/data/gfx/terrain/grass_flower3.png create mode 100644 game/modules/boot/data/gfx/terrain/tree_alpha1.png create mode 100644 game/modules/boot/data/gfx/terrain/tree_alpha2.png create mode 100644 game/modules/boot/data/gfx/terrain/tree_alpha3.png create mode 100644 game/modules/boot/data/gfx/terrain/tree_alpha4.png create mode 100644 game/modules/boot/data/gfx/terrain/tree_alpha5.png create mode 100644 game/modules/boot/data/gfx/terrain/water_floor.png create mode 100644 game/modules/boot/data/gfx/terrain/water_floor_alpha.png create mode 100644 game/modules/boot/data/rooms/pilar.lua create mode 100644 game/modules/boot/data/rooms/simple.lua create mode 100644 game/modules/boot/data/talents.lua create mode 100644 game/modules/boot/data/timed_effects.lua create mode 100644 game/modules/boot/data/zones/dungeon/grids.lua create mode 100644 game/modules/boot/data/zones/dungeon/npcs.lua create mode 100644 game/modules/boot/data/zones/dungeon/objects.lua create mode 100644 game/modules/boot/data/zones/dungeon/traps.lua create mode 100644 game/modules/boot/data/zones/dungeon/zone.lua create mode 100644 game/modules/boot/dialogs/LoadGame.lua create mode 100644 game/modules/boot/dialogs/MainMenu.lua create mode 100644 game/modules/boot/dialogs/NewGame.lua create mode 100644 game/modules/boot/dialogs/Profile.lua create mode 100644 game/modules/boot/dialogs/ProfileLogin.lua create mode 100644 game/modules/boot/init.lua create mode 100644 game/modules/boot/load.lua diff --git a/game/engines/default/engine/Game.lua b/game/engines/default/engine/Game.lua index 377eee86a1..27fcb2a324 100644 --- a/game/engines/default/engine/Game.lua +++ b/game/engines/default/engine/Game.lua @@ -98,14 +98,14 @@ end --- Displays the screen -- Called by the engine core to redraw the screen every frame function _M:display() + if self.flyers then + self.flyers:display() + end + for i, d in ipairs(self.dialogs) do d:display() d:toScreen(d.display_x, d.display_y) end - - if self.flyers then - self.flyers:display() - end end --- Returns the player diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua index 1d1abc19c0..840411b123 100644 --- a/game/engines/default/engine/Module.lua +++ b/game/engines/default/engine/Module.lua @@ -33,24 +33,12 @@ function _M:listModules() -- print("Search Path: ") for k,e in ipairs(fs.getSearchPath()) do print("*",k,e) end for i, short_name in ipairs(fs.list("/modules/")) do ---print("!!", short_name) - local dir = "/modules/"..short_name - if fs.exists(dir.."/init.lua") then - local mod = self:loadDefinition(dir) + if short_name ~= "boot" then + local mod = self:createModule(short_name) if mod then table.insert(ms, mod) ms[mod.short_name] = mod end - elseif short_name:find(".team$") then - fs.mount(fs.getRealPath(dir), "/testload", false) - if fs.exists("/testload/mod/init.lua") then - local mod = self:loadDefinition("/testload", dir) - if mod then - table.insert(ms, mod) - ms[mod.short_name] = mod - end - end - fs.umount(fs.getRealPath(dir)) end end @@ -65,6 +53,26 @@ function _M:listModules() return ms end +function _M:createModule(short_name) + local dir = "/modules/"..short_name + if fs.exists(dir.."/init.lua") then + local mod = self:loadDefinition(dir) + if mod and mod.short_name then + return mod + end + elseif short_name:find(".team$") then + fs.mount(fs.getRealPath(dir), "/testload", false) + if fs.exists("/testload/mod/init.lua") then + local mod = self:loadDefinition("/testload", dir) + if mod then + table.insert(ms, mod) + end + end + fs.umount(fs.getRealPath(dir)) + if mod and mod.short_name then return mod end + end +end + --- Get a module definition from the module init.lua file function _M:loadDefinition(dir, team) local mod_def = loadfile(team and (dir.."/mod/init.lua") or (dir.."/init.lua")) @@ -140,10 +148,15 @@ end -- @param mod the module definition as given by Module:loadDefinition() -- @param name the savefile name -- @param new_game true if the game must be created (aka new character) -function _M:instanciate(mod, name, new_game) - local popup = Dialog:simplePopup("Loading module", "Please wait while loading the game module...", nil, true) - popup.__showup = nil - core.display.forceRedraw() +function _M:instanciate(mod, name, new_game, no_reboot) + if not no_reboot then + local popup = Dialog:simplePopup("Loading module", "Please wait while loading "..mod.long_name.."...", nil, true) + popup.__showup = nil + core.display.forceRedraw() + + util.showMainMenu(false, "te4", "LATEST", mod.short_name, name, new_game) + return + end profile.generic.modules_loaded = profile.generic.modules_loaded or {} profile.generic.modules_loaded[mod.short_name] = (profile.generic.modules_loaded[mod.short_name] or 0) + 1 diff --git a/game/engines/default/engine/dialogs/VideoOptions.lua b/game/engines/default/engine/dialogs/VideoOptions.lua index 68562bf4b1..f0c9c065a1 100644 --- a/game/engines/default/engine/dialogs/VideoOptions.lua +++ b/game/engines/default/engine/dialogs/VideoOptions.lua @@ -69,6 +69,15 @@ function _M:generateList() game:registerDialog(menu) end,} + local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="Controls the particle effects density.\nThis option allows to change the density of the many particle effects in the game.\nIf the game is slow when displaying spell effects try to lower this setting.#WHITE#"} + list[#list+1] = { zone=zone, name="Particle effects density", status=function(item) + return tostring(config.settings.particles_density and "enabled" or "disabled") + end, fct=function(item) + + game:saveSettings("particles_density", ("particles_density = %s\n"):format(tostring(config.settings.particles_density))) + self.c_list:drawItem(item) + end,} + local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="Activates antialiased texts.\nTexts will look nicer but it can be slower on some computers.\n\n#LIGHT_RED#You must restart the game for it to take effect.#WHITE#"} list[#list+1] = { zone=zone, name="Antialiased texts", status=function(item) return tostring(core.display.getTextBlended() and "enabled" or "disabled") diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index 173d137543..1a6bae5af7 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -533,7 +533,7 @@ function util.findFreeGrid(sx, sy, radius, block, what) return gs[1][1], gs[1][2] end -function util.showMainMenu(no_reboot) +function util.showMainMenu(no_reboot, reboot_engine, reboot_engine_version, reboot_module, reboot_name, reboot_new) -- Turn based by default core.game.setRealtime(0) @@ -550,14 +550,13 @@ function util.showMainMenu(no_reboot) if game and type(game) == "table" then game:joinThreads(30) end if no_reboot then - local Menu = require("special.mainmenu.class.Game") --- local Menu = require("special.mainmenu.class.TestUI") - game = Menu.new() - game:run() + local Module = require("engine.Module") + local mod = Module:createModule(__load_module) + Module:instanciate(mod, __player_name, __player_new, true) else -- Tell the C engine to discard the current lua state and make a new one - print("[MAIN] rebooting lua state") - core.game.reboot() + print("[MAIN] rebooting lua state: ", reboot_engine, reboot_engine_version, reboot_module, reboot_name, reboot_new) + core.game.reboot(reboot_engine or "te4", reboot_engine_version or "LATEST", reboot_module or "boot", reboot_name or "player", reboot_new) end end diff --git a/game/engines/default/engine/version.lua b/game/engines/default/engine/version.lua index 55236756bd..fceb3f17b8 100644 --- a/game/engines/default/engine/version.lua +++ b/game/engines/default/engine/version.lua @@ -18,7 +18,7 @@ -- darkgod@te4.org -- Engine Version -engine.version = {0,9,12,"te4",4} +engine.version = {0,9,13,"te4",4} engine.require_c_core = engine.version[5] engine.version_id = ("%s-%d_%d.%d.%d"):format(engine.version[4], engine.require_c_core, engine.version[1], engine.version[2], engine.version[3]) diff --git a/game/loader/init.lua b/game/loader/init.lua index f91165821e..1dba2c50a0 100644 --- a/game/loader/init.lua +++ b/game/loader/init.lua @@ -25,6 +25,11 @@ fs.mount(homepath, "/") local args = {...} local req_engine = args[1] or "te4" local req_version = args[2] or "LATEST" +__load_module = args[3] or "boot" +__player_name = args[4] or "player" +__player_new = args[5] and true or false + +print("Reboot using", req_engine, req_version, __load_module, __player_name, __player_new) local engines = {} diff --git a/game/modules/boot/ai/player.lua b/game/modules/boot/ai/player.lua new file mode 100644 index 0000000000..796b743eec --- /dev/null +++ b/game/modules/boot/ai/player.lua @@ -0,0 +1,58 @@ +-- ToME - Tales of Middle-Earth +-- 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 Astar = require "engine.Astar" + +-- AI for the escort quest +-- the NPC will run toward the portal, if hostiles are in sight he attacks them +newAI("player_demo", function(self) + if self:runAI("target_simple") then + -- One in "talent_in" chance of using a talent + if rng.chance(self.ai_state.talent_in or 6) and self:reactionToward(self.ai_target.actor) < 0 then + self:runAI("dumb_talented") + end + if not self.energy.used then + self:runAI("move_simple") + end + return true + else + self:runAI("move_player") + end +end) + +newAI("move_player", function(self) + if self.player_waypoint then + local path = self.player_waypoint + if not path[1] then + self.player_waypoint = nil + return self:runAI("move_simple") + else + local ret = self:move(path[1].x, path[1].y) + table.remove(path, 1) + return ret + end + else + local e = rng.table(game.level.e_array) + while e == self do e = rng.table(game.level.e_array) end + print("Selecting waypoint", e.x, e.y, "::", e.name) + + local a = Astar.new(game.level.map, self) + self.player_waypoint = a:calc(self.x, self.y, e.x, e.y) + end +end) diff --git a/game/modules/boot/class/Actor.lua b/game/modules/boot/class/Actor.lua new file mode 100644 index 0000000000..324efdd90d --- /dev/null +++ b/game/modules/boot/class/Actor.lua @@ -0,0 +1,179 @@ +-- ToME - Tales of Middle-Earth +-- 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" +require "engine.Actor" +require "engine.Autolevel" +require "engine.interface.ActorTemporaryEffects" +require "engine.interface.ActorLife" +require "engine.interface.ActorProject" +require "engine.interface.ActorLevel" +require "engine.interface.ActorStats" +require "engine.interface.ActorTalents" +require "engine.interface.ActorFOV" +require "mod.class.interface.Combat" +local Map = require "engine.Map" + +module(..., package.seeall, class.inherit( + engine.Actor, + engine.interface.ActorTemporaryEffects, + engine.interface.ActorLife, + engine.interface.ActorProject, + engine.interface.ActorLevel, + engine.interface.ActorStats, + engine.interface.ActorTalents, + engine.interface.ActorFOV, + mod.class.interface.Combat +)) + +function _M:init(t, no_default) + -- Define some basic combat stats + self.combat_armor = 0 + + -- Default regen + t.life_regen = t.life_regen or 0.25 -- Life regen real slow + + -- Default melee barehanded damage + self.combat = { dam=1 } + + engine.Actor.init(self, t, no_default) + engine.interface.ActorTemporaryEffects.init(self, t) + engine.interface.ActorLife.init(self, t) + engine.interface.ActorProject.init(self, t) + engine.interface.ActorTalents.init(self, t) + engine.interface.ActorStats.init(self, t) + engine.interface.ActorLevel.init(self, t) + engine.interface.ActorFOV.init(self, t) +end + +function _M:act() + if not engine.Actor.act(self) then return end + + self.changed = true + + -- Cooldown talents + self:cooldownTalents() + -- Regen resources + self:regenLife() + -- Compute timed effects + self:timedEffects() + + -- Still enough energy to act ? + if self.energy.value < game.energy_to_act then return false end + + return true +end + +function _M:move(x, y, force) + local moved = false + if force or self:enoughEnergy() then + moved = engine.Actor.move(self, x, y, force) + if not force and moved and not self.did_energy then self:useEnergy() end + end + self.did_energy = nil + return moved +end + +function _M:onTakeHit(value, src) + return value +end + +function _M:die(src) + engine.interface.ActorLife.die(self, src) + + -- Gives the killer some exp for the kill + if src and src.gainExp then + src:gainExp(self:worthExp(src)) + end + + -- Spawn an other one somewhere + local gen = game.zone:getGenerator("actor", game.level) + gen:generateOne() + + return true +end + +function _M:levelup() + self.max_life = self.max_life + 2 + + -- Healp up on new level + self.life = self.max_life +end + +--- Notifies a change of stat value +function _M:onStatChange(stat, v) + if stat == self.STAT_CON then + self.max_life = self.max_life + 2 + end +end + +function _M:attack(target) + self:bumpInto(target) +end + +--- Called before a talent is used +-- Check the actor can cast it +-- @param ab the talent (not the id, the table) +-- @return true to continue, false to stop +function _M:preUseTalent(ab, silent) + if not self:enoughEnergy() then return false end + return true +end + +--- Called before a talent is used +-- Check if it must use a turn, mana, stamina, ... +-- @param ab the talent (not the id, the table) +-- @param ret the return of the talent action +-- @return true to continue, false to stop +function _M:postUseTalent(ab, ret) + if not ret then return end + + self:useEnergy() + return true +end + +--- How much experience is this actor worth +-- @param target to whom is the exp rewarded +-- @return the experience rewarded +function _M:worthExp(target) + return 1 +end + +--- Can the actor see the target actor +-- This does not check LOS or such, only the actual ability to see it.<br/> +-- Check for telepathy, invisibility, stealth, ... +function _M:canSee(actor, def, def_pct) + if not actor then return false, 0 end + + return true, 100 +end + +--- Can the target be applied some effects +-- @param what a string describing what is being tried +function _M:canBe(what) + if what == "poison" and rng.percent(100 * (self:attr("poison_immune") or 0)) then return false end + if what == "cut" and rng.percent(100 * (self:attr("cut_immune") or 0)) then return false end + if what == "confusion" and rng.percent(100 * (self:attr("confusion_immune") or 0)) then return false end + if what == "blind" and rng.percent(100 * (self:attr("blind_immune") or 0)) then return false end + if what == "stun" and rng.percent(100 * (self:attr("stun_immune") or 0)) then return false end + if what == "fear" and rng.percent(100 * (self:attr("fear_immune") or 0)) then return false end + if what == "knockback" and rng.percent(100 * (self:attr("knockback_immune") or 0)) then return false end + if what == "instakill" and rng.percent(100 * (self:attr("instakill_immune") or 0)) then return false end + return true +end diff --git a/game/modules/boot/class/Game.lua b/game/modules/boot/class/Game.lua new file mode 100644 index 0000000000..36ba9a88a0 --- /dev/null +++ b/game/modules/boot/class/Game.lua @@ -0,0 +1,469 @@ +-- ToME - Tales of Middle-Earth +-- 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" +require "engine.GameEnergyBased" +require "engine.interface.GameSound" +require "engine.interface.GameMusic" +require "engine.interface.GameTargeting" +require "engine.KeyBind" + +local Module = require "engine.Module" +local Dialog = require "engine.ui.Dialog" +local Tooltip = require "engine.Tooltip" +local MainMenu = require "mod.dialogs.MainMenu" + +local Shader = require "engine.Shader" +local Zone = require "engine.Zone" +local Map = require "engine.Map" +local Level = require "engine.Level" +local FlyingText = require "engine.FlyingText" + +local Grid = require "mod.class.Grid" +local Actor = require "mod.class.Actor" +local Player = require "mod.class.Player" +local NPC = require "mod.class.NPC" + +module(..., package.seeall, class.inherit(engine.GameEnergyBased, engine.interface.GameMusic, engine.interface.GameSound)) + +function _M:init() + engine.interface.GameMusic.init(self) + engine.interface.GameSound.init(self) + engine.GameEnergyBased.init(self, engine.KeyBind.new(), 100, 100) + self.profile_font = core.display.newFont("/data/font/VeraIt.ttf", 14) + + self.tooltip = Tooltip.new(nil, 14, nil, colors.DARK_GREY, 400) + + self.refuse_threads = true + self.normal_key = self.key + + self:loaded() +end + +function _M:loaded() + engine.GameEnergyBased.loaded(self) + engine.interface.GameMusic.loaded(self) + engine.interface.GameSound.loaded(self) +end + +function _M:run() + self.flyers = FlyingText.new() + self:setFlyingText(self.flyers) + self.log = function(style, ...) end + self.logSeen = function(e, style, ...) end + self.logPlayer = function(e, style, ...) end + + -- Starting from here we create a new game + self:newGame() + + -- Ok everything is good to go, activate the game in the engine! + self:setCurrent() + + self.mod_list = Module:listModules() + self.save_list = Module:listSavefiles() + + self:checkLogged() + self:engineVersion() + + -- Setup display + self:registerDialog(MainMenu.new()) + + -- Run the current music if any + self:volumeMusic(config.settings.music.volume) + self:playMusic("The saga begins.ogg") + + -- Get news + if not self.news then + self.news = profile:getNews() + + if self.news then + local f = loadstring(self.news.text) + if f then + local env = {} + setfenv(f, env) + pcall(f) + if env.text and env.version then + self.news.text = env.text + print("Latest engine version available: ", env.version[4], env.version[1], env.version[2], env.version[3]) + self.latest_engine_version = env.version + if env.link then self.news.link = env.link end + else + self.news = nil + end + end + end + + if not self.news then + self.news = { + title = 'Welcome to T-Engine and the Tales of Middle-earth', + text = [[From this interface you can create new characters for the game modules you want to play. + +#GOLD#"Tales of Middle-earth"#WHITE# is the default game module, you can also install more by selecting "Install a game module" or by going to http://te4.org/ + +When inside a module remember you can press Escape to bring up a menu to change keybindings, resolution and other module specific options. + +Remember that in most roguelikes death is usually permanent so be careful! + +Now go and have some fun!]] + } + end + + if self.news.link then + self.tooltip:set("#AQUAMARINE#%s#WHITE#\n---\n%s\n---\n#LIGHT_BLUE##{underline}#%s#LAST##{normal}#", self.news.title, self.news.text, self.news.link) + else + self.tooltip:set("#AQUAMARINE#%s#WHITE#\n---\n%s", self.news.title, self.news.text) + end + end + +-- self:installNewEngine() + + if not self.firstrunchecked then + -- Check first time run for online profile + self.firstrunchecked = true + self:checkFirstTime() + end + + if self.s_log then + local w, h = self.s_log:getSize() + self.mouse:registerZone(self.w - w, self.h - h, w, h, function(button) + if button == "left" then util.browserOpenUrl(self.logged_url) end + end, {button=true}) + end + + if self.news.link then + self.mouse:registerZone(5, self.tooltip.h - 30, self.tooltip.w, 30, function(button) + if button == "left" then util.browserOpenUrl(self.news.link) end + end, {button=true}) + end +end + +function _M:checkLogged() + if profile.auth then + self.logged_url = "http://te4.org/players/"..profile.auth.page + local str = "Online Profile: "..profile.auth.name.."[#LIGHT_BLUE##{underline}#"..self.logged_url.."#LAST##{normal}#]" + local plain = str:removeColorCodes() + local w, h = self.profile_font:size(plain) + self.s_log = core.display.newSurface(w, h) + self.s_log:erase(0, 0, 0) + self.s_log:drawColorStringBlended(self.profile_font, str, 0, 0, 255, 255, 0) + else + self.logged_url = nil + self.s_log = nil + end +end + +function _M:engineVersion() + self.s_version = core.display.drawStringBlendedNewSurface(self.profile_font, ("T-Engine4 version: %d.%d.%d"):format(engine.version[1], engine.version[2], engine.version[3]), 185, 225, 0) +end + +function _M:newGame() + self.player = Player.new{name=self.player_name, game_ender=true} + Map:setViewerActor(self.player) + self:setupDisplayMode() + + self.player:resolve() + self.player:resolve(nil, true) + self.player.energy.value = self.energy_to_act + + Zone:setup{npc_class="mod.class.NPC", grid_class="mod.class.Grid", } + self:changeLevel(1, "dungeon") +end + +function _M:onResolutionChange() + engine.Game.onResolutionChange(self) + print("[RESOLUTION] changed to ", self.w, self.h) + self:setupDisplayMode() +end + +function _M:setupDisplayMode() + Map:setViewPort(0, 0, self.w, self.h, 32, 32, nil, 22, true, true) + Map:resetTiles() + Map.tiles.use_images = true + + -- Create the framebuffer + self.fbo = core.display.newFBO(game.w, game.h) + if self.fbo then + self.fbo_shader = Shader.new("main_fbo") + if not self.fbo_shader.shad then + self.fbo = nil self.fbo_shader = nil + else + self.fbo_shader:setUniform("colorize", {1,1,1}) + end + end +end + +function _M:changeLevel(lev, zone) + local old_lev = (self.level and not zone) and self.level.level or -1000 + if zone then + if self.zone then + self.zone:leaveLevel(false, lev, old_lev) + self.zone:leave() + end + if type(zone) == "string" then + self.zone = Zone.new(zone) + else + self.zone = zone + end + end + self.zone:getLevel(self, lev, old_lev) + + if lev > old_lev then + self.player:move(self.level.default_up.x, self.level.default_up.y, true) + else + self.player:move(self.level.default_down.x, self.level.default_down.y, true) + end + self.level:addEntity(self.player) +end + +function _M:getPlayer() + return self.player +end + +function _M:tick() + if self.level then + engine.GameEnergyBased.tick(self) + -- Fun stuff: this can make the game realtime, although callit it in display() will make it work better + -- (since display is on a set FPS while tick() ticks as much as possible + -- engine.GameEnergyBased.tick(self) + end + return false +end + +--- Called every game turns +-- Does nothing, you can override it +function _M:onTurn() + -- The following happens only every 10 game turns (once for every turn of 1 mod speed actors) + if self.turn % 10 ~= 0 then return end + + -- Process overlay effects + self.level.map:processEffects() +end + +function _M:display() + -- Display using Framebuffer, so that we can use shaders and all + if self.fbo then self.fbo:use(true) end + + -- Now the map, if any + if self.level and self.level.map and self.level.map.finished then + -- Display the map and compute FOV for the player if needed + if self.level.map.changed then + self.player:playerFOV() + end + + self.level.map:display() + end + + -- Draw it here, inside the FBO + self.flyers:display() + + -- Display using Framebuffer, sotaht we can use shaders and all + if self.fbo then + self.fbo:use(false) + _2DNoise:bind(1, false) + self.fbo:toScreen( + self.level.map.display_x, self.level.map.display_y, + self.level.map.viewport.width, self.level.map.viewport.height, + self.fbo_shader.shad + ) + else +-- core.display.drawQuad(0, 0, game.w, game.h, 128, 128, 128, 128) + end + + if self.s_log then + local w, h = self.s_log:getSize() + self.s_log:toScreen(self.w - w, self.h - h) + end +-- local w, h = self.s_version:getSize() +-- self.s_version:toScreen(0, self.h - h) + + self.tooltip:display() + self.tooltip:toScreen(5, 5) + + local old = self.flyers + self.flyers = nil + engine.GameEnergyBased.display(self) + self.flyers = old +end + +--- Skip to a module directly ? +function _M:commandLineArgs(args) + local req_mod = nil + local req_save = nil + local req_new = false + for i, arg in ipairs(args) do + if arg:find("^%-M") then + -- Force module loading + req_mod = arg:sub(3) + end + if arg:find("^%-u") then + -- Force save loading + req_save = arg:sub(3) + end + if arg:find("^%-n") then + -- Force save loading + req_new = true + end + end + + if req_mod then + local mod = self.mod_list[req_mod] + if mod then + Module:instanciate(mod, req_save or "player", req_new) + else + print("Error: module "..req_mod.." not found!") + end + end +end + +--- Ask if we realy want to close, if so, save the game first +function _M:onQuit() + if self.is_quitting then return end + self.is_quitting = Dialog:yesnoPopup("Quit", "Really exit T-Engine/ToME?", function(ok) + self.is_quitting = false + if ok then os.exit() end + end, "Quit", "Continue") +end + +profile_help_text = [[#LIGHT_GREEN#T-Engine4#LAST# allows you to sync your player profile with the website #LIGHT_BLUE#http://te4.org/#LAST# + +This allows you to: +* Play from several computers without having to copy unlocks and achievements. +* Keep track of your modules progression, kill count, ... +* Cool statistics for each module to help sharpen your gameplay style +* Help the game developers balance and refine the game + +Later on you will have an online profile page you can show to people to brag. +This is all optional, you are not forced to use this feature at all, but the developers would thank you if you did as it will +make balancing easier. +Online profile requires an internet connection, if not available it will wait and sync when it finds one.]] + +function _M:checkFirstTime() + if not profile.generic.firstrun then + profile:checkFirstRun() + local text = "Thanks for downloading T-Engine/ToME.\n\n"..profile_help_text + Dialog:yesnoLongPopup("Welcome to T-Engine", text, 400, function(ret) + if ret then + self:registerDialog(require("mod.dialogs.Profile").new()) + end + end, "Register now", "Maybe later") + end +end + +function _M:selectStepInstall() + local linda, th = Module:loadRemoteList() + local rawdllist = linda:receive("moduleslist") + th:join() + + local dllist = {} + for i, mod in ipairs(rawdllist) do + if not self.mod_list[mod.short_name] then + dllist[#dllist+1] = mod + else + local lmod = self.mod_list[mod.short_name] + if mod.version[1] * 1000000 + mod.version[2] * 1000 + mod.version[3] > lmod.version[1] * 1000000 + lmod.version[2] * 1000 + lmod.version[3] then + dllist[#dllist+1] = mod + end + end + end + + if #dllist == 0 then + Dialog:simplePopup("No modules available", "There are no modules to install or upgrade.") + return + end + + local display_module = Dialog.new("", self.w * 0.73, self.h, self.w * 0.26, 0, 255) + + for i, mod in ipairs(dllist) do + mod.fct = function() + local d = DownloadDialog.new("Downloading: "..mod.long_name, mod.download, function(di, data) + fs.mkdir("/modules") + local f = fs.open("/modules/"..mod.short_name..".team", "w") + for i, v in ipairs(data) do f:write(v) end + f:close() + + -- Relist modules and savefiles + self.mod_list = Module:listModules() + self.save_list = Module:listSavefiles() + + if self.mod_list[mod.short_name] then + Dialog:simplePopup("Success!", "Your new game is now installed, you can play!", function() self:unregisterDialog(display_module) self:selectStepMain() end) + else + Dialog:simplePopup("Error!", "The downloaded game does not seem to respond to the test. Please contact contact@te4.org") + end + end) + self:registerDialog(d) + d:startDownload() + end + mod.onSelect = function() + display_module.title = mod.long_name + display_module.changed = true + end + end + + display_module.drawDialog = function(self, s) + local lines = dllist[game.step.selected].description:splitLines(self.w - 8, self.font) + local r, g, b + for i = 1, #lines do + r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) + end + end + self:registerDialog(display_module) + + self.step = ButtonList.new(dllist, 10, 10, self.w * 0.24, (5 + 35) * #dllist, nil, 5) + self.step:setKeyHandling() + self.step:setMouseHandling() + self.step.key:addBind("EXIT", function() self:unregisterDialog(display_module) self:selectStepMain() end) +end + +function _M:createProfile(loginItem) + if self.justlogin then + profile:performlogin(loginItem.login, loginItem.pass) + if profile.auth then + Dialog:simplePopup("Profile logged in!", "Your online profile is active now...", function() self:checkLogged() self:selectStepProfile() end ) + else + Dialog:simplePopup("Log in rejected", "Couldn't log you...", function() self:selectStepProfile() end ) + end + return + end + profile:newProfile(loginItem.login, loginItem.name, loginItem.pass, loginItem.email) + if (profile.auth) then + Dialog:simplePopup("Profile created!", "Your online profile is active now...", function() self:checkLogged() end ) + else + Dialog:simplePopup("Profile Failed to authenticate!", "Try logging in in a few moments", function() end ) + end + +end + +function _M:installNewEngine() + if not self.latest_engine_version then return end + print("te4.org told us latest engine is", self.latest_engine_version[4], self.latest_engine_version[5], self.latest_engine_version[1], self.latest_engine_version[2], self.latest_engine_version[3]) + if engine.version_check(self.latest_engine_version) == "newer" then + local url = ("http://te4.org/dl/engines/%s_%d:%d.%d.%d.teae"):format(self.latest_engine_version[4], self.latest_engine_version[5], self.latest_engine_version[1], self.latest_engine_version[2], self.latest_engine_version[3]) + local d = DownloadDialog.new(("Downloading: T-Engine 4 %d.%d.%d"):format(self.latest_engine_version[1], self.latest_engine_version[2], self.latest_engine_version[3]), url, function(di, data) + fs.mkdir("/engines") + local f = fs.open(("/engines/%s-%d_%d.%d.%d.teae"):format(self.latest_engine_version[4], self.latest_engine_version[5], self.latest_engine_version[1], self.latest_engine_version[2], self.latest_engine_version[3]), "w") + for i, v in ipairs(data) do f:write(v) end + f:close() + + Dialog:simplePopup("Success!", "The new engine is installed, it will now restart using it.", function() util.showMainMenu() end) + end) + self:registerDialog(d) + d:startDownload() + end +end \ No newline at end of file diff --git a/game/modules/boot/class/Grid.lua b/game/modules/boot/class/Grid.lua new file mode 100644 index 0000000000..fcdf9ce2ce --- /dev/null +++ b/game/modules/boot/class/Grid.lua @@ -0,0 +1,107 @@ +-- ToME - Tales of Middle-Earth +-- 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" +require "engine.Grid" +local DamageType = require "engine.DamageType" + +module(..., package.seeall, class.inherit(engine.Grid)) + +function _M:init(t, no_default) + engine.Grid.init(self, t, no_default) +end + +function _M:block_move(x, y, e, act, couldpass) + -- Open doors + if self.door_opened and act then + game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list.DOOR_OPEN) + return true + elseif self.door_opened and not couldpass then + return true + end + + -- Pass walls + if e and self.can_pass and e.can_pass then + for what, check in pairs(e.can_pass) do + if self.can_pass[what] and self.can_pass[what] <= check then return false end + end + end + + return self.does_block_move +end + +function _M:on_move(x, y, who, forced) + if forced then return end + if who.move_project and next(who.move_project) then + for typ, dam in pairs(who.move_project) do + DamageType:get(typ).projector(who, x, y, typ, dam) + end + end +end + +--- Generate sub entities to make nice trees +function _M:makeTrees(base, max) + local function makeTree(nb, z) + local inb = 4 - nb + return engine.Entity.new{ + z = z, + display_scale = rng.float(0.5 + inb / 6, 1.3), + display_x = rng.range(-engine.Map.tile_w / 3 * nb / 3, engine.Map.tile_w / 3 * nb / 3), + display_y = rng.range(-engine.Map.tile_h / 3 * nb / 3, engine.Map.tile_h / 3 * nb / 3), + display_on_seen = true, + display_on_remember = true, + image = (base or "terrain/tree_alpha")..rng.range(1,max or 5)..".png", + } + end + + local v = rng.range(0, 100) + local tbl + if v < 33 then + tbl = { makeTree(3, 16), makeTree(3, 17), makeTree(3, 18), } + elseif v < 66 then + tbl = { makeTree(2, 16), makeTree(2, 17), } + else + tbl = { makeTree(1, 16), } + end + table.sort(tbl, function(a,b) return a.display_y < b.display_y end) + return tbl +end + +--- Generate sub entities to make translucent water +function _M:makeWater(z, prefix) + prefix = prefix or "" + return { engine.Entity.new{ + z = z and 16 or 9, + image = "terrain/"..prefix.."water_floor_alpha.png", + shader = prefix.."water", textures = { function() return _3DNoise, true end }, + display_on_seen = true, + display_on_remember = true, + } } +end + +--- Merge sub entities +function _M:mergeSubEntities(...) + local tbl = {} + for i, t in ipairs{...} do if t then + for j, e in ipairs(t) do + tbl[#tbl+1] = e + end + end end + return tbl +end diff --git a/game/modules/boot/class/NPC.lua b/game/modules/boot/class/NPC.lua new file mode 100644 index 0000000000..f838e97286 --- /dev/null +++ b/game/modules/boot/class/NPC.lua @@ -0,0 +1,53 @@ +-- ToME - Tales of Middle-Earth +-- 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 ActorAI = require "engine.interface.ActorAI" +local Faction = require "engine.Faction" +require "mod.class.Actor" + +module(..., package.seeall, class.inherit(mod.class.Actor, engine.interface.ActorAI)) + +function _M:init(t, no_default) + mod.class.Actor.init(self, t, no_default) + ActorAI.init(self, t) +end + +function _M:act() + -- Do basic actor stuff + if not mod.class.Actor.act(self) then return end + + -- Compute FOV, if needed + self:computeFOV(self.sight or 20) + + -- Let the AI think .... beware of Shub ! + -- If AI did nothing, use energy anyway + self:doAI() + if not self.energy.used then self:useEnergy() end +end + +--- Called by ActorLife interface +-- We use it to pass aggression values to the AIs +function _M:onTakeHit(value, src) + if not self.ai_target.actor and src.targetable then + self.ai_target.actor = src + end + + return mod.class.Actor.onTakeHit(self, value, src) +end diff --git a/game/modules/boot/class/Player.lua b/game/modules/boot/class/Player.lua new file mode 100644 index 0000000000..be09dbff6d --- /dev/null +++ b/game/modules/boot/class/Player.lua @@ -0,0 +1,108 @@ +-- ToME - Tales of Middle-Earth +-- 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" +require "mod.class.NPC" +local Map = require "engine.Map" +local ActorTalents = require "engine.interface.ActorTalents" + +--- Defines the player +-- It is a normal actor, with some redefined methods to handle user interaction.<br/> +-- It is also able to run and rest and use hotkeys +module(..., package.seeall, class.inherit( + mod.class.NPC +)) + +function _M:init(t, no_default) + t.display=t.display or '@' + t.color_r=t.color_r or 230 + t.color_g=t.color_g or 230 + t.color_b=t.color_b or 230 + + t.player = true + t.type = t.type or "humanoid" + t.subtype = t.subtype or "player" + t.faction = t.faction or "players" + + t.lite = t.lite or 0 + + t.ai = "player_demo" + t.ai_state = { talent_in=2, }, + + mod.class.NPC.init(self, t, no_default) + + self:learnTalent(self.T_MANATHRUST, true, 2) + self:learnTalent(self.T_FLAME, true, 2) + self:learnTalent(self.T_FIREFLASH, true, 2) + self:learnTalent(self.T_LIGHTNING, true, 2) + self:learnTalent(self.T_SUNSHIELD, true, 2) +end + +function _M:move(x, y, force) + local moved = mod.class.NPC.move(self, x, y, force) + if moved then + game.level.map:moveViewSurround(self.x, self.y, 8, 8) + end + return moved +end + +function _M:act() + if not mod.class.NPC.act(self) then return end +end + +-- Precompute FOV form, for speed +local fovdist = {} +for i = 0, 30 * 30 do + fovdist[i] = math.max((20 - math.sqrt(i)) / 14, 0.6) +end + +function _M:playerFOV() + -- Clean FOV before computing it + game.level.map:cleanFOV() + -- Compute both the normal and the lite FOV, using cache + self:computeFOV(self.sight or 20, "block_sight", function(x, y, dx, dy, sqdist) + game.level.map:apply(x, y, fovdist[sqdist]) + end, true, false, true) + self:computeFOV(self.lite, "block_sight", function(x, y, dx, dy, sqdist) game.level.map:applyLite(x, y) end, true, true, true) +end + +function _M:onTakeHit(value, src) + return 0 +end + +function _M:setName(name) + self.name = name + game.save_name = name +end + +--- Notify the player of available cooldowns +function _M:onTalentCooledDown(tid) + local t = self:getTalentFromId(tid) + + local x, y = game.level.map:getTileToScreen(self.x, self.y) + game.flyers:add(x, y, 30, -0.3, -3.5, ("%s available"):format(t.name:capitalize()), {0,255,00}) + game.log("#00ff00#Talent %s is ready to use.", t.name) +end + +function _M:levelup() + mod.class.NPC.levelup(self) + + local x, y = game.level.map:getTileToScreen(self.x, self.y) + game.flyers:add(x, y, 80, 0.5, -2, "LEVEL UP!", {0,255,255}) +end diff --git a/game/modules/boot/class/interface/Combat.lua b/game/modules/boot/class/interface/Combat.lua new file mode 100644 index 0000000000..3ea59a4439 --- /dev/null +++ b/game/modules/boot/class/interface/Combat.lua @@ -0,0 +1,56 @@ +-- ToME - Tales of Middle-Earth +-- 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 DamageType = require "engine.DamageType" +local Map = require "engine.Map" +local Target = require "engine.Target" +local Talents = require "engine.interface.ActorTalents" + +--- Interface to add ToME combat system +module(..., package.seeall, class.make) + +--- Checks what to do with the target +-- Talk ? attack ? displace ? +function _M:bumpInto(target) + local reaction = self:reactionToward(target) + if reaction < 0 then + return self:attackTarget(target) + elseif reaction >= 0 then + if self.move_others then + -- Displace + game.level.map:remove(self.x, self.y, Map.ACTOR) + game.level.map:remove(target.x, target.y, Map.ACTOR) + game.level.map(self.x, self.y, Map.ACTOR, target) + game.level.map(target.x, target.y, Map.ACTOR, self) + self.x, self.y, target.x, target.y = target.x, target.y, self.x, self.y + end + end +end + +--- Makes the death happen! +function _M:attackTarget(target, mult) + if self.combat then + local dam = self.combat.dam + self:getStr() - target.combat_armor + DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, math.max(0, dam)) + end + + -- We use up our own energy + self:useEnergy(game.energy_to_act) +end diff --git a/game/modules/boot/data/birth/descriptors.lua b/game/modules/boot/data/birth/descriptors.lua new file mode 100644 index 0000000000..64f6113656 --- /dev/null +++ b/game/modules/boot/data/birth/descriptors.lua @@ -0,0 +1,56 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +newBirthDescriptor{ + type = "base", + name = "base", + desc = { + }, + experience = 1.0, + + copy = { + max_level = 10, + lite = 4, + max_life = 25, + }, +} + +newBirthDescriptor{ + type = "role", + name = "Destroyer", + desc = + { + "Crashhhhh!", + }, + talents = { + [ActorTalents.T_KICK]=1, + }, +} + +newBirthDescriptor{ + type = "role", + name = "Acid-maniac", + desc = + { + "Zshhhhhhhh!", + }, + talents = { + [ActorTalents.T_ACID_SPRAY]=1, + }, +} diff --git a/game/modules/boot/data/damage_types.lua b/game/modules/boot/data/damage_types.lua new file mode 100644 index 0000000000..146b89c756 --- /dev/null +++ b/game/modules/boot/data/damage_types.lua @@ -0,0 +1,61 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- The basic stuff used to damage a grid +setDefaultProjector(function(src, x, y, type, dam) + local target = game.level.map(x, y, Map.ACTOR) + if target then + if src.player then dam = dam * 6 end + local sx, sy = game.level.map:getTileToScreen(x, y) + if target:takeHit(dam, src) then + if src == game.player or target == game.player then + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, "Kill!", {255,0,255}) + end + else + if src == game.player then + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, tostring(-math.ceil(dam)), {0,255,0}) + elseif target == game.player then + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, tostring(-math.ceil(dam)), {255,0,0}) + end + end + return dam + end + return 0 +end) + +newDamageType{ + name = "physical", type = "PHYSICAL", +} +newDamageType{ + name = "acid", type = "ACID", text_color = "#GREEN#", +} +newDamageType{ + name = "arcane", type = "ARCANE", text_color = "#PURPLE#", +} +-- Light damage +newDamageType{ + name = "light", type = "LIGHT", text_color = "#YELLOW#", +} +-- The elemental damges +newDamageType{ + name = "fire", type = "FIRE", text_color = "#LIGHT_RED#", +} +newDamageType{ + name = "lightning", type = "LIGHTNING", text_color = "#ROYAL_BLUE#", +} diff --git a/game/modules/boot/data/general/grids/basic.lua b/game/modules/boot/data/general/grids/basic.lua new file mode 100644 index 0000000000..171745a3c4 --- /dev/null +++ b/game/modules/boot/data/general/grids/basic.lua @@ -0,0 +1,83 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +newEntity{ + define_as = "UP_WILDERNESS", + name = "exit to the wilds", + display = '<', color_r=255, color_g=0, color_b=255, back_color=colors.DARK_GREY, + always_remember = true, + notice = true, + change_level = 1, + change_zone = "wilderness", +} + +newEntity{ + define_as = "UP", + name = "previous level", + display = '<', color_r=255, color_g=255, color_b=0, back_color=colors.DARK_GREY, + notice = true, + always_remember = true, + change_level = -1, +} + +newEntity{ + define_as = "DOWN", + name = "next level", + display = '>', color_r=255, color_g=255, color_b=0, back_color=colors.DARK_GREY, + notice = true, + always_remember = true, + change_level = 1, +} + +newEntity{ + define_as = "FLOOR", + name = "floor", image = "terrain/marble_floor.png", + display = ' ', color_r=255, color_g=255, color_b=255, back_color=colors.DARK_GREY, +} + +newEntity{ + define_as = "WALL", + 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 = true, + air_level = -20, + dig = "FLOOR", +} + +newEntity{ + define_as = "DOOR", + name = "door", image = "terrain/granite_door1.png", + display = '+', color_r=238, color_g=154, color_b=77, back_color=colors.DARK_UMBER, + notice = true, + always_remember = true, + block_sight = true, + door_opened = "DOOR_OPEN", + dig = "DOOR_OPEN", +} + +newEntity{ + define_as = "DOOR_OPEN", + name = "open door", image = "terrain/granite_door1_open.png", + display = "'", color_r=238, color_g=154, color_b=77, back_color=colors.DARK_GREY, + always_remember = true, + door_closed = "DOOR", +} diff --git a/game/modules/boot/data/general/grids/forest.lua b/game/modules/boot/data/general/grids/forest.lua new file mode 100644 index 0000000000..aea5985f38 --- /dev/null +++ b/game/modules/boot/data/general/grids/forest.lua @@ -0,0 +1,46 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +newEntity{ + define_as = "GRASS", + name = "grass", image = "terrain/grass.png", + display = '.', color=colors.LIGHT_GREEN, back_color={r=44,g=95,b=43}, +} + +for i = 1, 20 do +newEntity{ + define_as = "TREE"..(i > 1 and i or ""), + name = "tree", + image = "terrain/grass.png", + add_displays = class:makeTrees("terrain/tree_alpha"), + display = '#', color=colors.LIGHT_GREEN, back_color={r=44,g=95,b=43}, + always_remember = true, + can_pass = {pass_tree=1}, + does_block_move = true, + block_sight = true, + dig = "GRASS", +} +end + +newEntity{ + define_as = "FLOWER", + name = "flower", image = "terrain/grass_flower3.png", + display = ';', color=colors.YELLOW, back_color={r=44,g=95,b=43}, +} + diff --git a/game/modules/boot/data/general/grids/water.lua b/game/modules/boot/data/general/grids/water.lua new file mode 100644 index 0000000000..ab01a738bf --- /dev/null +++ b/game/modules/boot/data/general/grids/water.lua @@ -0,0 +1,40 @@ +-- ToME - Tales of Middle-Earth +-- 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 + + +------------------------------------------------------------ +-- For outside +------------------------------------------------------------ + +newEntity{ + define_as = "SHALLOW_WATER", + name = "shallow water", image = "terrain/water_floor.png", + display = '~', color=colors.LIGHT_BLUE, back_color=colors.DARK_BLUE, + add_displays = class:makeWater(false), + always_remember = true, +} + +newEntity{ + define_as = "DEEP_WATER", + name = "deep water", image = "terrain/water_floor.png", + display = '~', color=colors.AQUAMARINE, back_color=colors.DARK_BLUE, + add_displays = class:makeWater(true), + always_remember = true, + air_level = -5, air_condition="water", +} diff --git a/game/modules/boot/data/general/npcs/canine.lua b/game/modules/boot/data/general/npcs/canine.lua new file mode 100644 index 0000000000..3cce1c1d4a --- /dev/null +++ b/game/modules/boot/data/general/npcs/canine.lua @@ -0,0 +1,86 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- last updated: 5:11 PM 1/29/2010 + +newEntity{ + define_as = "BASE_NPC_CANINE", + type = "animal", subtype = "canine", + display = "C", color=colors.WHITE, + level_range = {1, nil}, exp_worth = 1, + + ai = "dumb_talented_simple", ai_state = { talent_in=2, }, + energy = { mod=1.1 }, + combat = { dammod={str=0.6} }, + combat_armor = 1, combat_def = 1, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "wolf", color=colors.UMBER, image="npc/canine_w.png", + desc = [[Lean, mean, and shaggy, it stares at you with hungry eyes.]], + rarity = 1, + max_life = resolvers.rngavg(40,70), + combat_armor = 1, combat_def = 3, + combat = { dam=5, atk=15, apr=3 }, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "great wolf", color=colors.UMBER, image="npc/canine_gw.png", + desc = [[Larger than a normal wolf, it prowls and snaps at you.]], + rarity = 3, + max_life = resolvers.rngavg(60,90), + combat_armor =2, combat_def = 4, + combat = { dam=6, atk=15, apr=3 }, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "dire wolf", color=colors.DARK_UMBER, image="npc/canine_dw.png", + desc = [[Easily as big as a horse, this wolf menaces you with its claws and fangs.]], + rarity = 3, + max_life = resolvers.rngavg(80,110), + combat_armor = 3, combat_def = 5, + combat = { dam=9, atk=15, apr=4 }, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "white wolf", color=colors.WHITE, image="npc/canine_ww.png", + desc = [[A large and muscled wolf from the northern wastes. Its breath is cold and icy and its fur coated in frost.]], + rarity = 3, + max_life = resolvers.rngavg(70,100), + combat_armor = 3, combat_def = 4, + combat = { dam=8, atk=15, apr=3 }, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "warg", color=colors.BLACK, image="npc/canine_warg.png", + desc = [[It is a large wolf with eyes full of cunning.]], + rarity = 4, + max_life = resolvers.rngavg(60,100), + combat_armor = 5, combat_def = 7, + combat = { dam=10, atk=17, apr=5 }, +} + +newEntity{ base = "BASE_NPC_CANINE", + name = "fox", color=colors.RED, image="npc/canine_fox.png", + desc = [[The quick brown fox jumps over the lazy dog.]], + rarity = 3, + max_life = resolvers.rngavg(40,50), + combat_armor = 1, combat_def = 3, + combat = { dam=4, atk=10, apr=3 }, +} diff --git a/game/modules/boot/data/general/npcs/skeleton.lua b/game/modules/boot/data/general/npcs/skeleton.lua new file mode 100644 index 0000000000..8ab706971c --- /dev/null +++ b/game/modules/boot/data/general/npcs/skeleton.lua @@ -0,0 +1,72 @@ +-- ToME - Tales of Middle-Earth +-- 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{ + define_as = "BASE_NPC_SKELETON", + type = "undead", subtype = "skeleton", + display = "s", color=colors.WHITE, + level_range = {1, nil}, exp_worth = 1, + + combat = { dam=1, atk=1, apr=1 }, + + ai = "dumb_talented_simple", ai_state = { talent_in=1, }, + energy = { mod=1 }, + + open_door = true, + + blind_immune = 1, + fear_immune = 1, + see_invisible = 2, + undead = 1, +} + +newEntity{ base = "BASE_NPC_SKELETON", + name = "degenerated skeleton warrior", color=colors.WHITE, image="npc/degenerated_skeleton_warrior.png", + rarity = 1, + max_life = resolvers.rngavg(40,50), + combat_armor = 5, combat_def = 1, +} + +newEntity{ base = "BASE_NPC_SKELETON", + name = "skeleton warrior", color=colors.SLATE, image="npc/skeleton_warrior.png", + rarity = 1, + max_life = resolvers.rngavg(90,100), + combat_armor = 5, combat_def = 1, + ai_state = { talent_in=1, }, +} + +newEntity{ base = "BASE_NPC_SKELETON", + name = "skeleton mage", color=colors.LIGHT_RED, image="npc/skeleton_mage.png", + rarity = 3, + max_life = resolvers.rngavg(50,60), + max_mana = resolvers.rngavg(70,80), + combat_armor = 3, combat_def = 1, + stats = { str=10, dex=12, cun=14, mag=14, con=10 }, + resolvers.talents{ [Talents.T_FLAME]=2, [Talents.T_MANATHRUST]=3 }, +} + +newEntity{ base = "BASE_NPC_SKELETON", + name = "armoured skeleton warrior", color=colors.STEEL_BLUE, + level_range = {10, nil}, exp_worth = 1, + rarity = 5, + max_life = resolvers.rngavg(90,100), + combat_armor = 5, combat_def = 1, +} diff --git a/game/modules/boot/data/general/npcs/troll.lua b/game/modules/boot/data/general/npcs/troll.lua new file mode 100644 index 0000000000..21634632fd --- /dev/null +++ b/game/modules/boot/data/general/npcs/troll.lua @@ -0,0 +1,77 @@ +-- ToME - Tales of Middle-Earth +-- 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{ + define_as = "BASE_NPC_TROLL", + type = "giant", subtype = "troll", + display = "T", color=colors.UMBER, + level_range = {1, nil}, exp_worth = 1, + + ai = "dumb_talented_simple", ai_state = { talent_in=1, }, + energy = { mod=1 }, + + open_door = true, + fear_immune = 1, +} + +newEntity{ base = "BASE_NPC_TROLL", + name = "forest troll", color=colors.YELLOW_GREEN, image="npc/troll_f.png", + desc = [[Green-skinned and ugly, this massive humanoid glares at you, clenching wart-covered green fists.]], + rarity = 1, + max_life = resolvers.rngavg(100,120), + combat_armor = 4, combat_def = 0, +} + +newEntity{ base = "BASE_NPC_TROLL", + name = "stone troll", color=colors.DARK_SLATE_GRAY, image="npc/troll_s.png", + desc = [[A giant troll with scabrous black skin. With a shudder, you notice the belt of dwarf skulls around his massive waist.]], + rarity = 1, + max_life = resolvers.rngavg(120,140), + combat_armor = 7, combat_def = 0, +} + +newEntity{ base = "BASE_NPC_TROLL", + name = "cave troll", color=colors.SLATE, image="npc/troll_c.png", + desc = [[This huge troll wields a massive spear and has a disturbingly intelligent look in its piggy eyes.]], + rarity = 2, + max_life = resolvers.rngavg(120,140), + combat_armor = 9, combat_def = 3, +} + +newEntity{ base = "BASE_NPC_TROLL", + name = "mountain troll", color=colors.UMBER, image="npc/troll_m.png", + desc = [[A large and athletic troll with an extremely tough and warty hide.]], + rarity = 3, + max_life = resolvers.rngavg(120,140), + combat_armor = 12, combat_def = 4, +} + +newEntity{ base = "BASE_NPC_TROLL", + name = "mountain troll thunderer", color=colors.AQUAMARINE, image="npc/troll_mt.png", + desc = [[A large and athletic troll with an extremely tough and warty hide.]], + rarity = 5, + rank = 3, + max_life = resolvers.rngavg(120,140), + combat_armor = 8, combat_def = 4, + resolvers.talents{ + [Talents.T_LIGHTNING]=4, + }, +} diff --git a/game/modules/boot/data/gfx/npc/armored_skeleton_warrior.png b/game/modules/boot/data/gfx/npc/armored_skeleton_warrior.png new file mode 100644 index 0000000000000000000000000000000000000000..00cbcabaab62a815eb6b327de79fdc53fcf8b513 GIT binary patch literal 1582 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_dw.png b/game/modules/boot/data/gfx/npc/canine_dw.png new file mode 100644 index 0000000000000000000000000000000000000000..d84ce1a8175fbe4fd67afc5c0721e80c10cf6634 GIT binary patch literal 3466 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_fox.png b/game/modules/boot/data/gfx/npc/canine_fox.png new file mode 100644 index 0000000000000000000000000000000000000000..8953918dd4248bfc69673af6f016ec6446935ff9 GIT binary patch literal 3178 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_gw.png b/game/modules/boot/data/gfx/npc/canine_gw.png new file mode 100644 index 0000000000000000000000000000000000000000..a69f00dec334894976b0994de802e5ca8a7861c1 GIT binary patch literal 3640 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_w.png b/game/modules/boot/data/gfx/npc/canine_w.png new file mode 100644 index 0000000000000000000000000000000000000000..a8eaac992e3e5ffbcb2a92b0c46bf011c4473522 GIT binary patch literal 3319 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_warg.png b/game/modules/boot/data/gfx/npc/canine_warg.png new file mode 100644 index 0000000000000000000000000000000000000000..2316dbf3f1b62737e2916ba530d85684a0970cab GIT binary patch literal 3694 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 USY*!D{|zAzH}4%K+qjAU2YOIldjJ3c literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/canine_ww.png b/game/modules/boot/data/gfx/npc/canine_ww.png new file mode 100644 index 0000000000000000000000000000000000000000..8a026700505f16fd7a568fd8554903226b07c852 GIT binary patch literal 3847 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 Xh2JWgmUSEc1<=lq_iVox$~pEw#}}s& literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/degenerated_skeleton_warrior.png b/game/modules/boot/data/gfx/npc/degenerated_skeleton_warrior.png new file mode 100644 index 0000000000000000000000000000000000000000..f8f88c0bb8a14b30931954be40af9e286d12b3d0 GIT binary patch literal 994 zcmV<810DQ{P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;ynoLjXg28M;*f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igh& z6Dcu<g=p0P000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0009$Nkl<Z zSi`-RTWDNW6o$Wb&csA(%?vHA6l7A3V5}}P#yr$ir5zM1R;`Ly@sd7>r7iXHU|#B* zRD2XmFCvIOM1uHY5vfh1RfHC$=n6Kdq0?f`LoS_+lR}x>_GL$gVUQ*{CqEoG>;q^0 z`(OY5*A^B5=iFn!9l)%Je8dtUizILnxD13MqDvtVETZ_Gz$M^A;DU25wX6h0<Q(uT zFak^hv&%}r;{xzLP!<u#a>C5k)%jq_kZUN2s$yn`oO8E|$lt(IEiEl)M5L!wDkXvM zfX%>ORXq|(3(Rc(^+7ZHty-<#*xA`RQLEK@fcMJf^4U_UbkI3B5QgCw&bblb$0$AU zqM12h5ZKq<-TlVg++4eetOou8wgEGhN~M@ir(dqsY9qiSb;Pd;iU0ZbgsRHC8qT?{ zN~N+vL=4ygtOZ^Q!|+nESS&i{z6Ksp)uk)o`~J-QZd8>Z2*#$Tr^kT;a1po(C<H+; zIW;wPOCpg7fYC@=Fu#k&-H6B}a03tmGe8&6=2LMckd8YC?(=PX4QK_P1n%{PbDmIS z9%zQQft&sF4e$rB7cgKIP>EZCFIDv@um<P@_NeOjsyYii1dPS4z};qc3>fwyHeqIG zfwzG5en`ctz&pV8Knrjc*a+MMJnmaE5t{=3NWK8P2~>f<fOWuUz+oU0s|Wm%T<c9Y z3QVZ#PGEy`Zg<;?6$R(qrno2Jbv~Z!*S@Mbcl)kv_Ltws#}AoV??U2O7D!+XFr=!R z>(<__RjavHIDh*1@e}<611m*jE-n+^5B$)ub}k6RbXO)btl8|oEy?63*;HzMHkFE& zfS2{QCIkw=n3>Vj+xx+{g~HRo0pQs<Ot>F-zTwkGKM(%H$$WkXaCu96`=<|Y-P)hY zWL}L*frG&Q29tdbWSWfO;Y2cdxHvQOaYskT5$9Ya3Y-F7Zz%RBFy83=lleTW`}%$^ zUb%8Cm&*-_NF-5wmrucZ^a4LMyNC=94wijNMs^C$1C3g~0<<mkDz;bxo0`o0QQ(A+ z>DUQu2aYy);{fm+u-(iajb#{ldHaFRCOLqr4w+frPsG^fz&0~$jrza;0tWa)RzfKf QN&o-=07*qoM6N<$f^(m|wg3PC literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/skeleton_mage.png b/game/modules/boot/data/gfx/npc/skeleton_mage.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f45e2ad85bcbfbfb300c737aabdadc2cc22657 GIT binary patch literal 1388 zcmV-y1(W)TP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igh& z6E`G38MEO4000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000EXNkl<Z zSi`-SeQ1?s7{-71-kqKKwT+$OHfLzgoSXX2O+yx9K~W@GDn%cZ7$q8s+K1Mbr7jgR zGSYz<{t=O>MNlT<tUytyku!X3)%s9|dNk|k@;KYh&N+8~^c*<8p0iGFc>nXjxu5e~ z*L&U9eZS8uTt}$teZY92Q$)5gkfEwMedQbdO=Z13u@kC#L{-1*FP@|zAR?ma{;+l0 z&eg?k(b916a!bz8oZ60qXPQ==ThKUAPoL4glLt=kIK1J=9l@i&?_8I+Wi&TBRCR@_ z?o-t*Hyqp;Gs2SS7>M&E(~WIyZSO>*(Fzec1I+dLd@Uj}B@&4gsOmSPMvYomP*AWh zle0>PjI2J}b@~`URwlks)%hhQC0XU=<-vG7J_UHAx3~9bBob*-)t5x%!~FdG8MZz9 zjAemU58M>HIls~0L`DQe1XZmrEG*pI)zy_RB1OO@U=|RHMx)^|W5&!Akp^I@sy?Ht zzLelJ`eM=EdnQC{M!%l3%*lv=B{5W0tD@291Q9X7qd+k*&vBf4!{Kn)?pqBQ;C)qX zP*q=YXN@nbEGg2`oLlm$YI?)bSjHYu)pvp2#+XGS5(Z+xgTNu+H{f>Hbt@Akv#Pyq zb6Z3twvZJ#Sz6}L9dt6*9lyY^*tG13uOll%4|a;k9uYYY3<ew^1XKdKz&TIJ5RvDA z&w%$-_5Q7F;Bx%R?34cEzQew~MHiyyc5VE-kxU6pwChwL8(0eXf!RPUaNg@18>d9% z31C<}9^bU;#FDzQ%Hh!}XOh#I6?`+L2eg54DF|G$>#M+Spaj?t{0uAx78_$y8rZJe zMWp40rxwmF9bdAk-T!s*o^bOgsq0-&qo)NYs_Kh&2#H<qGRAZmW9l;<@vSW_6>F9+ zKkdzBd%(-crAjwZBun_y*7E}qnQXUAGRAxkd}$3FG{zhS>Rs1u?yHr@78JY!obu+f zlB5S^GT9VzmX|H_nZV|xR$2=T0(?L>PzO{4cmJ0Pj^orhj?<P_0a*{)z);3fNEbc! z=XD}dV`T?;9C+Tg;VEDQumec@QmP090Lp+u;OUA$fc8)*jRM0tM(MB;x|w17cv0f% zUDw49pT)p&An3a8lfVSv;Utn*g#~*H*xMcoT?Kxv0<CMRxwxvB>G$T*b7i~a4r%e6 z*8v1v*Znl<w;-_37=!D&7-M`uuO*y_v4FoK5OCT<p;%fIQu|tvpGx=BJ!mlg!Mjr^ zS^ymIzM+9BuIsj11tbA(<<GXm>#!I68UiA6NL4qY8U+4GLEtt!ev{(|dqGW<DUvLI zMIf-&D!0g6|7#Wamqc@#iwHn@3IcxtKlaf8tw3peC=^eX%ybkdo^B=Zm?u>S<fby9 zjVhosT{2?=7N7}O=>hu~sPUN2>zn3&6R-dWfn^@BUw~iJDWIw&fIIt5AnC2>z3toz ze49#via;P&L`DPkwj=sY-~{0Pcvz6m1MQ(uH_!#-dEI}#1g6_dNOBwpzOZB3tH49R zNFY2=C71(j^>|}F@CY!+7&9{oycYNfc(y$h>PdHC_W$UB`F5lpN@37l#+Zxtl9HH! u-vir#uLkOiXqGX?pXvUD>C%}m5&r;YBmS1#i|f$<0000<MNUMnLSTZMse(iR literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/skeleton_warrior.png b/game/modules/boot/data/gfx/npc/skeleton_warrior.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc98593afb983bfc3c0b7c9538e8d96b7dac0e0 GIT binary patch literal 1153 zcmV-{1b+L8P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igh& z6E7&Vv}cn5000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000BrNkl<Z zSi`lKZ%CbG7{-6k>yH15O>sL(pdvenO?{!A2^xCPNHm))MX^6iW+S7_So;td`;t<y zZwk{WLS*=5WS|K-E9#6xvr$w&(iF0Rm3UG|XNvQ9PUqZxc^!5*vCX~b{&>F+&wV}D zeLdH6zlu1~+S+<%dwctjv9YmF<9!#A<3J%0P)dEw1B4MTxwW;GxxBnQ+Sk{2psTAZ z-jV^}Mc_>m6>hgX+S1Z87U%RwMC4l$c}+w<6Ol~Y82~U0<MpMbr4#M#?L{3O9e46d zsq4TD@HMak1QNzVO-)U}<MI3)hlRIA<g$o-kg(!yZEZ9(G&JSr=1N^%-7`CRt@Wc8 zh($Z<sJksFEiL^m6bfZdO--F&Sy_1!_!DSyI2_lMQtn_d_>j}-3@t7$7M7Qn`)$(z zKy!05+1c4IM<S8Tp`oFd3JMCAqS2@uI3Es&e+-2}7l3zCQ&UIF%F0q~6(PXr=qM8t z6IasG(yk2;4;O{Q;r&XfgTOk_0Njj3BAdy{$ww`3uonQpz`y{#y}kV+LQhXmlVKRM z!1KT(KwDB$($nkf>nlpBWf7?m5qkl2c6L%*Tbq-gpZ~ef=Tn=To2!-p_XD>K!&q5c zTgwB!0xFbJ_5u)*6cM@Ya=A_ggF!|{Mvgli4h7r<9s^PVzfx))$P$r!TLAzyN~w*( z!NJwq+S-WM>&+Y=AHS56l43-o(Px39mLUOr1RS%?3q%AFIV>ViiO5Nx&(~N~R3sjc z=da4jO23F4&{})6)`zV1Cq(2xLX1g7vR7AE&lVOI{$Aj8-k836b$(`Mrh8YLszoIG zenq(No*hdme}8}fPm`0A8%y)^58qx}%Sua6Uz(en`ymhre3OxpF`|_6+kOpqM|E}e zTN@i2hjNOG_0+|S2XpfBPN%1*yZwGYN~xKISn;km0PE_VjYgy4P$2N??DX{ZvHJS& zlv3}Srn!(1BiaemG_lO*4Z|>gNq0KmJKf#wHj<K#0Ij<~CPo8RECN}kY2F!BBpUSt zm$lZIris?t0fa5U_G$pg0KBo?0o+z`0Clg$_rkyvfG;L@-5OAL+7wEN2CUN^jsxHp zFlYNr_&*{%2fPsTsT@e%ivah+zy;u3OtDXZ$M2`_55m9|;7m-hUx9^q3{+KB?Q^+Y z%FbI*3^-!PM&SE+40yfXe-;)Nq@<+8HUKw(*w^NjyJ=u{cGflk71j{)pZ$R^fs+Z< zfEA6vhcViCA9xOE)LPdk#ELC?PXVqtK7eVOm$cS@179TUPf2UtptVl1d*6QnApoa9 T4=V7q00000NkvXXu0mjf@lXOg literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/troll_c.png b/game/modules/boot/data/gfx/npc/troll_c.png new file mode 100644 index 0000000000000000000000000000000000000000..3b90530ca3d1f5eacf0e5f9c25f70ac90bcbfd8a GIT binary patch literal 2725 zcmWkwc~lbz6W_355m_J}z(&vnP{68?qe>7V3CiIGXj{dv2uZm5EDCBrL5>^*7E!L^ z!67w>SgcUx(^e3XB&-%ML`6gk7*bI{fdnW*Ajy95kD1?l^WMCf-yClyFFZ8Zdf~E# z003BT4cW5Wgw!vx05a*LX=(qOz%n^x-%$XtTJl8*Kt&~DG7=NGJGlVxzru|9U8lcz z#DwhL2>@xU06;7S03B|^5diov4FEp!0AOPo05~L-MBfhrfQ61*w{X8r{ac{NaW=*= z69a;aTbQn{u6@&-B3eld!~y6^#9(cQdwW*uWXN`YS!ro0H!`x48xW8sk;!oRnKMft zDlJKzWQ$Gd6DQR(of_C+c@ZyNzenF5ce|>J`Z}Qa{)?<l@UrSkm@z6xvUNH{h#Nbo z?(V@Qu7KHsadE9zv}F0j`!PpUE;7<adqn_7<@5Q_3VS!yCH*kkc7_B6|MxKDzH=Y? z#`y(&w7HJF)NT-A`S_4uc6Zw@`2>i8tXjzo#h>1nK3k?-P0Lm*pS!{?Y!TlhJ|>P& z`k55pG<WVSSKCE~_E+W9x0u1fa#39BE#Y!$`IQBjA1|cEpf4CE0Sl<93&dHPiJJ~^ z=`~esYVyQdX>G1!Q1w5&nE2U=DGD}nz6fEZ>JX#esMOAQKfA$ZlZ8TIr=sn10atvg zfE!T!IB3_dMN9V+FM(9?fX(ZpgRFOgONr?DnH986!ikcLeXoG}^1Qqewy*EJC}DX& z-Z$z5NBfKTv-h92ClCB{5F!L8p}#v&Xbt4=nflqu9JS>smbAIX1$40Rl9rL!pl|ry znwphS!2pr8q*>8BLqTf#3`mr}kQn+?KtO<^$$>vh%un|`CxT&06r=C<(Ol=>JbqCs z-;_AB=qHs}Z#iEz;Rwq+HT=}HAtE?)-4;$18JP_RL%a4<j~DdacikyXP5_CM-;gzY zHDpdSHntmvf1S>MF7d}jzxY3C7?}=(;(2>Y7cUi%{!zz=+wtR4b>krR_Ioc*VNAo# zn=ws7!$zz&OolA3??#r!4wAE1#gX4;WOQnWC-$NaDmgvUB5iQGCkalvu<5V=gqqlF zTIBz5V_TIW)aps=4Uw1B$$@s!Qp<dB>LlX_EuKJ95Ag;FZUKIAKF~LIWOVaPG@Zs| zN+;P;lw}Kcs#Nf(T+W~w^iQY{?myLrEeOHi#SaXf5j1J_Oz_gL(>UH!Bw(3+=}ePe z>nLr0nF*Ow=U=o_;A#bN$m_v9ZO)9Zl;~xPsbmV`Olh9v=d*d-$Zw04=TDuM<XL-k zDlx@^{2$Ry3eqan-BZ{+G1sVsyjiUp1lP!EV7p8OXUL*yA)*9mW7~&z{8KQicsW24 z=O1b3B52kCDc>$OS$Je~&51tG-1jb?%3GCXZng`TogCZid&%H4Aj_GboZX<W+_6-6 zy)v-cai`|>ZbjLew)~IbTNQu!eXRNI6Z24IcyM5z<c3@R+AbTLH8%TtpY8v8suy>D zZBBB%^(bW9wmJE5V+zcutcUG4pm0FnD9o1RoeUF~N=_9>)$<xQXS<MA**aID><Y|P zV|N;1mA^1{c3PGr$vZ7o+tR*n#pgSzD=$CL%JK@-YzuEp^5YEokT9b?`Q3XFiuV*M zE}80BMq5`p=LO`a`>b|G!{j(4(&z9Zq%#2i{&QsP>pzAVpsx%7t!TmZ{b?nVCCK`E zjzuOoH`gttz`>z!<5-_ZDHvKuK|c2%2ajE8$XYDTFJVaU{%j+qJp+dUs+e|QbNGp5 zgP~13?MMsdKGzOWHuqGF!IjmMaGy#}8{PO*j=H<c0mhLs<N`ZQ(UAC_iMHFWpta0S z7N{w-E0+URj10n~)>M6ky1Vfvyo#|6rfL{C{#dKQ*|3Fa@KCVn_H9YvcoKAoS8hDB zy-eLcUklr|GU&<1;}5AG?oX>?c#pI@jec082uD~K{NiWtPvj}X-<;*o>kM!%B{Mes z!vL@NLre@$XOj<A9k&%vpSpE*F}TLU<M|B%%fuw2;bp3V>HsWr>CjG&Q6Mhg2D;m* zA&&z~M#gH3$Xjx7W{Y8>T=I!c3-+)CzR`!F(QFEs+3~chs!GAD_H0)e#x14UH&%kD z(N)rt8US>6aKI?`#~}Ch2O;ZLZ;#IeA}MGYVD}YUXaJV!QDvEiA}wT5q!+7KVs(43 zBT)pai$x`Kuu8&RJxF?#{L7T5v?d9{o_jSS*ZkA0ubPv;y+<U^>Sht6*>=uDl?Ev* zD@R(*I`qadM@-`DjeJ;fM<Sqb06t5clar$!?Qnp@T7?OzHuXRF>j7w`D^prgu?)Pj zvNh!(h^6qI(K?57^eftVW@5DeS?;a}1>B%Wv3k7!4y=(I1iZmT#P~Q~xAj^-5!i+8 z4c|JeUW0XdmHoN$ON@b$uG;*n$Yf1F1<jSQkPF86Se9XM9oE~JL>2-^*LGPPM#W`| zQ9N3FP;w#!+8VVF{r9hUo}Hc$5pIZU(he@iEO;Mr<*GLA*}SuAU2Z3wA~Em%xZQyq z+OL~i;$xKsn6BO8182Tkz1m<AXyr&F*s&^@PR6tX?jA+9dZhi6V%xT2wefBp43*fx zU3=>V;pgo6&*+Q<9&ad~WOl1k2s&<v7UD4x$0s+?9BHjO(R8s#vkoBK3)xW@5w+)o z+`4Nov_5k)nl8|o=Ja@*7PqyKwl=T9&Yqu_th=IuJEQ$#fVim$&2IEl*juL4@rf+; z=<h|1xQbPSb|#uGw9BvEF2ux)tc91uu17N>tSZ3X%t<(UoUo+3@Ee#NHO17YJ-nyI zWq>rV!5n<2iH2R)Cc>8X=U`7a#?|}-uWTpEtw|i>av;%4pTM8%pi;f=Yd`ClXi^fJ z5%JqRW0*Bh*M{UJ7>Va>cc?B)&OM@Mn8&L-!^Ro<jPz2qs>mCTpF9bFXcipF|M-g4 z3j&1f9)dt1z_~luU!%wn1UK?ir>4BHsXL8gpLZ0jh$2T)vzsm#MrSIUaHZv6jdCHg zPyg?EwS7Z<>XG?{SYwKYyaf@`h8GgLa$HKOm(b~i#ABwtd|PPgas0%@VPJb8ub}<D zQv0EYg0|0S=q=4tC)EA(VK7^SXV=}G`ImsP-L|4BeK>jc&1-U#IPKp1K_qocE3?ly zx8llx)gE8K?1ZIE^XAPPWKdU5j+Ol0FXK?sbxrh^IbE)VkGny8>|g2qd{cUyBdrd+ zHoHk-T42Gvj#&s<tSE#qT#l2SzFUt>Zt7_G8zga3H;YF)y?MUjVzqk%)@HS~U&y>c zYr)Oy!7|C8P7STWkg7E^hG>6#V8<%UCnGLi7YKD)5sZu1T~;X~&CawRu;2jdC#WLm zp{UqxvpJooy$N1==LLFs+hXo+{wT#EjDF$<lUXmCv%DOxHrpt+9S)~^#zVWDqaebf zMbI&>C1evSL`Aq!xUYdn2+D*_c|9f+yNWxGK#QuliHV5=FGw$^2%n1uK8N;4whVPA z1g-sZb%UQX;Q4|(7FfKZYOc1_bAO34-jod8=BF7assgCKP{WCS9N}(yDS@qlp<Av6 H@Us37k&*F> literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/troll_f.png b/game/modules/boot/data/gfx/npc/troll_f.png new file mode 100644 index 0000000000000000000000000000000000000000..010069759326f8e031559e9e3c7cb3e4315f363f GIT binary patch literal 2213 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/troll_m.png b/game/modules/boot/data/gfx/npc/troll_m.png new file mode 100644 index 0000000000000000000000000000000000000000..9b04cfe50d7a64b5b7199c3073b54127ae20c789 GIT binary patch literal 2331 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 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/troll_mt.png b/game/modules/boot/data/gfx/npc/troll_mt.png new file mode 100644 index 0000000000000000000000000000000000000000..6632624cc142f60ca0ec60f4187377e7858ca3c2 GIT binary patch literal 2938 zcmX9=dpy(o8~@H`X1?MSZA;o56-wDfg-%9<NK{Vg#&^R=PMn<NZZp0)-H7DU94i%F z<kE#4Y;5jCr4k)1w=URnm)U;n_50&_-mlm5dOgqk^ZGpR=XpJUd+vAE#;(Bv0HD3s z#qppDNk0vPR{8Mg=ouAghPfOK2LP>AKMeuo^6;u4D$w5D9stUPm_?hA7a?l+B zqBj5ln+pJoN)>(qfOBL3_zD4lO*R1FLeqU4oB#kT+v{lW71_&_E0uI4W?b27hfPC# zGO=q1#XLK?y6Yvx_i&P8Pcz_r2BWQOXYP2cSzUbAPQE6E!12~fDZUk}Pm0$(Ck!Sj z+%9?mYM4p3@pP8rh}S_@+VcuXe0#rMa86YgX6!&V$-=?{iNoQL#>U2?iqHZ4;g2yb z8hW-`;!yU25oHB{A#KDDHN3e13KVibZ^1~jGThlh?tDmt+OF^ag3l*$laf%}%1SG4 zVyP*&jJJt<FJOr$&T;PHKYn^UBC9r2Xd3Lvoujbxu%jQPgvD6xa-P#XH47UyQ6 zGQo34axzRc4N6W<CPZc*B{1`-gvj-_<lkbZF4?nvFH4Bz<?DTJ)b4iP%e2t@6@PyE z?#4I$k)#(l5N7@VvXW&3SmXQ|a^=NoYPDRRA(@vBkj+j7$+gPMN~xHOn$)CxdZX;C zzA-oPDV1A^Av$ywxPi|52mDICXaSzr(04u1?xgJi(_r^0HPuU3EG&P+iJmym6CV3m zHH1x!IKw42he-pMxOdpz`YD35rBq6+ayiRLF&2kjv7H5<x1iJHOUQQndxt@Kyxb4+ zV+QJe`ozd4NNTpf@%Gd5?DQbbIbt<IK-cc_=MQ1WBIcCGZ});&A?m2^*yQAv)@K=+ znVEGh&9LM{p9v_fjSGOr2B8$oR6kEa8CUZD?Qt-qHyAXC+Jk?$_G3H5FKFT4zw;6@ zQKsJdbJh%wIx=a3yz(DH+*umnzy_GR&%gS2M+v@NQHwe@cMXyKIRdMxN!hn=9*Ufv z-GZbWm>}P$(!ul6{(#D*Wj%-al1}ivD;3`>NPq@cb0WpAB91*fIg2q;_8t_lZygId zZAtJaT5h5McEGKj?1!w8;8SVF@$s=DVPoS<*>W2WuAR-o_b8u`i%dULf{G04^#yUt zs|;oWUG%2VgOHt*Ev$Y2>TqCJhN78Yd{0E$WL__uT0ZXC74Gon+S)3lN~^oDcFCGC zPd#KY2=_y9tI1CMsNhE%ANjP$Wl~!-WZ?wzPCNqh=?MW=VX?co;X#0?{H`0}c0mDQ zGQYZw|B%s25;nEUQoqne7j*LR>vq*NLZ(Z3MqkD^!!<YO`qVmXHN*p)1-d!*CmqV} zaanW%(?BHcloD7y!?YmR(=^$13hiTTaf@tzo(>D>Ie63}iV$Ch%$UEQ0p8lw3a;w2 z!9NyMx63A$Jm7q~l3E?LlqnH4Nx|`PIe60}p6MAmlT>Fdj3Rc8>yozbey&SmXLnsZ z?irkg(5DJ)s~HPFB!*lJSvo!SCr$oiE`xK0tGzO$>-DYEowo2U3tw{4*C4W^)kezG z_J@px@85_b3&=(?{0SNy8D$PiMj5*3@@+oVtR&Xn2!9O#rF-@y@W9T$?y*|qSgHjP zA72~yQdnmu^rY9IA3+BX(2*tvn?<RD1U&QPLHxeqPFl~JTw1Jr?t<i7Cmq_BdB~Np z=1m_#AzyAFZQ9f-Tl}^I4*jSkRO{y)<;e}R#``J<aJj43ak`NzG7S6T%N;FMuC&F4 z1^kH~YeGC=SenR9WjKBCFHJ-i&LIqeM{0x|{M@<^Ey6lci?Fd?ESr5#gey9URMgQZ z?%N65XB^4r5AGn5t_U0)xz6+)zQM|_nqI?g`L<*5>7@`0JHY4yQ1t6H<OWy9#=~$Z zkxrGX{m>gu*;80S@%~Ie9%@d23yySBs^b@PB^I8OZ`*c&H+`gjk%~|HqSO2;-^&6* zdq)SMKTZLfoB2|Da%e!o6?NYDTJ1c1RsIPzTowPIehQIo1t5~f+0%9**a!bvd3)on zq89N<zQC#*DSD!^<5_iBd9fj(yW5N4zy1#U6>z!CLrGAShtrDMVgszAM%Iev!ecm@ ztqyS54juSH0)Y@uv-Mk0o&<M|ab7E(@fsv{S^?lC;Q_xFm&C_2($e%8z@FrZu_o}0 z(>q9g$K*+kji&1$eRE7ZfeFgfSAy+A(}f1HNH(&xvoyDS*bBW)orcyj#z75tn@hj+ ztPHaprwdX!L!UL%R;rU~kx%qg9EM@Ilnz@Y*CRJ8Uouo^0p4gG1zWLBVD8tAgEq>B zZ%06)_yb4(?Rm73!(O<l6GwINcA=f9BwvzrYlq3GrY%LBLyCNX5^tNZ*s42m<E3nI zi3V@3m?X<fpR+eau(I!}{>o=Xh#!lEMlb#W2X;QDRYynY_jE0wLe!Q6JP#aTKz{xK zk!OQ<iYk;k_%lSG4${MwjbYFiCffQO4!r>iVhfu(L-F26m1K+-woW^*`t0g*!y{=E z>KKy02D^Ol$%`!Mm+#ZlloN5YJV~Pol&513z4WnemnoKGU<+YnAoBoA=Gw|w=rnDE zRt-0hw%)4OJ^oLVY-+--)Mz9wPnbTnfsE>>lwuS7BSlmAgMGc;N(8I!uOPo<B+E=C zEnGvhw#E_CVHBl&pJia;?PTq2<*r;Dx%zl+8)-{Goowc<I^RN_u&%$RgI_Y#N&e&2 zKJ*s#062VJ3Kh>+)GVIS2yWnHH$O8{RU1nT?RqUE>Jk&D7VqV~n9P1$Sf0Q00W4`N z84#vlL-vGyQ0)_<2A9t>6S<9!f$z6U(W9scG~!(<_~vUb;f!sJuvGfN9d7Ge!8YH2 zAy+5Z+81CR4l+Q3KR1;&+k6?}zTWxPxysxWHmTGr7V#)Ofr1ZSPjPW;qso<An!;wp z+hC%yfwQ5|QbcF1LnGhdp=bt6cXTz&rEJD=`@Ejz%><|CYf*q&il(!h<}l_L&)ZiI zWaP9U9u@HVe`7_?DprZ~e?N&1FYts<;;xc|s%-WtmCLI{g=70brqHQ9RT|39)Ggf# zL=73-iB^AZ#@c-|FukV=0U*Rl1v)_ejqkZ<zuEoa!0<7H%$l2Jvn_`3QYf8Pt<<FO zGV|>34GZ@UJh;@y^^K2i3j6ZqcQ`OriZFOs!r3-Nhs9#YQk!|Z3PM-J?d??oDyelK z2Ke{AR$jNg%kKDNMZ{Kg1T-|<_hbpw$^R{|a%Rw$d!f)ol$A=X-EFw^V{&LG7&=fJ z7^xhgA+<JZA#oZe+!qgQxB=OlD=`{s3{rgZ8G;?a2nS5#Y}DfDtFBLN^Y7-w0jJ=6 zK?249{w8v4)L&c)kGGlo;QmN`_6^0AF7d2kclyHel8&fkAYU~Z(Veo%U7i?x@XJ;w zu-*A8`@vva7b-<f)u)@t+T9dn-@pJkF1(HFem;bFH=$&Y2<Nj>3AMI0B4JC5X6u#< z_8l&y+Glg$|D7=@=_m7QV*Zv4y?-wp?bU!TE$WE!=`zbg%<NjM>H-UibMP#1M0w5B zBHoP<={3-TVp(6LOi<!0UsMuia+Kr~>Nd<R{|4ViB1N%9#&)k{y4{}^rlxV^GF$YL z<SkJX%G4gGG&FH@$K;=_fuG&~#aW8)-VVBVrTb!ndF*42At524w7<r05ZK=GpI^pk zu|Q^d>RtQRtwknDP~JOTYe}(OE(gtpYi4!GDDnFB{ZFN~EUk0H?%tp4vA?o``cWWl zH&h-NiG9GauhVkB&W}E!)|(!s(eS$KA4-w+9j%Ff5)yxS>7-cLZ_d~0qN@A!)~^M0 aAplkrlVbWWA5q=Oz}`Lk9Ut$868{I36JW9c literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/npc/troll_s.png b/game/modules/boot/data/gfx/npc/troll_s.png new file mode 100644 index 0000000000000000000000000000000000000000..dfcefa0afcb494e1f9a331c36048868de24c75fb GIT binary patch literal 2419 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 Q1qt}%<EW6jUD&CA0JpDFZvX%Q literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/particles/acid.lua b/game/modules/boot/data/gfx/particles/acid.lua new file mode 100644 index 0000000000..6a5cfb89e9 --- /dev/null +++ b/game/modules/boot/data/gfx/particles/acid.lua @@ -0,0 +1,38 @@ +-- ToME - Tales of Middle-Earth +-- 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 { + base = 1000, + + angle = { 0, 360 }, anglev = { 2000, 4000 }, anglea = { 200, 600 }, + + life = { 5, 10 }, + size = { 3, 6 }, sizev = {0, 0}, sizea = {0, 0}, + + r = {0, 0}, rv = {0, 0}, ra = {0, 0}, + g = {80, 200}, gv = {0, 10}, ga = {0, 0}, + b = {0, 0}, bv = {0, 0}, ba = {0, 0}, + a = {255, 255}, av = {0, 0}, aa = {0, 0}, + +}, function(self) + self.nb = (self.nb or 0) + 1 + if self.nb < 4 then + self.ps:emit(100) + end +end diff --git a/game/modules/boot/data/gfx/particles/bolt_fire.lua b/game/modules/boot/data/gfx/particles/bolt_fire.lua new file mode 100644 index 0000000000..06a5245b98 --- /dev/null +++ b/game/modules/boot/data/gfx/particles/bolt_fire.lua @@ -0,0 +1,51 @@ +-- ToME - Tales of Middle-Earth +-- 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 { generator = function() + local radius = 0 + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(0, sradius / 4) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) + local static = rng.percent(40) + + return { + trail = 1, + life = 6, + size = 3, sizev = 0, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = a, dirv = 0, dira = 0, + vel = sradius / 2 / 6, velv = 0, vela = 0, + + r = rng.range(200, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 170)/255, gv = 0.005, ga = 0.0005, + b = rng.range(0, 10)/255, bv = 0, ba = 0, + a = rng.range(25, 220)/255, av = static and -0.034 or 0, aa = 0.005, + } +end, }, +function(self) + self.ps:emit(30) +end, +30*6 diff --git a/game/modules/boot/data/gfx/particles/fireflash.lua b/game/modules/boot/data/gfx/particles/fireflash.lua new file mode 100644 index 0000000000..f26ba9ee4b --- /dev/null +++ b/game/modules/boot/data/gfx/particles/fireflash.lua @@ -0,0 +1,96 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +--[[ +-- Make up the grids list +local gs = {} + +-- Compute the clipping circle +local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 +for i = -radius, radius do for j = -radius, radius do + local lastx, lasty = 0, 0 + local l = line.new(0, 0, i, j) + local lx, ly = l() + while lx do + if grids[lx+tx] and grids[lx+tx][ly+ty] then + lastx, lasty = lx, ly + else + gs[lx] = gs[lx] or {} + gs[lx][ly] = {x=lastx, y=lasty, radius=math.sqrt(lastx^2 + lasty^2)} +-- print("block", lx, ly, "=>", math.sqrt(lastx^2 + lasty^2)) + end + lx, ly = l() + end +end end +]] + +local nb = 0 +return { generator = function() + local radius = radius + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(sradius - 5, sradius) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) +--[[ + if gs[bx] and gs[bx][by] and rng.chance(2) then +-- print("block at angle", ad, radius, ":=>", gs[bx][by].radius) + radius = gs[bx][by].radius + sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local r = rng.float(sradius - 5, sradius) + x = r * math.cos(a) + y = r * math.sin(a) + end +--]] + local static = rng.percent(40) + + return { + trail = 1, + life = 24, + size = 3, sizev = static and 0.05 or 0.15, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = static and a + math.rad(90 - rng.range(10, 20)) or a, dirv = 0, dira = 0, + vel = static and -2 or 0.5 * (-1-nb) * radius / 2.7, velv = 0, vela = static and -0.01 or rng.float(-0.3, -0.2) * 0.3, + + r = rng.range(200, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 170)/255, gv = 0.005, ga = 0.0005, + b = rng.range(0, 10)/255, bv = 0, ba = 0, + a = rng.range(25, 220)/255, av = static and -0.034 or 0, aa = 0.005, + } +end, }, +function(self) + if nb < 5 then + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + end +end, +5*radius*266 diff --git a/game/modules/boot/data/gfx/particles/firetrail.lua b/game/modules/boot/data/gfx/particles/firetrail.lua new file mode 100644 index 0000000000..627a958d8e --- /dev/null +++ b/game/modules/boot/data/gfx/particles/firetrail.lua @@ -0,0 +1,56 @@ +-- ToME - Tales of Middle-Earth +-- 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 nb = 0 + +return { generator = function() + local radius = 0 + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(0.1, sradius / 2) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) + local static = rng.percent(40) + + return { + trail = 1, + life = 3 + 9 * (sradius - r) / sradius, + size = 3, sizev = 0, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = 0, dirv = 0, dira = 0, + vel = 0, velv = 0, vela = 0, + + r = rng.range(200, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 170)/255, gv = 0.005, ga = 0.0005, + b = rng.range(0, 10)/255, bv = 0, ba = 0, + a = rng.range(25, 220)/255, av = static and -0.034 or 0, aa = 0.005, + } +end, }, +function(self) + if nb < 1 then + self.ps:emit(40) + end + nb = nb + 1 +end, +40 diff --git a/game/modules/boot/data/gfx/particles/flame.lua b/game/modules/boot/data/gfx/particles/flame.lua new file mode 100644 index 0000000000..4fb697c6de --- /dev/null +++ b/game/modules/boot/data/gfx/particles/flame.lua @@ -0,0 +1,38 @@ +-- ToME - Tales of Middle-Earth +-- 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 { + base = 1000, + + angle = { 0, 360 }, anglev = { 2000, 4000 }, anglea = { 200, 600 }, + + life = { 5, 10 }, + size = { 3, 6 }, sizev = {0, 0}, sizea = {0, 0}, + + r = {200, 255}, rv = {0, 0}, ra = {0, 0}, + g = {120, 170}, gv = {0, 0}, ga = {0, 0}, + b = {0, 0}, bv = {0, 0}, ba = {0, 0}, + a = {255, 255}, av = {0, 0}, aa = {0, 0}, + +}, function(self) + self.nb = (self.nb or 0) + 1 + if self.nb < 4 then + self.ps:emit(100) + end +end diff --git a/game/modules/boot/data/gfx/particles/golden_shield.lua b/game/modules/boot/data/gfx/particles/golden_shield.lua new file mode 100644 index 0000000000..90126fa96b --- /dev/null +++ b/game/modules/boot/data/gfx/particles/golden_shield.lua @@ -0,0 +1,46 @@ +-- ToME - Tales of Middle-Earth +-- 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 { generator = function() + local ad = rng.range(0, 360) + local a = math.rad(ad) + local dir = math.rad(ad + 90) + local r = rng.range(12, 20) + local dirv = math.rad(1) + + return { + trail = 1, + life = 10, + size = 4, sizev = -0.1, sizea = 0, + + x = r * math.cos(a), xv = 0, xa = 0, + y = r * math.sin(a), yv = 0, ya = 0, + dir = dir, dirv = dirv, dira = 0, + vel = rng.percent(50) and -1 or 1, velv = 0, vela = 0, + + r = rng.range(220, 255)/255, rv = 0, ra = 0, + g = rng.range(200, 230)/255, gv = 0, ga = 0, + b = 0, bv = 0, ba = 0, + a = rng.range(25, 220)/255, av = 0, aa = 0, + } +end, }, +function(self) + self.ps:emit(10) +end, +100 diff --git a/game/modules/boot/data/gfx/particles/lightning.lua b/game/modules/boot/data/gfx/particles/lightning.lua new file mode 100644 index 0000000000..33e8acccd9 --- /dev/null +++ b/game/modules/boot/data/gfx/particles/lightning.lua @@ -0,0 +1,83 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- Make the 2 main forks +local forks = {{}, {}} +local m1 = forks[1] +local m2 = forks[2] +local tiles = math.ceil(math.sqrt(tx*tx+ty*ty)) +local tx = tx * engine.Map.tile_w +local ty = ty * engine.Map.tile_h +local breakdir = math.rad(rng.range(-8, 8)) +m1.bx = 0 +m1.by = 0 +m1.thick = 5 +m1.dir = math.atan2(ty, tx) + breakdir +m1.size = math.sqrt(tx*tx+ty*ty) / 2 + +m2.bx = m1.size * math.cos(m1.dir) +m2.by = m1.size * math.sin(m1.dir) +m2.thick = 5 +m2.dir = math.atan2(ty, tx) - breakdir +m2.size = math.sqrt(tx*tx+ty*ty) / 2 + +-- Add more forks +for i = 1, math.min(math.max(3, m1.size / 5), 20) do + local m = rng.percent(50) and forks[1] or forks[2] + if rng.percent(60) then m = rng.table(forks) end + local f = {} + f.thick = 2 + f.dir = m.dir + math.rad(rng.range(-30,30)) + f.size = rng.range(6, 25) + local br = rng.range(1, m.size) + f.bx = br * math.cos(m.dir) + m.bx + f.by = br * math.sin(m.dir) + m.by + forks[#forks+1] = f +end + +-- Populate the lightning based on the forks +return { generator = function() + local f = rng.table(forks) + local a = f.dir + local rad = rng.range(-3,3) + local ra = math.rad(rad) + local r = rng.range(1, f.size) + + return { + life = life or 4, + size = f.thick, sizev = 0, sizea = 0, + + x = r * math.cos(a) + 3 * math.cos(ra) + f.bx, xv = 0, xa = 0, + y = r * math.sin(a) + 3 * math.sin(ra) + f.by, yv = 0, ya = 0, + dir = 0, dirv = 0, dira = 0, + vel = 0, velv = 0, vela = 0, + + r = rng.range(140, 200)/255, rv = 0, ra = 0, + g = rng.range(180, 220)/255, gv = 0, ga = 0, + b = rng.range(220, 240)/255, bv = 0, ba = 0, + a = rng.range(230, 255)/255, av = 0, aa = 0, + } +end, }, +function(self) + self.nb = (self.nb or 0) + 1 + if self.nb < 4 then + self.ps:emit(230*tiles) + end +end, +4*(nb_particles or 230)*tiles diff --git a/game/modules/boot/data/gfx/particles/mana_beam.lua b/game/modules/boot/data/gfx/particles/mana_beam.lua new file mode 100644 index 0000000000..38992e1699 --- /dev/null +++ b/game/modules/boot/data/gfx/particles/mana_beam.lua @@ -0,0 +1,58 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- Make the ray +local ray = {} +local tiles = math.ceil(math.sqrt(tx*tx+ty*ty)) +local tx = tx * engine.Map.tile_w +local ty = ty * engine.Map.tile_h +local breakdir = math.rad(rng.range(-8, 8)) +ray.dir = math.atan2(ty, tx) +ray.size = math.sqrt(tx*tx+ty*ty) + +-- Populate the beam based on the forks +return { generator = function() + local a = ray.dir + local rad = rng.range(-3,3) + local ra = math.rad(rad) + local r = rng.range(1, ray.size) + + return { + life = 14, + size = rng.range(4, 6), sizev = -0.1, sizea = 0, + + x = r * math.cos(a) + 2 * math.cos(ra), xv = 0, xa = 0, + y = r * math.sin(a) + 2 * math.sin(ra), yv = 0, ya = 0, + dir = rng.percent(50) and ray.dir + math.rad(rng.range(50, 130)) or ray.dir - math.rad(rng.range(50, 130)), dirv = 0, dira = 0, + vel = rng.percent(30) and 1 or 0, velv = -0.1, vela = 0.01, + + r = rng.range(180, 220)/255, rv = 0, ra = 0, + g = rng.range(0, 0)/255, gv = 0, ga = 0, + b = rng.range(200, 255)/255, bv = 0, ba = 0, + a = rng.range(80, 220)/255, av = 0, aa = 0, + } +end, }, +function(self) + self.nb = (self.nb or 0) + 1 + if self.nb < 6 then + self.ps:emit(30*tiles) + end +end, +14*30*tiles, +"particle_torus" diff --git a/game/modules/boot/data/gfx/particles/sunburst.lua b/game/modules/boot/data/gfx/particles/sunburst.lua new file mode 100644 index 0000000000..081f87e2e9 --- /dev/null +++ b/game/modules/boot/data/gfx/particles/sunburst.lua @@ -0,0 +1,132 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- Make up the grids list +local gs = {} +max_alpha = max_alpha or 220 + +-- Compute the clipping circle +local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 +for i = -radius, radius do for j = -radius, radius do + local lastx, lasty = 0, 0 + local l = line.new(0, 0, i, j) + local lx, ly = l() + while lx do + if grids[lx+tx] and grids[lx+tx][ly+ty] then + lastx, lasty = lx, ly + else + gs[lx] = gs[lx] or {} + gs[lx][ly] = {x=lastx, y=lasty, radius=math.sqrt(lastx^2 + lasty^2)} +-- print("block", lx, ly, "=>", math.sqrt(lastx^2 + lasty^2)) + end + lx, ly = l() + end +end end + +local nb = 0 +return { generator = function() + local radius = radius + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(sradius - 12, sradius) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) + if gs[bx] and gs[bx][by] and rng.chance(2) then +-- print("block at angle", ad, radius, ":=>", gs[bx][by].radius) + radius = gs[bx][by].radius + sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local r = rng.float(sradius - 5, sradius) + x = r * math.cos(a) + y = r * math.sin(a) + end + local static = rng.percent(40) + + return { + trail = 1, + life = 24, + size = 3, sizev = static and 0.05 or 0.15, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = static and a + math.rad(90 + rng.range(10, 20)) or a, dirv = 0, dira = 0, + vel = static and 2 or 0.5 * (-1-nb) * radius / 2.7, velv = 0, vela = static and 0.01 or rng.float(-0.3, -0.2) * 0.3, + + r = rng.range(220, 255)/255, rv = 0, ra = 0, + g = rng.range(200, 230)/255, gv = 0, ga = 0, + b = 0, bv = 0, ba = 0, + a = rng.range(25, max_alpha)/255, av = static and -0.034 or 0, aa = 0.005, + } +end, }, +function(self) + if nb < 5 then + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + self.ps:emit(radius*266) + nb = nb + 1 + end +end, +5*radius*266 + +--[[ +local nb = 0 +return { generator = function() + local ad = rng.range(0, 360) + local a = math.rad(ad) + local r = rng.range(2, sradius) + local boundx = r * math.cos(a) + local boundy = r * math.sin(a) + local x = math.floor(boundx / engine.Map.tile_w) + tx + local y = math.floor(boundy / engine.Map.tile_h) + ty + if not grids[x] or not grids[x][y] then return end + + return { + trail = 1, + life = 12, + size = 3, sizev = 0.3, sizea = 0, + + x = boundx, xv = 0, xa = 0, + y = boundy, yv = 0, ya = 0, +-- x = r * math.cos(a), xv = -0.1, xa = 0, +-- y = r * math.sin(a), yv = -0.1, ya = 0, + dir = a + 5 * math.rad(rng.range(10, 20)), dirv = math.rad(rng.range(10, 20)), dira = -math.rad(2), + vel = 1, velv = 0, vela = 0.1, + + r = rng.range(200, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 170)/255, gv = 0, ga = 0, + b = rng.range(0, 10)/255, bv = 0, ba = 0, + a = rng.range(25, 220)/255, av = 0, aa = 0, + } +end, }, +function(self) + if nb < 2 then + self.ps:emit(800) + nb = nb + 1 + end +end, +5000 +]] \ No newline at end of file diff --git a/game/modules/boot/data/gfx/shaders/main_fbo.frag b/game/modules/boot/data/gfx/shaders/main_fbo.frag new file mode 100644 index 0000000000..d092ce9fc3 --- /dev/null +++ b/game/modules/boot/data/gfx/shaders/main_fbo.frag @@ -0,0 +1,172 @@ +uniform float hp_warning; +uniform float motionblur; +uniform float blur; +uniform float tick; +uniform sampler2D noisevol; +uniform vec2 texSize; +uniform sampler2D tex; +uniform vec3 colorize; + +void main(void) +{ + gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); + + if (motionblur > 0.0) + { + int blursize = int(motionblur); + vec2 offset = 0.8/texSize; + + float fTime0_X = tick / 20000.0; + float coord = gl_TexCoord[0].x + gl_TexCoord[0].y * texSize[0]; + float noisy1 = texture2D(noisevol,vec2(coord,fTime0_X)).r; + float noisy2 = texture2D(noisevol,vec2(coord/5.0,fTime0_X/1.5)).r; + float noisy3 = texture2D(noisevol,vec2(coord/7.0,fTime0_X/2)).r; + float noisy = (noisy1+noisy2+noisy3)/3.0; + + // Center Pixel + vec4 sample = vec4(0.0,0.0,0.0,0.0); + float factor = ((float(blursize)*2.0)+1.0); + factor = factor*factor; + + if (noisy < 0.25) + { + for(int i = -blursize; i <= 0; i++) + { + for(int j = -blursize; j <= 0; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + } + else if (noisy < 0.50) + { + for(int i = 0; i <= blursize; i++) + { + for(int j = 0; j <= blursize; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + } + else if (noisy < 0.75) + { + for(int i = 0; i <= blursize; i++) + { + for(int j = -blursize; j <= 0; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + } + else + { + for(int i = -blursize; i <= 0; i++) + { + for(int j = 0; j <= blursize; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + } + sample /= float((blursize*1.5) * (blursize*0.5)); +// gl_FragColor = sample * (0.3 + noise * 0.7); + gl_FragColor = sample; + } + else if (blur > 0.0) + { + int blursize = int(blur); + vec2 offset = 1.0/texSize; + + // Center Pixel + vec4 sample = vec4(0.0,0.0,0.0,0.0); + float factor = ((float(blursize)*2.0)+1.0); + factor = factor*factor; + + for(int i = -blursize; i <= blursize; i++) + { + for(int j = -blursize; j <= blursize; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + sample /= float((blur*2.0) * (blur*2.0)); + gl_FragColor = sample; + } + + if (colorize.r > 0.0 || colorize.g > 0.0 || colorize.b > 0.0) + { + float grey = gl_FragColor.r*0.3+gl_FragColor.g*0.59+gl_FragColor.b*0.11; + gl_FragColor = vec4(vec3(colorize*grey),1.0); + } + + if (hp_warning > 0.0) + { + vec4 hp_warning_color = vec4(hp_warning / 1.9, 0.0, 0.0, hp_warning / 1.5); + float dist = length(gl_TexCoord[0].xy - vec2(0.5)) / 2.0; + gl_FragColor = mix(gl_FragColor, hp_warning_color, dist); + } +} + +/*uniform sampler2D tex; +uniform vec2 texSize; +int blursize = 5; + +void main(void) +{ + vec2 offset = 1.0/texSize; + + // Center Pixel + vec4 sample = vec4(0.0,0.0,0.0,0.0); + float factor = ((float(blursize)*2.0)+1.0); + factor = factor*factor; + + for(int i = -blursize; i <= blursize; i++) + { + for(int j = -blursize; j <= blursize; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + sample /= float((blursize*2) * (blursize*2)); + + float grey = sample.r*0.3+sample.g*0.59+sample.b*0.11; + vec3 color = vec3(1, 0, 0); + gl_FragColor = vec4(vec3(color*grey),1.0); +} +*/ +/* +uniform sampler2D tex; +uniform sampler3D noiseVol; +uniform float tick; +float do_blur = 3.0; +uniform vec2 texSize; + +void main(void) +{ + if (do_blur > 0.0) + { + vec2 offset = 1.0/texSize; + offset.y += texture3D(noiseVol, vec3(gl_TexCoord[0].xy, tick/100000))/30; + + // Center Pixel + vec4 sample = vec4(0.0,0.0,0.0,0.0); + float factor = ((float(do_blur)*2.0)+1.0); + factor = factor*factor; + + for(int i = -do_blur; i <= do_blur; i++) + { + for(int j = -do_blur; j <= do_blur; j++) + { + sample += texture2D(tex, vec2(gl_TexCoord[0].xy+vec2(float(i)*offset.x, float(j)*offset.y))); + } + } + sample /= float((do_blur*2) * (do_blur*2)); + + gl_FragColor = sample; + } + + float grey = gl_FragColor.r*0.3+gl_FragColor.g*0.59+gl_FragColor.b*0.11; + vec3 color = vec3(1, 0, 0); + gl_FragColor = vec4(vec3(color*grey),1.0); +} +*/ diff --git a/game/modules/boot/data/gfx/shaders/main_fbo.lua b/game/modules/boot/data/gfx/shaders/main_fbo.lua new file mode 100644 index 0000000000..95fb5732e3 --- /dev/null +++ b/game/modules/boot/data/gfx/shaders/main_fbo.lua @@ -0,0 +1,28 @@ +-- ToME - Tales of Middle-Earth +-- 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 { + frag = "main_fbo", + vert = nil, + args = { + tex = { texture = 0 }, + noisevol = { texture = 1 }, + }, + clone = false, +} diff --git a/game/modules/boot/data/gfx/shaders/water.frag b/game/modules/boot/data/gfx/shaders/water.frag new file mode 100644 index 0000000000..264bbed35b --- /dev/null +++ b/game/modules/boot/data/gfx/shaders/water.frag @@ -0,0 +1,21 @@ +uniform float tick; +uniform sampler3D noisevol; +uniform vec2 mapCoord; +uniform vec4 displayColor; +uniform vec4 color1; +uniform vec4 color2; + +void main(void) +{ + float fTime0_X = tick / 100000.0; + vec2 coord = mapCoord+gl_TexCoord[0].xy; + float noisy = texture3D(noisevol,vec3(coord,fTime0_X)).r; + float noisy2 = texture3D(noisevol,vec3(coord/5.0,fTime0_X)).r; + float noisy3 = texture3D(noisevol,vec3(coord/7.0,fTime0_X)).r; + float noise = (noisy+noisy2+noisy3)/3.0; + + float bump = 1.0-abs((2.0 * noise)-1.0); + bump *= bump - 0.3; + gl_FragColor = mix(color1, color2, bump) * displayColor; + gl_FragColor.a = 0.4; +} diff --git a/game/modules/boot/data/gfx/shaders/water.lua b/game/modules/boot/data/gfx/shaders/water.lua new file mode 100644 index 0000000000..7e2c2cea2d --- /dev/null +++ b/game/modules/boot/data/gfx/shaders/water.lua @@ -0,0 +1,29 @@ +-- ToME - Tales of Middle-Earth +-- 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 { + frag = "water", + vert = nil, + args = { + noisevol = { texture = 1 }, + color1 = {0,0,1,1}, + color2 = {0,0.6,0.8,1}, + }, + clone = false, +} diff --git a/game/modules/boot/data/gfx/terrain/grass.png b/game/modules/boot/data/gfx/terrain/grass.png new file mode 100644 index 0000000000000000000000000000000000000000..bf3598e27f7fcafb1f8b5c58eaed7e0db0195a13 GIT binary patch literal 684 zcmV;d0#p5oP)<h;3K|Lk000e1NJLTq001BW001Be0ssI2{21+{00006bW%=J0RI60 z0RJ~w8+`x(0%1u+K~zYIeO5Vc!!QsWF@#|RMqn5$Qs^6bVjkf#e^bfAaABx$kz(l4 zXvu_yz-zDB*`=m(1;?!dK$%e&oD1@CFnO-HRg@V(+mw`!>H;5GGZ&*$Aezw~PF$+Y zn{z&rX0j~p!QQ#-_H{G5d_+FB$<Bb}`8}8BowU2l0#i#`;$~eoFE;}&?_nwkUkVo4 z5JbQZt8B_6h!lRVTgbkYD*-oI<;^75JdIpZIHV*XCS!pDx|ulh19W-zfcRB<wW&~~ zq~$<v2JMc!^m>TlD1@TKOyxQ#F~aAi6h<)9<0FNQMGBC59U`eK^Tu7#|G5q@bf6}- z3J*fwJ=g7sq4>Ag6;jQeOhEd4y5llNplKse{#M7lcw4`4y5Ri-heMm__;JVM-gZux zwk$IahZu3n?MQcYGxzjz1pMnw@E_23Wqic+`4bg|A&9&Zxnzs}tJk(9=kI|!cM#T& zs1aRd1j_j`;l)3lc?VY1o<mPQI!0(oor6ktuVqr0x>DT<?LoHB1=(dnqKHWVjSlr` z9~J?C6q-zEzmg9fhC}TnNa?|-%c~dl&y%<QVLn|%9NUn2@hYqDJGdNbb2KJ!<Xv9` ztRas(1&=(*9+0OhG;I%sJ~@$+Yw@SJ@BSW;bH)MmzDu`~II0&WG=qQuIxc!|=<BZ4 zLES&LeKJWE?O;^ru3f$+f9gUIE)RPcgobQ*Ruu|;%8q8BTJMo(qUWpL70+d&9Y@f+ zLbZPgud*Ru!%t%~pqV<4tCq9Q9PbAnLEZl}a!mStXZXXn#d(4eUo~8ui2VW`2wnHT Syy9a30000<MNUMnLSTY>%{Z+9 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/grass_flower3.png b/game/modules/boot/data/gfx/terrain/grass_flower3.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1738db36965626785124ac7b24dec2ebef132d GIT binary patch literal 1096 zcmV-O1h@N%P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1KmkPK~z{rg;%kv z6G0Tc8=he4LJA8($PXxNDHcLX5iGJFV3GF;5&I|Z2UsM9V4D<zSOzS_&ISE}2ZCi_ zEd_-*XLir#?9HssBkX48&OPUzd*^0eHeK7%y7BYSzD)0!9e$YInwnkQ9Do1w%M5#9 zkHJ0aM{Tg~$3!RKC<P9i6cNl3c+3xM)F*<{&wzk3Bp@B96j6J=b6+Z2tdTUI0|H>i zjeKH`5~~U%#x+fy&cP2pQ~~cA+<<_03UD2&f>(yAAP`AtT77-t`xybvmx5rPMai1* za|Bf-wMINSC^2C!w9r&StkYqtE>dZsPY<Z6;JyiS;w4l<s)SdCPPO<HNYx-ARb`^l zsewOsS|19aYNc#aYG{3AXhdTT2u(zZ(C)08gozXp5>2VgVEZCesR-(G&MM|pk0_5D z)<E3%2vt|#qXO9ERnUmwP;CGVd;c5*Nfjf5$Zo&2Bm1>V>z(P^lh5tXN1i{org_yr z)#lCJtS9JeL+!GQD?2h;=3J{(jh}r#+H6+YYPE{fn~m9Wd3s{5uG<}0d;cICbU{YV z+5ldE3baMC<8cFkUr&#=URwkwh>|0CQJC=qrBZ2OhaEH`pC{G?i;0nHluc7vS(YIU zRpg@4!ybf5Lj7ych$--qXaO?ch)PN9_oZ#OyMAj~E^I#E#_ygi>OtjKym$=R)740i z6(Z4GEBK9<tvisxyxQTN!>5928100r0t$+eLGtaYi#!zE?-$U%BN67YPGIiCDpRR` zK_VVQ-&%bKLlGhcXCyEO@a|bwi=f0do&XHggr0;Hk^UGesK3X70MdZLqgPc`0H_18 zqJQFqa2>Xnmu5cyY+g)F|69NuJ^i{kC9Zqh+0K^vm-ubHZl&-&U<lmhBuLXVrM<g< zDptMd0`sgBg21jgZ2;J!Zb{`is=RgXiL?=)fa@ZJAI5CoL;{RlixKNF1%C0~bZK?d zCGr|xS}8!JRRbrNU@BN)n^|T>9`@Ipke+2vz{?6ENOe+y>v?`6fSn-GjYZRvh=WB4 za^Wv3N83OeM4-T@$F04#VE#YR--y@?V2n-JoEp1)7r}Hy@Gu|2p;q`nt#5~oeVh-$ z{dIlxLh@+8uz?byLJtlwWFqfox~TCDK>(a>cl18s`+!y{Pef0|lW00%5Rf{-EDFyT zoE|}T!n5Ck)F!BrOh;D>ni2y0oF^TG$^J$Jga7?g$u`PjdibFw#&hn;`(IezH+aLs z-T?tj{3~bbkaq@Fvo9`h3*Vg6|G3Tws6(v>N|<t@w`H$FY9E563V#6<TB*(A>KmH? O0000<MNUMnLSTaRB=Xb% literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/tree_alpha1.png b/game/modules/boot/data/gfx/terrain/tree_alpha1.png new file mode 100644 index 0000000000000000000000000000000000000000..c81bfcdb5db734efc6d1ad3e8c88af742e93020d GIT binary patch literal 829 zcmV-D1H$}?P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igW5 z3J@Ega<O0l00O;9L_t(o!|j$oNE=}o#(&Y^C8uHuqM^`;R7ju&3DqH?sa8s{v{Tzb zibLp-(z>~qP&zu7LI!aVx9;N5R)x3-Qko$FOK6trwWO42(nzk?A$*s+%f<XDC={P1 z$@hKadEV!J-tP`BaDj7%qn*Q(aknPE?`?MjP<emkI1>SrakrM38>RPT7J%cP_3HXU zPCwI$7)Lu*$EF7WnDBZ4*geRT%DMnZem&G(X-ABsm8t=HPi8spSvQdiVu0|-Fwu~# z@4s37L1ka99?F#(@;I9*+&#!M<$1zpevJvQhg8<3liHmX`@smLhsppHaBGc3G<6_6 zGHhBilBY9e0)EM~pU#v`zY9s}-yB%D@eqK`{2HmOi`eu4sjQ3ZqrX|qC^})lFOkla zDJgO_1Hyg)Uk46|>Z2jq)PDhM4ul&*pf#A4ebv?pN3-y)>=cQHWK*B50tA_ne0d{B zNs)066)7olLjlfa;fsdWS=lMp^*=n~s#`M#G~ky=Q>+n<byKkk<Ebr(O%LcZZxojJ z_=|L=%+tF*Q?<c}Y6@W?DVYV>xZr5z0!qw{)>*&2QM&<+LPQt~NeSne;bQ7E1EONP zacndJR#f&?`om@0k!+ZOTd&e=#RE)uJuGGvpql7lL^aKONs-x80AzEu2K0x8IJNaL z?4Rq>w&DTZ`sc5Yh;>~h@#hNw!HBAlm=I;XkOF>*z3po6+s=S<rcZnOvahc1Q=h#Y zKK`j&ZgfPnLO4_BNUIqziqLPw`9V|gtR!OI#iwztQ2^U}A@R92W2&vyi9KO=E7iCC zMue|6ngUDrW>|gqflsnlBQl;l<w4Za!^i~7pS`#<Zco&N)QGP0ektu(!1$o7<-UCf z;L7kN#s_8XOa;t8x~+eox*7Z@5p84X*?sN7waXe8_#f~W@?4mv3&a1-00000NkvXX Hu0mjf!o7e| literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/tree_alpha2.png b/game/modules/boot/data/gfx/terrain/tree_alpha2.png new file mode 100644 index 0000000000000000000000000000000000000000..231b4f39819ae076c5a086ccb0064e95900a3b96 GIT binary patch literal 777 zcmV+k1NQuhP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igW5 z3okF{+6CSK00N0gL_t(o!|hi+YujKDek45g7Zh8uMFn0<p@^UgS^_SZ7z$n7-2__5 z&?$@OZ2kxR1071If(iytvPqRLS)C|}WI5y)bX5oWUY)EjTQQ|WZn1Ro``&ZUJ$J7J zJlYQIWQ4H)8DW*R@~%L9{Gg)>2zeLTANPmi-gv(q2zi@8S$;17+~pXS?VzP>pFpi@ zaPoCc9a^IUFL1E%fqQ?)08T~-yQUu9j2r=kd|IQE`2W);03f{n4e3E8;8|;p4zxxG z$NeERC8B+P>i~%8cb8)Tz+MwDjR?fEmz!xpDg!_i1AsL>g&V9uv3ipa0PI<OeeVNh zOh_A1pJBv6Th+xq0r6J9q=_OBwX)W7mt(OxXM@bL9cT(LIRjd&@+|zg95#wIQ7g(k zFF3WGro^S77Np>tnp8$V56ZLVI+&a#*K;Py^iVj6)`QP)9Z`F#Gf@u!2m5hw>WVTp z3g-Y1Ox9L)v{j`13m=%2u(0(g)*78uaLxqJey^Dwj2Ot94RT)niT>`ZVWQ8)0Alli zQ{GaLI1|bkC@cXD9ugaMgvS7J0UqqfzD>J9&QrfNJw;p9MQ}GrPCVXv{F(rnMjAv` z8FDqyR`t}h^H$jt%|#;fg0wHI6>C|GxbS%2b9EO5<e(~PfK$D`F>PoX(X(~+)AhPP zZxH2*kk9mUTb%P^^KV}h=YJ~Ob31-sbReYwShgeTOc}2hhdi54Zx3;E72xOfMP>pl zVByP2>H&YLoPEz;*fZ+k>o+d|05?|wroS$6;r)?N!BPz51?Bc*b|(P{j|LG753x$G z<vpVw9t{pLckhJndF#o)`7r4j^$-Bw4ZcW}eO3A&?Z2?U+$3bFGm0BZ00000NkvXX Hu0mjf=v_*5 literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/tree_alpha3.png b/game/modules/boot/data/gfx/terrain/tree_alpha3.png new file mode 100644 index 0000000000000000000000000000000000000000..3cb5f05f8462385e8ec7d6ce2c445060971378de GIT binary patch literal 1016 zcmV<U0|)$xP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igW5 z3os$gI3`B`00VeQL_t(o!|j$mXd_n?g}<>J7)>)`2#j!yehfinz>pxB6cR$VF#&^% zUDXRA5OB4yslt*rkjl+E5ErSG#$ZTcTL`&WY>~pfnw4a<i^T>ZHRCa1cT$Y*lg75R zk~btp_BQkPm~-yA=f3;kL;T-En`QCFUc#%jI`k_R0B@eZ)Br+Xe&UTj&l2;~EV7=4 zy>#4WC%?((dq3kXIz&<~eSBau-vv<5vk=jy0WW4v9xpuv;HMYA0q}U~A)kKtt52k2 zDN?<<j@CA&CNVcjZn<BM`jS_jGR=mr$gHoL^y>Ohfr>>W<*I*3X=YYnV=v)}+{LU( zv!UNwb9mfmeboe@*6L8qnv_oZLw{UcF}LFJ_Edw5t~NCR0k1k`&M&(v+p1G0XB5<X zKoLtMU78JDT>*~v{!&CH5xCQY0@7^gu;kah+0Y?VR=_*?O{&o{Pf88ICXsY`wtEd+ z4>B{}2|l(2*3+=}V=*}-F$l?UD`Su|3d)eBlfGKhtLv<<nn49%ERnf!;5nlJz)pUX zt&9->Kfmm<T{%!{q~kWltZ&Fj%H{CbC*&<UgkoVru`t9e@0|n$Ttc!JB@#)OoKavo zYAHgsRtJD*yVq!K1As`%#anc^cN>byr0<1Dv!PRU$^mlH1(B4CneUR0+e!%uQQ|HY z0tC`=o19TlKwB9<Be@E>{WFJJtE1Ylm^D@XMFF|L*H*lhG1xzINF-gBqZT=%KrTO2 zLu60KZNQ~;(pNj6Uw<E5N}{#LcSJyeTC1b_U1I;tujO{-K=~jYw@JrsYOM~t--lGz zYmYu6l5!QW#H4n-(*xU;0}@F$-~)MHj#`0|c#96bx=yd|E7Pm%QycIWf$k4i+LKZv zu%ij^sOioxy9#W5)eIPJG=#{cHk`j}@(FP1q>r>^v*8SCZ*zEj4fx<f<fidP%P^?~ z^EVYJfhXft%;ayk{<N#*JL>)6aUZR1p#i-yz@b?9p67v><;|@GRg>u_+pI`@({zEX zW0p5*9*jK?2=lk6+O07x;9A>Y<03pBe8LQv?nE?TF9!<&mH1}iV|HKt83djJbow*l z;74zszZ^)Z0awHC3nQR20>4*$``|P0;FqtxuT~bk_x+c4AMmxcFE~2-!%w>Xn%_@f m{X3bDk^uN86!;MTdHfCT2kUjSedYxK0000<MNUMnLSTZ{7t)RZ literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/tree_alpha4.png b/game/modules/boot/data/gfx/terrain/tree_alpha4.png new file mode 100644 index 0000000000000000000000000000000000000000..9c836a0e8d34748fdf5d171ae93e3ac2f1d647f3 GIT binary patch literal 4267 zcmeHJUr5tY6h5<|n}5foO<c_dfkgB$A3~#`WagG+VLkZgC47@!g7vhQ{yjyHy;W2X zp}sWmO~fDwvL}6r5F?lhi!@ly%nWSnes??D+1zx$2|amazkBcb-Fv?8e&^i#^WoLr z?z-ycY9gu&_<en<_26fzRL@{2w4oYPu&=w5!m~$K)Sz;>r`tzK{JnfL_E>qWxBLS^ zqMAnhO!VYglX6zw4qWc4`doR`*>-wl>8XRrb~50*aJ}DZ^4KCjOlnG5X1`-iVqxrs zHM@Q}W;mWXySlU<1NWbAptdGEg<Cf1!_o?^zdKCZq~tJ2!VqJCGjQu6KP~*7)U3lZ zo4TEPm7wh}Dpv+bRFt;w+*tr;WAMqTIJ*qBC8t2{9thYyz#%`()=IR#T1V#NwT4nr zatc78y~RnMV_Q1+v)^JG7>~t>B|`+Y04p^WB`biS00-D&1Urw@#L$RVq7BE5oSH<! z@slbHD6yvkvg!w1gFw=t`3k_FUk8czD*PS=xvK(Fc7Su$0}PgxK2B>37V=+g(0;Cq z_0{yzgtrKhq6%=)VJ(1QoB{ywafA|CB6^yBfDlr_R;(rpD!|~lX!MbDan+N~Gr$We z0#IA)mR1A6s0Y|?HBnFjDLn#!2<M!43lcCM16-Qiz4U7(;7F$=hX8=_P6mhUFaU9% z$7qob-Z{q7m{cJ$oRSOymK^~y(yRVhbX3PIb^^xV#tHWWjEfI_21j3-Bm)wfz+T`i zvs6X~MIT2{=AYjKknaZeaa)i8zeLNx>?^b@0MwuR&QtVPF*7LAk#(R?LG0dd)42MS zVuuXMvLc2A)|X2<aK)LVzH-+bsp^MYR(^U~>RRRjT5%_2YCykt+mn~>*6S`8mma7` z23Rc~_Ii_Ny^S<HJxjhC7ft_)>W@%SZ~@5ixXmR1uOfj4yMsw!?MOeE#OTZlb+~Qx zVE&VQo$c=ej!8Q&qV$3UrHDwmvQbk#r@M*<)ki22{gYB+{^QOK<uA$`8(tvSLj{+k zmS&dMXu1q=#477QB1vMu{R0AJD}el8yV6Y2)eUl6%;Y<9Ds^lO=A<kF4E}oJD?Oi^ s+gBkQ<eNJkM5L!&6=aNq{(%DjU4izFM>j)5Jy8G$x_W)#&Kvjt0WdqQng9R* literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/tree_alpha5.png b/game/modules/boot/data/gfx/terrain/tree_alpha5.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a3635292abc63e852ecc41953eb2c8b09cc537 GIT binary patch literal 4267 zcmeHJT}YEr7=Gu^rW2>GteJ|Epc7$KlwNeBGMO`6ArW;EL>EDHBiNO6)m;!>)LnQ{ zbRl#hR5u9;yQ~a_ku-%ULEZXc=F*v(uJ1W#@7LM(?FSRO@g4il&wI{!pYyyw-*@Mn z&r@zaVkM$-Z?AhmYa8(|F>BAE;o()SF%1oPx@l~x?u#}sUp?b-QyjlX6Qd8bnbN`D zOG89u2k<x1!x4uzX}Rg`@3G98>kpr_7rqR35mmN%-CgG|mYNzXKP_u3{;!zwHQ#rw zDUP+V^{Ck5fATHwz1eIOWmB>a##=hK$a$!O96P5eTrf?6nOMB8)=YsH%cjoD)dF1P z$8TC#+@#3VTHfIDUdRlby<pWq94G&GzrZ&!`jc@|U5#DzU%v~BHXk540kC0aAoz7f z91b>Ac^fPg!;fzk^=XZzoNVL-fYSh?##OE#Y_mrw8j-hUgP=dMx~Z>;Nt`(W<ct9} z?rU}l-{YpU{$ec{<xOE2(&9wU5v{FA5D3<UE}qR5IRS`M6A$>_*Gv?4PHQu$7(`4x z`jg}HWUnkDYpD%G(H9+}j36?)fPIexK!3Y)J8@RBQMjPmaZu=RdrnhKsBVEvjF`v> z00%Jr(fJLsHCPBOglQH7?V-?}6bHIKgK_9H0)TN&IpjS=r5oe5=sFr9;SUk+ik5Gm z>E^uHFb4SMM3Q#+9?~v+FSouLNAMMhfyxLQmaB+RtTYjY3Y}CpWMx40JwSkoXHR1| zC&||sCn8=1o}<-H(a$nFcr61<WyBBw2cVk!ILJs47hu5FD9ME3)Ih{Ot`QrXJG8xC zpfiyMfayoE;cAE5Kc5RwC?LzOY5<2L?J$RSAr#S#L8ppQaY_S#4JrtX83_8Lk7b?b zTraB-KBmH|og~_^Rt1wvJ@gPm08pg50gSM48CJ6ZI?z8kvm}<)*h_RK_~yJ{4Mf8% z;Dv|*raeT(JgpU`zbU8&8;rv+3;Zp|vOKf@S&!C`0o4U6{dg1N_6WS$pmYUFFNCuI zeRVEV@L?S!4FEna*uR_w{PyFB$41tn&jPl%DuyDzcv?g~1}gQeOalOgtG`MV4t(9X z+N{*nQ7dkZbVFdB@-*;%QeVfIOuey~%kqU}2tYqw>)20}=~WE#sBBCz#wzs~12l`# zjakQSP5``6ZBs$9E+2!uc2C+H9j^Gu^^SP=G3m1194g-?yVwci{eyk<eQAqYE%o#- kAa-GXf6G1t`wZ+I1NZN}Z@zW(ZWm7U_W0am-B)h^0h#gkB>(^b literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/water_floor.png b/game/modules/boot/data/gfx/terrain/water_floor.png new file mode 100644 index 0000000000000000000000000000000000000000..8a8cee416c5a551c1220ba3dc2effce6e6d25b8e GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`a29w(7Bet# z3xhBt!>l<HKtc8rPhVH|TP%{|DohLW<(z>+I-V|$As)xyo;74-FyLTNh~?(KdF<bN uuNUmBCth`W>-TZ8Ff}?T2yn18>B?I~GV)CE4BQLU$KdJe=d#Wzp$P!&d?hRZ literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/gfx/terrain/water_floor_alpha.png b/game/modules/boot/data/gfx/terrain/water_floor_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..c7f29d19c698962f1cf88c94c9592ac33be5a269 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmPio2;mo*kyt5Af1MuE{-7)hu@xa zWMpt)VK7+N>U8TxapBPqH-&UoKR>=u{5T6L5Ubt8xWq^8!h4`*22WQ%mvv4FO#lT? BFQfnf literal 0 HcmV?d00001 diff --git a/game/modules/boot/data/rooms/pilar.lua b/game/modules/boot/data/rooms/pilar.lua new file mode 100644 index 0000000000..337f27805e --- /dev/null +++ b/game/modules/boot/data/rooms/pilar.lua @@ -0,0 +1,26 @@ +-- ToME - Tales of Middle-Earth +-- 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 { +[[#!!!!!!!!#]], +[[!........!]], +[[!...##...!]], +[[!........!]], +[[#!!!!!!!!#]], +} diff --git a/game/modules/boot/data/rooms/simple.lua b/game/modules/boot/data/rooms/simple.lua new file mode 100644 index 0000000000..bcaf29ac19 --- /dev/null +++ b/game/modules/boot/data/rooms/simple.lua @@ -0,0 +1,37 @@ +-- ToME - Tales of Middle-Earth +-- 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 function(gen, id) + local w = rng.range(5, 12) + local h = rng.range(5, 12) + return { name="simple"..w.."x"..h, w=w, h=h, generator = function(self, x, y, is_lit) + for i = 1, self.w do + for j = 1, self.h do + if i == 1 or i == self.w or j == 1 or j == self.h then + gen.map.room_map[i-1+x][j-1+y].can_open = true + gen.map(i-1+x, j-1+y, Map.TERRAIN, gen.grid_list[gen:resolve('#')]) + else + gen.map.room_map[i-1+x][j-1+y].room = id + gen.map(i-1+x, j-1+y, Map.TERRAIN, gen.grid_list[gen:resolve('.')]) + end + if is_lit then gen.map.lites(i-1+x, j-1+y, true) end + end + end + end} +end diff --git a/game/modules/boot/data/talents.lua b/game/modules/boot/data/talents.lua new file mode 100644 index 0000000000..5d22407f2e --- /dev/null +++ b/game/modules/boot/data/talents.lua @@ -0,0 +1,168 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +newTalentType{ type="misc/misc", name = "combat", description = "Combat techniques" } + +newTalent{ + name = "Kick", + type = {"misc/misc", 1}, + points = 1, + cooldown = 6, + power = 2, + range = 1, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + target:knockback(self.x, self.y, 2 + self:getDex()) + return true + end, + info = function(self, t) + return "Kick!" + end, +} + +newTalent{ + name = "Acid Spray", + type = {"misc/misc", 1}, + points = 1, + cooldown = 6, + power = 2, + range = 6, + action = function(self, t) + local tg = {type="ball", range=self:getTalentRange(t), radius=1, talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, DamageType.ACID, 1 + self:getDex(), {type="acid"}) + return true + end, + info = function(self, t) + return "Zshhhhhhhhh!" + end, +} + +newTalent{ + name = "Manathrust", + type = {"misc/misc", 1}, + points = 5, + cooldown = 5, + range = 20, + direct_hit = true, + reflectable = true, + requires_target = true, + action = function(self, t) + local tg = {type="beam", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, DamageType.ARCANE, rng.range(10, 20) * self:getTalentLevel(t)) + local _ _, x, y = self:canProject(tg, x, y) + game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "mana_beam", {tx=x-self.x, ty=y-self.y}) + return true + end, + info = function(self, t) + return "Zzttt" + end, +} + +newTalent{ + name = "Flame", + type = {"misc/misc",1}, + points = 5, + cooldown = 5, + range = 20, + reflectable = true, + proj_speed = 20, + requires_target = true, + action = function(self, t) + local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_fire", trail="firetrail"}} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:projectile(tg, x, y, DamageType.FIRE,rng.range(10, 20) * self:getTalentLevel(t), {type="flame"}) + return true + end, + info = function(self, t) + return "burnnn" + end, +} + +newTalent{ + name = "Fireflash", + type = {"misc/misc",3}, + points = 5, + cooldown = 20, + range = 15, + proj_speed = 4, + direct_hit = true, + requires_target = true, + action = function(self, t) + local tg = {type="ball", range=self:getTalentRange(t), radius=1 + self:getTalentLevelRaw(t), friendlyfire=false, talent=t, display={particle="bolt_fire", trail="firetrail"}} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:projectile(tg, x, y, DamageType.FIRE, rng.range(4, 8) * self:getTalentLevel(t), function(self, tg, x, y, grids) + game.level.map:particleEmitter(x, y, tg.radius, "fireflash", {radius=tg.radius, grids=grids, tx=x, ty=y}) + end) + return true + end, + info = function(self, t) + return "Zoooshh!" + end, +} + +newTalent{ + name = "Lightning", + type = {"misc/misc", 1}, + points = 5, + cooldown = 5, + range = 20, + direct_hit = true, + reflectable = true, + requires_target = true, + action = function(self, t) + local tg = {type="beam", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local dam = rng.range(15, 30) * self:getTalentLevel(t) + self:project(tg, x, y, DamageType.LIGHTNING, rng.avg(dam / 3, dam, 3)) + local _ _, x, y = self:canProject(tg, x, y) + game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "lightning", {tx=x-self.x, ty=y-self.y}) + return true + end, + info = function(self, t) + return "Zappp!" + end, +} + +newTalent{ + name = "Sunshield", + type = {"misc/misc", 3}, + points = 5, + cooldown = 100, + range = 20, + direct_hit = true, + action = function(self, t) + self:setEffect(self.EFF_SUNSHIELD, 30, {}) + return true + end, + info = function(self, t) + return "ooooh" + end, +} + diff --git a/game/modules/boot/data/timed_effects.lua b/game/modules/boot/data/timed_effects.lua new file mode 100644 index 0000000000..ac877b1ca0 --- /dev/null +++ b/game/modules/boot/data/timed_effects.lua @@ -0,0 +1,47 @@ +-- ToME - Tales of Middle-Earth +-- 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 Stats = require "engine.interface.ActorStats" +local Particles = require "engine.Particles" + +newEffect{ + name = "ACIDBURN", + desc = "Burning from acid", + type = "physical", + status = "detrimental", + parameters = { power=1 }, + on_gain = function(self, err) return "#Target# is covered in acid!", "+Acid" end, + on_lose = function(self, err) return "#Target# is free from the acid.", "-Acid" end, + on_timeout = function(self, eff) + DamageType:get(DamageType.ACID).projector(eff.src or self, self.x, self.y, DamageType.ACID, eff.power) + end, +} + +newEffect{ + name = "SUNSHIELD", + desc = "Sunshield", + type = "magical", + status = "beneficial", + activate = function(self, eff) + eff.particle = self:addParticles(Particles.new("golden_shield", 1)) + end, + deactivate = function(self, eff) + self:removeParticles(eff.particle) + end, +} diff --git a/game/modules/boot/data/zones/dungeon/grids.lua b/game/modules/boot/data/zones/dungeon/grids.lua new file mode 100644 index 0000000000..17d13d0604 --- /dev/null +++ b/game/modules/boot/data/zones/dungeon/grids.lua @@ -0,0 +1,22 @@ +-- ToME - Tales of Middle-Earth +-- 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/forest.lua") +load("/data/general/grids/water.lua") diff --git a/game/modules/boot/data/zones/dungeon/npcs.lua b/game/modules/boot/data/zones/dungeon/npcs.lua new file mode 100644 index 0000000000..3b3f983ec8 --- /dev/null +++ b/game/modules/boot/data/zones/dungeon/npcs.lua @@ -0,0 +1,22 @@ +-- ToME - Tales of Middle-Earth +-- 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/npcs/skeleton.lua") +load("/data/general/npcs/canine.lua") +load("/data/general/npcs/troll.lua") diff --git a/game/modules/boot/data/zones/dungeon/objects.lua b/game/modules/boot/data/zones/dungeon/objects.lua new file mode 100644 index 0000000000..cad1c5dde4 --- /dev/null +++ b/game/modules/boot/data/zones/dungeon/objects.lua @@ -0,0 +1,18 @@ +-- ToME - Tales of Middle-Earth +-- 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 diff --git a/game/modules/boot/data/zones/dungeon/traps.lua b/game/modules/boot/data/zones/dungeon/traps.lua new file mode 100644 index 0000000000..cad1c5dde4 --- /dev/null +++ b/game/modules/boot/data/zones/dungeon/traps.lua @@ -0,0 +1,18 @@ +-- ToME - Tales of Middle-Earth +-- 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 diff --git a/game/modules/boot/data/zones/dungeon/zone.lua b/game/modules/boot/data/zones/dungeon/zone.lua new file mode 100644 index 0000000000..9faab8139f --- /dev/null +++ b/game/modules/boot/data/zones/dungeon/zone.lua @@ -0,0 +1,52 @@ +-- ToME - Tales of Middle-Earth +-- 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 = "Forest", + level_range = {1, 5}, + level_scheme = "player", + max_level = 5, + decay = {300, 800}, + actor_adjust_level = function(zone, level, e) return zone.base_level + level.level-1 + rng.range(-1,2) end, + width = 50, height = 50, +-- all_remembered = true, + all_lited = true, + generator = { + map = { + class = "engine.generator.map.Forest", + edge_entrances = {4,6}, + zoom = 4, + sqrt_percent = 30, + noise = "fbm_perlin", + floor = function() if rng.chance(20) then return "FLOWER" else return "GRASS" end end, + wall = {"TREE","TREE2","TREE3","TREE4","TREE5","TREE6","TREE7","TREE8","TREE9","TREE10","TREE11","TREE12","TREE13","TREE14","TREE15","TREE16","TREE17","TREE18","TREE19","TREE20",}, + up = "GRASS", + down = "GRASS", + do_ponds = { + nb = {1, 2}, + size = {w=25, h=25}, + pond = {{0.6, "DEEP_WATER"}, {0.8, "SHALLOW_WATER"}}, + }, + }, + actor = { + class = "engine.generator.actor.Random", + nb_npc = {20, 30}, + }, + }, +} diff --git a/game/modules/boot/dialogs/LoadGame.lua b/game/modules/boot/dialogs/LoadGame.lua new file mode 100644 index 0000000000..ffa730861c --- /dev/null +++ b/game/modules/boot/dialogs/LoadGame.lua @@ -0,0 +1,111 @@ +-- TE4 - T-Engine 4 +-- 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 Module = require "engine.Module" +local Dialog = require "engine.ui.Dialog" +local TreeList = require "engine.ui.TreeList" +local Button = require "engine.ui.Button" +local Textzone = require "engine.ui.Textzone" +local Separator = require "engine.ui.Separator" +local Savefile = require "engine.Savefile" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init() + Dialog.init(self, "Load Game", game.w, game.h) + + local list = table.clone(game.save_list, true) + + self.c_delete = Button.new{text="Delete", fct=function(text) self:deleteSave() end} + self.c_desc = Textzone.new{width=math.floor(self.iw / 3 * 2 - 10), height=self.ih - self.c_delete.h - 10, text=""} + + self.tree = {} + local found = false + for i, mod in ipairs(list) do + local nodes = {} + + for j, save in ipairs(mod.savefiles) do + save.fct = function() + Module:instanciate(mod, save.name, false) + end + save.mod = mod + save.zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="#{bold}##GOLD#"..mod.long_name..": "..save.name.."#WHITE##{normal}#\n\n"..save.description} + table.insert(nodes, save) + found = true + end + + if #nodes > 0 then + table.insert(self.tree, { + name="#{bold}##GOLD#"..mod.name.."#WHITE##{normal}#", + fct=function() end, + shown=true, + nodes=nodes, + zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="#{bold}##GOLD#"..mod.long_name.."#WHITE##{normal}#\n\n"..mod.description} + }) + end + end + + self.c_tree = TreeList.new{width=math.floor(self.iw / 3 - 10), height=self.ih, scrollbar=true, columns={ + {width=100, display_prop="name"}, + }, tree=self.tree, + fct=function(item, sel, v) end, + select=function(item, sel) self:select(item) end, + } + + self:loadUI{ + {left=0, top=0, ui=self.c_tree}, + {right=0, top=0, ui=self.c_desc}, + {right=0, bottom=0, ui=self.c_delete}, + {left=self.c_tree.w + 5, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_tree) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:select(item) + if item and self.uis[2] then + self.uis[2].ui = item.zone + self.cur_sel = item + end +end + +function _M:deleteSave() + if not self.cur_sel then return end + + Dialog:yesnoPopup("Delete savefile", "Really delete #{bold}##GOLD#"..self.cur_sel.name.."#WHITE##{normal}#", function(ret) + if ret then + local base = Module:setupWrite(self.cur_sel.mod) + local save = Savefile.new(self.cur_sel.name) + save:delete() + save:close() + fs.umount(base) + + game.save_list = Module:listSavefiles() + + local d = new() + d.__showup = false + game:replaceDialog(self, d) + end + end, "Delete", "Cancel") +end diff --git a/game/modules/boot/dialogs/MainMenu.lua b/game/modules/boot/dialogs/MainMenu.lua new file mode 100644 index 0000000000..b55c663801 --- /dev/null +++ b/game/modules/boot/dialogs/MainMenu.lua @@ -0,0 +1,59 @@ +-- TE4 - T-Engine 4 +-- 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 List = require "engine.ui.List" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init() + Dialog.init(self, "Main Menu", 300, 400, 450, 50) + + self.list = { + {name="New Game", fct=function() game:registerDialog(require("mod.dialogs.NewGame").new()) end}, + {name="Load Game", fct=function() game:registerDialog(require("mod.dialogs.LoadGame").new()) end}, + {name="Player Profile", fct=function() game:registerDialog(require("mod.dialogs.Profile").new()) end}, +-- {name="Install Module", fct=function() end}, + {name="Options", fct=function() + local menu menu = require("engine.dialogs.GameMenu").new{ + "resume", + "keybinds_all", + "video", + "sound", + } + game:registerDialog(menu) + end}, + {name="Exit", fct=function() game:onQuit() end}, + } + + self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) end, font={"/data/font/VeraBd.ttf", 16}} + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + } + self:setupUI(false, true) +end + +function _M:on_recover_focus() + game:unregisterDialog(self) + local d = new() + d.__showup = false + game:registerDialog(d) +end diff --git a/game/modules/boot/dialogs/NewGame.lua b/game/modules/boot/dialogs/NewGame.lua new file mode 100644 index 0000000000..c5771f4a25 --- /dev/null +++ b/game/modules/boot/dialogs/NewGame.lua @@ -0,0 +1,69 @@ +-- TE4 - T-Engine 4 +-- 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 Module = require "engine.Module" +local Dialog = require "engine.ui.Dialog" +local ListColumns = require "engine.ui.ListColumns" +local Textzone = require "engine.ui.Textzone" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init() + Dialog.init(self, "New Game", game.w, game.h) + + self.list = table.clone(game.mod_list, true) + + self.c_desc = Textzone.new{width=math.floor(self.iw / 3 * 2 - 10), height=self.ih, text=""} + + for i, mod in ipairs(self.list) do + mod.name = "#{bold}##GOLD#"..mod.name.."#{normal}##WHITE#" + mod.fct = function(mod) + game:registerDialog(require('engine.dialogs.GetText').new("Enter your character's name", "Name", 2, 25, function(text) + Module:instanciate(mod, text, true) + end)) + end + mod.version_txt = ("%d.%d.%d"):format(mod.version[1], mod.version[2], mod.version[3]) + mod.zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="#{bold}##GOLD#"..mod.long_name.."#WHITE##{normal}#\n\n"..mod.description} + end + + self.c_list = ListColumns.new{width=math.floor(self.iw / 3 - 10), height=self.ih - 10, scrollbar=true, columns={ + {name="Game Module", width=80, display_prop="name"}, + {name="Version", width=20, display_prop="version_txt"}, + }, list=self.list, fct=function(item) end, select=function(item, sel) self:select(item) end} + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=0, ui=self.c_desc}, + {left=self.c_list.w + 5, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_list) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:select(item) + if item and self.uis[2] then + self.uis[2].ui = item.zone + end +end diff --git a/game/modules/boot/dialogs/Profile.lua b/game/modules/boot/dialogs/Profile.lua new file mode 100644 index 0000000000..154ba65f67 --- /dev/null +++ b/game/modules/boot/dialogs/Profile.lua @@ -0,0 +1,86 @@ +-- TE4 - T-Engine 4 +-- 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 Module = require "engine.Module" +local Dialog = require "engine.ui.Dialog" +local List = require "engine.ui.List" +local Textzone = require "engine.ui.Textzone" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init() + Dialog.init(self, "Player Profile", 400, 200) + + self.c_desc = Textzone.new{width=300, height=self.ih, text=""} + + self.list = {} + if profile.auth then + self.list[#self.list+1] = {name="Logout", fct=function() + Dialog:yesnoPopup("You are logged in", "Do you want to log out?", function(ret) + if ret then + profile:logOut() + game:checkLogged() + end + end, "Log out", "Cancel") + end} + else + self.list[#self.list+1] = {name="Login", fct=function() + local dialogdef = {} + dialogdef.fct = function(login) self:setPlayerLogin(login) end + dialogdef.name = "login" + dialogdef.justlogin = true + game:registerDialog(require('mod.dialogs.ProfileLogin').new(dialogdef, game.profile_help_text)) + end} + self.list[#self.list+1] = {name="Create Account", fct=function() + local dialogdef = {} + dialogdef.fct = function(login) self:setPlayerLogin(login) end + dialogdef.name = "creation" + dialogdef.justlogin = false + game:registerDialog(require('mod.dialogs.ProfileLogin').new(dialogdef, game.profile_help_text)) + end} + end + + self.c_list = List.new{width=150, nb_items=#self.list, list=self.list, fct=function(item) end} + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=0, ui=self.c_desc}, + {left=self.c_list.w + 5, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setupUI(true, true) + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:select(item) + if item and self.uis[2] then +-- self.uis[2].ui = item.zone + end +end + +function _M:on_recover_focus() + game:unregisterDialog(self) + local d = new() + d.__showup = false + game:registerDialog(d) +end diff --git a/game/modules/boot/dialogs/ProfileLogin.lua b/game/modules/boot/dialogs/ProfileLogin.lua new file mode 100644 index 0000000000..fe3195cff3 --- /dev/null +++ b/game/modules/boot/dialogs/ProfileLogin.lua @@ -0,0 +1,85 @@ +-- TE4 - T-Engine 4 +-- 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 Button = require "engine.ui.Button" +local Textbox = require "engine.ui.Textbox" +local Textzone = require "engine.ui.Textzone" + +module(..., package.seeall, class.inherit(Dialog)) + +function _M:init(dialogdef, profile_help_text) + Dialog.init(self, "Online profile "..dialogdef.name, 500, 400) + self.profile_help_text = profile_help_text + self.dialogdef = dialogdef + self.alpha = 230 + self.justlogin = dialogdef.justlogin + + self.c_desc = Textzone.new{width=math.floor(self.iw - 10), auto_height=true, text=self.profile_help_text} + + if self.justlogin then + self.c_login = Textbox.new{title="Login: ", text="", chars=30, max_len=20, fct=function(text) self:okclick() end} + self.c_pass = Textbox.new{title="Password: ", text="", chars=30, max_len=20, hide=true, fct=function(text) self:okclick() end} + local ok = require("engine.ui.Button").new{text="Login", fct=function() self:okclick() end} + local cancel = require("engine.ui.Button").new{text="Cancel", fct=function() self:cancelclick() end} + + self:loadUI{ + {left=0, top=0, ui=self.c_desc}, + {left=0, top=self.c_desc.h, ui=self.c_login}, + {left=0, top=self.c_desc.h+self.c_login.h+5, ui=self.c_pass}, + {left=0, bottom=0, ui=ok}, + {right=0, bottom=0, ui=cancel}, + } + self:setFocus(self.c_login) + else + self.c_login = Textbox.new{title="Login: ", text="", chars=30, max_len=20, fct=function(text) self:okclick() end} + self.c_pass = Textbox.new{title="Password: ", text="", chars=30, max_len=20, hide=true, fct=function(text) self:okclick() end} + self.c_email = Textbox.new{title="Email: ", text="", chars=30, max_len=60, fct=function(text) self:okclick() end} + self.c_name = Textbox.new{title="Name: ", text="", chars=30, max_len=60, fct=function(text) self:okclick() end} + local ok = require("engine.ui.Button").new{text="Create", fct=function() self:okclick() end} + local cancel = require("engine.ui.Button").new{text="Cancel", fct=function() self:cancelclick() end} + + self:loadUI{ + {left=0, top=0, ui=self.c_desc}, + {left=0, top=self.c_desc.h, ui=self.c_login}, + {left=0, top=self.c_desc.h+self.c_login.h+5, ui=self.c_pass}, + {left=0, top=self.c_desc.h+self.c_login.h+self.c_pass.h+10, ui=self.c_email}, + {left=0, top=self.c_desc.h+self.c_login.h+self.c_pass.h+self.c_email.h+15, ui=self.c_name}, + {left=0, bottom=0, ui=ok}, + {right=0, bottom=0, ui=cancel}, + } + self:setFocus(self.c_login) + end + self:setupUI(true, true) + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + + +function _M:okclick() + game:unregisterDialog(self) + game:createProfile({login=self.c_login.text, pass=self.c_pass.text, email=self.c_email and self.c_email.text, name=self.c_name and self.c_name.text}) +end + +function _M:cancelclick() + self.key:triggerVirtual("EXIT") +end diff --git a/game/modules/boot/init.lua b/game/modules/boot/init.lua new file mode 100644 index 0000000000..47c000f7a8 --- /dev/null +++ b/game/modules/boot/init.lua @@ -0,0 +1,30 @@ +-- ToME - Tales of Middle-Earth +-- 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 = "Boot" +long_name = "Boot for T-Engine4" +short_name = "boot" +author = { "DarkGod", "darkgod@te4.org" } +homepage = "http://te4.org/" +version = {1,0,0} +engine = {0,9,13} +description = [[ +Bootmenu! +]] +starter = "mod.load" diff --git a/game/modules/boot/load.lua b/game/modules/boot/load.lua new file mode 100644 index 0000000000..9ad039df44 --- /dev/null +++ b/game/modules/boot/load.lua @@ -0,0 +1,64 @@ +-- ToME - Tales of Middle-Earth +-- 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 + +-- This file loads the game module, and loads data +local KeyBind = require "engine.KeyBind" +local DamageType = require "engine.DamageType" +local ActorStats = require "engine.interface.ActorStats" +local ActorResource = require "engine.interface.ActorResource" +local ActorTalents = require "engine.interface.ActorTalents" +local ActorAI = require "engine.interface.ActorAI" +local ActorLevel = require "engine.interface.ActorLevel" +local ActorTemporaryEffects = require "engine.interface.ActorTemporaryEffects" +local Birther = require "engine.Birther" + +local n = core.noise.new(2) +_2DNoise = n:makeTexture2D(64, 64) + +-- Usefull keybinds +KeyBind:load("move,hotkeys,inventory,actions,interface,debug") + +-- Damage types +DamageType:loadDefinition("/data/damage_types.lua") + +-- Talents +ActorTalents:loadDefinition("/data/talents.lua") + +-- Timed Effects +ActorTemporaryEffects:loadDefinition("/data/timed_effects.lua") + +-- Actor resources +ActorResource:defineResource("Power", "power", nil, "power_regen", "Power represent your ability to use special talents.") + +-- Actor stats +ActorStats:defineStat("Strength", "str", 10, 1, 100, "Strength defines your character's ability to apply physical force. It increases your melee damage, damage with heavy weapons, your chance to resist physical effects, and carrying capacity.") +ActorStats:defineStat("Dexterity", "dex", 10, 1, 100, "Dexterity defines your character's ability to be agile and alert. It increases your chance to hit, your ability to avoid attacks and your damage with light weapons.") +ActorStats:defineStat("Constitution", "con", 10, 1, 100, "Constitution defines your character's ability to withstand and resist damage. It increases your maximun life and physical resistance.") + +-- Actor AIs +ActorAI:loadDefinition("/engine/ai/") +ActorAI:loadDefinition("/mod/ai/") + +-- Birther descriptor +Birther:loadDefinition("/data/birth/descriptors.lua") + +-- Switch to realtime, with 10 ticks per second +core.game.setRealtime(8) + +return {require "mod.class.Game" } diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index b6070fe171..a6099e4530 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -713,7 +713,7 @@ newEntity{ base = "BASE_STAFF", define_as = "VOICE_SARUMAN", } newEntity{ base = "BASE_WAND", - unided_name = "glowing wand", + unided_name = "glowing rod", name = "Gwai's Burninator", color=colors.LIGHT_RED, unique=true, cost = 50, rarity = 220, diff --git a/src/core_lua.c b/src/core_lua.c index 4973e27216..bbb83c7982 100644 --- a/src/core_lua.c +++ b/src/core_lua.c @@ -409,9 +409,26 @@ static int lua_exit_engine(lua_State *L) exit_engine = TRUE; return 0; } -extern bool reboot_lua; +extern bool reboot_lua, reboot_new; +extern char *reboot_engine, *reboot_engine_version, *reboot_module, *reboot_name; static int lua_reboot_lua(lua_State *L) { + if (reboot_engine) free(reboot_engine); + if (reboot_engine_version) free(reboot_engine_version); + if (reboot_module) free(reboot_module); + if (reboot_name) free(reboot_name); + + reboot_engine = (char *)luaL_checkstring(L, 1); + reboot_engine_version = (char *)luaL_checkstring(L, 2); + reboot_module = (char *)luaL_checkstring(L, 3); + reboot_name = (char *)luaL_checkstring(L, 4); + reboot_new = lua_toboolean(L, 5); + + if (reboot_engine) reboot_engine = strdup(reboot_engine); + if (reboot_engine_version) reboot_engine_version = strdup(reboot_engine_version); + if (reboot_module) reboot_module = strdup(reboot_module); + if (reboot_name) reboot_name = strdup(reboot_name); + reboot_lua = TRUE; return 0; } diff --git a/src/main.c b/src/main.c index f0ff461d0d..4c3e534a0b 100644 --- a/src/main.c +++ b/src/main.c @@ -48,7 +48,8 @@ lua_State *L = NULL; int current_mousehandler = LUA_NOREF; int current_keyhandler = LUA_NOREF; int current_game = LUA_NOREF; -bool reboot_lua = FALSE; +bool reboot_lua = FALSE, reboot_new = FALSE; +char *reboot_engine = NULL, *reboot_engine_version = NULL, *reboot_module = NULL, *reboot_name = NULL; bool exit_engine = FALSE; bool no_sound = FALSE; bool isActive = TRUE; @@ -547,6 +548,7 @@ void setupRealtime(float freq) if (!freq) { if (realtime_timer_id) SDL_RemoveTimer(realtime_timer_id); + realtime_timer_id = NULL; printf("[ENGINE] Switching to turn based\n"); } else @@ -691,6 +693,9 @@ void boot_lua(int state, bool rebooting, int argc, char *argv[]) /* When rebooting we destroy the lua state to free memory and we reset physfs */ if (rebooting) { + current_mousehandler = LUA_NOREF; + current_keyhandler = LUA_NOREF; + current_game = LUA_NOREF; lua_close(L); PHYSFS_deinit(); } @@ -768,7 +773,12 @@ void boot_lua(int state, bool rebooting, int argc, char *argv[]) // And run the lua engine scripts luaL_loadfile(L, "/loader/init.lua"); - docall(L, 0, 0); + if (reboot_engine) lua_pushstring(L, reboot_engine); else lua_pushnil(L); + if (reboot_engine_version) lua_pushstring(L, reboot_engine_version); else lua_pushnil(L); + if (reboot_module) lua_pushstring(L, reboot_module); else lua_pushnil(L); + if (reboot_name) lua_pushstring(L, reboot_name); else lua_pushnil(L); + lua_pushboolean(L, reboot_new); + docall(L, 5, 0); } } @@ -792,6 +802,16 @@ int main(int argc, char *argv[]) // Change to line buffering setvbuf(stdout, (char *) NULL, _IOLBF, 0); + // Parse arguments + int i; + for (i = 1; i < argc; i++) + { + char *arg = argv[i]; + if (!strncmp(arg, "-M", 2)) reboot_module = strdup(arg+2); + if (!strncmp(arg, "-u", 2)) reboot_name = strdup(arg+2); + if (!strncmp(arg, "-n", 2)) reboot_new = TRUE; + } + boot_lua(1, FALSE, argc, argv); // initialize engine and set up resolution and depth @@ -928,6 +948,8 @@ int main(int argc, char *argv[]) /* Reboot the lua engine */ if (reboot_lua) { + tickPaused = FALSE; + setupRealtime(0); boot_lua(1, TRUE, argc, argv); boot_lua(2, TRUE, argc, argv); } -- GitLab