From 13dbd5d1c09f9eaf45e99d7a9acd70c360a1678a Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Thu, 24 Dec 2009 00:47:30 +0000
Subject: [PATCH] drops; multi-floor pickup dialog

git-svn-id: http://svn.net-core.org/repos/t-engine4@145 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engine/Map.lua                           | 27 ++++++
 game/engine/Zone.lua                          |  7 +-
 game/engine/dialogs/ShowPickupFloor.lua       | 97 +++++++++++++++++++
 game/engine/interface/ActorInventory.lua      | 14 ++-
 game/modules/tome/class/Actor.lua             | 11 +++
 game/modules/tome/class/Game.lua              |  9 +-
 .../tome/data/zones/ancient_ruins/npcs.lua    |  3 +-
 game/modules/tome/resolvers.lua               | 26 +++++
 8 files changed, 186 insertions(+), 8 deletions(-)
 create mode 100644 game/engine/dialogs/ShowPickupFloor.lua

diff --git a/game/engine/Map.lua b/game/engine/Map.lua
index ac2268107f..069acf48d9 100644
--- a/game/engine/Map.lua
+++ b/game/engine/Map.lua
@@ -460,4 +460,31 @@ function _M:addObject(x, y, o)
 	while self(x, y, i) do i = i + 1 end
 	-- Fill it
 	self(x, y, i, o)
+	return true
+end
+
+function _M:getObject(x, y, i)
+	-- Compute the map stack position
+	i = i - 1 + self.OBJECT
+	return self(x, y, i)
+end
+
+function _M:removeObject(x, y, i)
+	-- Compute the map stack position
+	i = i - 1 + self.OBJECT
+	if not self(x, y, i) then return false end
+	-- Remove it
+	self:remove(x, y, i)
+	-- Move the last one to its position, to never get a "hole"
+	local j = i + 1
+	while self(x, y, j) do j = j + 1 end
+	j = j - 1
+	-- If the removed one was not the last
+	if j > i then
+		local o = self(x, y, j)
+		self:remove(x, y, j)
+		self(x, y, i, o)
+	end
+
+	return true
 end
diff --git a/game/engine/Zone.lua b/game/engine/Zone.lua
index d1cf158c25..75fd6a4d1d 100644
--- a/game/engine/Zone.lua
+++ b/game/engine/Zone.lua
@@ -142,18 +142,18 @@ function _M:makeEntity(level, type, filter)
 	end
 	if tries == 0 then return nil end
 
-	e = self:finishEntity(level, type, e)
+	e = self:finishEntity(level, type, e, filter and filter.ego_chance)
 
 	return e
 end
 
 --- Finishes generating an entity
-function _M:finishEntity(level, type, e)
+function _M:finishEntity(level, type, e, ego_chance)
 	e = e:clone()
 	e:resolve()
 
 	-- Add "ego" properties, sometimes
-	if e.egos and e.egos_chance and rng.percent(e.egos_chance) then
+	if e.egos and e.egos_chance and rng.percent(e.egos_chance + (ego_chance or 0)) then
 		local egos = self:getEgosList(level, type, e.egos, e.__CLASSNAME)
 		local ego = self:pickEntity(egos)
 		if ego then
@@ -169,6 +169,7 @@ function _M:finishEntity(level, type, e)
 			print("applying ego", ego.name, "to ", e.name, "::", newname)
 			table.merge(e, ego, true)
 			e.name = newname
+			e.egoed = true
 		end
 	end
 	return e
diff --git a/game/engine/dialogs/ShowPickupFloor.lua b/game/engine/dialogs/ShowPickupFloor.lua
new file mode 100644
index 0000000000..260c67dec8
--- /dev/null
+++ b/game/engine/dialogs/ShowPickupFloor.lua
@@ -0,0 +1,97 @@
+require "engine.class"
+require "engine.Dialog"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(title, x, y, filter, action)
+	self.x, self.y = x, y
+	self.filter = filter
+	self.action = action
+	engine.Dialog.init(self, title or "Pickup", game.w * 0.8, game.h * 0.8)
+
+	self:generateList()
+
+	self.sel = 1
+	self:keyCommands{
+		_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) end,
+		_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) end,
+		_RETURN = function() self:use() end,
+		_ESCAPE = function() game:unregisterDialog(self) end,
+		_ASTERISK = function() while self:use() do end end,
+		__TEXTINPUT = function(c)
+			if c:find("^[a-z]$") then
+				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
+				self:use()
+			end
+		end,
+	}
+	self:mouseZones{
+		{ x=2, y=5, w=350, h=self.font_h*#self.list, fct=function(button, x, y, xrel, yrel, tx, ty)
+			self.sel = util.bound(1 + math.floor(ty / self.font_h), 1, #self.list)
+			if button == "left" then self:use()
+			elseif button == "right" then
+			end
+		end },
+	}
+end
+
+function _M:use()
+	if self.list[self.sel] then
+		self.action(self.list[self.sel].object, self.list[self.sel].item)
+	end
+	self:generateList()
+	if #self.list == 0 then
+		game:unregisterDialog(self)
+		return false
+	end
+	return true
+end
+
+function _M:generateList()
+	-- Makes up the list
+	local list = {}
+	local idx = 1
+	local i = 1
+	while true do
+		local o = game.level.map:getObject(self.x, self.y, idx)
+		if not o then break end
+		if not self.filter or self.filter(o) then
+			list[#list+1] = { name=string.char(string.byte('a') + i)..")  "..o:getName(), object=o, item=idx }
+			i = i + 1
+		end
+		idx = idx + 1
+	end
+	self.list = list
+	self.sel = 1
+end
+
+function _M:drawDialog(s)
+	-- Description part
+	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
+
+	local talentshelp = ([[Keyboard: #00FF00#up key/down key#FFFFFF# to select an object; #00FF00#enter#FFFFFF# to use.
+Mouse: #00FF00#Left click#FFFFFF# to pickup.
+]]):splitLines(self.iw / 2 - 10, self.font)
+
+	local lines = {}
+	local h = 2
+	for i = 1, #talentshelp do
+		s:drawColorString(self.font, talentshelp[i], self.iw / 2 + 5, h)
+		h = h + self.font:lineSkip()
+	end
+
+	h = h + self.font:lineSkip()
+	if self.list[self.sel] then
+		lines = self.list[self.sel].object:getDesc():splitLines(self.iw / 2 - 10, self.font)
+	else
+		lines = {}
+	end
+	self:drawWBorder(s, self.iw / 2 + self.iw / 6, h - 0.5 * self.font:lineSkip(), self.iw / 6)
+	for i = 1, #lines do
+		s:drawColorString(self.font, lines[i], self.iw / 2 + 5, 2 + h)
+		h = h + self.font:lineSkip()
+	end
+
+	-- Talents
+	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name")
+end
diff --git a/game/engine/interface/ActorInventory.lua b/game/engine/interface/ActorInventory.lua
index 278272593b..42205ae03d 100644
--- a/game/engine/interface/ActorInventory.lua
+++ b/game/engine/interface/ActorInventory.lua
@@ -2,6 +2,7 @@ require "engine.class"
 local Map = require "engine.Map"
 local ShowInventory = require "engine.dialogs.ShowInventory"
 local ShowEquipment = require "engine.dialogs.ShowEquipment"
+local ShowPickupFloor = require "engine.dialogs.ShowPickupFloor"
 
 --- Handles actors stats
 module(..., package.seeall, class.make)
@@ -69,11 +70,10 @@ end
 
 --- Picks an object from the floor
 function _M:pickupFloor(i, vocal)
-	i = i - 1 + Map.OBJECT
-	local o = game.level.map(self.x, self.y, i)
+	local o = game.level.map:getObject(self.x, self.y, i)
 	if o then
 		if self:addObject(self.INVEN_INVEN, o) then
-			game.level.map:remove(self.x, self.y, i)
+			game.level.map:removeObject(self.x, self.y, i)
 
 			if vocal then game.logSeen(self, "%s picks up: %s.", self.name:capitalize(), o:getName()) end
 		else
@@ -130,6 +130,14 @@ function _M:showEquipment(title, filter, action)
 	game:registerDialog(d)
 end
 
+--- Show floor pickup dialog
+-- @param filter nil or a function that filters the objects to list
+-- @param action a function called when an object is selected
+function _M:showPickupFloor(title, filter, action)
+	local d = ShowPickupFloor.new(title, self.x, self.y, filter, action)
+	game:registerDialog(d)
+end
+
 --- Wear/wield an item
 function _M:wearObject(o, replace, vocal)
 	local inven = o:wornInven()
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index e43feffabb..6f748a3bef 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -125,6 +125,17 @@ function _M:die(src)
 	end
 	-- Do we get a blooooooody death ?
 	if rng.percent(33) then self:bloodyDeath() end
+
+	-- Drop stuff
+	for inven_id, inven in pairs(self.inven) do
+		for i, o in ipairs(inven) do
+			if not o.no_drop then
+				game.level.map:addObject(self.x, self.y, o)
+			end
+		end
+	end
+	self.inven = {}
+
 	return true
 end
 
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index fd21bafb4c..878887b39d 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -295,7 +295,14 @@ function _M:setupCommands()
 
 		-- Pickup object
 		_g = function()
-			self.player:pickupFloor(1, true)
+			-- If 2 or more objects, display a pickup dialog, otehrwise just picks up
+			if self.level.map:getObject(self.player.x, self.player.y, 2) then
+				self.player:showPickupFloor(nil, nil, function(o, item)
+					self.player:pickupFloor(item, true)
+				end)
+			else
+				self.player:pickupFloor(1, true)
+			end
 		end,
 
 		-- Show inventory
diff --git a/game/modules/tome/data/zones/ancient_ruins/npcs.lua b/game/modules/tome/data/zones/ancient_ruins/npcs.lua
index 1ed80a2a48..1815be1235 100644
--- a/game/modules/tome/data/zones/ancient_ruins/npcs.lua
+++ b/game/modules/tome/data/zones/ancient_ruins/npcs.lua
@@ -72,7 +72,8 @@ newEntity{
 		BODY = 1, HEAD = 1, HANDS = 1, FEET = 1,
 		TOOL = 1,
 	},
-	equipment = resolvers.equip{ {type="weapon", subtype="longsword"},  {type="armor", subtype="massive"}, {type="armor", subtype="shield"}, },
+	equipment = resolvers.equip{ {type="weapon", subtype="longsword"}, {type="armor", subtype="massive"}, {type="armor", subtype="shield"}, },
+	drops = resolvers.drops{chance=100, nb=3, {ego_chance=100} },
 
 	stats = { str=14, dex=12, mag=8, con=13 },
 	talents = { },
diff --git a/game/modules/tome/resolvers.lua b/game/modules/tome/resolvers.lua
index 1435156eab..dbeee886ab 100644
--- a/game/modules/tome/resolvers.lua
+++ b/game/modules/tome/resolvers.lua
@@ -11,6 +11,32 @@ function resolvers.calc.equip(t, e)
 		if o then
 			print("Zone made us an equipment according to filter!", o:getName())
 			e:wearObject(o, true, false)
+
+			-- Do not drop it unless it is an ego or better
+			if not o.egoed and not o.unique then o.no_drop = true end
+		end
+	end
+	-- Delete the origin field
+	return nil
+end
+
+--- Resolves drops creation for an actor
+function resolvers.drops(t)
+	return {__resolver="drops", t}
+end
+--- Actually resolve the drops creation
+function resolvers.calc.drops(t, e)
+	t = t[1]
+	if not rng.percent(t.chance) then return nil end
+
+	-- Iterate of object requests, try to create them and drops them
+	for i = 1, (t.nb or 1) do
+		local filter = t[rng.range(1, #t)]
+		print("Drops resolver", filter.type, filter.subtype)
+		local o = game.zone:makeEntity(game.level, "object", filter)
+		if o then
+			print("Zone made us an drop according to filter!", o:getName())
+			e:addObject(e.INVEN_INVEN, o)
 		end
 	end
 	-- Delete the origin field
-- 
GitLab