From ebc359ad77bd8eaff3a59cc6357c6aa5049bcc17 Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Tue, 15 Nov 2016 17:49:40 +0100
Subject: [PATCH] new ID challenge: headhunter

---
 game/modules/tome/class/GameState.lua         | 88 ++++++++++++++++---
 .../modules/tome/data/timed_effects/other.lua |  2 +-
 2 files changed, 78 insertions(+), 12 deletions(-)

diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua
index 6354cbfcc1..ed4c382112 100644
--- a/game/modules/tome/class/GameState.lua
+++ b/game/modules/tome/class/GameState.lua
@@ -2475,13 +2475,14 @@ function _M:infiniteDungeonChallenge(zone, lev, data, id_layout_name, id_grids_n
 	self.id_challenge.count = self.id_challenge.count + 1
 
 	local challenges = {
-		{ id = "pacifist", rarity = 3 },
 		{ id = "exterminator", rarity = 1 },
-		{ id = "dream-horror", rarity = 10, min_lev = 15 },
+		{ id = "pacifist", rarity = 3 },
 		{ id = "fast-exit", rarity = 3, min_lev = 8 },
-		{ id = "near-sighted", rarity = 3, min_lev = 4 },
-		{ id = "mirror-match", rarity = 9, min_lev = 5 },
+		{ id = "near-sighted", rarity = 4, min_lev = 4 },
+		{ id = "mirror-match", rarity = 6, min_lev = 5 },
 		{ id = "multiplicity", rarity = 8, min_lev = 10 },
+		{ id = "dream-horror", rarity = 10, min_lev = 15 },
+		{ id = "headhunter", rarity = 12, min_lev = 12 },
 	}
 	
 	self:triggerHook{"InfiniteDungeon:getChallenges", challenges=challenges}
@@ -2505,6 +2506,7 @@ function _M:makeChallengeQuest(level, name, desc, data, alter_effect)
 		desc = function(self, who)
 			local desc = {}
 			desc[#desc+1] = self.challenge_desc
+			if self.dynamic_desc then self:dynamic_desc(desc, who) end
 			if self.reward_desc then desc[#desc+1] = "\nYou completed the challenge and received:\n"..self.reward_desc end
 			return table.concat(desc, "\n")
 		end,
@@ -2559,6 +2561,15 @@ function _M:infiniteDungeonChallengeFinish(zone, level)
 		})
 	elseif id_challenge == "exterminator" then
 		self:makeChallengeQuest(level, "Exterminator", "Exterminate every foe on the level.", {
+			dynamic_desc = function(self, desc, who)
+				if not self.check_level then return end
+				local nb = 0
+				for uid, e in pairs(self.check_level.entities) do
+					if who:reactionToward(e) < 0 then nb = nb + 1 end
+				end
+
+				desc[#desc+1] = "Foes left: #LIGHT_RED#"..nb
+			end,
 			on_exit_check = function(self, who)
 				if not self.check_level then return end
 				local nb = 0
@@ -2585,12 +2596,8 @@ function _M:infiniteDungeonChallengeFinish(zone, level)
 			local turns = #path * 3
 			self:makeChallengeQuest(level, "Rush Hour ("..turns..")", "Leave the level in less than "..turns.." turns (exit is revealed on your map).", {
 				turns_left = turns,
-				desc = function(self, who)
-					local desc = {}
-					desc[#desc+1] = self.challenge_desc
-					desc[#desc+1] = ""
+				dynamic_desc = function(self, desc)
 					desc[#desc+1] = "Turns left: #LIGHT_GREEN#"..self.turns_left
-					return table.concat(desc, "\n")
 				end,
 				on_exit_check = function(self, who)
 					if self.turns_left >= 0 then who:setQuestStatus(self.id, self.COMPLETED) end
@@ -2609,7 +2616,7 @@ function _M:infiniteDungeonChallengeFinish(zone, level)
 		if m then
 			local x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
 			local tries = 0
-			while not m:canMove(x, y) and tries < 100 do
+			while not m:canMove(x, y) and tries < 100 and not level.map.attrs(x, y, "no_teleport") do
 				x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
 				tries = tries + 1
 			end
@@ -2625,7 +2632,7 @@ function _M:infiniteDungeonChallengeFinish(zone, level)
 	elseif id_challenge == "mirror-match" then
 		local x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
 		local tries = 0
-		while not game.player:canMove(x, y) and tries < 100 do
+		while not game.player:canMove(x, y) and tries < 100 and not level.map.attrs(x, y, "no_teleport") do
 			x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
 			tries = tries + 1
 		end
@@ -2709,6 +2716,57 @@ function _M:infiniteDungeonChallengeFinish(zone, level)
 				end
 			end)
 		end end, "Refuse", "Accept", true)
+	elseif id_challenge == "headhunter" then
+		local mlist = {}
+		for i = 1, 1 do
+			local m = zone:makeEntity(level, "actor", {type="demon", random_boss={
+				nb_classes = 2,
+				rank=3.5, ai = "tactical",
+				life_rating=function(v) return v * 1.5 + 5 end,
+				loot_quantity = 2,
+				no_loot_randart = true,
+				name_scheme = "#rng# the Spawn of Urh'Rok",
+			}}, nil, true)
+			if m then
+				local x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
+				local tries = 0
+				while not m:canMove(x, y) and tries < 100 and not level.map.attrs(x, y, "no_teleport") do
+					x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
+					tries = tries + 1
+				end
+				if tries < 100 then
+					m.is_headhunter_npc = true
+					zone:addEntity(level, m, "actor", x, y)
+					mlist[#mlist+1] = m
+				end
+			end
+		end
+		if #mlist > 0 then
+			Dialog:yesnoPopup("Challenge: #PURPLE#Headhunter", "Kill all "..#mlist.." spawns of Urh'Rok on the level and no other creatures, for a reward.", function(r)
+				if not r then
+					local q = self:makeChallengeQuest(level, "Headhunter", "Kill all "..#mlist.." spawns of Urh'Rok on the level and no other creatures.", {
+						dynamic_desc = function(self, desc)
+							desc[#desc+1] = ("%d / %d spawns killed."):format(self.nb_killed, self.to_kill)
+						end,
+						nb_killed = 0, to_kill = #mlist,
+						on_kill_foe = function(self, who, target)
+							if self:isEnded() then return end
+							if target.is_headhunter_npc then
+								self.nb_killed = self.nb_killed + 1
+								if self.nb_killed >= self.to_kill then
+									who:setQuestStatus(self.id, self.COMPLETED)
+								end
+							else
+								who:setQuestStatus(self.id, self.FAILED)
+							end
+						end,
+						forbid_rewards = {"randart", "stat_pts", "generic_pt"},
+					})
+				else
+					for _, m in ipairs(mlist) do m:disappear() m:removed() end
+				end
+			end, "Refuse", "Accept", true)
+		end
 	else
 		self:triggerHook{"InfiniteDungeon:setupChallenge", id_challenge=id_challenge, zone=zone, level=level}
 	end
@@ -2751,6 +2809,14 @@ function _M:infiniteDungeonChallengeReward(quest, who)
 	}
 	self:triggerHook{"InfiniteDungeon:getRewards", rewards=rewards}
 
+	if quest.forbid_rewards then
+		for _, f in ipairs(quest.forbid_rewards) do
+			for i, r in ipairs(rewards) do
+				if r.id == f then table.remove(rewards, i) break end
+			end
+		end
+	end
+
 	local reward = rng.rarityTable(rewards)
 	reward.name = reward.give(who) or reward.name
 	self.id_challenge.rewarded[reward.id] = (self.id_challenge.rewarded[reward.id] or 0) + 1
diff --git a/game/modules/tome/data/timed_effects/other.lua b/game/modules/tome/data/timed_effects/other.lua
index 1dd999ba50..52da72ffbe 100644
--- a/game/modules/tome/data/timed_effects/other.lua
+++ b/game/modules/tome/data/timed_effects/other.lua
@@ -3061,7 +3061,7 @@ newEffect{
 	end,
 	callbackOnKill = function(self, eff, who, death_note)
 		if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return end
-		self:hasQuest(eff.id_challenge_quest):check("on_kill_foe", self)
+		self:hasQuest(eff.id_challenge_quest):check("on_kill_foe", self, who)
 	end,
 	callbackOnActBase = function(self, eff)
 		if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return end
-- 
GitLab