Page 1 of 2

Player achievements

Posted: Tue Jul 13, 2010 9:37 pm
by shani
I advance to patch files! :)
This one is for player achievements gaining (made it quite hackish :D )
and dumping them in character dump.
if you want the player achievements in the Actor.lua of the engine, or somewhere else more module oriented, i'm all in for it.
The patch also cnotains a small fix for object.lua getTextualDesc (fixing a previous mistake of mine)

Code: Select all

Index: engine/interface/WorldAchievements.lua
===================================================================
--- engine/interface/WorldAchievements.lua	(revision 906)
+++ engine/interface/WorldAchievements.lua	(working copy)
@@ -27,86 +27,102 @@
 
 --- Loads achievements
 function _M:loadDefinition(dir)
-	for i, file in ipairs(fs.list(dir)) do
-		if file:find("%.lua$") then
-			local f, err = loadfile(dir.."/"..file)
-			if not f and err then error(err) end
-			setfenv(f, setmetatable({
-				newAchievement = function(t) self:newAchievement(t) end,
-			}, {__index=_G}))
-			f()
-		end
-	end
+    for i, file in ipairs(fs.list(dir)) do
+        if file:find("%.lua$") then
+            local f, err = loadfile(dir.."/"..file)
+            if not f and err then error(err) end
+            setfenv(f, setmetatable({
+                newAchievement = function(t) self:newAchievement(t) end,
+            }, {__index=_G}))
+            f()
+        end
+    end
 end
 
 --- Make a new achivement with a name and desc
 function _M:newAchievement(t)
-	assert(t.name, "no achivement name")
-	assert(t.desc, "no achivement desc")
+    assert(t.name, "no achivement name")
+    assert(t.desc, "no achivement desc")
 
-	t.mode = t.mode or "none"
-	t.id = t.id or t.name
-	t.id = t.id:upper():gsub("[ ]", "_")
-	t.order = #self.achiev_defs+1
+    t.mode = t.mode or "none"
+    t.id = t.id or t.name
+    t.id = t.id:upper():gsub("[ ]", "_")
+    t.order = #self.achiev_defs+1
 
-	self.achiev_defs[t.id] = t
-	self.achiev_defs[#self.achiev_defs+1] = t
-	print("[ACHIEVEMENT] defined", t.order, t.id)
+    self.achiev_defs[t.id] = t
+    self.achiev_defs[#self.achiev_defs+1] = t
+    print("[ACHIEVEMENT] defined", t.order, t.id)
 end
 
 function _M:loadAchievements()
-	self.achieved = {}
+    self.achieved = {}
+    self.playerachieved = {}
 
-	for k, e in pairs(profile.mod) do
-		if k:find('^achievement%.') then
-			local id = k:gsub('^achievement%.', '')
-			if self.achiev_defs[id] then
-				self.achieved[id] = e
-			end
-		end
-	end
+    for k, e in pairs(profile.mod) do
+        if k:find('^achievement%.') then
+            local id = k:gsub('^achievement%.', '')
+            if self.achiev_defs[id] then
+                self.achieved[id] = e
+            end
+        end
+    end
 end
 
 function _M:getAchievementFromId(id)
-	return self.achiev_defs[id]
+    return self.achiev_defs[id]
 end
 
+
+--- Gain Personal achievement for player only
+-- @param id the achivement to gain
+-- @param src who did it
+function _M:gainPersonalAchievement(id, src, a, ...)
+   if self.playerachieved[id] then return end
+   
+   self.playerachieved[id] = {turn=game.turn, when=os.date("%Y-%m-%d %H:%M:%S")}
+   game.log("#LIGHT_GREEN#Personal New Achievement: %s!", a.name)
+   Dialog:simplePopup("Personal New Achievement: #LIGHT_GREEN#"..a.name, a.desc)
+end
+
 --- Gain an achievement
 -- @param id the achivement to gain
 -- @param src who did it
 function _M:gainAchievement(id, src, ...)
-	local a = self.achiev_defs[id]
-	if not a then error("Unknown achievement "..id) return end
-	if self.achieved[id] then return end
+    local a = self.achiev_defs[id]
+    if not a then error("Unknown achievement "..id) return end    
 
-	if a.can_gain then
-		local data = nil
-		if a.mode == "world" then
-			self.achievement_data = self.achievement_data or {}
-			self.achievement_data[id] = self.achievement_data[id] or {}
-			data = self.achievement_data[id]
-		elseif a.mode == "game" then
-			game.achievement_data = game.achievement_data or {}
-			game.achievement_data[id] = game.achievement_data[id] or {}
-			data = game.achievement_data[id]
-		elseif a.mode == "player" then
-			src.achievement_data = src.achievement_data or {}
-			src.achievement_data[id] = src.achievement_data[id] or {}
-			data = src.achievement_data[id]
-		end
-		if not a.can_gain(data, src, ...) then return end
-	end
+    if self.achieved[id] and self.playerachieved[id] then return end
+                                  
+    if a.can_gain then
+        local data = nil
+        if a.mode == "world" then
+             self.achievement_data = self.achievement_data or {}
+            self.achievement_data[id] = self.achievement_data[id] or {}
+            data = self.achievement_data[id]
+        elseif a.mode == "game" then
+            game.achievement_data = game.achievement_data or {}
+            game.achievement_data[id] = game.achievement_data[id] or {}
+            data = game.achievement_data[id]
+        elseif a.mode == "player" then
+            src.achievement_data = src.achievement_data or {}
+            src.achievement_data[id] = src.achievement_data[id] or {}
+            data = src.achievement_data[id]
+        end            
+        if not a.can_gain(data, src, data, ...) then return end
+    end
+    
+    if self.achieved[id] then self:gainPersonalAchievement(id, src, a, ...) return end
 
-	self.achieved[id] = {turn=game.turn, who=self:achievementWho(src), when=os.date("%Y-%m-%d %H:%M:%S")}
-	profile:saveModuleProfile("achievement."..id, self.achieved[id])
-
-	game.log("#LIGHT_GREEN#New Achievement: %s!", a.name)
-	Dialog:simplePopup("New Achievement: #LIGHT_GREEN#"..a.name, a.desc)
+    self.achieved[id] = {turn=game.turn, who=self:achievementWho(src), when=os.date("%Y-%m-%d %H:%M:%S")}
+    profile:saveModuleProfile("achievement."..id, self.achieved[id])
+    self.playerachieved[id] = {turn=game.turn, when=os.date("%Y-%m-%d %H:%M:%S")}
+    game.log("#LIGHT_GREEN#New Achievement: %s!", a.name)
+    Dialog:simplePopup("New Achievement: #LIGHT_GREEN#"..a.name, a.desc)
 end
 
 --- Format an achievement source
 -- By default just uses the actor's name, you can overload it to do more
 -- @param src the actor who did it
 function _M:achievementWho(src)
-	return src.name
+    return src.name
 end
Index: modules/tome/class/Object.lua
===================================================================
--- modules/tome/class/Object.lua	(revision 906)
+++ modules/tome/class/Object.lua	(working copy)
@@ -154,10 +154,7 @@
 
 --- Gets the full textual desc of the object without the name and requirements
 function _M:getTextualDesc()
-	local desc = {}
-	if self.encumber then
-		desc[#desc+1] = ("#67AD00#%0.2f Encumbrance."):format(self.encumber)
-	end
+	local desc = {}	
 
 	desc[#desc+1] = ("Type: %s / %s"):format(self.type, self.subtype)
 
@@ -295,6 +292,10 @@
 	if reqs then
 		desc[#desc+1] = reqs
 	end
+	
+	if self.encumber then
+		desc[#desc+1] = ("#67AD00#%0.2f Encumbrance."):format(self.encumber)
+	end
 
 	local textdesc = table.concat(self:getTextualDesc(), "\n")
 
Index: modules/tome/dialogs/CharacterSheet.lua
===================================================================
--- modules/tome/dialogs/CharacterSheet.lua	(revision 906)
+++ modules/tome/dialogs/CharacterSheet.lua	(working copy)
@@ -397,9 +397,17 @@
 				end
 			end
 		end
-	end
+	end	
 
 	nl()
+	nl("  [Player Achievments]")
+	nl()
+	for id, data in pairs(world.playerachieved) do
+		local a = world:getAchievementFromId(id)
+		nl(("%s Was Achieved for %s At %s"):format(a.name,a.desc,data.when))
+	end
+	
+	nl()
 	nl("  [Character Inventory]")
 	nl()
 	for item, o in ipairs(self.actor:getInven("INVEN")) do

Re: Player achievements

Posted: Tue Jul 13, 2010 9:49 pm
by darkgod
Could you change your indentation to tabs please?
Otherwise I can not apply it :/

Re: Player achievements

Posted: Wed Jul 14, 2010 5:24 am
by shani
I thought you said spaces instead of tabs :)
Want me to repost with tabs?

Re: Player achievements

Posted: Wed Jul 14, 2010 10:26 am
by darkgod
Yeah please: )
Send it to me by mail it'll be less chance of broking too :)

Re: Player achievements

Posted: Wed Jul 14, 2010 5:06 pm
by shani
Patch is ready, but what's your email? :)

Re: Player achievements

Posted: Wed Jul 14, 2010 5:08 pm
by darkgod
darkgod (at) the domain of the te4 website
:)

Re: Player achievements

Posted: Wed Jul 14, 2010 5:19 pm
by shani
Ok sent. Did you receive?

Re: Player achievements

Posted: Wed Jul 14, 2010 5:34 pm
by darkgod
Yup thanks!

Re: Player achievements

Posted: Wed Jul 14, 2010 8:41 pm
by darkgod
Integrated and fixed a few bugs :)

Now for player profiles:
- Have a new main menu entry to handle them (it exists already but it does not work)
- check out engine/PlayerProfile class
- make a screen to handle player profile, it should allow to display profile informations, create a new profile and register a profile online
- make the profile count interresting things, like the number of characters players, which modules, what birth descriptors, ...
- when online profile is requested it will need to ask for a login, and email and a password (see PlayerProfile:newProfile, you need to change it to work better)
- when the game runs for the first time make it ask the player for his online account or create one if he wishes

The synchronisation itself is already present and working you "just" need to make an UI for it

Thanks :)

Re: Player achievements

Posted: Wed Jul 14, 2010 11:23 pm
by Antagonist
Hmm, a thought.

Some people's natural instinct will be 'no online profile, dont submit, keep local, dont track my personal information, dont install spyware and track my shopping habits and sign me up for spam!'

For those, you already have an offline profile.

But how about, if you have an offline profile, then either YAWP or YASD you want to share, be able to sumbit an 'anonymous' character dump?

Unrelated, but I am VERY interested in the online profiles tho for data mining for balancing. Like... tracking that 65% of all players die when encountering The Master. Or 80% of Fighters lose against Bill, while only 10% of mages die. Or theres 20x more shadowblade players than rogue players. Or all the sources where ppl died due to a single hit doing >100% of their hp. Amount of poison/burning deaths and from what. All from a collection of character dumps.

Maybe even track some non-dump info, like instances where >70% of hp dmg is done in a single source, but doesn't kill the player. Or how common it is to do Trollshanks first, or those who go directly to Old Forest say.

This could be insanely useful for improving the balance of the game.

EDIT: Well, tracking statistically anyway, not individually.

Re: Player achievements

Posted: Wed Jul 14, 2010 11:52 pm
by darkgod
Yeah such things would indeed be super good for balancing things

Re: Player achievements

Posted: Thu Jul 15, 2010 5:43 am
by shani
The synchronisation itself is already present and working you "just" need to make an UI for it
I liked the "just" bit :)
I'll start working on it after my current test, it's a hard one.

Re: Player achievements

Posted: Thu Jul 15, 2010 7:47 am
by darkgod
Yeah I have a way to make things sound so easy ;)

Re: Player achievements

Posted: Thu Jul 15, 2010 7:57 am
by darkgod
BTW shani could you check out this thread please ?
http://forum.t-o-m-e.net/viewtopic.php?f=36&t=21351
:)

Re: Player achievements

Posted: Thu Jul 15, 2010 9:47 am
by shani
I read it long time ago...
Everything I contribute is yours to do whatever you want with.
Throw use abuse, whatever suits you... :)