From acda0b339c9ae6f45896dfaf7d9dad34fabf95f7 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sun, 29 Nov 2009 14:11:49 +0000
Subject: [PATCH] talents

git-svn-id: http://svn.net-core.org/repos/t-engine4@70 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engine/interface/ActorStats.lua          |  11 ++
 game/engine/interface/ActorTalents.lua        |  31 +++-
 game/modules/tome/class/Game.lua              |   4 +-
 game/modules/tome/data/talents.lua            |  12 +-
 .../tome/dialogs/LevelupStatsDialog.lua       |   4 +
 .../tome/dialogs/LevelupTalentsDialog.lua     | 134 +++++++++++++-----
 6 files changed, 149 insertions(+), 47 deletions(-)

diff --git a/game/engine/interface/ActorStats.lua b/game/engine/interface/ActorStats.lua
index 24a826cbfd..022642030b 100644
--- a/game/engine/interface/ActorStats.lua
+++ b/game/engine/interface/ActorStats.lua
@@ -70,6 +70,17 @@ function _M:getStat(stat, scale)
 	return val
 end
 
+--- Is the stat maxed ?
+function _M:isStatMax(stat)
+	local val
+	if type(stat) == "string" then
+		val = self.stats[_M.stats_def[stat].id]
+	else
+		val = self.stats[stat]
+	end
+	if math.floor(val) == _M.stats_def[stat].max then return true end
+end
+
 --- Notifies a change of stat value
 function _M:onStatChange(stat, v)
 end
diff --git a/game/engine/interface/ActorTalents.lua b/game/engine/interface/ActorTalents.lua
index 08e3f685aa..3757eb5c35 100644
--- a/game/engine/interface/ActorTalents.lua
+++ b/game/engine/interface/ActorTalents.lua
@@ -23,6 +23,8 @@ end
 function _M:newTalentType(t)
 	assert(t.name, "no talent type name")
 	assert(t.type, "no talent type type")
+	t.description = t.description or ""
+	t.points = t.points or 1
 	t.talents = {}
 	table.insert(self.talents_types_def, t)
 	self.talents_types_def[t.type] = t
@@ -38,6 +40,7 @@ function _M:newTalent(t)
 	t.mana = t.mana or 0
 	t.stamina = t.stamina or 0
 	t.mode = t.mode or "activated"
+	t.points = t.points or 1
 	assert(t.mode == "activated" or t.mode == "sustained", "wrong talent mode, requires either 'activated' or 'sustained'")
 	assert(t.info, "no talent info")
 
@@ -120,6 +123,14 @@ function _M:learnTalent(t_id)
 	return true
 end
 
+--- Actor forgets a talent
+-- @param t_id the id of the talent to learn
+-- @return true if the talent was unlearnt, nil and an error message otherwise
+function _M:unlearnTalent(t_id)
+	self.talents[t_id] = nil
+	return true
+end
+
 function _M:canLearnTalent(t)
 	-- Check prerequisites
 	if t.require then
@@ -136,7 +147,7 @@ function _M:canLearnTalent(t)
 
 	-- Check talent type
 	local known = self:numberKnownTalent(t.type[1])
-	if known < t.type[1] - 1 then
+	if known < t.type[2] - 1 then
 		return nil, "not enough talents of this type known"
 	end
 
@@ -145,11 +156,21 @@ function _M:canLearnTalent(t)
 end
 
 --- Do we know this talent type
-function _M:knowTalentType(t)
-	return self.talents_types[t]
+function _M:knowTalentType(name)
+	return self.talents_types[name]
 end
 
 --- Do we know this talent
-function _M:knowTalent(t)
-	return self.talents[t]
+function _M:knowTalent(id)
+	return self.talents[id] and true or false
+end
+
+--- Return talent definition from id
+function _M:getTalentFromId(id)
+	return _M.talents_def[id]
+end
+
+--- Return talent definition from id
+function _M:getTalentTypeFrom(id)
+	return _M.talents_types_def[id]
 end
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index ec2f15108e..76875d8dc3 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -85,7 +85,6 @@ end
 
 function _M:loaded()
 	Zone:setup{npc_class="mod.class.NPC", grid_class="mod.class.Grid", object_class="engine.Entity"}
---	Map:setViewPort(0, 0, self.w, math.floor(self.h * 0.80), 20, 20, "/data/font/10X20.FON", 20)
 	Map:setViewPort(self.w * 0.2, 0, self.w * 0.8, math.floor(self.h * 0.80), 16, 16)
 	engine.GameTurnBased.loaded(self)
 	self.key = engine.KeyCommand.new()
@@ -115,6 +114,9 @@ function _M:tick()
 	if self.target.target.entity and not self.level:hasEntity(self.target.target.entity) then self.target.target.entity = false end
 
 	engine.GameTurnBased.tick(self)
+	-- Fun stuff: this can make the game realtime, although callit it in display() will make it work better
+	-- (since display is on a set FPS while tick() ticks as much as possible
+	-- engine.GameEnergyBased.tick(self)
 
 	if not self.day_of_year or self.day_of_year ~= self.calendar:getDayOfYear(self.turn) then
 		self.log(self.calendar:getTimeDate(self.turn))
diff --git a/game/modules/tome/data/talents.lua b/game/modules/tome/data/talents.lua
index 10c7d5d7d9..35dda48f91 100644
--- a/game/modules/tome/data/talents.lua
+++ b/game/modules/tome/data/talents.lua
@@ -24,9 +24,9 @@ newTalent{
 		self:project(t, x, y, DamageType.ARCANE, 10 + self:getMag())
 		return true
 	end,
-	require = { stat = { mag=12 }, },
+	require = { stat = { mag=10 }, },
 	info = function(self)
-		return ([[Conjures up mana into a powerful bolt doing %0.2f arcane damage",
+		return ([[Conjures up mana into a powerful bolt doing %0.2f arcane damage
 		The damage will increase with the Magic stat]]):format(10 + self:getMag())
 	end,
 }
@@ -42,7 +42,7 @@ newTalent{
 	end,
 	require = { stat = { mag=12 }, },
 	info = function(self)
-		return ([[Uses mana instead of life to take damage",
+		return ([[Uses mana instead of life to take damage
 		The damage to mana ratio increases with the Magic stat]]):format(10 + self:getMag())
 	end,
 }
@@ -61,7 +61,7 @@ newTalent{
 	end,
 	require = { stat = { mag=10 }, },
 	info = function(self)
-		return ([[Creates a globe of pure light with a radius of %d that illuminates the area.",
+		return ([[Creates a globe of pure light with a radius of %d that illuminates the area.
 		The radius will increase with the Magic stat]]):format(5 + self:getMag(10))
 	end,
 }
@@ -82,7 +82,7 @@ newTalent{
 	end,
 	require = { stat = { mag=16 }, },
 	info = function(self)
-		return ([[Conjures up a flash of fire doing %0.2f fire damage in a radius of %d",
+		return ([[Conjures up a flash of fire doing %0.2f fire damage in a radius of %d.
 		The damage will increase with the Magic stat]]):format(8 + self:getMag(70), math.min(6, 3 + self:getMag(6)))
 	end,
 }
@@ -101,7 +101,7 @@ newTalent{
 	end,
 	require = { stat = { mag=16 }, },
 	info = function(self)
-		return ([[Teleports you randomly on a small scale range (%d)",
+		return ([[Teleports you randomly on a small scale range (%d)
 		The range will increase with the Magic stat]]):format(10 + self:getMag(10))
 	end,
 }
diff --git a/game/modules/tome/dialogs/LevelupStatsDialog.lua b/game/modules/tome/dialogs/LevelupStatsDialog.lua
index 999644a856..643b31865e 100644
--- a/game/modules/tome/dialogs/LevelupStatsDialog.lua
+++ b/game/modules/tome/dialogs/LevelupStatsDialog.lua
@@ -33,6 +33,10 @@ function _M:incStat(v)
 			self:simplePopup("Not enough stat points", "You have no stat points left!")
 			return
 		end
+		if self.actor:isStatMax(self.statsel) then
+			self:simplePopup("Stat is at the maximun", "You can not increase this stat further!")
+			return
+		end
 	else
 		if self.actor_dup:getStat(self.statsel) == self.actor:getStat(self.statsel) then
 			self:simplePopup("Impossible", "You cannot take out more points!")
diff --git a/game/modules/tome/dialogs/LevelupTalentsDialog.lua b/game/modules/tome/dialogs/LevelupTalentsDialog.lua
index 3ca700bbcb..f260004f13 100644
--- a/game/modules/tome/dialogs/LevelupTalentsDialog.lua
+++ b/game/modules/tome/dialogs/LevelupTalentsDialog.lua
@@ -8,75 +8,139 @@ function _M:init(actor)
 	self.actor_dup = actor:clone()
 	engine.Dialog.init(self, "Talents Levelup: "..actor.name, 800, 600)
 
+	self:generateList()
+
+	self.talentsel = 1
+	self:keyCommands{
+		_UP = function() self.talentsel = util.boundWrap(self.talentsel - 1, 1, #self.list) end,
+		_DOWN = function() self.talentsel = util.boundWrap(self.talentsel + 1, 1, #self.list) end,
+		_LEFT = function() self:learn(false) end,
+		_RIGHT = function() self:learn(true) end,
+		_ESCAPE = function() game:unregisterDialog(self) end,
+	}
+	self:mouseZones{
+		{ x=2, y=45, w=350, h=self.font_h*#self.list, fct=function(button, x, y, xrel, yrel, tx, ty)
+			self.talentsel = 1 + math.floor(ty / self.font_h)
+			if button == "left" then self:learn(true)
+			elseif button == "right" then self:learn(false)
+			end
+		end },
+	}
+end
+
+function _M:generateList()
 	-- Makes up the list
 	local list, known = {}, {}
 	for i, tt in ipairs(self.actor.talents_types_def) do
 		local cat = tt.type:gsub("/.*", "")
-		list[#list+1] = { name="<"..cat:capitalize().." / "..tt.name:capitalize()..">", type=tt.type }
-		if actor:knowTalentType(tt.type) then known[#known+1] = "known" else known[#known+1] = "unknown" end
+		list[#list+1] = { name=cat:capitalize().." / "..tt.name:capitalize() .." (category)", type=tt.type }
+		if self.actor:knowTalentType(tt.type) then known[#known+1] = "#00FF00#known" else known[#known+1] = tt.points.." point(s)" end
 
 		-- Find all talents of this school
 		for j, t in ipairs(tt.talents) do
-			list[#list+1] = { name="    "..t.name, talent=t.id }
-			if actor:knowTalent(t.id) then known[#known+1] = "known" else known[#known+1] = "unknown" end
+			local typename = "talent"
+			if t.type[1]:find("^spell/") then typename = "spell" end
+			list[#list+1] = { name="    "..t.name.." ("..typename..")", talent=t.id }
+			if self.actor:knowTalent(t.id) then known[#known+1] = "#00FF00#known" else known[#known+1] = t.points.." point(s)" end
 		end
 	end
 	self.list = list
 	self.list_known = known
+end
 
-	self.talentsel = 1
-	self:keyCommands{
-		_UP = function() self.talentsel = util.boundWrap(self.talentsel - 1, 1, #list) end,
-		_DOWN = function() self.talentsel = util.boundWrap(self.talentsel + 1, 1, #list) end,
---		_LEFT = function() self:incStat(-1) end,
---		_RIGHT = function() self:incStat(1) end,
-		_ESCAPE = function() game:unregisterDialog(self) end,
-	}
-	self:mouseZones{
-		{ x=2, y=45, w=350, h=self.font_h*#list, fct=function(button, x, y, xrel, yrel, tx, ty)
-			self.talentsel = 1 + math.floor(ty / self.font_h)
---			if button == "left" then self:incStat(1)
---			elseif button == "right" then self:incStat(-1)
---			end
-		end },
-	}
+function _M:learn(v)
+	if self.list[self.talentsel].type then
+		self:learnType(self.list[self.talentsel].type, v)
+	else
+		self:learnTalent(self.list[self.talentsel].talent, v)
+	end
 end
---[[
-function _M:incStat(v)
-	if v == 1 then
+
+function _M:learnTalent(t, v)
+	if v then
 		if self.actor.unused_talents == 0 then
 			self:simplePopup("Not enough talent points", "You have no talent points left!")
 			return
 		end
+		if not self.actor:canLearnTalent(self.actor:getTalentFromId(t)) then
+			self:simplePopup("Cannot learn talent", "Prerequisites not met!")
+			return
+		end
+		if self.actor:knowTalent(t) then
+			self:simplePopup("Already known", "You already know this talent!")
+			return
+		end
+		self.actor:learnTalent(t)
+		self.actor.unused_talents = self.actor.unused_talents - 1
+	else
+		if not self.actor:knowTalent(t) then
+			self:simplePopup("Impossible", "You do not know this talent!")
+			return
+		end
+		if self.actor_dup:knowTalent(t) == true and self.actor:knowTalent(t) == true then
+			self:simplePopup("Impossible", "You cannot unlearn talents!")
+			return
+		end
+		self.actor:unlearnTalent(t)
+		self.actor.unused_talents = self.actor.unused_talents + 1
+	end
+	self:generateList()
+end
+
+function _M:learnType(tt, v)
+	if v then
+		if self.actor.unused_talents_types == 0 then
+			self:simplePopup("Not enough talent category points", "You have no talent category points left!")
+			return
+		end
+		self.actor.unused_talents_types = self.actor.unused_talents_types - 1
 	else
 		if self.actor_dup:getStat(self.talentsel) == self.actor:getStat(self.talentsel) then
 			self:simplePopup("Impossible", "You cannot take out more points!")
 			return
 		end
+		self.actor.unused_talents_types = self.actor.unused_talents_types + 1
 	end
 
-	self.actor:incStat(self.talentsel, v)
-	self.actor.unused_stats = self.actor.unused_stats - v
+	self:generateList()
 end
-]]
+
 function _M:drawDialog(s)
 	-- Description part
 	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
---[=[
-	local statshelp = ([[Keyboard: #00FF00#up key/down key#FFFFFF# to select a stat; #00FF00#right key#FFFFFF# to increase stat; #00FF00#left key#FFFFFF# to decrease a stat.
-Mouse: #00FF00#Left click#FFFFFF# to increase a stat; #00FF00#right click#FFFFFF# to decrease a stat.
+
+	local talentshelp = ([[Keyboard: #00FF00#up key/down key#FFFFFF# to select a stat; #00FF00#right key#FFFFFF# to learn; #00FF00#left key#FFFFFF# to unlearn.
+Mouse: #00FF00#Left click#FFFFFF# to learn; #00FF00#right click#FFFFFF# to unlearn.
 ]]):splitLines(self.iw / 2 - 10, self.font)
-	local lines = self.actor.talents_def[self.talentsel].description:splitLines(self.iw / 2 - 10, self.font)
-	for i = 1, #statshelp do
-		s:drawColorString(self.font, statshelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip())
+
+	local lines, helplines = {}
+	if self.list[self.talentsel].type then
+		local str = ""
+		str = str .. "#00FFFF#Talent Category\n"
+		str = str .. "#00FFFF#A talent category allows you to learn talents of this category. You gain a talent category point every few levels. You may also find trainers or artifacts that allows you to learn more.\n\n"
+		helplines = str:splitLines(self.iw / 2 - 10, self.font)
+		lines = self.actor:getTalentTypeFrom(self.list[self.talentsel].type).description:splitLines(self.iw / 2 - 10, self.font)
+	else
+		local str = ""
+		str = str .. "#00FFFF#Talent\n"
+		str = str .. "#00FFFF#A talent allows you to perform new combat moves, cast spells, improve your character. You gain a talent point every level. You may also find trainers or artifacts that allows you to learn more.\n\n"
+		helplines = str:splitLines(self.iw / 2 - 10, self.font)
+		lines = self.actor:getTalentFromId(self.list[self.talentsel].talent).info(self.actor):splitLines(self.iw / 2 - 10, self.font)
+	end
+	for i = 1, #talentshelp do
+		s:drawColorString(self.font, talentshelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip())
+	end
+	self:drawWBorder(s, self.iw / 2 + self.iw / 6, 2 + (0.5 + #talentshelp) * self.font:lineSkip(), self.iw / 6)
+	for i = 1, #helplines do
+		s:drawColorString(self.font, helplines[i], self.iw / 2 + 5, 2 + (i + #talentshelp) * self.font:lineSkip())
 	end
+	self:drawWBorder(s, self.iw / 2 + self.iw / 6, 2 + (1.5 + #talentshelp + #helplines) * self.font:lineSkip(), self.iw / 6)
 	for i = 1, #lines do
-		s:drawColorString(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #statshelp + 1) * self.font:lineSkip())
+		s:drawColorString(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #helplines + #talentshelp + 2) * self.font:lineSkip())
 	end
-]=]
 
 	-- Talents
-	s:drawColorString(self.font, "Talents types points left: #00FF00#"..self.actor.unused_talents_types, 2, 2)
+	s:drawColorString(self.font, "Talent categories points left: #00FF00#"..self.actor.unused_talents_types, 2, 2)
 	s:drawColorString(self.font, "Talents points left: #00FF00#"..self.actor.unused_talents, 2, 2 + self.font_h)
 	self:drawWBorder(s, 2, 40, 200)
 
-- 
GitLab