From 0e3de789a82381904024ea54b8c02d3ebc361775 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Wed, 4 Aug 2010 21:06:53 +0000 Subject: [PATCH] PlayerProfiles online sync UI is now available! Thanks to shani Various stats for ToME are saved in the profile, artifacts found, uniques killed, characters created and killed git-svn-id: http://svn.net-core.org/repos/t-engine4@983 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/data/gfx/cursor.png | Bin 0 -> 969 bytes game/engine/Birther.lua | 6 +- game/engine/Button.lua | 57 +++++ game/engine/ButtonList.lua | 2 +- game/engine/DebugConsole.lua | 4 +- game/engine/Dialog.lua | 60 ++++- game/engine/PlayerProfile.lua | 44 +++- game/engine/TextBox.lua | 128 ++++++++++ game/engine/dialogs/DownloadDialog.lua | 4 +- .../engine/interface/ControlCursorSupport.lua | 58 +++++ game/engine/interface/WorldAchievements.lua | 4 +- game/modules/tome/class/Actor.lua | 8 + game/modules/tome/class/Game.lua | 3 +- game/modules/tome/class/Player.lua | 6 +- .../tome/class/interface/PlayerStats.lua | 68 ++++++ .../data/talents/techniques/superiority.lua | 228 +++++++++--------- .../tome/data/talents/techniques/warcries.lua | 194 +++++++-------- game/modules/tome/dialogs/CharacterSheet.lua | 14 +- game/special/mainmenu/class/Game.lua | 148 +++++++----- .../special/mainmenu/dialogs/ProfileLogin.lua | 130 ++++++++++ 20 files changed, 873 insertions(+), 293 deletions(-) create mode 100644 game/data/gfx/cursor.png create mode 100644 game/engine/Button.lua create mode 100644 game/engine/TextBox.lua create mode 100644 game/engine/interface/ControlCursorSupport.lua create mode 100644 game/modules/tome/class/interface/PlayerStats.lua create mode 100644 game/special/mainmenu/dialogs/ProfileLogin.lua diff --git a/game/data/gfx/cursor.png b/game/data/gfx/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..4eb962c0372553077a9be3827c27d1a356743d75 GIT binary patch literal 969 zcmeAS@N?(olHy`uVBq!ia0vp^j6lr6!3-q%kE9lZ1d4;)ofy`glX(f`u%tWsIx;Y9 z?C1WI$O_~uBzpw;GB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZk1_ow^ z0G|-oi<d9oxp(iy%a@<OeEI+XKT!QB7!84u69P+Chd%@QR<gu3q9iy!t)x7$D3u`~ zF*C13&(AePq0Cs%RL|72>(2wAA~8=F#}J9|<b;%jq=d8t#w0a1H#Rl~h6N0QuDh5L QfGQX~UHx3vIVCg!0CinICjbBd literal 0 HcmV?d00001 diff --git a/game/engine/Birther.lua b/game/engine/Birther.lua index 0bbfe67a9b..5d40f06b6f 100644 --- a/game/engine/Birther.lua +++ b/game/engine/Birther.lua @@ -254,17 +254,17 @@ function _M:drawDialog(s) Mouse: #00FF00#Left click#FFFFFF# to accept; #00FF00#right click#FFFFFF# to go back. ]]):splitLines(self.iw / 2 - 10, self.font) for i = 1, #birthhelp do - s:drawColorString(self.font, birthhelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip()) + s:drawColorStringBlended(self.font, birthhelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip()) end local lines = self.list[self.sel].desc:splitLines(self.iw / 2 - 10, self.font) local r, g, b for i = 1, #lines do - r, g, b = s:drawColorString(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #birthhelp + 1) * self.font:lineSkip(), r, g, b) + r, g, b = s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #birthhelp + 1) * self.font:lineSkip(), r, g, b) end -- Stats - s:drawColorString(self.font, "Selecting: "..self.current_type:capitalize(), 2, 2) + s:drawColorStringBlended(self.font, "Selecting: "..self.current_type:capitalize(), 2, 2) self:drawWBorder(s, 2, 20, 200) self:drawSelectionList(s, 2, 25, self.font_h, self.list, self.sel, "name") diff --git a/game/engine/Button.lua b/game/engine/Button.lua new file mode 100644 index 0000000000..09c034c639 --- /dev/null +++ b/game/engine/Button.lua @@ -0,0 +1,57 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +require "engine.Tiles" +require "engine.Mouse" +require "engine.KeyBind" +local ButtonList = require "engine.ButtonList" + +--- Handles dialog windows +module(..., package.seeall, class.make) + +function _M:init(name, text, x, y, w, height, owner, font, fct) + self.type = "Button" + self.owner = owner + self.name = name + self.fct = fct + self.font = font + self.x = x + self.y = y + self.btn = { + susel = ButtonList:makeButton(text, self.font, w, height, false), + sel = ButtonList:makeButton(text, self.font, 50, height, true), + h = height, + mouse_over= function(button) + if self.owner.state ~= self.name then self.owner:focusControl(self.name) end + if button == "left" then + self:fct() + end + end + } + self.owner.mouse:registerZone(owner.display_x + self.x, owner.display_y + self.y + height, w, height, self.btn.mouse_over) +end + +function _M:drawControl(s) + if (self.owner.state == self.name) then + s:merge(self.btn.sel, self.x, self.y) + else + s:merge(self.btn.susel, self.x, self.y) + end +end \ No newline at end of file diff --git a/game/engine/ButtonList.lua b/game/engine/ButtonList.lua index 0ceeb9d7d1..dc59b722d2 100644 --- a/game/engine/ButtonList.lua +++ b/game/engine/ButtonList.lua @@ -156,7 +156,7 @@ function _M:makeButton(name, font, w, h, sel) end local sw, sh = font:size(name) - s:drawColorString(font, name, (w - sw) / 2, (h - sh) / 2) + s:drawColorStringBlended(font, name, (w - sw) / 2, (h - sh) / 2) return s end diff --git a/game/engine/DebugConsole.lua b/game/engine/DebugConsole.lua index 8c092de9b0..7ae661a70f 100644 --- a/game/engine/DebugConsole.lua +++ b/game/engine/DebugConsole.lua @@ -79,10 +79,10 @@ function _M:drawDialog(s, w, h) local i, dh = 1, 0 while dh < self.h do if not self.history[self.scroll + i] then break end - s:drawString(self.font, self.history[self.scroll + i], 0, self.ih - (i + 1) * self.font:lineSkip(), 255, 255, 255) + s:drawStringBlended(self.font, self.history[self.scroll + i], 0, self.ih - (i + 1) * self.font:lineSkip(), 255, 255, 255) i = i + 1 dh = dh + self.font:lineSkip() end - s:drawString(self.font, self.line, 0, self.ih - self.font:lineSkip(), 255, 255, 255) + s:drawStringBlended(self.font, self.line, 0, self.ih - self.font:lineSkip(), 255, 255, 255) end diff --git a/game/engine/Dialog.lua b/game/engine/Dialog.lua index f421082c37..9d11b14ec7 100644 --- a/game/engine/Dialog.lua +++ b/game/engine/Dialog.lua @@ -82,6 +82,10 @@ end --- Create a Dialog function _M:init(title, w, h, x, y, alpha, font) self.title = title + self.controls = { } + self.tabindex = 0 + self.state = "" + self.currenttabindex = 0 self.w, self.h = math.floor(w), math.floor(h) self.display_x = math.floor(x or (game.w - self.w) / 2) self.display_y = math.floor(y or (game.h - self.h) / 2) @@ -100,7 +104,8 @@ function _M:display() if not self.changed then return self.surface end local s = self.surface - s:erase(0,0,0,200) + self.alpha = self.alpha or 200 + s:erase(0,0,0,self.alpha) s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_7.png"), 0, 0) s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_9.png"), self.w - 8, 0) @@ -118,7 +123,7 @@ function _M:display() local tw, th = self.font:size(self.title) s:drawColorStringBlended(self.font, self.title, (self.w - tw) / 2, 4, 255,255,255) - + self.internal_surface:erase() self:drawDialog(self.internal_surface) s:merge(self.internal_surface, 5, 20 + 3) @@ -126,6 +131,57 @@ function _M:display() return self.surface end +function _M:addControl(control) + control.tabindex = self.tabindex + self.tabindex = self.tabindex + 1 + self.controls[control.name] = control + table.sort(self.controls, function(a,b) return a.tabindex<b.tabindex end) +end + +function _M:changeFocus(up) + local add = 1 + if not up then add = -1 end + self.currenttabindex = self.currenttabindex + add + if (self.currenttabindex==self.tabindex) then self.currenttabindex = 0 end + if self.currenttabindex==-1 then self.currenttabindex=self.tabindex-1 end + local name = "" + for i, cntrl in pairs(self.controls) do + if cntrl.tabindex==self.currenttabindex then + if self.controls[self.state] and self.controls[self.state].unFocus then self.controls[self.state]:unFocus() end + cntrl.focused=true + name=i + end + end + return name +end + +function _M:focusControl(focusOn) + if focusOn==self.state then return end + local oldstate = self.state + for i, cntrl in pairs(self.controls) do + if i==focusOn then cntrl.focused=true self.state=i self.currenttabindex=cntrl.tabindex end + if i==oldstate and cntrl.unFocus then cntrl:unFocus() end + end +end + + +function _M:databind() + local result = { } + for i, cntrl in pairs(self.controls or { }) do + if cntrl.type and cntrl.type=="TextBox" then + result[cntrl.name] = cntrl.text + end + end + return result +end + + +function _M:drawControls(s) + for i, cntrl in pairs(self.controls or { }) do + cntrl:drawControl(s) + end +end + function _M:toScreen(x,y) self.surface:toScreenWithTexture(self.texture,x,y) end diff --git a/game/engine/PlayerProfile.lua b/game/engine/PlayerProfile.lua index 910343b824..35091d88f7 100644 --- a/game/engine/PlayerProfile.lua +++ b/game/engine/PlayerProfile.lua @@ -66,7 +66,7 @@ end ------------------------------------------------------------ ---- Handles the player profile, possible online +--- Handles the player profile, possibly online module(..., package.seeall, class.make) function _M:init(name) @@ -96,8 +96,8 @@ end function _M:loadGenericProfile() local d = "/profiles/"..self.name.."/generic/" fs.mount(engine.homepath, "/") - for i, file in ipairs(fs.list(d)) do + if file:find(".profile$") then local f, err = loadfile(d..file) if not f and err then error(err) end @@ -115,9 +115,8 @@ end function _M:loadModuleProfile(short_name) local d = "/profiles/"..self.name.."/modules/"..short_name.."/" fs.mount(engine.homepath, "/") - + print("[Module Profile] ", engine.homepath.."/"..d) self.modules[short_name] = self.modules[short_name] or {} - for i, file in ipairs(fs.list(d)) do if file:find(".profile$") then local f, err = loadfile(d..file) @@ -195,6 +194,7 @@ function _M:rpc(data) end function _M:tryAuth() + print("[ONLINE PROFILE] auth") local data = self:rpc{action="TryAuth", login=self.login, pass=self.pass} if not data then print("[ONLINE PROFILE] not auth") return end print("[ONLINE PROFILE] auth as ", data.name, data.hash) @@ -232,7 +232,6 @@ function _M:setConfigs(module, name, val) local data = self:rpc{action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data={[name] = val}} if not data then return end print("[ONLINE PROFILE] saved ", module, name, val) - util.show_backtrace() end function _M:syncOnline(module) @@ -249,9 +248,40 @@ function _M:syncOnline(module) print("[ONLINE PROFILE] saved ", module) end -function _M:newProfile() - local data = self:rpc{action="NewProfile", login=tostring(os.time()), email=os.time().."te4@mailcatch.com", name=os.time().."member", pass="test"} +function _M:newProfile(Login, Name, Password, Email) + print("[ONLINE PROFILE] profile options ", Login, Email, Name, Password) + local data = self:rpc{action="NewProfile", login=Login, email=Email, name=Name, pass=Password} if not data then print("[ONLINE PROFILE] could not create") return end print("[ONLINE PROFILE] profile id ", data.id) + self:performlogin(Login, Password, Name, Email) +end + +function _M:performlogin(login, pass, name, email) + self.login=login + self.pass=pass + print("[ONLINE PROFILE] attempting log in ", self.login, self.pass) + self:tryAuth() + if (profile.auth) then + self.generic.online = { login=login, pass=pass,name=name or "", email=email or "" } + self:saveGenericProfile("online", self.generic.online) + self:getConfigs("generic") + self:syncOnline("generic") + end end +function _M:logOut() + profile.generic.online = nil + profile.auth = nil + local d = "/profiles/"..self.name.."/generic/" + fs.mount(engine.homepath, "/") + fs.delete(d.."online.profile") +end + +function _M:checkFirstRun() + local result = self.generic.firstrun + if not result then + firstrun = { When=os.time() } + self:saveGenericProfile("firstrun", firstrun, false) + end + return result +end \ No newline at end of file diff --git a/game/engine/TextBox.lua b/game/engine/TextBox.lua new file mode 100644 index 0000000000..a4551e8bdc --- /dev/null +++ b/game/engine/TextBox.lua @@ -0,0 +1,128 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +require "engine.Tiles" +require "engine.Mouse" +require "engine.KeyBind" +require "engine.interface.ControlCursorSupport" +--- Handles textbox input control +module(..., package.seeall, class.inherit( + engine.interface.ControlCursorSupport)) + +tiles = engine.Tiles.new(16, 16) + +function _M:init(dialogdef, owner, font, mask, fct) + --name, title, min, max, x, y, w, height + self.type = "TextBox" + self.name = dialogdef.name + self.title = dialogdef.title + self.min = dialogdef.min or 2 + self.max = dialogdef.max or 25 + self.h = dialogdef.h or 30 + self.font = font + self.w = dialogdef.w or 200 + self.x = dialogdef.x + self.y = dialogdef.y + self.text = "" + self.owner = owner + self.btn = { + h = dialogdef.h, + mouse_over= function(button) + if self.owner.state ~= self.name then self.focused=true self.owner:focusControl(self.name) end + if button == "right" then + self.text="" + self.ownwer.changed=true + end + end + } + self.owner.mouse:registerZone(self.owner.display_x + self.x, self.owner.display_y + self.y + self.h, self.w, self.h, self.btn.mouse_over) + self:startCursor() +end + +function _M:delete() + if (self.cursorPosition>=self.maximumCurosrPosition) then return end + local temptext = self.text:sub(1, self.cursorPosition) + if self.cursorPosition < self.maximumCurosrPosition - 1 then temptext = temptext..self.text:sub(self.cursorPosition + 2, self.text:len()) end + self.text = temptext + self.maximumCurosrPosition = self.maximumCurosrPosition - 1 +end + +function _M:backSpace() + if (self.cursorPosition==0) then return end + local temptext = self.text:sub(1, self.cursorPosition - 1) + if self.cursorPosition < self.maximumCurosrPosition then temptext = temptext..self.text:sub(self.cursorPosition + 1, self.text:len()) end + self.text = temptext + self.maximumCurosrPosition = self.maximumCurosrPosition - 1 + self.cursorPosition = self.cursorPosition - 1 +end + +function _M:textInput(c) + if self.text:len() < self.max then + local temp=nil + if self.cursorPosition < self.maximumCurosrPosition then temp=self.text:sub(self.cursorPosition + 1, self.text:len()) end + self.text = self.text:sub(1,self.cursorPosition) .. c + if temp then self.text=self.text..temp end + self.owner.changed = true + print("[Cursor] before ", c, self.cursorPosition, self.maximumCurosrPosition) + self:moveRight(1, true) + print("[Cursor] after ", c, self.cursorPosition, self.maximumCurosrPosition) + end +end + +function _M:unFocus() + self.focused = false +end + + +function _M:drawControl(s) + + local r, g, b + local w = self.w + local h = self.h + local tw, th = self.font:size(self.title) + tw = tw + 10 + local title=self.title + if self.owner.state==self.name then + title = title.."*" + end + r, g, b = s:drawColorStringBlended(self.font, title, self.x, self.y + ((h - th) / 2), r, g, b) + + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_7"..(sel and "_sel" or "")..".png"), self.x + tw, self.y) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_9"..(sel and "_sel" or "")..".png"), w + self.x - 8, self.y) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_1"..(sel and "_sel" or "")..".png"), self.x + tw, self.y + h - 8) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_3"..(sel and "_sel" or "")..".png"), self.x + w - 8, self.y + h - 8) + for i = 8, w - tw - 9 do + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_8.png"), self.x + tw + i, self.y) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_8.png"), self.x + tw + i, self.y + h - 3) + end + for i = 8, h - 9 do + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_4.png"), self.x + tw, self.y + i) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_4.png"), self.x + w - 3, self.y + i) + end + + local text = self.text + if text=="" then text=self.mask or "" end + local sw, sh = self.font:size(text) + + local baseX = self.x + tw + 10 + + s:drawColorStringBlended(self.font, text, baseX, self.y + h - sh - 8) + self:drawCursor(s, baseX, text) +end diff --git a/game/engine/dialogs/DownloadDialog.lua b/game/engine/dialogs/DownloadDialog.lua index f4eb9b7ec2..1dd07951ee 100644 --- a/game/engine/dialogs/DownloadDialog.lua +++ b/game/engine/dialogs/DownloadDialog.lua @@ -56,8 +56,8 @@ function _M:drawDialog(s) end end - s:drawString(self.font, "From: "..self.url, 2, 2, 255, 255, 255) - s:drawString(self.font, "Received: "..self.received, 2, 25, 255, 255, 255) + s:drawStringBlended(self.font, "From: "..self.url, 2, 2, 255, 255, 255) + s:drawStringBlended(self.font, "Received: "..self.received, 2, 25, 255, 255, 255) end function _M:startDownload() diff --git a/game/engine/interface/ControlCursorSupport.lua b/game/engine/interface/ControlCursorSupport.lua new file mode 100644 index 0000000000..e913e8f38a --- /dev/null +++ b/game/engine/interface/ControlCursorSupport.lua @@ -0,0 +1,58 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +require "engine.Tiles" + +local Dialog = require "engine.Dialog" + +tiles = engine.Tiles.new(16, 16) + +--- Handles player running +-- This should work for running inside tunnel, alongside walls, in open spaces.<br/> +module(..., package.seeall, class.make) + +function _M:startCursor() + self.cursorPosition = 0 + self.maximumCurosrPosition = 0 + self.focused = false +end + +function _M:moveRight(x, add) + if add and self.cursorPosition + x > self.maximumCurosrPosition then self.maximumCurosrPosition = self.cursorPosition + x end + if self.cursorPosition + x <= self.maximumCurosrPosition then + self.cursorPosition = self.cursorPosition + x + end +end + +function _M:moveLeft(x) + if self.cursorPosition - x >= 0 then + self.cursorPosition = self.cursorPosition - x + end +end + +-- @param s surface to draw on +function _M:drawCursor(s, baseX, text) + local sw, sh = self.font:size(text:sub(1, self.cursorPosition)) + local t = os.time() % 2 + if t < 1 and self.focused then + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "cursor.png"), sw + baseX, self.y + self.h - sh - 2) + s:merge(tiles:get(nil, 0,0,0, 0,0,0, "cursor.png"), sw + baseX, self.y + self.h - sh - 10) + end +end \ No newline at end of file diff --git a/game/engine/interface/WorldAchievements.lua b/game/engine/interface/WorldAchievements.lua index c489d85b40..4e8933528f 100644 --- a/game/engine/interface/WorldAchievements.lua +++ b/game/engine/interface/WorldAchievements.lua @@ -74,7 +74,7 @@ end --- Gain Personal achievement for player only --- @ +-- @param silent suppress the message to the player -- @param id the achivement to gain -- @param src who did it function _M:gainPersonalAchievement(silent, id, src, ...) @@ -98,7 +98,7 @@ 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] and self.playerachieved[id] then return end + if self.achieved[id] and src.achievements and src.achievements[id] then return end if a.can_gain then local data = nil diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 40daac61af..d36058be7f 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -572,6 +572,7 @@ function _M:die(src) for inven_id, inven in pairs(self.inven) do for i, o in ipairs(inven) do if not o.no_drop then + o.droppedBy = self.name game.level.map:addObject(self.x, self.y, o) end end @@ -597,6 +598,10 @@ function _M:die(src) world:gainAchievement("EXTERMINATOR", p, self) world:gainAchievement("PEST_CONTROL", p, self) + if self.unique then + p:registerUniqueKilled(self) + end + -- Record kills p.all_kills = p.all_kills or {} p.all_kills[self.name] = p.all_kills[self.name] or 0 @@ -756,6 +761,9 @@ function _M:onAddObject(o) -- Achievement checks if self.player then + if o.unique then + self:resolveSource():registerArtifactsPicked(o) + end world:gainAchievement("DEUS_EX_MACHINA", self, o) end end diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index fdec9a77eb..d337f057a6 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -72,7 +72,6 @@ function _M:init() engine.interface.GameSound.init(self) self.persistant_actors = {} - -- Pause at birth self.paused = true @@ -143,12 +142,14 @@ function _M:newGame() self.player.energy.value = self.energy_to_act self.always_target = true Map:setViewerFaction(self.player.faction) + self.paused = true print("[PLAYER BIRTH] resolved!") self.player:playerLevelup(function() self.player:grantQuest(self.player.starting_quest) self:registerDialog(require("mod.dialogs.IntroDialog").new(self.player)) self.player:resetToFull() + self.player:registerCharacterPlayed() end) end, quickbirth) self:registerDialog(birth) diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index b294963470..77a08cadb3 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -24,6 +24,7 @@ require "engine.interface.PlayerRun" require "engine.interface.PlayerHotkeys" require "engine.interface.PlayerSlide" require "engine.interface.PlayerMouse" +require "mod.class.interface.PlayerStats" local Map = require "engine.Map" local Dialog = require "engine.Dialog" local ActorTalents = require "engine.interface.ActorTalents" @@ -40,7 +41,8 @@ module(..., package.seeall, class.inherit( engine.interface.PlayerRun, engine.interface.PlayerHotkeys, engine.interface.PlayerMouse, - engine.interface.PlayerSlide + engine.interface.PlayerSlide, + mod.class.interface.PlayerStats )) function _M:init(t, no_default) @@ -239,6 +241,8 @@ function _M:die(src) engine.interface.ActorLife.die(self, src) game.paused = true self.energy.value = game.energy_to_act + self.killedBy = src + self:registerDeath(self.killedBy) game:registerDialog(DeathDialog.new(self)) else mod.class.Actor.die(self, src) diff --git a/game/modules/tome/class/interface/PlayerStats.lua b/game/modules/tome/class/interface/PlayerStats.lua new file mode 100644 index 0000000000..0ca9dca32b --- /dev/null +++ b/game/modules/tome/class/interface/PlayerStats.lua @@ -0,0 +1,68 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" + +module(..., package.seeall, class.make) + +function _M:playerStatGetCharacterIdentifier() + return self.descriptor.world..","..self.descriptor.subrace..","..self.descriptor.subclass..","..self.descriptor.difficulty +end + +function _M:registerDeath(src) + local pid = self:playerStatGetCharacterIdentifier() + local name = src.name + + profile.mod.deaths = profile.mod.deaths or {} + profile.mod.deaths.count = (profile.mod.deaths.count or 0) + 1 + + profile.mod.deaths.sources = profile.mod.deaths.sources or {} + profile.mod.deaths.sources[pid] = profile.mod.deaths.sources[pid] or {} + profile.mod.deaths.sources[pid][name] = (profile.mod.deaths.sources[pid][name] or 0) + 1 + profile:saveModuleProfile("deaths", profile.mod.deaths) +end + +function _M:registerUniqueKilled(who) + local pid = self:playerStatGetCharacterIdentifier() + + profile.mod.uniques = profile.mod.uniques or { uniques={} } + profile.mod.uniques.uniques[who.name] = profile.mod.uniques.uniques[who.name] or {} + profile.mod.uniques.uniques[who.name][pid] = (profile.mod.uniques.uniques[who.name][pid] or 0) + 1 + profile:saveModuleProfile("uniques", profile.mod.uniques) +end + +function _M:registerArtifactsPicked(what) + if what.stat_picked_up then return end + what.stat_picked_up = true + local pid = self:playerStatGetCharacterIdentifier() + local name = what:getName{do_color=false, do_count=false, force_id=true} + + profile.mod.artifacts = profile.mod.artifacts or { artifacts={} } + profile.mod.artifacts.artifacts[name] = profile.mod.artifacts.artifacts[name] or {} + profile.mod.artifacts.artifacts[name][pid] = (profile.mod.artifacts.artifacts[name][pid] or 0) + 1 + profile:saveModuleProfile("artifacts", profile.mod.artifacts) +end + +function _M:registerCharacterPlayed() + local pid = self:playerStatGetCharacterIdentifier() + + profile.mod.characters = profile.mod.characters or { characters={} } + profile.mod.characters.characters[pid] = (profile.mod.characters.characters[pid] or 0) + 1 + profile:saveModuleProfile("characters", profile.mod.characters) +end diff --git a/game/modules/tome/data/talents/techniques/superiority.lua b/game/modules/tome/data/talents/techniques/superiority.lua index ad5534b3de..d1e371e41f 100644 --- a/game/modules/tome/data/talents/techniques/superiority.lua +++ b/game/modules/tome/data/talents/techniques/superiority.lua @@ -1,116 +1,116 @@ --- ToME - Tales of Middle-Earth --- Copyright (C) 2009, 2010 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - - -newTalent{ - name = "Juggernaut", - type = {"technique/superiority", 1}, - require = techs_req_high1, - points = 5, +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + + +newTalent{ + name = "Juggernaut", + type = {"technique/superiority", 1}, + require = techs_req_high1, + points = 5, random_ego = "attack", - cooldown = 40, - stamina = 60, - action = function(self, t) - self:setEffect(self.EFF_EARTHEN_BARRIER, 20, {power=self:getTalentLevelRaw(t) * 5}) - return true - end, - info = function(self, t) - return ([[Concentrate on the battle, ignoring some of the damage you take. - Improves physical damage reduction by %d%%.]]):format(self:getTalentLevelRaw(t) * 5) - end, -} - -newTalent{ - name = "Onslaught", - type = {"technique/superiority", 2}, - require = techs_req_high2, - points = 5, - mode = "sustained", - cooldown = 60, - sustain_stamina = 80, - activate = function(self, t) - return { - onslaught = self:addTemporaryValue("onslaught", math.floor(self:getTalentLevel(t))), - stamina = self:addTemporaryValue("stamina_regen", -15), - } - end, - - deactivate = function(self, t, p) - self:removeTemporaryValue("onslaught", p.onslaught) - self:removeTemporaryValue("stamina_regen", p.stamina) - return true - end, - info = function(self, t) - return ([[Take an offensive stance, as you walk through your foes you knock them all back in an frontal arc (up to %d grids). - This consumes stamina rapidly.]]): - format(self:getTalentLevel(t)) - end, -} - -newTalent{ - name = "Battle Call", - type = {"technique/superiority", 3}, - require = techs_req_high3, - points = 5, + cooldown = 40, + stamina = 60, + action = function(self, t) + self:setEffect(self.EFF_EARTHEN_BARRIER, 20, {power=self:getTalentLevelRaw(t) * 5}) + return true + end, + info = function(self, t) + return ([[Concentrate on the battle, ignoring some of the damage you take. + Improves physical damage reduction by %d%%.]]):format(self:getTalentLevelRaw(t) * 5) + end, +} + +newTalent{ + name = "Onslaught", + type = {"technique/superiority", 2}, + require = techs_req_high2, + points = 5, + mode = "sustained", + cooldown = 60, + sustain_stamina = 80, + activate = function(self, t) + return { + onslaught = self:addTemporaryValue("onslaught", math.floor(self:getTalentLevel(t))), + stamina = self:addTemporaryValue("stamina_regen", -15), + } + end, + + deactivate = function(self, t, p) + self:removeTemporaryValue("onslaught", p.onslaught) + self:removeTemporaryValue("stamina_regen", p.stamina) + return true + end, + info = function(self, t) + return ([[Take an offensive stance, as you walk through your foes you knock them all back in an frontal arc (up to %d grids). + This consumes stamina rapidly.]]): + format(self:getTalentLevel(t)) + end, +} + +newTalent{ + name = "Battle Call", + type = {"technique/superiority", 3}, + require = techs_req_high3, + points = 5, random_ego = "attack", - cooldown = 10, - stamina = 30, - action = function(self, t) - local tg = {type="ball", range=0, friendlyfire=true, radius=2 + self:getTalentLevel(t), talent=t} - self:project(tg, self.x, self.y, function(px, py) - local target = game.level.map(px, py, Map.ACTOR) - if not target then return end - local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) - if tx and ty and target:canBe("teleport") then - target:move(tx, ty, true) - game.logSeen(target, "%s is called to battle!", target.name:capitalize()) - end - end) - return true - end, - info = function(self, t) - return ([[Call all foes in a radius of %d around you into battle, getting them into melee range in an instant.]]):format(2+self:getTalentLevel(t)) - end, -} - -newTalent{ - name = "Shattering Impact", - type = {"technique/superiority", 4}, - require = techs_req_high4, - points = 5, - mode = "sustained", - cooldown = 30, - sustain_stamina = 40, - activate = function(self, t) - return { - dam = self:addTemporaryValue("shattering_impact", self:combatTalentWeaponDamage(t, 0.2, 0.6)), - } - end, - - deactivate = function(self, t, p) - self:removeTemporaryValue("shattering_impact", p.dam) - return true - end, - info = function(self, t) - return ([[Put all your strength into your weapon blows, creating shattering impact that deal %d%% weapon damage to all nearby foes. - The impact damage will also affect you. - Each blow will drain 15 stamina.]]): - format(100 * self:combatTalentWeaponDamage(t, 0.1, 0.45)) - end, -} + cooldown = 10, + stamina = 30, + action = function(self, t) + local tg = {type="ball", range=0, friendlyfire=true, radius=2 + self:getTalentLevel(t), talent=t} + self:project(tg, self.x, self.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) + if tx and ty and target:canBe("teleport") then + target:move(tx, ty, true) + game.logSeen(target, "%s is called to battle!", target.name:capitalize()) + end + end) + return true + end, + info = function(self, t) + return ([[Call all foes in a radius of %d around you into battle, getting them into melee range in an instant.]]):format(2+self:getTalentLevel(t)) + end, +} + +newTalent{ + name = "Shattering Impact", + type = {"technique/superiority", 4}, + require = techs_req_high4, + points = 5, + mode = "sustained", + cooldown = 30, + sustain_stamina = 40, + activate = function(self, t) + return { + dam = self:addTemporaryValue("shattering_impact", self:combatTalentWeaponDamage(t, 0.2, 0.6)), + } + end, + + deactivate = function(self, t, p) + self:removeTemporaryValue("shattering_impact", p.dam) + return true + end, + info = function(self, t) + return ([[Put all your strength into your weapon blows, creating shattering impact that deal %d%% weapon damage to all nearby foes. + The impact damage will also affect you. + Each blow will drain 15 stamina.]]): + format(100 * self:combatTalentWeaponDamage(t, 0.1, 0.45)) + end, +} diff --git a/game/modules/tome/data/talents/techniques/warcries.lua b/game/modules/tome/data/talents/techniques/warcries.lua index 703d908117..d461b0f127 100644 --- a/game/modules/tome/data/talents/techniques/warcries.lua +++ b/game/modules/tome/data/talents/techniques/warcries.lua @@ -1,101 +1,101 @@ --- ToME - Tales of Middle-Earth --- Copyright (C) 2009, 2010 Nicolas Casalini --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- --- Nicolas Casalini "DarkGod" --- darkgod@te4.org - - -newTalent{ - name = "Shattering Shout", - type = {"technique/warcries", 1}, - require = techs_req_high1, - points = 5, +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + + +newTalent{ + name = "Shattering Shout", + type = {"technique/warcries", 1}, + require = techs_req_high1, + points = 5, random_ego = "attack", - cooldown = 7, - stamina = 20, - range = 4, - action = function(self, t) - local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, DamageType.PHYSICAL, (50 + self:getTalentLevel(t) * self:getStr()) / 2.3, {type="flame"}) - return true - end, - info = function(self, t) - return ([[A powerful shout, doing %0.2f physical damage in a radius %d cone in front of you. - The damage increases with Strength.]]):format((50 + self:getTalentLevel(t) * self:getStr()) / 2.3, 3 + self:getTalentLevelRaw(t)) - end, -} - -newTalent{ - name = "Second Wind", - type = {"technique/warcries", 2}, - require = techs_req_high2, - points = 5, + cooldown = 7, + stamina = 20, + range = 4, + action = function(self, t) + local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, DamageType.PHYSICAL, (50 + self:getTalentLevel(t) * self:getStr()) / 2.3, {type="flame"}) + return true + end, + info = function(self, t) + return ([[A powerful shout, doing %0.2f physical damage in a radius %d cone in front of you. + The damage increases with Strength.]]):format((50 + self:getTalentLevel(t) * self:getStr()) / 2.3, 3 + self:getTalentLevelRaw(t)) + end, +} + +newTalent{ + name = "Second Wind", + type = {"technique/warcries", 2}, + require = techs_req_high2, + points = 5, random_ego = "utility", - cooldown = 150, - action = function(self, t) - self:incStamina(20 + self:getTalentLevel(t) * 12) - return true - end, - info = function(self, t) - return ([[Take a deep breath to recover %d stamina.]]): - format(20 + self:getTalentLevel(t) * 12) - end, -} - -newTalent{ - name = "Battle Shout", - type = {"technique/warcries", 3}, - require = techs_req_high3, - points = 5, + cooldown = 150, + action = function(self, t) + self:incStamina(20 + self:getTalentLevel(t) * 12) + return true + end, + info = function(self, t) + return ([[Take a deep breath to recover %d stamina.]]): + format(20 + self:getTalentLevel(t) * 12) + end, +} + +newTalent{ + name = "Battle Shout", + type = {"technique/warcries", 3}, + require = techs_req_high3, + points = 5, random_ego = "defensive", - cooldown = 30, - stamina = 40, - action = function(self, t) - self:setEffect(self.EFF_BATTLE_SHOUT, 5 + self:getTalentLevelRaw(t) * 2, {power=10+self:getTalentLevelRaw(t)}) - return true - end, - info = function(self, t) - return ([[Boost your life and stamina by %d%% for %d turns by uttering your battle shout.]]):format(5 + self:getTalentLevelRaw(t) * 2, 10 + self:getTalentLevelRaw(t)) - end, -} - -newTalent{ - name = "Battle Cry", - type = {"technique/warcries", 4}, - require = techs_req_high4, - points = 5, + cooldown = 30, + stamina = 40, + action = function(self, t) + self:setEffect(self.EFF_BATTLE_SHOUT, 5 + self:getTalentLevelRaw(t) * 2, {power=10+self:getTalentLevelRaw(t)}) + return true + end, + info = function(self, t) + return ([[Boost your life and stamina by %d%% for %d turns by uttering your battle shout.]]):format(5 + self:getTalentLevelRaw(t) * 2, 10 + self:getTalentLevelRaw(t)) + end, +} + +newTalent{ + name = "Battle Cry", + type = {"technique/warcries", 4}, + require = techs_req_high4, + points = 5, random_ego = "attack", - cooldown = 30, - stamina = 40, - range = 4, - action = function(self, t) - local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, function(px, py) - local target = game.level.map(px, py, Map.ACTOR) - if not target or not target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 10) then return end - target:setEffect(target.EFF_BATTLE_CRY, 7, {power=7 * self:getTalentLevel(t)}) - end, {type="flame"}) - return true - end, - info = function(self, t) - return ([[Your battle cry shatters the will of your foes, lowering their defense by %d for 7 turns, making them easier to hit.]]): - format(7 * self:getTalentLevel(t)) - end, -} + cooldown = 30, + stamina = 40, + range = 4, + action = function(self, t) + local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target or not target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 10) then return end + target:setEffect(target.EFF_BATTLE_CRY, 7, {power=7 * self:getTalentLevel(t)}) + end, {type="flame"}) + return true + end, + info = function(self, t) + return ([[Your battle cry shatters the will of your foes, lowering their defense by %d for 7 turns, making them easier to hit.]]): + format(7 * self:getTalentLevel(t)) + end, +} diff --git a/game/modules/tome/dialogs/CharacterSheet.lua b/game/modules/tome/dialogs/CharacterSheet.lua index 3ee95ec605..638111e999 100644 --- a/game/modules/tome/dialogs/CharacterSheet.lua +++ b/game/modules/tome/dialogs/CharacterSheet.lua @@ -203,7 +203,7 @@ function _M:dump() if o.combat then strings[1] = ("Attack(Main Hand): %3d"):format(game.player:combatAttack(o.combat)) strings[2] = ("Damage(Main Hand): %3d"):format(game.player:combatDamage(o.combat)) - strings[3] = ("APR (Main Hand): %3d"):format(game.player:combatAPR(o.combat)) + strings[3] = ("APR (Main Hand): %3d"):format(game.player:combatAPR(o.combat)) strings[4] = ("Crit (Main Hand): %3d%%"):format(game.player:combatCrit(o.combat)) strings[5] = ("Speed (Main Hand): %0.2f"):format(game.player:combatSpeed(o.combat)) end @@ -256,8 +256,8 @@ function _M:dump() nl() nl(("Attack (Off Hand): %3d"):format(game.player:combatAttack(o.combat))) nl(("Damage (Off Hand): %3d"):format(game.player:combatDamage(o.combat) * offmult)) - nl(("APR (Off Hand): %3d"):format(game.player:combatAPR(o.combat))) - nl(("Crit (Off Hand): %3d%%"):format(game.player:combatCrit(o.combat))) + nl(("APR (Off Hand): %3d"):format(game.player:combatAPR(o.combat))) + nl(("Crit (Off Hand): %3d%%"):format(game.player:combatCrit(o.combat))) nl(("Speed (Off Hand): %0.2f"):format(game.player:combatSpeed(o.combat))) end end @@ -393,6 +393,9 @@ function _M:dump() local char = string.char(string.byte('a') + index) nl(("%s) %s"):format(char, o:getName{force_id=true})) nl((" %s"):format(table.concat(o:getTextualDesc(), "\n "))) + if o.droppedBy then + nl((" Dropped by %s"):format(o.droppedBy)) + end index = index + 1 end end @@ -402,7 +405,7 @@ function _M:dump() nl() nl(" [Player Achievments]") nl() - for id, data in pairs(self.actor.achievements) do + for id, data in pairs(self.actor.achievements or {}) do local a = world:getAchievementFromId(id) nl(("%s Was Achieved for %s At %s"):format(a.name,a.desc,data.when)) end @@ -415,6 +418,9 @@ function _M:dump() local char = string.char(string.byte('a') + item - 1) nl(("%s) %s"):format(char, o:getName{force_id=true})) nl((" %s"):format(table.concat(o:getTextualDesc(), "\n "))) + if o.droppedBy then + nl((" Dropped by %s"):format(o.droppedBy)) + end end end diff --git a/game/special/mainmenu/class/Game.lua b/game/special/mainmenu/class/Game.lua index 9219d94c89..261c88bce1 100644 --- a/game/special/mainmenu/class/Game.lua +++ b/game/special/mainmenu/class/Game.lua @@ -105,6 +105,10 @@ function _M:commandLineArgs(args) if req_mod then local mod = self.mod_list[req_mod] if mod then + profile.generic.modules_loaded = profile.generic.modules_loaded or {} + profile.generic.modules_loaded[req_mod] = (profile.generic.modules_loaded[req_mod] or 0) + 1 + profile:saveGenericProfile("modules_loaded", profile.generic.modules_loaded) + local M, W = mod.load() _G.game = M.new() @@ -140,6 +144,21 @@ function _M:onQuit() os.exit() end +function _M:checkFirstTime() + if not profile.generic.firstrun then + profile:checkFirstRun() + Dialog:yesnoPopup("First run profile notification", "Do you want to create or login to an Online profile?", function(ret) + if ret then + self:selectStepOnlineProfile() + else + self:selectStepMain() + end + end) + else + self:selectStepMain() + end +end + function _M:selectStepMain() if self.step and self.step.close then self.step:close() end @@ -177,6 +196,14 @@ function _M:selectStepMain() end, }, }, self.w * 0.3, self.h * 0.2, self.w * 0.4, self.h * 0.3) + + if not self.firstrunchecked then + -- Check first time run for online profile + self.firstrunchecked = true + self:checkFirstTime() + return + end + self.step:setKeyHandling() self.step:setMouseHandling() end @@ -201,7 +228,7 @@ function _M:selectStepNew() local lines = game.mod_list[game.step.selected].description:splitLines(self.w - 8, self.font) local r, g, b for i = 1, #lines do - r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) + r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) end end self:registerDialog(display_module) @@ -264,7 +291,7 @@ function _M:selectStepLoad() local lines = list[game.step.selected].description:splitLines(self.w - 8, self.font) local r, g, b for i = 1, #lines do - r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) + r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) end end self:registerDialog(display_module) @@ -331,7 +358,7 @@ function _M:selectStepInstall() local lines = dllist[game.step.selected].description:splitLines(self.w - 8, self.font) local r, g, b for i = 1, #lines do - r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) + r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) end end self:registerDialog(display_module) @@ -342,68 +369,75 @@ function _M:selectStepInstall() self.step.key:addBind("EXIT", function() self:unregisterDialog(display_module) self:selectStepMain() end) end -function _M:selectStepProfile() - local linda, th = Module:loadRemoteList() - local rawdllist = linda:receive("moduleslist") - th:join() - - local dllist = {} - for i, mod in ipairs(rawdllist) do - if not self.mod_list[mod.short_name] then - dllist[#dllist+1] = mod +function _M:createProfile(loginItem) + if self.justlogin then + profile:performlogin(loginItem.login, loginItem.pass) + if profile.auth then + Dialog:simplePopup("Profile logged in!", "Your online profile is active now...", function() self:checkLogged() self:selectStepProfile() end ) else - local lmod = self.mod_list[mod.short_name] - if mod.version[1] * 1000000 + mod.version[2] * 1000 + mod.version[3] > lmod.version[1] * 1000000 + lmod.version[2] * 1000 + lmod.version[3] then - dllist[#dllist+1] = mod - end + Dialog:simplePopup("Log in rejected", "Couldn't log you...", function() self:selectStepProfile() end ) end - end - - if #dllist == 0 then - Dialog:simplePopup("No modules available", "There are no modules to install or upgrade.") return end - - local display_module = Dialog.new("", self.w * 0.73, self.h, self.w * 0.26, 0, 255) - - for i, mod in ipairs(dllist) do - mod.fct = function() - local d = DownloadDialog.new("Downloading: "..mod.long_name, mod.download, function(di, data) - fs.mkdir("/modules") - local f = fs.open("/modules/"..mod.short_name..".team", "w") - for i, v in ipairs(data) do f:write(v) end - f:close() - - -- Relist modules and savefiles - self.mod_list = Module:listModules() - self.save_list = Module:listSavefiles() - - if self.mod_list[mod.short_name] then - Dialog:simplePopup("Success!", "Your new game is now installed, you can play!", function() self:unregisterDialog(display_module) self:selectStepMain() end) - else - Dialog:simplePopup("Error!", "The downloaded game does not seem to respond to the test. Please contact contact@te4.org") - end - end) - self:registerDialog(d) - d:startDownload() - end - mod.onSelect = function() - display_module.title = mod.long_name - display_module.changed = true - end + profile:newProfile(loginItem.login, loginItem.name, loginItem.pass, loginItem.email) + if (profile.auth) then + Dialog:simplePopup("Profile created!", "Your online profile is active now...", function() self:checkLogged() self:selectStepProfile() end ) + else + Dialog:simplePopup("Profile Failed to authenticate!", "Try logging in in a few moments", function() self:selectStepProfile() end ) end - display_module.drawDialog = function(self, s) - local lines = dllist[game.step.selected].description:splitLines(self.w - 8, self.font) - local r, g, b - for i = 1, #lines do - r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b) - end - end - self:registerDialog(display_module) +end - self.step = ButtonList.new(dllist, 10, 10, self.w * 0.24, (5 + 35) * #dllist, nil, 5) +function _M:selectStepProfile() + self.step = ButtonList.new({ + { + name = "Online Profile", + fct = function() + self:selectStepOnlineProfile() + end, + }, + { + name = "Browse Generic Profile", + fct = function() + self:selectGenericProfile() + end, + }, + { + name = "Browse Module Profiles", + fct = function() + self:selectModuleProfile() + end, + }, + { + name = "Exit", + fct = function() + game:unregisterDialog(self) + self:selectStepMain() + end, + }, + }, self.w * 0.3, self.h * 0.2, self.w * 0.4, self.h * 0.3) self.step:setKeyHandling() self.step:setMouseHandling() self.step.key:addBind("EXIT", function() self:unregisterDialog(display_module) self:selectStepMain() end) end + +function _M:selectStepOnlineProfile() + if (profile.auth) then + Dialog:yesnoPopup("You are logged in", "Do you want to log out?", function(ret) + if ret then + profile:logOut() + self:checkLogged() + end + end) + else + local dialogdef = { } + dialogdef.name = "Profile login"; + dialogdef.short = "Login"; + dialogdef.fct = function(login) self:setPlayerLogin(login) end + Dialog:yesnoPopup("You are not registered", "Do you want to login to an existing profile?", function(ret) + self.justlogin = ret + dialogdef.justlogin = ret + self:registerDialog(require('special.mainmenu.dialogs.ProfileLogin').new(dialogdef)) + end) + end +end diff --git a/game/special/mainmenu/dialogs/ProfileLogin.lua b/game/special/mainmenu/dialogs/ProfileLogin.lua new file mode 100644 index 0000000000..95c1f5ec34 --- /dev/null +++ b/game/special/mainmenu/dialogs/ProfileLogin.lua @@ -0,0 +1,130 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +require "engine.Dialog" +local ButtonList = require "engine.ButtonList" +local Button = require "engine.Button" +local TextBox = require "engine.TextBox" + +module(..., package.seeall, class.inherit(engine.Dialog)) + +function _M:init(dialogdef) + engine.Dialog.init(self, "Profile Login "..dialogdef.name, 500, 400) + self.dialogdef = dialogdef + self.alpha = 230 + self.justlogin = dialogdef.justlogin + + self:keyCommands({ + _DELETE = function() + if self.controls[self.state] and self.controls[self.state].delete then + self.controls[self.state]:delete() + end + end, + _TAB = function() + self.state = self:changeFocus(true) + end, + _DOWN = function() + self.state = self:changeFocus(true) + end, + _UP = function() + self.state = self:changeFocus(false) + end, + _RIGHT = function() + if self.state ~= "" and self.controls[self.state] and self.controls[self.state].moveRight then + self.controls[self.state]:moveRight(1) + else + self.state = self:changeFocus(true) + end + end, + _LEFT = function() + if self.state ~= "" and self.controls[self.state] and self.controls[self.state].moveLeft then + self.controls[self.state]:moveLeft(1) + else + self.state = self:changeFocus(false) + end + end, + _BACKSPACE = function() + if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="TextBox" then + self.controls[self.state]:backSpace() + end + end, + __TEXTINPUT = function(c) + if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="TextBox" then + self.controls[self.state]:textInput(c) + end + end, + _RETURN = function() + if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="Button" then + self.controls[self.state]:fct() + end + end, + }, { + EXIT = function() + game:unregisterDialog(self) + game:selectStepProfile() + end + }) + self:setMouseHandling() + self:addControl(Button.new("ok", "Ok", 50, 330, 50, 30, self, self.font, function() self:okclick() end)) + self:addControl(Button.new("cancel", "Canel", 400, 330, 50, 30, self, self.font, function() self:cancelclick() end)) + self:addControl(TextBox.new({name="login",title="You Login:",min=2, max=25, x=30, y=30, w=350, h=30}, self, self.font, "login name")) + self:addControl(TextBox.new({name="pass",title="Password:",min=2, max=25, x=30, y=70, w=350, h=30}, self, self.font, "password")) + if not self.justlogin then + self:addControl(TextBox.new({name="email",title="Email Address:",min=2, max=25, x=30, y=110, w=350, h=30}, self, self.font, "email address")) + self:addControl(TextBox.new({name="name",title="Name:",min=2, max=25, x=30, y=150, w=350, h=30}, self, self.font, "name")) + end + self:focusControl("login") +end + + +function _M:okclick() + game:unregisterDialog(self) + results = self:databind() + game:selectStepProfile() + game:createProfile(results) +end + +function _M:cancelclick() + game:unregisterDialog(self) + game:selectStepProfile() +end + +function _M:setMouseHandling() + self.old_mouse = engine.Mouse.current + self.mouse = engine.Mouse.new() + self.mouse:setCurrent() + game.mouse = self.mouse +end + + +function _M:drawDialog(s, w, h) + local y = 200 + local x = 30 + lines = { "What's the online profile for?", "* Playing from several computers without copying and worries.", "* Keep track of your modules progression.", "* For Example Kill Count, Unlockables unlocked and achievments in TOME", "* Unlimited possibilites for migrating your module information.", "* Cool statistics for each module to help sharpen your gameplay style", "* Who doesn't like statistics?"} + for i = 1, #lines do + r, g, b = s:drawColorStringBlended(self.font, lines[i], x, y + i * self.font:lineSkip(), r, g, b) + end + self:drawControls(s) +end + +function _M:close() + if self.old_key then self.old_key:setCurrent() end + if self.old_mouse then self.old_mouse:setCurrent() end +end -- GitLab