From 6e8ee7bfcef43660209dc873ca8184b61966595d Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Fri, 22 Apr 2011 20:01:05 +0000
Subject: [PATCH] Random parties of (hostile) adventurers can be met on the
 worldmap

git-svn-id: http://svn.net-core.org/repos/t-engine4@3261 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/modules/tome/class/Actor.lua             |  2 +-
 game/modules/tome/class/GameState.lua         | 46 ++++++++-----
 .../data/general/encounters/maj-eyal-npcs.lua | 25 +++++++
 game/modules/tome/data/general/npcs/all.lua   |  2 +
 .../general/npcs/humanoid_random_boss.lua     | 66 +++++++++++++++++++
 5 files changed, 125 insertions(+), 16 deletions(-)
 create mode 100644 game/modules/tome/data/general/npcs/humanoid_random_boss.lua

diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index bcde5b5b8c..cd435792f0 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1351,7 +1351,7 @@ function _M:die(src)
 	if src and src.attr and src:attr("vim_on_death") and not self:attr("undead") then src:incVim(src:attr("vim_on_death")) end
 	if src and src.last_vim_turn and src.last_vim_turn >= game.turn - 30 then src:incVim(src.last_vim_spent) src.last_vim_turn = nil end
 
-	if src and src.resolveSource and src:resolveSource().player or src.player then
+	if src and ((src.resolveSource and src:resolveSource().player) or src.player) then
 		-- Achievements
 		local p = game.party:findMember{main=true}
 		if math.floor(p.life) <= 1 and not p.dead then world:gainAchievement("THAT_WAS_CLOSE", p) end
diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua
index 1787940cf9..0ff38d328d 100644
--- a/game/modules/tome/class/GameState.lua
+++ b/game/modules/tome/class/GameState.lua
@@ -962,7 +962,10 @@ end
 function _M:entityFilterPost(zone, level, type, e, filter)
 	if type == "actor" then
 		if filter.random_boss then
-			e = self:createRandomBoss(e, zone:level_adjust_level(level, zone, type))
+			if _G.type(filter.random_boss) == "boolean" then filter.random_boss = {}
+			else filter.random_boss = table.clone(filter.random_boss, true) end
+			filter.random_boss.level = filter.random_boss.level or zone:level_adjust_level(level, zone, type)
+			e = self:createRandomBoss(e, filter.random_boss)
 		end
 	end
 
@@ -1209,7 +1212,7 @@ function _M:createRandomZone(zbase)
 	return zone, boss
 end
 
-function _M:createRandomBoss(base, level)
+function _M:createRandomBoss(base, data)
 	local b = base:clone()
 
 	------------------------------------------------------------
@@ -1221,10 +1224,14 @@ function _M:createRandomBoss(base, level)
 	b.unique = b.name
 	local boss_id = "RND_BOSS_"..b.name:upper():gsub("[^A-Z]", "_")
 	b.color = colors.VIOLET
-	b.rank = rng.percent(30) and 4 or 3.5
-	b.level_range[1] = level
+	b.rank = data.rank or (rng.percent(30) and 4 or 3.5)
+	b.level_range[1] = data.level
 	b.fixed_rating = true
-	b.life_rating = b.life_rating * 1.7 + rng.range(4, 9)
+	if data.life_rating then
+		b.life_rating = data.life_rating(b.life_rating)
+	else
+		b.life_rating = b.life_rating * 1.7 + rng.range(4, 9)
+	end
 	b.max_life = b.max_life or 150
 
 	-- Force resolving some stuff
@@ -1255,8 +1262,8 @@ function _M:createRandomBoss(base, level)
 	for _, k in ipairs(todel) do b[k] = nil end
 
 	-- Boss worthy drops
-	b[#b+1] = resolvers.drops{chance=100, nb=3, {tome_drops="boss"} }
-	b[#b+1] = resolvers.drop_randart{}
+	b[#b+1] = resolvers.drops{chance=100, nb=data.loot_quantity or 3, {tome_drops=data.loot_quality or "boss"} }
+	if not data.no_loot_randart then b[#b+1] = resolvers.drop_randart{} end
 
 	------------------------------------------------------------
 	-- Apply talents from classes
@@ -1286,7 +1293,7 @@ function _M:createRandomBoss(base, level)
 				for i, d in ipairs(resolver[1]) do
 					d.name = nil
 					d.ego_chance = nil
-					d.tome_drops = "boss"
+					d.tome_drops = data.loot_quality or "boss"
 					d.force_drop = true
 				end
 				b[#b+1] = resolver
@@ -1304,14 +1311,14 @@ function _M:createRandomBoss(base, level)
 			if not t.no_npc_use then
 				local max = (t.points == 1) and 1 or math.ceil(t.points * 1.6)
 				local step = max / 50
-				tres[1][tid] = v + math.ceil(step * level)
+				tres[1][tid] = v + math.ceil(step * data.level)
 			end
 		end
 
 		-- Select additional talents from the class
 		local list = {}
 		for _, t in pairs(b.talents_def) do if b.talents_types[t.type[1]] and not t.no_npc_use then list[t.id] = true end end
-		local nb = 4 + (level / 7)
+		local nb = 4 + (data.level / 7)
 		nb = math.max(rng.range(math.floor(nb * 0.7), math.ceil(nb * 1.3)), 1)
 		print("Adding "..nb.." random class talents to boss")
 
@@ -1322,7 +1329,7 @@ function _M:createRandomBoss(base, level)
 				print(" * talent", tid)
 				local max = (t.points == 1) and 1 or math.ceil(t.points * 1.6)
 				local step = max / 50
-				b.learn_tids[tid] = math.ceil(step * level)
+				b.learn_tids[tid] = math.ceil(step * data.level)
 			end
 		end
 	end
@@ -1332,10 +1339,11 @@ function _M:createRandomBoss(base, level)
 	local classes = Birther.birth_descriptor_def.subclass
 	local list = {}
 	for name, data in pairs(classes) do if not data.not_on_random_boss then list[#list+1] = data end end
-	local c1, c2 = rng.tableRemove(list), rng.tableRemove(list)
-
-	apply_class(table.clone(c1, true))
-	apply_class(table.clone(c2, true))
+	for i = 1, data.nb_classes or 2 do
+		local c = rng.tableRemove(list)
+		if not c then break end
+		apply_class(table.clone(c, true))
+	end
 
 	b.rnd_boss_on_added_to_level = b.on_added_to_level
 	b.on_added_to_level = function(self, ...)
@@ -1347,6 +1355,14 @@ function _M:createRandomBoss(base, level)
 		self.rnd_boss_on_added_to_level = nil
 		self.learn_tids = nil
 		self.on_added_to_level = nil
+
+		-- Cheat a bit with ressources
+		self.max_mana = self.max_mana * 3 self.mana_regen = self.mana_regen + 1
+		self.max_vim = self.max_vim * 3 self.vim_regen = self.vim_regen + 1
+		self.max_stamina = self.max_stamina * 3 self.stamina_regen = self.stamina_regen + 1
+		self.max_psi = self.max_psi * 3 self.psi_regen = self.psi_regen + 2
+		self.equilibrium_regen = self.equilibrium_regen - 2
+		self:resetToFull()
 	end
 
 	return b, boss_id
diff --git a/game/modules/tome/data/general/encounters/maj-eyal-npcs.lua b/game/modules/tome/data/general/encounters/maj-eyal-npcs.lua
index 5ded3b2f11..caf07ac2c2 100644
--- a/game/modules/tome/data/general/encounters/maj-eyal-npcs.lua
+++ b/game/modules/tome/data/general/encounters/maj-eyal-npcs.lua
@@ -78,3 +78,28 @@ newEntity{
 	ai = "world_hostile", ai_state = {chase_distance=3},
 	on_encounter = {type="ambush", width=10, height=10, nb={1,1}, filters={{type="dragon"}}},
 }
+
+newEntity{
+	name = "adventurers party",
+	type = "hostile", subtype = "humanoid",
+	display = '@', color = colors.UMBER,
+	level_range = {14, nil},
+	sight = 1,
+	rarity = 12,
+	unit_power = 14,
+	ai = "world_hostile", ai_state = {chase_distance=3},
+	on_encounter = {
+		type="ambush",
+		width=14,
+		height=14,
+		nb={2, 4},
+		filters={{special_rarity="humanoid_random_boss", random_boss={
+			nb_classes=1,
+			rank=3,
+			life_rating=function(v) return v * 1.3 + 2 end,
+			loot_quality = "store",
+			loot_quantity = 1,
+			no_loot_randart = true,
+		}}}
+	},
+}
diff --git a/game/modules/tome/data/general/npcs/all.lua b/game/modules/tome/data/general/npcs/all.lua
index a7fe14e3ad..f01de81f5e 100644
--- a/game/modules/tome/data/general/npcs/all.lua
+++ b/game/modules/tome/data/general/npcs/all.lua
@@ -80,6 +80,8 @@ loadIfNot("/data/general/npcs/vermin.lua")
 loadIfNot("/data/general/npcs/wight.lua")
 loadIfNot("/data/general/npcs/xorn.lua")
 
+loadIfNot("/data/general/npcs/humanoid_random_boss.lua")
+
 -- Select some random dominant ones for random zones
 if random_zone_mode then
 	local nt = {}
diff --git a/game/modules/tome/data/general/npcs/humanoid_random_boss.lua b/game/modules/tome/data/general/npcs/humanoid_random_boss.lua
new file mode 100644
index 0000000000..f35e502e3f
--- /dev/null
+++ b/game/modules/tome/data/general/npcs/humanoid_random_boss.lua
@@ -0,0 +1,66 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010, 2011 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
+
+-- Those are never generated randomly, but used when we want humanoid random bosses
+
+newEntity{
+	define_as = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	type = "humanoid", subtype = "human",
+	display = "p", color=colors.UMBER,
+
+	level_range = {1, nil},
+	infravision = 20,
+	lite = 1,
+
+	life_rating = 10,
+	rank = 3,
+	size_category = 3,
+
+	open_door = true,
+}
+
+newEntity{ base = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	name = "human", subtype = "human", color=colors.LIGHT_UMBER,
+	humanoid_random_boss = 1,
+	resolvers.racial(),
+}
+
+newEntity{ base = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	name = "thalore", subtype = "thalore", color=colors.LIGHT_GREEN,
+	humanoid_random_boss = 1,
+	resolvers.racial(),
+}
+
+newEntity{ base = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	name = "shalore", subtype = "shalore", color=colors.LIGHT_BLUE,
+	humanoid_random_boss = 1,
+	resolvers.racial(),
+}
+
+newEntity{ base = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	name = "halflint", subtype = "halfling", color=colors.BLUE,
+	humanoid_random_boss = 1,
+	resolvers.racial(),
+}
+
+newEntity{ base = "BASE_NPC_HUMANOID_RANDOM_BOSS",
+	name = "dwarf", subtype = "dwarf", color=colors.UMBER,
+	humanoid_random_boss = 2,
+	resolvers.racial(),
+}
-- 
GitLab