From a581ee6a454c231b4ea39e7c5412208e479ff037 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Tue, 12 Jan 2010 22:49:04 +0000
Subject: [PATCH] key binder

git-svn-id: http://svn.net-core.org/repos/t-engine4@236 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/data/keybinds/inventory.lua              |  10 +-
 game/engine/Dialog.lua                        |  11 +-
 game/engine/KeyBind.lua                       |  47 +++++-
 game/engine/dialogs/GameMenu.lua              |  73 +++++++++
 game/engine/dialogs/KeyBinder.lua             | 149 ++++++++++++++++++
 game/modules/tome/class/Game.lua              |   5 +
 .../modules/tome/data/general/npcs/vermin.lua |   8 +-
 src/core_lua.c                                |   7 +
 8 files changed, 295 insertions(+), 15 deletions(-)
 create mode 100644 game/engine/dialogs/GameMenu.lua
 create mode 100644 game/engine/dialogs/KeyBinder.lua

diff --git a/game/data/keybinds/inventory.lua b/game/data/keybinds/inventory.lua
index 0d366c2274..7d6c6d186a 100644
--- a/game/data/keybinds/inventory.lua
+++ b/game/data/keybinds/inventory.lua
@@ -2,13 +2,13 @@ defineAction{
 	default = { "uni:i", },
 	type = "SHOW_INVENTORY",
 	group = "inventory",
-	name = "shows inventory",
+	name = "Show inventory",
 }
 defineAction{
 	default = { "uni:e", },
 	type = "SHOW_EQUIPMENT",
 	group = "inventory",
-	name = "shows equipment",
+	name = "Show equipment",
 }
 
 defineAction{
@@ -28,18 +28,18 @@ defineAction{
 	default = { "uni:w", },
 	type = "WEAR_ITEM",
 	group = "inventory",
-	name = "wield/wear items",
+	name = "Wield/wear items",
 }
 defineAction{
 	default = { "uni:t", },
 	type = "TAKEOFF_ITEM",
 	group = "inventory",
-	name = "takeoff items",
+	name = "Takeoff items",
 }
 
 defineAction{
 	default = { "uni:u", },
 	type = "USE_ITEM",
 	group = "inventory",
-	name = "use items",
+	name = "Use items",
 }
diff --git a/game/engine/Dialog.lua b/game/engine/Dialog.lua
index d4dd056726..5cc9f7bc17 100644
--- a/game/engine/Dialog.lua
+++ b/game/engine/Dialog.lua
@@ -121,18 +121,21 @@ function _M:drawHBorder(s, x, y, h)
 	end
 end
 
-function _M:drawSelectionList(s, x, y, hskip, list, sel, prop, scroll, max)
+function _M:drawSelectionList(s, x, y, hskip, list, sel, prop, scroll, max, color, selcolor)
+	selcolor = selcolor or {0,255,255}
+	color = color or {255,255,255}
 	scroll = scroll or 1
 	max = max or 99999
 
 	for i = scroll, math.min(#list, scroll + max - 1) do
 		v = list[i]
-		if prop then v = tostring(v[prop])
+		if prop and type(v[prop]) == "string" then v = tostring(v[prop])
+		elseif prop and type(v[prop]) == "function" then v = tostring(v[prop](v))
 		else v = tostring(v) end
 		if sel == i then
-			s:drawColorString(self.font, v, x, y + (i-scroll) * hskip, 0, 255, 255)
+			s:drawColorString(self.font, v, x, y + (i-scroll) * hskip, selcolor[1], selcolor[2], selcolor[3])
 		else
-			s:drawColorString(self.font, v, x, y + (i-scroll) * hskip)
+			s:drawColorString(self.font, v, x, y + (i-scroll) * hskip, color[1], color[2], color[3])
 		end
 	end
 end
diff --git a/game/engine/KeyBind.lua b/game/engine/KeyBind.lua
index 7f06e40c70..2b203439a8 100644
--- a/game/engine/KeyBind.lua
+++ b/game/engine/KeyBind.lua
@@ -8,13 +8,16 @@ module(..., package.seeall, class.inherit(engine.KeyCommand))
 _M.binds_def = {}
 _M.binds_remap = {}
 _M.binds_loaded = {}
+_M.bind_order = 1
 
 function _M:defineAction(t)
 	assert(t.default, "no keybind default")
 	assert(t.name, "no keybind name")
 	t.desc = t.desc or t.name
 
+	t.order = _M.bind_order
 	_M.binds_def[t.type] = t
+	_M.bind_order = _M.bind_order + 1
 end
 
 --- Loads a list of keybind definitions
@@ -51,11 +54,51 @@ function _M:loadRemap(file)
 	end
 end
 
+--- Saves a keybinds remap
+function _M:saveRemap(file)
+	local restore = false
+	if not file then
+		restore = fs.getWritePath()
+		fs.setWritePath(engine.homepath)
+		file = "keybinds.cfg"
+	end
+
+	local f = fs.open(file, "w")
+
+	for virtual, keys in pairs(_M.binds_remap) do
+		if keys[1] and not keys[2] then
+			f:write(("%s = {%q,nil}\n"):format(virtual, keys[1]))
+		elseif not keys[1] and keys[2] then
+			f:write(("%s = {nil,%q}\n"):format(virtual, keys[2]))
+		elseif keys[1] and keys[2] then
+			f:write(("%s = {%q,%q}\n"):format(virtual, keys[1], keys[2]))
+		elseif not keys[1] and not keys[2] then
+			f:write(("%s = {nil,nil}\n"):format(virtual))
+		end
+	end
+
+	f:close()
+
+	if restore then
+		fs.setWritePath(restore)
+	end
+end
+
+--- Returns the binding table for the given type
+function _M:getBindTable(type)
+	return _M.binds_remap[type.type] or type.default
+end
+
 function _M:init()
 	engine.KeyCommand.init(self)
 	self.virtuals = {}
-	self.binds = {}
 
+	self:bindKeys()
+end
+
+--- Binds all virtuals to keys, either defaults or remapped ones
+function _M:bindKeys()
+	self.binds = {}
 	-- Bind defaults
 	for type, t in pairs(_M.binds_def) do
 		for i, ks in ipairs(_M.binds_remap[type] or t.default) do
@@ -70,7 +113,7 @@ end
 
 function _M:receiveKey(sym, ctrl, shift, alt, meta, unicode)
 	local ks, us = self:makeKeyString(sym, ctrl, shift, alt, meta, unicode)
-	print("[BIND]", sym, ctrl, shift, alt, meta, unicode, " :=: ", ks, us, " ?=? ", self.binds[ks], us and self.binds[us])
+--	print("[BIND]", sym, ctrl, shift, alt, meta, unicode, " :=: ", ks, us, " ?=? ", self.binds[ks], us and self.binds[us])
 	if self.binds[ks] and self.virtuals[self.binds[ks]] then
 		self.virtuals[self.binds[ks]](sym, ctrl, shift, alt, meta, unicode)
 		return
diff --git a/game/engine/dialogs/GameMenu.lua b/game/engine/dialogs/GameMenu.lua
new file mode 100644
index 0000000000..b2afd3a15a
--- /dev/null
+++ b/game/engine/dialogs/GameMenu.lua
@@ -0,0 +1,73 @@
+require "engine.class"
+require "engine.Dialog"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(actions)
+	self:generateList(actions)
+
+	engine.Dialog.init(self, "Game Menu", 300, #self.list * 30 + 20)
+
+	self.sel = 1
+	self.scroll = 1
+	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+
+	self:keyCommands({
+		__TEXTINPUT = function(c)
+			if c:find("^[a-z]$") then
+				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
+				self:use()
+			end
+		end,
+	},{
+		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
+		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
+		ACCEPT = function() self:use() end,
+		EXIT = function() game:unregisterDialog(self) end,
+	})
+	self:mouseZones{
+		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty)
+			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
+			if button == "left" then self:use()
+			elseif button == "right" then
+			end
+		end },
+	}
+end
+
+function _M:use()
+	self.list[self.sel].fct()
+end
+
+function _M:generateList(actions)
+	local default_actions = {
+		resume = { "Resume", function() game:unregisterDialog(self) end },
+		keybinds = { "Key Bindings", function()
+			game:unregisterDialog(self)
+			local menu = require("engine.dialogs.KeyBinder").new(game.normal_key)
+			game:registerDialog(menu)
+		end },
+		save = { "Save Game", function() game:saveGame() end },
+		quit = { "Save and Exit", function() game:onQuit() end },
+	}
+
+	-- Makes up the list
+	local list = {}
+	local i = 0
+	for _, act in ipairs(actions) do
+		if type(act) == "string" then
+			local a = default_actions[act]
+			list[#list+1] = { name=string.char(string.byte('a') + i)..")  "..a[1], fct=a[2] }
+			i = i + 1
+		else
+			local a = act
+			list[#list+1] = { name=string.char(string.byte('a') + i)..")  "..a[1], fct=a[2] }
+			i = i + 1
+		end
+	end
+	self.list = list
+end
+
+function _M:drawDialog(s)
+	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max)
+end
diff --git a/game/engine/dialogs/KeyBinder.lua b/game/engine/dialogs/KeyBinder.lua
new file mode 100644
index 0000000000..fb90a741b5
--- /dev/null
+++ b/game/engine/dialogs/KeyBinder.lua
@@ -0,0 +1,149 @@
+require "engine.class"
+require "engine.Dialog"
+local KeyBind = require "engine.KeyBind"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(key_source)
+	engine.Dialog.init(self, "Key bindings", 600, game.h / 1.2)
+
+	self:generateList(key_source)
+
+	self.key_source = key_source
+
+	self.selcol = 1
+	self.sel = 1
+	self.scroll = 1
+	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+
+	self:keyCommands({
+	},{
+		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
+		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
+		MOVE_LEFT = function() self.selcol = util.boundWrap(self.selcol - 1, 1, 2) self.changed = true end,
+		MOVE_RIGHT = function() self.selcol = util.boundWrap(self.selcol + 1, 1, 2) self.changed = true end,
+		ACCEPT = function() self:use() end,
+		EXIT = function()
+			game:unregisterDialog(self)
+			self.key_source:bindKeys()
+			KeyBind:saveRemap()
+		end,
+	})
+	self:mouseZones{
+		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty)
+			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
+			if button == "left" then self:use()
+			elseif button == "right" then
+			end
+		end },
+	}
+end
+
+function _M:use()
+	local t = self.list[self.sel]
+
+	--
+	-- Make a dialog to ask for the key
+	--
+	local title = "Press a key (or escape) for: "..t.name
+	local font = self.font
+	local w, h = font:size(title)
+	local d = engine.Dialog.new(title, w + 8, h + 25, nil, nil, nil, font)
+	d:keyCommands{__DEFAULT=function(sym, ctrl, shift, alt, meta, unicode)
+		-- Modifier keys are not treated
+		if sym == KeyBind._LCTRL or sym == KeyBind._RCTRL or
+		   sym == KeyBind._LSHIFT or sym == KeyBind._RSHIFT or
+		   sym == KeyBind._LALT or sym == KeyBind._RALT or
+		   sym == KeyBind._LMETA or sym == KeyBind._RMETA then
+			return
+		end
+
+		if sym == KeyBind._BACKSPACE then
+			t["bind"..self.selcol] = nil
+			KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
+			KeyBind.binds_remap[t.type][self.selcol] = nil
+		elseif sym ~= KeyBind._ESCAPE then
+			local ks = KeyBind:makeKeyString(sym, ctrl, shift, alt, meta, unicode)
+			print("Binding", t.name, "to", ks)
+			t["bind"..self.selcol] = ks
+
+			KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
+			KeyBind.binds_remap[t.type][self.selcol] = ks
+		end
+		game:unregisterDialog(d)
+	end}
+	d.drawDialog = function(self, s)
+		s:drawColorStringCentered(self.font, self.selcol == 1 and "Bind key" or "Bind alternate key", 2, 2, self.iw - 2, self.ih - 2)
+	end
+	game:registerDialog(d)
+end
+
+function _M:formatKeyString(ks)
+	if not ks then return "--" end
+
+	if ks:find("^uni:") then
+		return ks:sub(5)
+	else
+		local i, j, sym, ctrl, shift, alt, meta = ks:find("^sym:([0-9]+):([a-z]+):([a-z]+):([a-z]+):([a-z]+)$")
+		if not i then return "--" end
+
+		ctrl = ctrl == "true" and true or false
+		shift = shift == "true" and true or false
+		alt = alt == "true" and true or false
+		meta = meta == "true" and true or false
+		sym = tonumber(sym) or sym
+		sym = KeyBind.sym_to_name[sym] or sym
+		sym = sym:gsub("^_", "")
+
+		if ctrl then sym = "[ctrl]+"..sym end
+		if shift then sym = "[shift]+"..sym end
+		if alt then sym = "[alt]+"..sym end
+		if meta then sym = "[meta]+"..sym end
+
+		return sym
+	end
+end
+
+function _M:generateList(key_source)
+	local l = {}
+
+	for virtual, t in pairs(KeyBind.binds_def) do
+		if key_source.virtuals[virtual] then
+			l[#l+1] = t
+		end
+	end
+	table.sort(l, function(a,b)
+		if a.group ~= b.group then
+			return a.group < b.group
+		else
+			return a.order < b.order
+		end
+	end)
+
+	-- Makes up the list
+	local list = {}
+	local i = 0
+	for _, k in ipairs(l) do
+		local binds = KeyBind:getBindTable(k)
+		list[#list+1] = {
+			k = k,
+			name = k.name,
+			type = k.type,
+			bind1 = binds[1],
+			bind2 = binds[2],
+			b1 = function(v) return self:formatKeyString(v.bind1) end,
+			b2 = function(v) return self:formatKeyString(v.bind2) end,
+		}
+		i = i + 1
+	end
+	self.list = list
+end
+
+function _M:drawDialog(s)
+	local col = {155,155,0}
+	local selcol = {255,255,0}
+
+	self:drawSelectionList(s, 2,   5, self.font_h, self.list, self.sel, "name", self.scroll, self.max)
+	self:drawSelectionList(s, 200, 5, self.font_h, self.list, self.sel, "b1", self.scroll, self.max, col, self.selcol == 1 and selcol or col)
+	self:drawSelectionList(s, 400, 5, self.font_h, self.list, self.sel, "b2", self.scroll, self.max, col, self.selcol == 2 and selcol or col)
+end
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 93eefaa40f..9cd5e648e9 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -453,6 +453,11 @@ function _M:setupCommands()
 			self.gfxmode = util.boundWrap(self.gfxmode + 1, 1, 3)
 			self:setupDisplayMode()
 		end,
+
+		EXIT = function()
+			local menu = require("engine.dialogs.GameMenu").new{"resume", "keybinds", "save", "quit"}
+			self:registerDialog(menu)
+		end,
 	}
 --[[
 	self.key:addCommands
diff --git a/game/modules/tome/data/general/npcs/vermin.lua b/game/modules/tome/data/general/npcs/vermin.lua
index 97786c32b6..697130b315 100644
--- a/game/modules/tome/data/general/npcs/vermin.lua
+++ b/game/modules/tome/data/general/npcs/vermin.lua
@@ -1,8 +1,8 @@
 local Talents = require("engine.interface.ActorTalents")
 
 newEntity{
-	define_as = "BASE_NPC_VERMIN",
-	type = "nuisance", subtype = "vermins",
+	define_as = "BASE_NPC_WORM",
+	type = "vermin", subtype = "worms",
 	display = "w", color=colors.WHITE,
 	can_multiply = 2,
 	body = { INVEN = 10 },
@@ -14,7 +14,7 @@ newEntity{
 	combat_armor = 1, combat_def = 1,
 }
 
-newEntity{ base = "BASE_NPC_VERMIN",
+newEntity{ base = "BASE_NPC_WORM",
 	name = "white worm mass", color=colors.WHITE,
 	level_range = {1, 15}, exp_worth = 1,
 	rarity = 4,
@@ -24,7 +24,7 @@ newEntity{ base = "BASE_NPC_VERMIN",
 	talents = resolvers.talents{ [Talents.T_CRAWL_POISON]=1 },
 }
 
-newEntity{ base = "BASE_NPC_VERMIN",
+newEntity{ base = "BASE_NPC_WORM",
 	name = "green worm mass", color=colors.GREEN,
 	level_range = {2, 15}, exp_worth = 1,
 	rarity = 5,
diff --git a/src/core_lua.c b/src/core_lua.c
index 1a6dab28a2..4fc385cc84 100644
--- a/src/core_lua.c
+++ b/src/core_lua.c
@@ -1344,6 +1344,12 @@ static int lua_fs_set_write_dir(lua_State *L)
 	return 0;
 }
 
+static int lua_fs_get_write_dir(lua_State *L)
+{
+	lua_pushstring(L, PHYSFS_getWriteDir());
+	return 1;
+}
+
 static int lua_fs_get_home_path(lua_State *L)
 {
 	lua_pushstring(L, TENGINE_HOME_PATH);
@@ -1371,6 +1377,7 @@ static const struct luaL_reg fslib[] =
 	{"delete", lua_fs_delete},
 	{"list", lua_fs_list},
 	{"setWritePath", lua_fs_set_write_dir},
+	{"getWritePath", lua_fs_get_write_dir},
 	{"getPathSeparator", lua_fs_get_path_separator},
 	{"getRealPath", lua_fs_get_real_path},
 	{"getUserPath", lua_fs_get_user_path},
-- 
GitLab