From 868e118b1fc23f57862ecd11d1829f93d9159df2 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Mon, 1 Aug 2011 19:24:40 +0000
Subject: [PATCH] Added local scoring, with a list of all your chars and their
 scores/cause of death/... Arena is merged into that system.

git-svn-id: http://svn.net-core.org/repos/t-engine4@4055 51575b47-30f0-44d4-a5cc-537603b46e54
---
 build/te4core.lua                             |  30 ++---
 game/engines/default/engine/Game.lua          |   4 +
 game/engines/default/engine/HighScores.lua    |  94 +++++++++++++
 .../default/engine/dialogs/GameMenu.lua       |   5 +
 .../default/engine/dialogs/ViewHighScores.lua |  52 +++++++
 .../default/modules/boot/dialogs/MainMenu.lua |   1 +
 .../modules/boot/dialogs/ViewHighScores.lua   | 127 ++++++++++++++++++
 game/modules/tome/class/Game.lua              |  51 ++++++-
 .../tome/class/interface/PlayerStats.lua      |   1 +
 game/modules/tome/dialogs/ArenaFinish.lua     |   6 +-
 game/modules/tome/init.lua                    |  37 ++++-
 premake4.lua                                  |   2 +-
 src/luajit2/src/lj_cparse.c                   |   2 +-
 13 files changed, 390 insertions(+), 22 deletions(-)
 create mode 100644 game/engines/default/engine/HighScores.lua
 create mode 100644 game/engines/default/engine/dialogs/ViewHighScores.lua
 create mode 100644 game/engines/default/modules/boot/dialogs/ViewHighScores.lua

diff --git a/build/te4core.lua b/build/te4core.lua
index 4a0ca6dfbf..44730276be 100644
--- a/build/te4core.lua
+++ b/build/te4core.lua
@@ -30,22 +30,22 @@ project "TEngine"
 
 	configuration "macosx"
 		files { "../src/mac/SDL*" }
-	        includedirs {
-        	      "/System/Library/Frameworks/OpenGL.framework/Headers",
-        	      "/System/Library/Frameworks/OpenAL.framework/Headers",
-	              "/Library/Frameworks/Ogg.framework/Headers",
-	              "/Library/Frameworks/Vorbis.framework/Headers",
-	              "/Library/Frameworks/SDL.framework/Headers",
-	              "/Library/Frameworks/SDL.framework/Headers",
-        	      "/Library/Frameworks/SDL_net.framework/Headers",
-	              "/Library/Frameworks/SDL_image.framework/Headers",
-        	      "/Library/Frameworks/SDL_ttf.framework/Headers",
-	              "/Library/Frameworks/SDL_mixer.framework/Headers"
-        	}
-	        defines { "USE_TENGINE_MAIN", 'SELFEXE_MACOSX', [[TENGINE_HOME_PATH='"/Library/Application Support/T-Engine/"']]  }
-		linkoptions { "-framework Vorbis", "-framework Ogg", "-framework SDL", "-framework SDL_image", "-framework SDL_ttf", "-framework SDL_mixer", "-framework Cocoa", "-framework OpenGL" , "-framework OpenAL" }
+		includedirs {
+  	      "/System/Library/Frameworks/OpenGL.framework/Headers",
+  	      "/System/Library/Frameworks/OpenAL.framework/Headers",
+		      "/Library/Frameworks/SDL.framework/Headers",
+		      "/Library/Frameworks/SDL.framework/Headers",
+  	      "/Library/Frameworks/SDL_net.framework/Headers",
+		      "/Library/Frameworks/SDL_image.framework/Headers",
+  	      "/Library/Frameworks/SDL_ttf.framework/Headers",
+		      "/Library/Frameworks/SDL_mixer.framework/Headers",
+			"/opt/local/include",
+			"/opt/local/include/Vorbis"
+  	}
+	  defines { "USE_TENGINE_MAIN", 'SELFEXE_MACOSX'  }
+		linkoptions { "-framework SDL", "-framework SDL_image", "-framework SDL_ttf", "-framework SDL_mixer", "-framework Cocoa", "-framework OpenGL" , "-framework OpenAL", "-pagezero_size 10000","-image_base 100000000" }
 		targetdir "."
-        	links { "IOKit" }
+		links { "IOKit" }
 
 	configuration "windows"
 		links { "mingw32", "SDLmain", "SDL", "SDL_ttf", "SDL_image", "openal32", "vorbisfile", "OPENGL32", "GLU32", "wsock32" }
diff --git a/game/engines/default/engine/Game.lua b/game/engines/default/engine/Game.lua
index 6c003d0e7f..ca04607d6b 100644
--- a/game/engines/default/engine/Game.lua
+++ b/game/engines/default/engine/Game.lua
@@ -429,6 +429,10 @@ end
 function _M:saveGame()
 end
 
+--- Saves the highscore of the current char
+function _M:registerHighscore()
+end
+
 --- Add a coroutine to the pool
 -- Coroutines registered will be run each game tick
 function _M:registerCoroutine(id, co)
diff --git a/game/engines/default/engine/HighScores.lua b/game/engines/default/engine/HighScores.lua
new file mode 100644
index 0000000000..b02e82e80e
--- /dev/null
+++ b/game/engines/default/engine/HighScores.lua
@@ -0,0 +1,94 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009, 2010, 2011 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"
+local Map = require "engine.Map"
+
+--- Define high score table support
+---
+--- This will eventually actually generate a GUI table and handle
+--- filtering etc but for now it just generates a text string which
+--- represents the list of high scores.
+
+module(..., package.seeall, class.make)
+
+--- to call these you need to have profile.mod pointing at the right module
+--- that's normally automatic? But the code which runs from the 'boot'
+--- module sets it up carefully
+
+-- register a highscore against a campaign
+function registerScore(world,details)
+	details.type = "dead"
+
+	profile:saveModuleProfile("scores", details)
+end
+
+-- scores of living players are stored against their name
+function noteLivingScore(world,name,details)
+	details.type = "alive"
+
+	profile:saveModuleProfile("scores", details)
+end
+
+-- and should be removed when the char dies
+function clearLivingScore(world,name)
+	-- don't know how to write this
+	-- don't know how to remove from the profile
+end
+
+
+function createHighScoreTable(world,formatters)
+	local highscores = ""
+
+	if not (profile.mod.scores and profile.mod.scores.sc and 
+			profile.mod.scores.sc[world]) then
+		print ("No scores yet!")
+		return ""
+	end
+
+	print ("createHighScoreTable called with world : ",world)
+
+	local defaultformatter = formatters.dead
+
+	-- clone the scores and insert the alive ones in
+	local scores = table.clone(profile.mod.scores.sc[world].dead or {})
+	local alive = profile.mod.scores.sc[world].alive or {}
+	for k,e in pairs(alive) do
+		e.formatter = "alive"
+		table.insert(scores,e)
+	end
+
+	table.sort(scores, function(a,b) return a.score and b.score and a.score > b.score end)
+	for k = 1, #scores do
+		local formatter = defaultformatter
+		if scores[k].formatter then
+			formatter = formatters[scores[k].formatter]
+		end
+		if formatter then
+			highscores = highscores .. formatter:gsub(
+				"{([a-z]+)}",
+				function(field) 
+					return scores[k][field] or "nil"
+				end
+			) .. "\n"
+		end
+	end
+
+	return highscores
+end
diff --git a/game/engines/default/engine/dialogs/GameMenu.lua b/game/engines/default/engine/dialogs/GameMenu.lua
index 23fd7ce553..f621228924 100644
--- a/game/engines/default/engine/dialogs/GameMenu.lua
+++ b/game/engines/default/engine/dialogs/GameMenu.lua
@@ -78,6 +78,11 @@ function _M:generateList(actions)
 			local menu = require("engine.dialogs.AudioOptions").new()
 			game:registerDialog(menu)
 		end },
+		highscores = { "View High Scores", function()
+			game:unregisterDialog(self)
+			local menu = require("engine.dialogs.ViewHighScores").new()
+			game:registerDialog(menu)
+	  end },
 		save = { "Save Game", function() game:unregisterDialog(self) game:saveGame() end },
 		quit = { "Save and Exit", function() game:unregisterDialog(self) game:onQuit() end },
 	}
diff --git a/game/engines/default/engine/dialogs/ViewHighScores.lua b/game/engines/default/engine/dialogs/ViewHighScores.lua
new file mode 100644
index 0000000000..df6362dd9b
--- /dev/null
+++ b/game/engines/default/engine/dialogs/ViewHighScores.lua
@@ -0,0 +1,52 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009, 2010, 2011 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"
+local Dialog = require "engine.ui.Dialog"
+local Textzone = require "engine.ui.Textzone"
+local HighScores = require "engine.HighScores"
+
+module(..., package.seeall, class.inherit(Dialog))
+
+function _M:init()
+	Dialog.init(self, "High Scores", game.w * 0.8, game.h * 0.8)
+
+	game:registerHighscore()
+
+	local text = self:generateScores()
+	self.c_desc = Textzone.new{width=self.iw, height=self.ih, text=text}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_desc},
+	}
+	self:setFocus(self.c_desc)
+	self:setupUI()
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:generateScores()
+	local player = game.party:findMember{main=true}
+	local campaign = player.descriptor.world
+	local formatters = game.__mod_info.score_formatters[campaign]
+
+	return HighScores.createHighScoreTable(campaign,formatters)
+end
diff --git a/game/engines/default/modules/boot/dialogs/MainMenu.lua b/game/engines/default/modules/boot/dialogs/MainMenu.lua
index 29f64cadf1..4c1e368fd3 100644
--- a/game/engines/default/modules/boot/dialogs/MainMenu.lua
+++ b/game/engines/default/modules/boot/dialogs/MainMenu.lua
@@ -34,6 +34,7 @@ function _M:init()
 	l[#l+1] = {name="New Game", fct=function() game:registerDialog(require("mod.dialogs.NewGame").new()) end}
 	l[#l+1] = {name="Load Game", fct=function() game:registerDialog(require("mod.dialogs.LoadGame").new()) end}
 	l[#l+1] = {name="Player Profile", fct=function() game:registerDialog(require("mod.dialogs.Profile").new()) end}
+	l[#l+1] = {name="View High Scores", fct=function() game:registerDialog(require("mod.dialogs.ViewHighScores").new()) end}
 	if config.settings.install_remote then l[#l+1] = {name="Install Module", fct=function() end} end
 	if config.settings.update_remote then l[#l+1] = {name="Update", fct=function() game:registerDialog(require("mod.dialogs.UpdateAll").new()) end} end
 	l[#l+1] = {name="Options", fct=function()
diff --git a/game/engines/default/modules/boot/dialogs/ViewHighScores.lua b/game/engines/default/modules/boot/dialogs/ViewHighScores.lua
new file mode 100644
index 0000000000..2d4ca37d90
--- /dev/null
+++ b/game/engines/default/modules/boot/dialogs/ViewHighScores.lua
@@ -0,0 +1,127 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009, 2010, 2011 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"
+local Module = require "engine.Module"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Button = require "engine.ui.Button"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
+local Savefile = require "engine.Savefile"
+local HighScores = require "engine.HighScores"
+
+require "engine.PlayerProfile"
+
+
+module(..., package.seeall, class.inherit(Dialog))
+
+function _M:init()
+	Dialog.init(self, "View High Scores", game.w * 0.8, game.h * 0.8)
+
+	-- high score table on right
+	self.c_desc = Textzone.new{width=math.floor(self.iw / 3 * 2 - 10), height=self.ih, text=""}
+
+	-- list of modules on left (top)
+
+	self:generateList()
+
+	self.c_list = ListColumns.new{width=math.floor(self.iw / 3 - 10), height=math.floor(self.ih / 2), scrollbar=true, columns={
+		{name="Game Module", width=80, display_prop="name"},
+		{name="Version", width=20, display_prop="version_txt"},
+	}, list=self.list, fct=function(item) end, select=function(item, sel) self:changemodules(item) end}
+
+	-- list of campaigns/worlds on left (bottom)
+	self.c_sublist = ListColumns.new{width=math.floor(self.iw / 3 - 10), height=math.floor(self.ih / 2),
+		columns = {{name="World",width=100,display_prop="world"}},
+		list={}, select=function(item,sel) self:changeworlds(item) end}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=0, ui=self.c_desc},
+		{left=0, bottom=0, ui=self.c_sublist},
+		{left=self.c_list.w + 5, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+
+	self:changemodules(self.list[1])
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:generateList()
+	local list = Module:listModules()
+	self.list = {}
+
+	for i = 1, #list do
+		for j, mod in ipairs(list[i].versions) do
+			if j > 1 then break end
+			if not mod.is_boot then
+				mod.name = tstring{{"font","bold"}, {"color","GOLD"}, mod.name, {"font","normal"}}
+				mod.version_txt = ("%d.%d.%d"):format(mod.version[1], mod.version[2], mod.version[3])
+
+				-- Have to load the profile to get the highscores
+				profile:addStatFields(unpack(mod.profile_stats_fields or {}))
+				profile:loadModuleProfile(mod.short_name)
+				print ("Loaded profile for "..mod.short_name.."\n")
+				mod.highscores = {}
+				if (profile.mod.scores and profile.mod.scores.sc) then
+					-- one formatter for each world
+					for world,formatter in pairs(mod.score_formatters) do
+						-- call module-provided formatter on each element in list
+						-- to generate a table for that world
+						print ("Preparing scores for ",world,"with",formatter)
+						mod.highscores[world] = HighScores.createHighScoreTable(world,formatter)
+					end
+				end
+				mod.zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="#{bold}##GOLD#"..mod.long_name.."#GREEN# High Scores#WHITE##{normal}#\n\n"}
+				table.insert(self.list, mod)
+			end
+		end
+	end
+end
+
+function _M:changemodules(item)
+	if item and self.uis[2] then
+		self.uis[2].ui = item.zone
+		self.cur_sel = item
+
+		local worlds = {}
+		for k,_ in pairs(item.highscores) do table.insert(worlds,{world=k}) end
+		self.c_sublist:setList(worlds)
+		if #worlds > 0 then
+			-- show text from first world
+			item.zone.text = "#{bold}##GOLD#"..item.long_name.."("..worlds[1].world..")".."#GREEN# High Scores#WHITE##{normal}#\n\n"
+			item.zone.text = item.zone.text .. item.highscores[worlds[1].world]
+			item.zone:generate()
+		end
+	end
+end
+
+function _M:changeworlds(item)
+	if item and self.uis[2] then
+		world = item.world;
+		self.cur_sel.zone.text = "#{bold}##GOLD#"..self.cur_sel.long_name.."("..world..")".."#GREEN# High Scores#WHITE##{normal}#\n\n"
+		self.cur_sel.zone.text = self.cur_sel.zone.text .. self.cur_sel.highscores[world]
+		self.cur_sel.zone:generate()
+	end
+end
\ No newline at end of file
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 01a84a8d23..5b8b6927d5 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -33,6 +33,7 @@ local Birther = require "mod.dialogs.Birther"
 local Astar = require "engine.Astar"
 local DirectPath = require "engine.DirectPath"
 local Shader = require "engine.Shader"
+local HighScores = require "engine.HighScores"
 
 local NicerTiles = require "mod.class.NicerTiles"
 local GameState = require "mod.class.GameState"
@@ -1056,7 +1057,22 @@ function _M:setupCommands()
 			end end
 		end end,
 		[{"_g","ctrl"}] = function() if config.settings.cheat then
-			self.state:debugRandomZone()
+--			self.state:debugRandomZone()
+
+			local list = {}
+			local grab_list = function(l)
+				for _, o in pairs(l) do
+					if o.unique and not o.randart and o.type~="lore" then list[o.unique] = o end
+				end
+			end
+			for i, zone in ipairs(fs.list("/data/zones/")) do
+				local file = "/data/zones/"..zone.."/objects.lua"
+				if fs.exists(file) then grab_list(mod.class.Object:loadList(file)) end
+			end
+
+			for u, o in pairs(list) do
+				print(o.type, o.subtype, o.name, unpack(table.keys(o.power_source or {})))
+			end
 		end end,
 		[{"_f","ctrl"}] = function() if config.settings.cheat then
 game.player:learnTalent('T_MULTIPLY',true)
@@ -1329,6 +1345,7 @@ game.player:learnTalent('T_GIFT_OF_AMAKTHEL',true)
 				"resume",
 				"achievements",
 				{ "Show known Lore", function() game:unregisterDialog(menu) game:registerDialog(require("mod.dialogs.ShowLore").new("Tales of Maj'Eyal Lore", self.player)) end },
+				"highscores",
 				{ "Inventory", function() game:unregisterDialog(menu) self.key:triggerVirtual("SHOW_INVENTORY") end },
 				{ "Character Sheet", function() game:unregisterDialog(menu) self.key:triggerVirtual("SHOW_CHARACTER_SHEET") end },
 				"keybinds",
@@ -1552,8 +1569,40 @@ function _M:onSavefilePush()
 	self.player:restStop("saving")
 end
 
+--- Saves the highscore of the current char
+function _M:registerHighscore()
+	local player = self:getPlayer(true)
+	local campaign = player.descriptor.world
+
+	local details = {
+		world = player.descriptor.world,
+		subrace = player.descriptor.subrace,
+		subclass = player.descriptor.subclass,
+		difficulty = player.descriptor.difficulty,
+		level = player.level,
+		name = player.name,
+		where = self.zone.name,
+		dlvl = self.level.level
+	}
+	if campaign == 'Arena' then
+		details.score = self.level.arena.score
+	else
+		-- fallback score based on xp, this is a placeholder
+		details.score = math.floor(10 * (player.level + (player.exp / player:getExpChart(player.level)))) + math.floor(player.money / 100)
+	end
+
+	if player.dead then
+		details.killedby = player.killedBy and player.killedBy.name or "???"
+		HighScores.registerScore(campaign, details)
+	else
+		HighScores.noteLivingScore(campaign, player.name, details)
+	end
+end
+
 --- Requests the game to save
 function _M:saveGame()
+	self:registerHighscore()
+
 	-- savefile_pipe is created as a global by the engine
 	savefile_pipe:push(self.save_name, "game", self)
 	world:saveWorld()
diff --git a/game/modules/tome/class/interface/PlayerStats.lua b/game/modules/tome/class/interface/PlayerStats.lua
index 47df51d59e..c42059646d 100644
--- a/game/modules/tome/class/interface/PlayerStats.lua
+++ b/game/modules/tome/class/interface/PlayerStats.lua
@@ -19,6 +19,7 @@
 
 require "engine.class"
 
+
 module(..., package.seeall, class.make)
 
 function _M:playerStatGetCharacterIdentifier(p)
diff --git a/game/modules/tome/dialogs/ArenaFinish.lua b/game/modules/tome/dialogs/ArenaFinish.lua
index 9b019c090a..515a35d18e 100644
--- a/game/modules/tome/dialogs/ArenaFinish.lua
+++ b/game/modules/tome/dialogs/ArenaFinish.lua
@@ -129,9 +129,9 @@ function _M:use(item)
 	local act = item.action
 
 	if act == "exit" then
-		local save = Savefile.new(game.save_name)
-		save:delete()
-		save:close()
+		game:getPlayer(true).dead = true
+		game:saveGame()
+
 		world:saveWorld()
 		if item.subaction == "none" then
 			util.showMainMenu()
diff --git a/game/modules/tome/init.lua b/game/modules/tome/init.lua
index 8c0d1a676c..fca9fbab3c 100644
--- a/game/modules/tome/init.lua
+++ b/game/modules/tome/init.lua
@@ -45,11 +45,12 @@ Still, this is a golden age. Civilisations are healing the wounds of thousands o
 You are an adventurer, set out to discover wonders, explore old places, and venture into the unknown for wealth and glory.
 ]]
 starter = "mod.load"
-profile_stats_fields = {"artifacts", "characters", "deaths", "uniques", "lore", "escorts"}
+profile_stats_fields = {"artifacts", "characters", "deaths", "uniques", "scores", "lore", "escorts"}
 allow_userchat = true -- We can talk to the online community
 no_get_name = true -- Name setting for new characters is done by the module itself
 
 -- Define the fields that are sync'ed online, and how they are sync'ed
+-- '
 profile_defs = {
 	allow_build = { {name="index:string:30"}, receive=function(data, save) save[data.name] = true end, export=function(env) for k, _ in pairs(env) do add{name=k} end end },
 	lore = { {name="index:string:30"}, receive=function(data, save) save.lore = save.lore or {} save.lore[data.name] = true end, export=function(env) for k, v in pairs(env.lore or {}) do add{name=k} end end },
@@ -60,4 +61,38 @@ profile_defs = {
 	deaths = { {cid="index:string:50"}, {source="index:string:50"}, {nb="number"}, receive=function(data, save) save.sources = save.sources or {} save.sources[data.cid] = save.sources[data.cid] or {} inc_set(save.sources[data.cid], data.source, data, "nb") end, export=function(env) for cid, d in pairs(env.sources or {}) do for name, v in pairs(d) do add{cid=cid, source=name, nb=v} end end end },
 	achievements = { {id="index:string:40"}, {gained_on="timestamp"}, {who="string:50"}, {turn="number"}, receive=function(data, save) save[data.id] = {who=data.who, when=data.gained_on, turn=data.turn} end, export=function(env) for id, v in pairs(env) do add{id=id, who=v.who, gained_on=v.when, turn=v.turn} end end },
 	donations = { no_sync=true, {last_ask="timestamp"}, receive=function(data, save) save.last_ask = data.last_ask end, export=function(env) add{last_ask=env.last_ask} end },
+	scores = {
+		nosync=true,
+		receive=function(data,save)
+			save.sc = save.sc or {}
+			save.sc[data.world] = save.sc[data.world] or {}
+			save.sc[data.world].alive = save.sc[data.world].alive or {}
+			save.sc[data.world].dead = save.sc[data.world].dead or {}
+			if data.type == "alive" then
+				save.sc[data.world].alive = save.sc[data.world].alive or {}
+				save.sc[data.world].alive[data.name] = data
+			else
+				-- clear any 'alive' entry with this name
+				save.sc[data.world].alive[data.name] = nil
+				save.sc[data.world].dead = save.sc[data.world].dead or {}
+				save.sc[data.world].dead[#save.sc[data.world].dead+1] = data
+			end
+		end
+	},
 }
+
+score_formatters = {
+	["Maj'Eyal"] = {
+		alive="#LIGHT_GREEN#{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# is still alive and well on #GREEN#{where}#LAST##WHITE#",
+		dead="{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# died on #GREEN#{where}#LAST#, killed by a #RED#{killedby}#LAST#"
+	},
+	["Infinite"] = {
+		alive="#LIGHT_GREEN#{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# is still alive and well on level #GREEN#{dlvl}#LAST##WHITE#",
+		dead="{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# died on level #GREEN#{dlvl}#LAST#, killed by a #RED#{killedby}#LAST#"
+	},
+	["Arena"] = {
+		alive="#LIGHT_GREEN#{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# is still alive and well on wave #GREEN#{dlvl}#LAST##WHITE#",
+		dead="{score} : #BLUE#{name}#LAST# the #LIGHT_RED#level {level} {subrace} {subclass}#LAST# died on wave #GREEN#{dlvl}#LAST#, killed by a #RED#{killedby}#LAST#"
+	}
+}
+
diff --git a/premake4.lua b/premake4.lua
index b8aba7a064..5ad300eca8 100644
--- a/premake4.lua
+++ b/premake4.lua
@@ -48,7 +48,7 @@ configuration "windows"
 configuration "Debug"
 	defines { }
 	flags { "Symbols" }
-	buildoptions { "-ggdb" }
+	buildoptions { "-ggdb", "-pagezero_size 10000", "-image_base 100000000" }
 	targetdir "bin/Debug"
 	if _OPTIONS.luaassert then defines {"LUA_USE_APICHECK"} end
 	if _OPTIONS.pedantic then buildoptions { "-Wall" } end
diff --git a/src/luajit2/src/lj_cparse.c b/src/luajit2/src/lj_cparse.c
index 523e51462e..aba68792a3 100644
--- a/src/luajit2/src/lj_cparse.c
+++ b/src/luajit2/src/lj_cparse.c
@@ -56,7 +56,7 @@ static LJ_AINLINE int cp_iseol(CPChar c)
   return (c == '\n' || c == '\r');
 }
 
-static LJ_AINLINE CPChar cp_get(CPState *cp);
+static CPChar cp_get(CPState *cp);
 
 /* Peek next raw character. */
 static LJ_AINLINE CPChar cp_rawpeek(CPState *cp)
-- 
GitLab