From 23ccecb6e803f3a5947d692e07fff55ec98beef0 Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Tue, 23 Dec 2014 17:32:37 +0100
Subject: [PATCH] Rooms and Maps can now be in a zone subfolder for better
 organisation

moar work on ogres unlock, nearly done
---
 game/engines/default/engine/Zone.lua          |   5 +
 .../default/engine/generator/map/Roomer.lua   |  20 ++
 .../engine/generator/map/RoomsLoader.lua      |  25 ++-
 .../default/engine/generator/map/Static.lua   |   3 +
 game/engines/default/engine/utils.lua         |   6 +
 game/modules/tome/class/Game.lua              |   2 +-
 game/modules/tome/data/lore/age-allure.lua    |   6 +-
 game/modules/tome/data/talents/misc/races.lua |   2 +-
 .../tome/data/texts/unlock-race_ogre.lua      |  31 +++
 .../maps}/conclave-vault-entrance.lua         |   2 +-
 .../tome/data/zones/conclave-vault/npcs.lua   | 198 ++++++++++++++----
 .../data/zones/conclave-vault/objects.lua     |  18 +-
 .../data/zones/conclave-vault/rooms/boss.lua  |  55 +++++
 .../conclave-vault/rooms/room1.lua}           |  14 +-
 .../data/zones/conclave-vault/rooms/room2.lua |  53 +++++
 .../tome/data/zones/conclave-vault/zone.lua   |  18 +-
 tiled-maps/conclave-vaul-entrance.tmx         |   2 +-
 17 files changed, 388 insertions(+), 72 deletions(-)
 create mode 100644 game/modules/tome/data/texts/unlock-race_ogre.lua
 rename game/modules/tome/data/{maps/zones => zones/conclave-vault/maps}/conclave-vault-entrance.lua (98%)
 create mode 100644 game/modules/tome/data/zones/conclave-vault/rooms/boss.lua
 rename game/modules/tome/data/{rooms/zones/conclave-vault-ogre-room-1.lua => zones/conclave-vault/rooms/room1.lua} (83%)
 create mode 100644 game/modules/tome/data/zones/conclave-vault/rooms/room2.lua

diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua
index 9e52037236..772f6bbf98 100644
--- a/game/engines/default/engine/Zone.lua
+++ b/game/engines/default/engine/Zone.lua
@@ -937,6 +937,11 @@ function _M:newLevel(level_data, lev, old_lev, game)
 	-- Generate the map
 	local generator = self:getGenerator("map", level, level_data.generator.map)
 	local ux, uy, dx, dy, spots = generator:generate(lev, old_lev)
+	if level.force_recreate then
+		level:removed()
+		return self:newLevel(level_data, lev, old_lev, game)
+	end
+
 	spots = spots or {}
 
 	for i = 1, #spots do print("[NEW LEVEL] spot", spots[i].x, spots[i].y, spots[i].type, spots[i].subtype) end
diff --git a/game/engines/default/engine/generator/map/Roomer.lua b/game/engines/default/engine/generator/map/Roomer.lua
index b854244fb2..b44957a0ad 100644
--- a/game/engines/default/engine/generator/map/Roomer.lua
+++ b/game/engines/default/engine/generator/map/Roomer.lua
@@ -119,6 +119,26 @@ function _M:generate(lev, old_lev)
 
 	local nb_room = util.getval(self.data.nb_rooms or 10)
 	local rooms = {}
+
+	-- Those we are required to have
+	if #self.required_rooms > 0 then
+		for i, rroom in ipairs(self.required_rooms) do
+			local ok = false
+			if type(rroom) == "table" and rroom.chance_room then
+				if rng.percent(rroom.chance_room) then rroom = rroom[1] ok = true end
+			else ok = true
+			end
+
+			if ok then
+				local r = self:roomAlloc(rroom, #rooms+1, lev, old_lev)
+				if r then rooms[#rooms+1] = r
+				else self.force_recreate = true return end
+				nb_room = nb_room - 1
+			end
+		end
+	end
+
+	-- Normal, random rooms	
 	while nb_room > 0 do
 		local rroom
 		while true do
diff --git a/game/engines/default/engine/generator/map/RoomsLoader.lua b/game/engines/default/engine/generator/map/RoomsLoader.lua
index e95e308c79..565f5d282a 100644
--- a/game/engines/default/engine/generator/map/RoomsLoader.lua
+++ b/game/engines/default/engine/generator/map/RoomsLoader.lua
@@ -25,19 +25,26 @@ module(..., package.seeall, class.make)
 
 function _M:init(data)
 	self.rooms = {}
+	self.required_rooms = {}
 
 	data.tunnel_change = self.data.tunnel_change or 30
 	data.tunnel_random = self.data.tunnel_random or 10
 
-	if not data.rooms then return end
-
-	for i, file in ipairs(data.rooms) do
+	if data.rooms then for i, file in ipairs(data.rooms) do
 		if type(file) == "table" then
 			table.insert(self.rooms, {self:loadRoom(file[1]), chance_room=file[2]})
 		else
 			table.insert(self.rooms, self:loadRoom(file))
 		end
-	end
+	end end
+
+	if data.required_rooms then for i, file in ipairs(data.required_rooms) do
+		if type(file) == "table" then
+			table.insert(self.required_rooms, {self:loadRoom(file[1]), chance_room=file[2]})
+		else
+			table.insert(self.required_rooms, self:loadRoom(file))
+		end
+	end end
 end
 
 local rooms_cache = {}
@@ -45,7 +52,10 @@ local rooms_cache = {}
 function _M:loadRoom(file)
 	if rooms_cache[file] then return rooms_cache[file] end
 
-	local f, err = loadfile("/data/rooms/"..file..".lua")
+	local filename = "/data/rooms/"..file..".lua"
+	-- Found in the zone itself ?
+	if file:find("^!") then filename = self.zone:getBaseName().."/rooms/"..file:sub(2)..".lua" end
+	local f, err = loadfile(filename)
 	if not f and err then error(err) end
 	setfenv(f, setmetatable({
 		Map = require("engine.Map"),
@@ -142,7 +152,7 @@ end
 
 --- Generates parse data for from an ascii def, for function room generators
 function _M:roomParse(def)
-	local room = { w=def[1]:len(), h=#def, spots={} }
+	local room = { w=def[1]:len(), h=#def, spots={}, special=def.special }
 
 	-- Read the room map
 	for j, line in ipairs(def) do
@@ -154,7 +164,7 @@ function _M:roomParse(def)
 				c = tonumber(c)
 				room.spots[c] = room.spots[c] or {}
 				room.spots[c][#room.spots[c]+1] = {x=i-1, y=j-1}
-				c = '.'
+				c = def.numbers or '.'
 			end
 
 			room[i][j] = c
@@ -178,6 +188,7 @@ function _M:roomFrom(id, x, y, is_lit, room)
 			else
 				self.map(i-1+x, j-1+y, Map.TERRAIN, self:resolve(c))
 			end
+			if room.special then self.map.room_map[i-1+x][j-1+y].special = true end
 			if is_lit then self.map.lites(i-1+x, j-1+y, true) end
 		end
 	end
diff --git a/game/engines/default/engine/generator/map/Static.lua b/game/engines/default/engine/generator/map/Static.lua
index 57605ccd9f..570128c69b 100644
--- a/game/engines/default/engine/generator/map/Static.lua
+++ b/game/engines/default/engine/generator/map/Static.lua
@@ -43,6 +43,9 @@ function _M:init(zone, map, level, data)
 end
 
 function _M:getMapFile(file)
+	-- Found in the zone itself ?
+	if file:find("^!") then return self.zone:getBaseName().."/maps/"..file:sub(2)..".lua" end
+
 	local _, _, addon, rfile = file:find("^([^+]+)%+(.+)$")
 	if addon and rfile then
 		return "/data-"..addon.."/maps/"..rfile..".lua"
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index f4a6dcfaef..776038ddcd 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -1784,6 +1784,12 @@ function util.bound(i, min, max)
 	return i
 end
 
+function util.squareApply(x, y, w, h, fct)
+	for i = x, x + w do for j = y, y + h do
+		fct(i, j)
+	end end
+end
+
 function util.minBound(i, min, max)
 	return math.max(math.min(max, i), min)
 end
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 697c3c8ca5..a9e6770f2e 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -1589,7 +1589,7 @@ function _M:setupCommands()
 			print("===============")
 		end end,
 		[{"_g","ctrl"}] = function() if config.settings.cheat then
-			game:changeLevel(1, "conclave-vault")
+			game:changeLevel(4, "conclave-vault")
 do return end
 			local npc = game.zone:makeEntity(game.level, "actor", {random_boss = {name_scheme="#rng# the Fearsome", class_filter=function(d) return d.name == "Demonologist" end}}, nil, true)
 			local nx, ny = util.findFreeGrid(game.player.x, game.player.y, 10, true, {[engine.Map.ACTOR]=true})
diff --git a/game/modules/tome/data/lore/age-allure.lua b/game/modules/tome/data/lore/age-allure.lua
index 655c5c980a..1699932733 100644
--- a/game/modules/tome/data/lore/age-allure.lua
+++ b/game/modules/tome/data/lore/age-allure.lua
@@ -243,5 +243,9 @@ This is my last order: I have prepared a golem to apply the Ogric inscriptions t
 It has been an honor working with such talented individuals, and an honor doing everything I can for a noble cause.  Perhaps we still had some progress to make, but overall I'm proud of what we've accomplished, and the contributions the Ogre race will make to all of Maj'Eyal.  
 
 Long live the Conclave,
-Healer Astelrid]]
+Healer Astelrid]],
+	on_learn = function(who)
+		game:setAllowedBuild("race_giant")
+		game:setAllowedBuild("race_ogre", true)
+   	end,
 }
diff --git a/game/modules/tome/data/talents/misc/races.lua b/game/modules/tome/data/talents/misc/races.lua
index e1d1c5e0ee..9d41f0ada2 100644
--- a/game/modules/tome/data/talents/misc/races.lua
+++ b/game/modules/tome/data/talents/misc/races.lua
@@ -963,7 +963,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[An ogre's body is used to spells and inscriptions.
-		Increases spell save by %d and improves the contibution of primary stats on infusions and runes by %d%%.]]):
+		Increases spell save by %d and improves the contribution of primary stats on infusions and runes by %d%%.]]):
 		format(t.getSave(self, t), t.getMult(self, t) * 100)
 	end,
 }
diff --git a/game/modules/tome/data/texts/unlock-race_ogre.lua b/game/modules/tome/data/texts/unlock-race_ogre.lua
new file mode 100644
index 0000000000..1b01de08dc
--- /dev/null
+++ b/game/modules/tome/data/texts/unlock-race_ogre.lua
@@ -0,0 +1,31 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return "New Race: #LIGHT_GREEN#Ogre",
+[[
+WRITE ME
+
+You have discovered the truth about the creation of the Ogres and can now create a new character with the #LIGHT_GREEN#Ogre race#WHITE#.
+
+Race features:#YELLOW#
+- Strong but not stupid
+- Efficient at using all kind of runes and infusions
+- Imbued with arcane forces
+#WHITE#
+]]
diff --git a/game/modules/tome/data/maps/zones/conclave-vault-entrance.lua b/game/modules/tome/data/zones/conclave-vault/maps/conclave-vault-entrance.lua
similarity index 98%
rename from game/modules/tome/data/maps/zones/conclave-vault-entrance.lua
rename to game/modules/tome/data/zones/conclave-vault/maps/conclave-vault-entrance.lua
index 3119d203ae..4176f49341 100644
--- a/game/modules/tome/data/maps/zones/conclave-vault-entrance.lua
+++ b/game/modules/tome/data/zones/conclave-vault/maps/conclave-vault-entrance.lua
@@ -45,7 +45,7 @@ return [[
 ################
 ########.O######
 ########..######
->*.............<
+>..............<
 ########..######
 ########.o######
 ################
diff --git a/game/modules/tome/data/zones/conclave-vault/npcs.lua b/game/modules/tome/data/zones/conclave-vault/npcs.lua
index d26e22b973..6fe968416f 100644
--- a/game/modules/tome/data/zones/conclave-vault/npcs.lua
+++ b/game/modules/tome/data/zones/conclave-vault/npcs.lua
@@ -33,6 +33,9 @@ newEntity{
 	infravision = 10,
 	
 	resolvers.racial(),
+	resolvers.sustains_at_birth(),
+	resolvers.inscriptions(1, "rune"),
+	resolvers.inscriptions(1, "infusion"),
 
 	autolevel = "warriormage",
 	ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, },
@@ -48,74 +51,151 @@ newEntity{
 
 newEntity{ base = "BASE_NPC_OGRE",
 	name = "ogre guard", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
+	desc = [[A maul-wield ogre. Ready to CRUSH!]],
+	level_range = {20, nil}, exp_worth = 1,
 	rarity = 2,
 	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	max_life = resolvers.rngavg(150,170), life_rating = 14,
+
+	resolvers.equip{{type="weapon", subtype="greatmaul", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_SUNDER_ARMOUR]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_WEAPONS_MASTERY]={base=4, every=5, max=7},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
-	name = "ogre warmaster", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
-	rarity = 2,
-	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	name = "ogre warmaster", color=colors.CRIMSON,
+	desc = [[A master of combat, he is impatient to test his newfound skills.]],
+	level_range = {21, nil}, exp_worth = 1,
+	rarity = 4,
+	rank = 3,
+	max_life = resolvers.rngavg(110,120), life_rating = 15,
+
+	resolvers.equip{
+		{type="weapon", subtype="mace", forbid_power_source={antimagic=true}, autoreq=true},
+		{type="armor", subtype="shield", forbid_power_source={antimagic=true}, autoreq=true},
+	},
+	resolvers.talents{
+		[Talents.T_BATTLE_CRY]={base=3, every=4, max=8},
+		[Talents.T_DISARM]={base=3, every=4, max=8},
+		[Talents.T_BATTLE_CALL]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_SHATTERING_BLOW]={base=3, every=4, max=8},
+		[Talents.T_WEAPONS_MASTERY]={base=4, every=5, max=7},
+		[Talents.T_ARMOUR_TRAINING]={base=4, every=5, max=7},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
-	name = "ogre mauler", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
+	name = "ogre mauler", color=colors.LIGHT_UMBER,
+	desc = [[Crush! Destroy! Maim!]],
+	level_range = {22, nil}, exp_worth = 1,
 	rarity = 2,
 	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	max_life = resolvers.rngavg(110,120), life_rating = 13,
+
+	resolvers.equip{{type="weapon", subtype="greatmaul", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_WARSHOUT_BERSERKER]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_WEAPONS_MASTERY]={base=4, every=5, max=7},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
-	name = "ogre pounder", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
-	rarity = 2,
-	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	name = "ogre pounder", color=colors.DARK_UMBER,
+	desc = [[This ogre closes in fast on you, arms open for the hug of death.]],
+	level_range = {20, nil}, exp_worth = 1,
+	rarity = 3,
+	rank = 3,
+	max_life = resolvers.rngavg(150,170), life_rating = 15,
+
+	resolvers.equip{{type="armor", subtype="hands", autoreq=true},},
+	resolvers.talents{
+		[Talents.T_DOUBLE_STRIKE] = {base=3, every=5, max=7},
+		[Talents.T_UPPERCUT] = {base=3, every=5, max=7},
+		[Talents.T_EMPTY_HAND] = 1,
+		[Talents.T_CLINCH] = {base=3, every=5, max=7},
+		[Talents.T_MAIM] = {base=3, every=5, max=7},
+		[Talents.T_UNARMED_MASTERY] = {base=4, every=6, max=8},
+		[Talents.T_WEAPON_COMBAT] = {base=2, every=6, max=8},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
-	name = "degenerated ogre", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
+	name = "degenerated ogric mass", color=colors.BLUE,
+	desc = [[This huge mass of deformed flesh was probably once an ogre, but something had gone wrong.]],
+	level_range = {20, nil}, exp_worth = 1,
 	rarity = 2,
 	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	max_life = resolvers.rngavg(110,120), life_rating = 13,
+
+	resolvers.equip{{type="weapon", subtype="greatmaul", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_EPIDEMIC]={base=3, every=4, max=8},
+		[Talents.T_ROTTING_DISEASE]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_CATALEPSY]={base=4, every=5, max=7},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
 	name = "ogric abomination", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
-	rarity = 2,
-	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	desc = [[This ogre seems to have tried to graft golem parts on its own body. To various interresting results.]],
+	level_range = {22, nil}, exp_worth = 1,
+	rarity = 4,
+	rank = 3,
+	max_life = resolvers.rngavg(110,120), life_rating = 13,
+
+	resolvers.equip{{type="weapon", subtype="greatmaul", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_GOLEM_CRUSH]={base=3, every=4, max=8},
+		[Talents.T_GOLEM_KNOCKBACK]={base=3, every=4, max=8},
+		[Talents.T_GOLEM_POUND]={base=3, every=4, max=8},
+		[Talents.T_GOLEM_REFLECTIVE_SKIN]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_WEAPONS_MASTERY]={base=4, every=5, max=7},
+	},
 }
 
 newEntity{ base = "BASE_NPC_OGRE",
-	name = "ogre rune-spinner", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
+	name = "ogre rune-spinner", color=colors.LIGHT_RED,
+	desc = [[A towering ogre guard, his skin covered in runes and arcane designs.]],
+	level_range = {23, nil}, exp_worth = 1,
 	rarity = 2,
-	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	rank = 3,
+	max_life = resolvers.rngavg(110,120), life_rating = 13,
+
+	resolvers.equip{{type="weapon", subtype="staff", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_LIGHTNING]={base=3, every=4, max=8},
+		[Talents.T_FLAME]={base=3, every=4, max=7},
+		[Talents.T_EARTHEN_MISSILES]={base=4, every=5, max=7},
+		[Talents.T_BARRIER]={base=4, every=5, max=7},
+	},
 }
 
+
+------------- Non random
+
 newEntity{ base = "BASE_NPC_OGRE", define_as = "OGRE_SENTRY",
-	name = "ogre sentry", color=colors.LIGHT_GREY,
-	desc = [[WRITE ME]],
-	level_range = {1, nil}, exp_worth = 1,
-	rarity = 2,
-	rank = 2,
-	max_life = resolvers.rngavg(110,120), life_rating = 14,
+	name = "ogre sentry", color=colors.GREY,
+	desc = [[This greatsword-wielding ogre looks at you with contempt and hatred.]],
+	level_range = {21, nil}, exp_worth = 1,
+	rank = 3,
+	max_life = resolvers.rngavg(110,120), life_rating = 13,
+	blind_immune = 1,
+
+	resolvers.equip{{type="weapon", subtype="greatsword", forbid_power_source={antimagic=true}, autoreq=true} },
+	resolvers.talents{
+		[Talents.T_STUNNING_BLOW]={base=3, every=4, max=8},
+		[Talents.T_WEAPON_COMBAT]={base=3, every=4, max=7},
+		[Talents.T_WEAPONS_MASTERY]={base=4, every=5, max=7},
+		[Talents.T_ILLUMINATE]={base=3, every=5, max=7},
+	},
+
 	seen_by = function(self, who)
 		if not game.party:hasMember(who) then return end
 		self.seen_by = nil
@@ -134,3 +214,43 @@ newEntity{ base = "OGRE_SENTRY", define_as = "OGRE_SENTRY2",
 		chat:invoke()
 	end,
 }
+
+
+newEntity{ base = "BASE_NPC_OGRE", define_as = "HEALER_ASTELRID",
+	name = "Healer Astelrid", color=colors.VIOLET,
+	desc = [[An enormous ogre, clad in a tattered set of robes with an officer's badge.  She clutches a healer's staff, wrapped in casting plaster and scalpels for use as a massive spiked club.]],
+	killer_message = "and spliced for experiments",
+	level_range = {23, nil}, exp_worth = 2,
+	female = 1,
+	rank = 4,
+	max_life = 170, life_rating = 14, fixed_rating = true,
+
+	stats = { str=20, dex=10, cun=8, mag=25, con=20 },
+	instakill_immune = 1,
+	move_others=true,
+
+	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, TOOL=1 },
+	resolvers.equip{
+		{defined="ASTELRID_CLUBSTAFF"},
+		{type="armor", subtype="cloth", forbid_power_source={antimagic=true}, force_drop=true, tome_drops="boss", autoreq=true},
+		{type="armor", subtype="head", forbid_power_source={antimagic=true}, force_drop=true, tome_drops="boss", autoreq=true},
+		{type="armor", subtype="feet", forbid_power_source={antimagic=true}, force_drop=true, tome_drops="boss", autoreq=true},
+	},
+	resolvers.drops{chance=100, nb=1, {defined="NOTE4"} },
+	resolvers.drops{chance=100, nb=3, {tome_drops="boss"} },
+
+	resolvers.talents{
+		[Talents.T_HEAL]={base=3, every=4, max=8},
+		[Talents.T_ARCANE_SHIELD]={base=3, every=4, max=8},
+		[Talents.T_AEGIS]={base=3, every=4, max=8},
+		[Talents.T_EARTHQUAKE]={base=3, every=4, max=8},
+		[Talents.T_RUSH]={base=3, every=4, max=8},
+		[Talents.T_STUNNING_BLOW]={base=3, every=4, max=8},
+		[Talents.T_LIVING_LIGHTNING]={base=3, every=4, max=8},
+	},
+	resolvers.inscriptions(2, "rune"),
+	resolvers.inscriptions(2, "infusion"),
+
+	autolevel = "warriormage",
+	ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", },
+}
diff --git a/game/modules/tome/data/zones/conclave-vault/objects.lua b/game/modules/tome/data/zones/conclave-vault/objects.lua
index 30edb9f14e..3e60c857a7 100644
--- a/game/modules/tome/data/zones/conclave-vault/objects.lua
+++ b/game/modules/tome/data/zones/conclave-vault/objects.lua
@@ -17,6 +17,9 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
+local Stats = require "engine.interface.ActorStats"
+local Talents = require "engine.interface.ActorTalents"
+
 load("/data/general/objects/objects-maj-eyal.lua")
 
 for i = 1, 4 do
@@ -29,20 +32,19 @@ newEntity{ base = "BASE_LORE",
 }
 end
 
-newEntity{ base = "BASE_GREATMAUL",
+newEntity{ base = "BASE_GREATMAUL", define_as = "ASTELRID_CLUBSTAFF",
 	power_source = {arcane=true},
 	name = "Astelrid's Clubstaff", color = colors.GREEN,
 	unided_name = "huge maul", unique = true,
 	desc = [[Like its former owner, this was once an instrument of altruistic healing, before fury and fear caused its twisting into a sadistic weapon.  Surges of restorative magic can be faintly felt under the layers of plaster and sharp surgical equipment.]],
-	level_range = {10, 20},
-	rarity = 300,
-	require = { stat = { str=20 }, },
+	level_range = {20, 30},
+	require = { stat = { str=23 }, },
 	cost = 650,
-	material_level = 2,
+	material_level = 3,
 	combat = {
-		dam = 32,
+		dam = 45,
 		apr = 4,
-		physcrit = 4,
+		physcrit = 8,
 		dammod = {str=1, mag=0.4},
 	},
 	wielder = {
@@ -50,5 +52,7 @@ newEntity{ base = "BASE_GREATMAUL",
 		inc_stats = {[Stats.STAT_MAG] = 4},
 		combat_spellpower = 15,
 		healing_factor = 0.25,
+		inscriptions_stat_multiplier = 0.15,
 	},
+	special_desc = function(self) return "Improves the contribution of primary stats on infusions and runes by 15%" end,
 }
diff --git a/game/modules/tome/data/zones/conclave-vault/rooms/boss.lua b/game/modules/tome/data/zones/conclave-vault/rooms/boss.lua
new file mode 100644
index 0000000000..fc9c4c33ed
--- /dev/null
+++ b/game/modules/tome/data/zones/conclave-vault/rooms/boss.lua
@@ -0,0 +1,55 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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 def = { numbers = '.',
+[[###########]],
+[[#42..#..23#]],
+[[#####.#####]],
+[[#...212...#]],
+[[#.#.....#.#]],
+[[#.........#]],
+[[#####+#####]],
+[[#####!#####]],
+}
+
+return function(gen, id)
+	local room = gen:roomParse(def)
+	return { name="conclave-ogre-final"..room.w.."x"..room.h, w=room.w, h=room.h, generator = function(self, x, y, is_lit)
+		gen:roomFrom(id, x, y, is_lit, room)
+
+		-- Everything but the entrance is special: cant have the player spawn here
+		util.squareApply(x, y, room.w, room.h-2, function(i, j) gen.map.room_map[i][j].special = true end)
+
+		local spot = room.spots[1][1]
+		local e = gen.zone:makeEntityByName(gen.level, "actor", "HEALER_ASTELRID")
+		if e then
+			gen:roomMapAddEntity(x + spot.x, y + spot.y, "actor", e)
+			gen.spots[#gen.spots+1] = {x=x+spot.x, y=y+spot.y, guardian=true, check_connectivity="entrance"}
+			e.on_added_to_level = nil
+		end
+
+		for _, spot in ipairs(room.spots[2]) do
+			local e = gen.zone:makeEntity(gen.level, "actor", {type="giant", subtype="ogre"}, nil, true)
+			if e then
+				gen:roomMapAddEntity(x + spot.x, y + spot.y, "actor", e)
+				e.on_added_to_level = nil
+			end
+		end
+	end}
+end
diff --git a/game/modules/tome/data/rooms/zones/conclave-vault-ogre-room-1.lua b/game/modules/tome/data/zones/conclave-vault/rooms/room1.lua
similarity index 83%
rename from game/modules/tome/data/rooms/zones/conclave-vault-ogre-room-1.lua
rename to game/modules/tome/data/zones/conclave-vault/rooms/room1.lua
index 324cc4eb84..f8b9220c4c 100644
--- a/game/modules/tome/data/rooms/zones/conclave-vault-ogre-room-1.lua
+++ b/game/modules/tome/data/zones/conclave-vault/rooms/room1.lua
@@ -17,29 +17,29 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
-local def = {
+local def = { numbers = '.',
 [[#!!!!!!!!!!#]],
-[[!..2....2..!]],
-[[!.#.#..#.#.!]],
+[[!.222..222.!]],
+[[!.#2#..#2#.!]],
 [[!.#1#..#1#.!]],
 [[!.###..###.!]],
 [[!..........!]],
 [[!.###..###.!]],
 [[!.#1#..#1#.!]],
-[[!.#.#..#.#.!]],
-[[!..2....2..!]],
+[[!.#2#..#2#.!]],
+[[!.222..222.!]],
 [[#!!!!!!!!!!#]],
 }
 
 return function(gen, id)
 	local room = gen:roomParse(def)
-	return { name="conclave-ogre"..room.w.."x"..room.h, w=room.w, h=room.h, generator = function(self, x, y, is_lit)
+	return { name="conclave-ogre-1"..room.w.."x"..room.h, w=room.w, h=room.h, generator = function(self, x, y, is_lit)
 
 		gen:roomFrom(id, x, y, is_lit, room)
 
 		for _, spot in ipairs(room.spots[1]) do
 			local e = gen.zone:makeEntity(gen.level, "actor", {type="giant", subtype="ogre"}, nil, true)
-			if e then gen:roomMapAddEntity(x + spot.x, y + spot.y, "actor", e) end
+			if e then gen:roomMapAddEntity(x + spot.x, y + spot.y, "actor", e) gen.map.room_map[x + spot.x][y + spot.y].special = true end
 		end
 		for _, spot in ipairs(room.spots[2]) do
 			game.level.map(x + spot.x, y + spot.y, gen.map.TRIGGER, engine.Entity.new{ on_move = function(self, x, y, who) if who and game.zone.awaken_ogres then
diff --git a/game/modules/tome/data/zones/conclave-vault/rooms/room2.lua b/game/modules/tome/data/zones/conclave-vault/rooms/room2.lua
new file mode 100644
index 0000000000..9ffecdbdc1
--- /dev/null
+++ b/game/modules/tome/data/zones/conclave-vault/rooms/room2.lua
@@ -0,0 +1,53 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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 def = { numbers = '.',
+[[###!!!!###]],
+[[###....###]],
+[[#1#....#1#]],
+[[#1#....#1#]],
+[[##222222##]],
+[[!.222222.!]],
+[[!.222222.!]],
+[[!.222222.!]],
+[[!.222222.!]],
+[[##222222##]],
+[[#1#....#1#]],
+[[#1#....#1#]],
+[[###....###]],
+[[###!!!!###]],
+}
+
+return function(gen, id)
+	local room = gen:roomParse(def)
+	return { name="conclave-ogre-2"..room.w.."x"..room.h, w=room.w, h=room.h, generator = function(self, x, y, is_lit)
+
+		gen:roomFrom(id, x, y, is_lit, room)
+
+		for _, spot in ipairs(room.spots[1]) do
+			local e = gen.zone:makeEntity(gen.level, "actor", {type="giant", subtype="ogre"}, nil, true)
+			if e then gen:roomMapAddEntity(x + spot.x, y + spot.y, "actor", e) gen.map.room_map[x + spot.x][y + spot.y].special = true end
+		end
+		for _, spot in ipairs(room.spots[2]) do
+			game.level.map(x + spot.x, y + spot.y, gen.map.TRIGGER, engine.Entity.new{ on_move = function(self, x, y, who) if who and game.zone.awaken_ogres then
+				game.zone.awaken_ogres(who, x, y, 4)
+			end end})
+		end
+	end}
+end
diff --git a/game/modules/tome/data/zones/conclave-vault/zone.lua b/game/modules/tome/data/zones/conclave-vault/zone.lua
index 8bab035b28..741fc6d6c8 100644
--- a/game/modules/tome/data/zones/conclave-vault/zone.lua
+++ b/game/modules/tome/data/zones/conclave-vault/zone.lua
@@ -19,7 +19,7 @@
 
 return {
 	name = "Old Conclave Vault",
-	level_range = {25, 35},
+	level_range = {20, 30},
 	level_scheme = "player",
 	max_level = 4,
 	decay = {300, 800, only={object=true}},
@@ -28,17 +28,18 @@ return {
 	persistent = "zone",
 --	all_remembered = true,
 	all_lited = true,
-	ambient_music = "Far away.ogg",
+	ambient_music = "Sinestra.ogg",
 	min_material_level = 2,
 	max_material_level = 3,
 	generator =  {
 		map = {
 			class = "engine.generator.map.Roomer",
 			nb_rooms = 10,
-			rooms = {"zones/conclave-vault-ogre-room-1"},
+			rooms = {"!room1", "!room2", },
 			lite_room_chance = 100,
 			['.'] = "FLOOR",
 			['#'] = "WALL",
+			['+'] = "DOOR",
 			up = "UP",
 			down = "DOWN",
 			door = "DOOR",
@@ -54,7 +55,7 @@ return {
 		},
 		trap = {
 			class = "engine.generator.trap.Random",
-			nb_trap = {0, 0},
+			nb_trap = {4, 4},
 		},
 	},
 	levels =
@@ -62,14 +63,17 @@ return {
 		[1] = {
 			generator = { map = {
 				class = "engine.generator.map.Static",
-				map = "zones/conclave-vault-entrance",
+				map = "!conclave-vault-entrance",
 			}, object = {
 				nb_object = {0, 0},
+			}, trap = {
+				nb_trap = {2, 2},
 			}},
 		},
 		[4] = {
-			generator = {
-			},
+			generator = { map = {
+				required_rooms = {"!boss"},
+			} },
 		},
 	},
 	post_process = function(level)
diff --git a/tiled-maps/conclave-vaul-entrance.tmx b/tiled-maps/conclave-vaul-entrance.tmx
index 40a6f4ba48..3ff3d4b448 100644
--- a/tiled-maps/conclave-vaul-entrance.tmx
+++ b/tiled-maps/conclave-vaul-entrance.tmx
@@ -50,7 +50,7 @@
  </tileset>
  <layer name="Terrain" width="16" height="16">
   <data encoding="base64" compression="zlib">
-   eJxjZGBgYBzFgxZbQTGt9AsCMTuSOlKwwCBw/yimDAMADdkF0w==
+   eJxjZGBgYBzFgxZbQTGt9AsiqSEVCwwC949iyjAAcw0GBg==
   </data>
  </layer>
  <objectgroup name="Actor" width="16" height="16">
-- 
GitLab