From e154f5f4a2bcb13d9272572831eff4c8713b837d Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Mon, 23 Nov 2009 18:21:31 +0000
Subject: [PATCH] class multiple inheritence soe new actor interfaces to handle
 levels, life, ...

git-svn-id: http://svn.net-core.org/repos/t-engine4@21 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engine/Actor.lua                         |  3 +-
 game/engine/Game.lua                          |  2 +
 game/engine/class.lua                         | 18 +++++-
 game/engine/generator/map/Rooms.lua           | 15 ++++-
 game/engine/interface/ActorLevel.lua          | 60 +++++++++++++++++++
 game/engine/interface/ActorLife.lua           | 29 +++++++++
 game/engine/utils.lua                         | 30 ++++++++++
 game/modules/tome/class/Actor.lua             | 37 +++---------
 game/modules/tome/class/Game.lua              |  1 +
 .../tome/data/zones/ancient_ruins/npcs.lua    |  6 +-
 .../tome/data/zones/ancient_ruins/zone.lua    |  2 +-
 11 files changed, 165 insertions(+), 38 deletions(-)
 create mode 100644 game/engine/interface/ActorLevel.lua
 create mode 100644 game/engine/interface/ActorLife.lua

diff --git a/game/engine/Actor.lua b/game/engine/Actor.lua
index 12f4f436ad..1013524301 100644
--- a/game/engine/Actor.lua
+++ b/game/engine/Actor.lua
@@ -6,7 +6,8 @@ module(..., package.seeall, class.inherit(Entity))
 
 function _M:init(t)
 	t = t or {}
-	self.name = t.name
+	self.name = t.name or "unknown npc"
+	self.level = t.level or 1
 	self.energy = t.energy or { value=0, mod=1 }
 	self.energy.value = self.energy.value or 0
 	self.energy.mod = self.energy.mod or 0
diff --git a/game/engine/Game.lua b/game/engine/Game.lua
index 0641eeb37b..05cf45dfc0 100644
--- a/game/engine/Game.lua
+++ b/game/engine/Game.lua
@@ -10,6 +10,8 @@ module(..., package.seeall, class.make)
 function _M:init(keyhandler)
 	self.key = keyhandler
 	self.level = nil
+	self.log = function() end
+	self.logSeen = function() end
 	self.w, self.h = core.display.size()
 end
 
diff --git a/game/engine/class.lua b/game/engine/class.lua
index b3d25cedcb..44bf1c4aa5 100644
--- a/game/engine/class.lua
+++ b/game/engine/class.lua
@@ -2,6 +2,14 @@ module("class", package.seeall)
 
 local base = _G
 
+local function search(k, plist)
+	for i=1, #plist do
+		local v = plist[i][k]     -- try `i'-th superclass
+		if v then return v end
+	end
+end
+
+
 function make(c)
 	setmetatable(c, {__index=_M})
 	c.new = function(...)
@@ -14,9 +22,15 @@ function make(c)
 	return c
 end
 
-function inherit(base)
+function inherit(base, ...)
+	local ifs = {...}
 	return function(c)
-		setmetatable(c, {__index=base})
+		if #ifs == 0 then
+			setmetatable(c, {__index=base})
+		else
+			table.insert(ifs, 1, base)
+			setmetatable(c, {__index=function(t, k) return search(k, ifs) end})
+		end
 		c.new = function(...)
 			local obj = {}
 			obj.__CLASSNAME = c._NAME
diff --git a/game/engine/generator/map/Rooms.lua b/game/engine/generator/map/Rooms.lua
index ebc91f6940..8ea57dcf2a 100644
--- a/game/engine/generator/map/Rooms.lua
+++ b/game/engine/generator/map/Rooms.lua
@@ -12,6 +12,7 @@ function _M:init(map, grid_list, data)
 	self.door = grid_list[data.door]
 	self.up = grid_list[data.up]
 	self.down = grid_list[data.down]
+	self.spots = {}
 end
 
 function _M:doRooms(room, no, tab)
@@ -33,6 +34,10 @@ function _M:doRooms(room, no, tab)
 			self:doRooms({ y=room.y, x=room.x + sx + 1, h=room.h, w=room.w - sx - 1}, no-1,"  "..tab)
 		end
 		self.map(room.x + sx, room.y + sy, Map.TERRAIN, self.door)
+	else
+		-- Final room, select an interresting "spot"
+		local spotx, spoty = rng.range(room.x, room.x + room.w - 1), rng.range(room.y, room.y + room.h - 1)
+		table.insert(self.spots, {x=spotx, y=spoty})
 	end
 end
 
@@ -47,7 +52,11 @@ function _M:generate()
 
 	self:doRooms({ x=1, y=1, h=self.map.h - 2, w=self.map.w - 2 }, 10, "#")
 
-	-- Always starts at 1, 1
-	self.map(1, 1, Map.TERRAIN, self.up)
-	return 1, 1
+	-- Select 2 spots, one for up and one for down, remove the up one, we dont want an actor generator to get smart
+	-- and place a monster where the player should be!
+	local up_spot = table.remove(self.spots, rng.range(1, #self.spots))
+	local down_spot = self.spots[rng.range(1, #self.spots)]
+	self.map(up_spot.x, up_spot.y, Map.TERRAIN, self.up)
+	self.map(down_spot.x, down_spot.y, Map.TERRAIN, self.down)
+	return up_spot.x, up_spot.y, self.spots
 end
diff --git a/game/engine/interface/ActorLevel.lua b/game/engine/interface/ActorLevel.lua
new file mode 100644
index 0000000000..92ba9d5df9
--- /dev/null
+++ b/game/engine/interface/ActorLevel.lua
@@ -0,0 +1,60 @@
+require "engine.class"
+
+--- Interface to add leveling capabilities to actors
+-- Defines the exp property, which is the current experience, level which is the current level and exp_worth which is a multiplicator
+-- to the monster level default exp
+module(..., package.seeall, class.make)
+
+_M.exp_chart = function(level)
+	local exp = 10
+	local mult = 10
+	for i = 2, level do
+		exp = exp + level * mult
+		mult = mult + 1
+	end
+	return exp
+end
+
+function _M:init(t)
+	self.level = t.level or 1
+	self.exp = t.exp or 0
+	self.exp_worth = t.exp_worth or 1
+end
+
+--- Defines the experience chart used
+-- Static!
+-- @param chart etiher a table of format "[level] = exp_needed" or a function that takes one parameter, a level, and returns the experience needed to reach it.
+function _M:defineExperienceChart(chart)
+	assert(type(chart) == "table" or type(chart) == "function", "chart is neither table nor function")
+	self.exp_chart = chart
+end
+
+function _M:getExpChart(level)
+	if type(self.exp_chart) == "table" then
+		return self.exp_chart[level]
+	else
+		return self.exp_chart(level)
+	end
+end
+
+--- Gains some experience
+-- If a levelup happens it calls self:levelup(), modules are encourraged to rewrite it to do whatever is needed.
+function _M:gainExp(value)
+	print("gain exp",self.exp,"+",value)
+	self.exp = self.exp + value
+	while self:getExpChart(self.level + 1) and self.exp >= self:getExpChart(self.level + 1) do
+		self.level = self.level + 1
+		self.exp = self.exp - self:getExpChart(self.level)
+		print("levelup", self.level, self.exp)
+		self:levelup()
+	end
+end
+
+--- How much experience is this actor worth
+-- @return the experience rewarded
+function _M:worthExp()
+	return self.level * self.exp_worth
+end
+
+function _M:levelup()
+end
diff --git a/game/engine/interface/ActorLife.lua b/game/engine/interface/ActorLife.lua
new file mode 100644
index 0000000000..0fa7d2eb7a
--- /dev/null
+++ b/game/engine/interface/ActorLife.lua
@@ -0,0 +1,29 @@
+require "engine.class"
+
+module(..., package.seeall, class.make)
+
+function _M:init(t)
+	self.life = t.life or 100
+end
+
+function _M:block_move(x, y, e)
+	-- Dont bump yourself!
+	if e and e ~= self then
+		e:attack(self)
+	end
+	return true
+end
+
+function _M:takeHit(value, src)
+	self.life = self.life - value
+	if self.life <= 0 then
+		game.logSeen(self, "%s killed %s!", src.name:capitalize(), self.name)
+		game.level:removeEntity(self)
+		self:die(src)
+	end
+end
+
+function _M:attack(target)
+	game.logSeen(target, "%s attacks %s.", self.name:capitalize(), target.name:capitalize())
+	target:takeHit(10, self)
+end
diff --git a/game/engine/utils.lua b/game/engine/utils.lua
index 2d13bab9e3..61bfae0d7b 100644
--- a/game/engine/utils.lua
+++ b/game/engine/utils.lua
@@ -1,3 +1,33 @@
+function string.capitalize(str)
+	if #str > 1 then
+		return string.upper(str:sub(1, 1))..str:sub(2)
+	elseif #str == 1 then
+		return str:upper()
+	else
+		return str
+	end
+end
+
+function string.bookCapitalize(str)
+	local words = str:split(' ')
+
+	for i = 1, #words do
+		local word = words[i]
+
+		-- Don't capitalize certain words unless they are at the begining
+		-- of the string.
+		if i == 1 or (word ~= "of" and word ~= "the" and word ~= "and" and word ~= "a" and word ~= "an")
+		then
+			words[i] = word:gsub("^(.)",
+							function(x)
+								return x:upper()
+							end)
+		end
+	end
+
+	return table.concat(words, " ")
+end
+
 function string.splitLine(str, max_width, font)
 	local space_w = font:size(" ")
 	local lines = {}
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 64a9c64d57..4c98adecea 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1,20 +1,14 @@
 require "engine.class"
 require "engine.Actor"
+require "engine.interface.ActorLife"
+require "engine.interface.ActorLevel"
 
-module(..., package.seeall, class.inherit(engine.Actor))
+module(..., package.seeall, class.inherit(engine.Actor, engine.interface.ActorLife, engine.interface.ActorLevel))
 
 function _M:init(t)
-	self.level = 1
-	self.life = 100
-	self.mana = 100
-	self.skills = {}
-	self.attacks = {}
 	engine.Actor.init(self, t)
-end
-
--- When saving, ignore some fields
-function _M:save()
-	return engine.Actor.save(self, {game=true})
+	engine.interface.ActorLife.init(self, t)
+	engine.interface.ActorLevel.init(self, t)
 end
 
 function _M:move(x, y, force)
@@ -26,27 +20,12 @@ function _M:move(x, y, force)
 	return moved
 end
 
-function _M:block_move(x, y, e)
-	-- Dont bump yourself!
-	if e and e ~= self then
-		game.log("%s attacks %s.", tostring(e.name), tostring(self.name))
-		self:takeHit(10, e)
-	end
-	return true
-end
-
 function _M:tooltip()
 	return self.name.."\n#ff0000#HP: "..self.life
 end
 
-function _M:takeHit(value, src)
-	self.life = self.life - value
-	if self.life <= 0 then
-		game.log("%s killed %s!", src.name, self.name)
-		game.level:removeEntity(self)
-		self:die()
+function _M:die(src)
+	if src then
+		src:gainExp(self:worthExp())
 	end
 end
-
-function _M:die()
-end
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 259eacc8e7..7ce7882f38 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -29,6 +29,7 @@ function _M:run()
 
 	self.log = engine.LogDisplay.new(self.w * 0.5, self.h * 0.20, nil, nil, nil, {255,255,255}, {30,30,30})
 	self.log("Welcome to #00FF00#Tales of Middle Earth!")
+	self.logSeen = function(e, ...) if e and self.level.map.seens(e.x, e.y) then self.log(...) end end
 
 	self.zone:getLevel(self, 1)
 
diff --git a/game/modules/tome/data/zones/ancient_ruins/npcs.lua b/game/modules/tome/data/zones/ancient_ruins/npcs.lua
index 53bfb522fd..7fd8183206 100644
--- a/game/modules/tome/data/zones/ancient_ruins/npcs.lua
+++ b/game/modules/tome/data/zones/ancient_ruins/npcs.lua
@@ -3,14 +3,16 @@ return {
 {
 	name = "dragon of death",
 	display = "D", color_r=255,
-	life = 1000,
+	level = 1, exp_worth = 20,
+	life = 20,
 	mana = 1000,
 	energy = { mod=0.8 },
 },
 {
 	name = "baby dragon",
 	display = "d", color_r=128,
-	life = 1000,
+	level = 1, exp_worth = 10,
+	life = 30,
 	mana = 1000,
 	energy = { mod=0.3 },
 },
diff --git a/game/modules/tome/data/zones/ancient_ruins/zone.lua b/game/modules/tome/data/zones/ancient_ruins/zone.lua
index 7b0e31eefd..27bc2ee98d 100644
--- a/game/modules/tome/data/zones/ancient_ruins/zone.lua
+++ b/game/modules/tome/data/zones/ancient_ruins/zone.lua
@@ -6,7 +6,7 @@ return {
 	all_lited = true,
 	generator =  {
 		map = {
-			class= "engine.generator.map.Rooms",
+			class= "engine.generator.map.Empty",
 			floor = "FLOOR",
 			wall = "WALL",
 			up = "UP",
-- 
GitLab