View Full Version : Tips'n'tricks
sphair
01-06-2007, 11:46 PM
Just thought a tips'n'tricks topic would be cool, so I'll start out with some editor customization :)
I wanted to be able to set zoom levels using keyboard. I wanted to map key 1 to 100% zoom, 2 to 200%, 3 to 300% and 4 to 400%.
I found the file called base\script\system\editor.lua which does some keybinding. But since Seth will probably often alter this file in new releases, I thought I'd place my own keybindings in a separate file. I created a file called editor_custombindings.lua, and inserted the following code:
--------------
//this script is run from start.lua in base when the engine is starting.
GetInputManager:AddBinding("1,editor_only", "OnEditorSetCameraScale1", C_ENTITY_NONE);
GetInputManager:AddBinding("2,editor_only", "OnEditorSetCameraScale2", C_ENTITY_NONE);
GetInputManager:AddBinding("3,editor_only", "OnEditorSetCameraScale3", C_ENTITY_NONE);
GetInputManager:AddBinding("4,editor_only", "OnEditorSetCameraScale4", C_ENTITY_NONE);
function OnEditorSetCameraScale1(bKeyDown)
GetCamera:SetScale(Vector2(1,1));
GetCamera:InstantUpdate();
return true;
end
function OnEditorSetCameraScale2(bKeyDown)
GetCamera:SetScale(Vector2(2,2));
GetCamera:InstantUpdate();
return true;
end
function OnEditorSetCameraScale3(bKeyDown)
GetCamera:SetScale(Vector2(3,3));
GetCamera:InstantUpdate();
return true;
end
function OnEditorSetCameraScale4(bKeyDown)
GetCamera:SetScale(Vector2(4,4));
GetCamera:InstantUpdate();
return true;
end
--------------
I'm no LUA expert, so it might have been done differently, but it works :)
Then I opened startup.lua and added the following line in the 'include other files' section:
--------------
RunScript("script/system/editor_custombindings.lua");
--------------
I already posted some utility functions in another thread (http://www.rtsoft.com/forums/showthread.php?t=1433), but here are some others:
-- Returns the entity the mouse is over, if any; otherwise, it returns nil.
function MouseOverEntity()
return GetEntityByWorldPos(ScreenToWorld(GetInputManager: GetMousePos()),
nil, false)
end
-- Example using Lua's and operator.
function OnKeyEscape(keydown)
-- DefaultEscapeHandler() is never called if keydown is false.
return keydown and DefaultEscapeHandler()
end
Oh yeah, I know Seth likes to use them. I wanted to add superfluous semi-colons too when I started using Python, but neither language is C. Semi-colons are only needed to avoid ambiguity when multiple statements are put on the same line, but you shouldn't put multiple statements on the same line anyway. In short: I hate semi-colons. :)
Oh yeah, I know Seth likes to use them. I wanted to add superfluous semi-colons too when I started using Python, but neither language is C. Semi-colons are only needed to avoid ambiguity when multiple statements are put on the same line, but you shouldn't put multiple statements on the same line anyway. In short: I hate semi-colons. :)
I basically want lua to be C++, because it's totally hurting my brain switching back and forth all day! :) I've added support for the != operator and // style comments to lua, now if I can just get { and } working for "then" and "end"... I'll be set! But don't worry, of course standard lua will always be supported too.
// breaks syntax highlighting in Scintilla, since it's not expecting C++ style comments (which is why I don't use them). :(
Also, is it plausible to make code blocks delineated by braces? It seems like that could potentially cause ambiguity with the table syntax (especially if you like using anonymous functions, which I do). And function definitions would have to have an optional "{" token after the signature. That could get ugly.
function UglyFunction(barf) return {barf} }
The thing that really makes my head explode is indexing starting from 1 instead of 0. I have a feeling this will cause untold grief in the future, but maybe not.
Yeah, the { } integration isn't a minor change I think, too many things to break and special cases to check. But hey, I can dream? :)
Yeah, the indexing at 1 will destroy my brain at some point I'm sure. math.random(10); will return 1-10 also, so beware!
The thing that I'm really getting in trouble with is that all variables are global by default, which is ass-backward. (Well, Python does it the "right" way. ;))
Anyway, onto semi-useless helper functions.
-- Executes a string of code and returns the result if there is one.
function Exec(s)
return assert(loadstring(s))()
end
-- Evaluates a string containing an expression.
function Eval(s)
return Exec("return " .. s)
end
-- Returns what should be a unique identifier (memory address) for some
-- objects, including functions and tables. This is accomplished by taking
-- the address from the string representation and thus will fail silently if
-- the table's metatable defines __tostring. Sadly, I can't think of a less
-- stupid way of doing this.
function ID(object)
return string.sub(tostring(object), -8)
end
-- Returns a string representation of any table, unlike table.concat which only
-- works on tables containing only string a number values.
function TableToString(t)
local result = "{"
for index, value in pairs(t) do
-- tostring generates an error if it doesn't know how to make a string
-- representation of an object; thus we have to catch the error with
-- pcall.
local statusCode, stringValue = pcall(tostring, value)
result = (result .. index .. " = " ..
(statusCode and stringValue or "???") .. ", ")
-- The value will appear as ??? if tostring is too stupid to tell us
-- what it actually is.
end
return result .. "}"
end
Nice stuff Ian. You've got a better handle on Lua than I do...
Hey, do you happen to know of any functions that will serialize any simple data table to a string (for storage) and back? Was thinking it would be an easy way to store large pieces of data. (could be stored in an entities Data() or the global one... they don't have any size limits...)
There is also a read/write lua lib addon that I think does this in a more direct way, but the less access to the HD the better, opens up security issues when running mods.
Programming in Lua has a custom function for it, so I guess there's no such facility built into the language. I wrote something far more comprehensive, however.
-- Returns whether or not the string s is a valid identifier. It should work
-- regardless of locale, but if you're using a locale other than C's then you
-- deserve bugs.
function IsIdentifier(s)
return string.find(s, "^[%a_][%w_]*$")
end
-- Pilfered from Programming in Lua.
function Set(t)
local set = {}
for _, value in ipairs(t) do
set[value] = true
end
return set
end
-- Tests if s is a reserved word.
do
-- This is not a local in the body of the function because it should only
-- have to be constructed once.
local reservedWords = Set{"and", "break", "do", "else", "elseif", "end",
"false", "for", "function", "if", "in", "local",
"nil", "not", "or", "repeat", "return", "then",
"true", "until", "while"}
function IsReservedWord(s)
return reservedWords[s]
end
end
-- Pads a number with zeros on the left, to fill a field of the specified
-- length.
function ZeroPad(number, length)
return string.rep("0", length - #tostring(number)) .. tostring(number)
end
-- Replaces indices from replacementTable found in s with their corresponding
-- values.
function StringReplace(s, replacementTable)
local result = s
for index, value in pairs(replacementTable) do
result = string.gsub(result, index, value)
end
return result
end
-- Returns a string representation of the object. For some object types,
-- Eval(Repr(object) == object.
function Repr(object, parentTables)
if type(object) == "number" or type(object) == "boolean" or
type(object) == "nil" then
return tostring(object)
elseif type(object) == "string" then
-- Converts quotation marks and backslashes and not much else.
local result = string.format("%q", object)
-- Convert escape codes. string.format put a backslash in front of
-- every newline character, so we have to remove that too.
result = StringReplace(result, {["\a"] = "\\a", ["\b"] = "\\b",
["\f"] = "\\f", ["\\\n"] = "\\n",
["\r"] = "\\r", ["\t"] = "\\t",
["\v"] = "\\v"})
-- Convert non-printable characters to their decimal escape codes. The
-- escape code is padded with zeros to three digits in case it is
-- followed by a digit character.
return string.gsub(result, "[^%w%p ]", function (character)
return "\\" .. ZeroPad(string.byte(character), 3)
end)
elseif type(object) == "table" then
-- If we encounter recursion, return a special value.
parentTables = parentTables or {}
if parentTables[object] then
return "{<recursive>}"
end
parentTables[object] = true
local items = {}
local lastIndex
for index, value in pairs(object) do
local key = ""
-- If the index is numerical and is successive then it can be
-- omitted.
if type(index) ~= "number" or lastIndex and
lastIndex ~= index - 1 then
-- If the index is a string and a valid identifier then it does
-- not need to be surrounded by brackets.
if type(index) == "string" and IsIdentifier(index) and
not IsReservedWord(index) then
key = index .. " = "
else
key = string.format("[%s] = ", Repr(index, parentTables))
end
end
table.insert(items, key .. Repr(value, parentTables))
lastIndex = index
end
return string.format("{%s}", table.concat(items, ", "))
elseif type(object) == "function" or type(object) == "thread" then
-- Use angled brackets to indicate that this is not a literal value.
return string.format("<%s>", tostring(object))
elseif type(object) == "userdata" then
-- tostring generates an error if it doesn't know how to make a string
-- representation of an object; thus we have to catch the error with
-- pcall.
local statusCode, stringValue = pcall(tostring, object)
return string.format("<%s>", (statusCode and stringValue or
"userdata: unknown"))
end
-- type() should never return any value other than the ones above,
-- the following line should never be executed.
error("type() returned a bogus value; something is seriously wrong.")
end
Ridiculous example:
T = {1, 2, 3}
T["function"] = Repr
T[234234] = "weird index"
T["nested table"] = {}
T["boolean"] = true
T["userdata"] = Map
T["thread"] = coroutine.create(Repr)
T[{}] = "table index"
T["escape_codes"] = "'\2\a\b\\"
T["new_line"] = "\
"
T["recursive_table"] = T
Returns the following string:
{1, 2, 3, new_line = "\n", [{}] = "table index", ["function"] = <function: 01AB91D0>, [234234] = "weird index", userdata = <userdata: 01A89C90>, thread = <thread: 01ADAE90>, escape_codes = "'\002\a\b\\", recursive_table = {<recursive>}, boolean = true, ["nested table"] = {}}
P.S. The small font apparently doesn't have a double quotation mark (") in it. Unless there's some other reason it doesn't appear in the log console.
Powered by vBulletin® Version 4.1.10 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.