Ask a simple question, get a simple answer
Re: Ask a simple question, get a simple answer
Is it possible to change, or at least rename BaseStats and their descriptions? For example, rename Strength into Constitution, or remove resistance bonus from stats, or change their description, or add brand new stat to the game (so characters will have 5 or more stats)?
Last edited by Marebre on Thu May 04, 2017 9:58 am, edited 1 time in total.
Re: Ask a simple question, get a simple answer
Good afternoon!
I'm trying to make an alternative ending for my mod and there was a question.
Is it possible to do so that, when entering the portal, the second video (the current where the heroes do not depart on the ship) starts to play?
And if you can, how exactly?
I'm trying to make an alternative ending for my mod and there was a question.
Is it possible to do so that, when entering the portal, the second video (the current where the heroes do not depart on the ship) starts to play?
And if you can, how exactly?
Re: Ask a simple question, get a simple answer
The Portal asset is defined with the ending video; you need to redefine the default asset, or define a new portal asset.Badgert wrote:Good afternoon!
I'm trying to make an alternative ending for my mod and there was a question.
Is it possible to do so that, when entering the portal, the second video (the current where the heroes do not depart on the ship) starts to play?
And if you can, how exactly?
Code: Select all
defineObject{
name = "portal",
baseObject = "portal",
components = {
{
class = "Portal",
onArrival = function()
--GameMode.completeGame("assets/videos/outro1.ivf") --original
GameMode.completeGame("mod_assets/videos/custom_ending_1.ivf") --custom
end,
},
},
}
http://www.grimrock.net/modding/creating-cinematics/
Re: Ask a simple question, get a simple answer
Isaac, many thanks!
-
- Posts: 3
- Joined: Thu May 04, 2017 1:58 am
Re: Ask a simple question, get a simple answer
Loaded up one of my dungeons in the editor after about a year and upon activating a simple dig script the play tester crashes with the error shown in the linked image. It used to work perfectly fine. Nothing has changed except maybe steam updating LoG2 over the past year. Cannot figure out what is going on here. Given it is line 55 of the init.lua, I think it will happen any time 'onRest' is called up. Fairly certain I am using the latest GrimTK available on the Nexus as it hasn't been updated since 2015. https://steamuserimages-a.akamaihd.net/ ... 996C14DEC/
What is going on/How do I fix this?
*edit*
Can confirm simply resting will cause the crash as well, same thing:
mod_assets/ext/grimtk/init.lua:55: attempt to index field 'GUI' (a nil value)
segment containing called out line:
What is going on/How do I fix this?
*edit*
Can confirm simply resting will cause the crash as well, same thing:
mod_assets/ext/grimtk/init.lua:55: attempt to index field 'GUI' (a nil value)
segment containing called out line:
Code: Select all
if fw_hook == nil then
defineObject{
name = "party",
baseObject = "party",
components = {
{
class = "Party",
onDrawGui = function(party, g)
end,
onRest = function(party)
if ( findEntity("GTK") ) then
if ( GTK.Core.GUI:isPartyLocked() ) then <--- Line 55 (this comment obviously not in the code)
return false;
end
end
end,
onWakeUp = function(party)
if ( findEntity("GTK") ) then
if ( GTK.Core.GUI:isPartyLocked() ) then
return false;
end
end
end
}
}
}
end
Re: Ask a simple question, get a simple answer
the onRest (and onWakeUp) hooks will both crash it - but they work fine
the field GUI is coming up with nil, GUI is a huge table of custom functions within core.lua ,
("mod_assets/grimtk/scripts/gtk/core.lua")
Have you checked to make sure grimtk's init file is being imported from your mods init.lua ?
grimtk's init.lua defines the "grimtk" object, which is a script entity object that creates more script components - loading a bunch of .lua files
double check that that is all still there and working
the field GUI is coming up with nil, GUI is a huge table of custom functions within core.lua ,
("mod_assets/grimtk/scripts/gtk/core.lua")
Have you checked to make sure grimtk's init file is being imported from your mods init.lua ?
grimtk's init.lua defines the "grimtk" object, which is a script entity object that creates more script components - loading a bunch of .lua files
SpoilerShow
Code: Select all
defineObject{
name = "grimtk",
baseObject = "script_entity",
components = {
{
class = "Null",
onInit = function(self)
if ( findEntity("GTK") == nil ) then
local gtk = spawn("script_entity", 1, 1, 1, 1, 1, "GTK");
gtk.script:loadFile("mod_assets/grimtk/scripts/hook.lua");
gtk:createComponent("Script", "Constants"):loadFile("mod_assets/grimtk/scripts/gtk/constants.lua");
gtk:createComponent("Script", "Input"):loadFile("mod_assets/grimtk/scripts/gtk/input.lua");
gtk:createComponent("Script", "Core"):loadFile("mod_assets/grimtk/scripts/gtk/core.lua");
gtk:createComponent("Script", "Widgets"):loadFile("mod_assets/grimtk/scripts/gtk/widgets.lua");
if ( Editor.isRunning() ) then
gtk:createComponent("Script", "Debug"):loadFile("mod_assets/grimtk/scripts/gtk/debug.lua");
end
delayedCall("GTK", 0.01, "emitGtkDidLoad");
end
if ( findEntity("GTKGui") == nil ) then
local gtkgui = spawn("script_entity", 1, 1, 1, 1, 1, "GTKGui");
gtkgui:createComponent("Script", "Basic"):loadFile("mod_assets/grimtk/scripts/gtkgui/basic.lua");
gtkgui:createComponent("Script", "Basic_modded"):loadFile("mod_assets/grimtk/scripts/gtkgui/basic_modded.lua");
gtkgui:createComponent("Script", "Dialogue"):loadFile("mod_assets/grimtk/scripts/gtkgui/dialogue.lua");
gtkgui:createComponent("Script", "Cinematic"):loadFile("mod_assets/grimtk/scripts/gtkgui/cinematic.lua");
end
end
}
}
}
Last edited by akroma222 on Thu May 04, 2017 9:42 am, edited 1 time in total.
Labyrinth of Lies (viewtopic.php?f=14&t=4400)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
Re: Ask a simple question, get a simple answer
It would be hard for an error to occur in grimtk/init.lua if grimtk/init.lua wasn't being imported...akroma222 wrote:Have you checked to make sure grimtk's init file is being imported?
Definitely look at core.lua.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
Re: Ask a simple question, get a simple answer
you're probably right, ya know!minmay wrote:It would be hard for an error to occur in grimtk/init.lua if grimtk/init.lua wasn't being imported...akroma222 wrote:Have you checked to make sure grimtk's init file is being imported?
Honestly, I think Ive rewritten that^^ message 7 times - and still missed that!
(its one of those days)
Labyrinth of Lies (viewtopic.php?f=14&t=4400)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
-
- Posts: 3
- Joined: Thu May 04, 2017 1:58 am
Re: Ask a simple question, get a simple answer
The core.lua does define GUI, and it is a lot of stuff. Not sure why it would be seen as nil if it has an abundance of values. What does it even mean by trying to index a nil value? I feel like this is turning out to not be a simple question, and needs more than just a simple answer, hah.
For sake of clarity, here is the entire grimtk init.lua, and yeah, it is definitely imported correctly, lol.
Also, here is the segment in the core.lua that defines GUI:
I'd simply pull out GrimTK but I planned to use it for dialogue popups to give hints/story elements without having to locate and read a note in places that don't really have that option. Honestly I don't remember what all uses it already, been so long.
For sake of clarity, here is the entire grimtk init.lua, and yeah, it is definitely imported correctly, lol.
Code: Select all
defineObject{
name = "grimtk",
baseObject = "script_entity",
components = {
{
class = "Null",
onInit = function(self)
if ( findEntity("GTK") == nil ) then
local gtk = spawn("script_entity", 1, 1, 1, 1, 1, "GTK");
gtk.script:loadFile("mod_assets/ext/grimtk/scripts/hook.lua");
gtk:createComponent("Script", "Constants"):loadFile("mod_assets/ext/grimtk/scripts/gtk/constants.lua");
gtk:createComponent("Script", "Input"):loadFile("mod_assets/ext/grimtk/scripts/gtk/input.lua");
gtk:createComponent("Script", "Core"):loadFile("mod_assets/ext/grimtk/scripts/gtk/core.lua");
gtk:createComponent("Script", "Widgets"):loadFile("mod_assets/ext/grimtk/scripts/gtk/widgets.lua");
if ( Editor.isRunning() ) then
gtk:createComponent("Script", "Debug"):loadFile("mod_assets/ext/grimtk/scripts/gtk/debug.lua");
end
delayedCall("GTK", 0.01, "emitGtkDidLoad");
end
if ( findEntity("GTKGui") == nil ) then
local gtkgui = spawn("script_entity", 1, 1, 1, 1, 1, "GTKGui");
gtkgui:createComponent("Script", "Basic"):loadFile("mod_assets/ext/grimtk/scripts/gtkgui/basic.lua");
gtkgui:createComponent("Script", "Dialogue"):loadFile("mod_assets/ext/grimtk/scripts/gtkgui/dialogue.lua");
gtkgui:createComponent("Script", "Cinematic"):loadFile("mod_assets/ext/grimtk/scripts/gtkgui/cinematic.lua");
end
end
}
}
}
--
-- If the Log2 Framework has been included before this script, then we will use the framework instead.
-- Otherwise, the default behaviour is to override these hooks.
-- NOTE: If you are using the framework, you MUST define an onDrawGui hook SOMEWHERE (even an empty one).
--
if fw_hook == nil then
defineObject{
name = "party",
baseObject = "party",
components = {
{
class = "Party",
onDrawGui = function(party, g)
end,
onRest = function(party)
if ( findEntity("GTK") ) then
if ( GTK.Core.GUI:isPartyLocked() ) then
return false;
end
end
end,
onWakeUp = function(party)
if ( findEntity("GTK") ) then
if ( GTK.Core.GUI:isPartyLocked() ) then
return false;
end
end
end
}
}
}
end
Code: Select all
GUI = {
_tkContext = TKContext.create(),
_debugMessages = { },
_windows = { },
_images = { },
_updateables = { },
_shortcuts = { },
_nextAutoId = 1,
_focusWidget = nil,
_partyLocks = 0,
_keyboardLocks = 0,
_aiLocks = 0,
_lastUpdateTime = 0,
_deltaTime = 0,
_maxDeltaTime = 0.25,
_delayedCalls = { },
isEnabled = true,
dimBackground = false,
mouseState = GTK.Input.MouseState.create(),
keyboardState = GTK.Input.KeyboardState.create(),
getContext = function(self)
return self._tkContext;
end,
addWindow = function(self, window)
if ( window:name() == nil ) then
Console.warn("Cannot create Root window without a name.");
return false;
end
if ( self._windows[window:name()] ~= nil ) then
Console.warn("Root window already exists with that name.");
return false;
end
self._windows[window:name()] = window;
self:showWindow(window:name());
return true;
end,
createWindow = function(self, args)
local window = GTK.Widgets.GWindow.create(args);
if ( self:addWindow(window) == true ) then
return window;
end
return nil;
end,
destroyWindow = function(self, window)
local windowName = nil;
if ( type(window) == "table" ) then
windowName = window:name();
elseif ( type(window) == "string" ) then
windowName = window;
end
if ( windowName and self._windows[windowName] ) then
self:hideWindow(windowName);
self._windows[windowName] = nil;
end
end,
getWindow = function(self, windowName)
return self._windows[windowName];
end,
showWindow = function(self, windowName)
local window = self._windows[windowName];
if ( window ) then
window:setVisible(true);
end
end,
hideWindow = function(self, windowName)
local window = self._windows[windowName];
if ( window ) then
window:setVisible(false);
end
end,
addUpdateable = function(self, updateable)
if ( updateable.update == nil or type(updateable.update) ~= "function" or updateable.updateableId == nil ) then
Console.warn("[GTK] Attempted to register invalid updateable");
return;
end
if ( self._updateables[updateable.updateableId] ~= nil ) then
Console.warn("[GTK] An updateable with that name already exists.");
return;
end
if ( updateable.startUpdates ~= nil ) then
updateable:startUpdates();
end
self._updateables[updateable.updateableId] = updateable;
end,
removeUpdateable = function(self, updateable)
local id = nil;
if ( type(updateable) == "string" ) then
id = updateable;
elseif ( updateable.updateableId ~= nil ) then
id = updateable.updateableId;
end
if ( id and self._updateables[id] ~= nil ) then
if ( self._updateables[id].endUpdates ~= nil ) then
self._updateables[id]:endUpdates();
end
self._updateables[id] = nil;
end
end,
update = function(self, guiContext)
if ( self.isEnabled == false ) then
return
end
local playTime = GameMode.getStatistic("play_time");
self._deltaTime = playTime - self._lastUpdateTime;
self._lastUpdateTime = playTime;
if ( self._deltaTime > self._maxDeltaTime ) then
self._deltaTime = self._maxDeltaTime;
end
if ( #self._delayedCalls > 0 ) then
local toRemove = { };
for i=#self._delayedCalls,1,-1 do
local v = self._delayedCalls[i];
v["delay"] = v["delay"] - self._deltaTime;
if ( v["delay"] < 0 ) then
if ( v["func"] ) then
v["func"](v["param"]);
end
table.remove(self._delayedCalls, i);
end
end
end
for _,updateable in pairs(self._updateables) do
updateable:update(self._deltaTime);
end
self._tkContext:start(guiContext);
self.mouseState:reset(self._tkContext);
self.keyboardState:reset(self._tkContext);
local dimScreen = false;
local screenSize = self._tkContext:size();
for _,window in pairs(self._windows) do
if ( window.data.dimScreen ) then
dimScreen = true;
end
end
if ( dimScreen ) then
self._tkContext:drawRect({ position={0,0}, size=screenSize, bgColor={0, 0, 0, 160} });
end
for _,window in pairs(self._windows) do
if ( window.data.visible == true ) then
if ( (window.data.fullscreen == true) and ((window.data.size[1] == screenSize[1]) or (window.data.size[2] ~= screenSize[2])) ) then
window:setSize(screenSize[1], screenSize[2]);
end
self:visit(window);
end
end
if ( #self._debugMessages > 0 ) then
self._tkContext:drawText({ position={10, 10}, size={1200, screenSize[2]-20}, text=table.concat(self._debugMessages, "\n"), textColor={255,255,255,255}, font="tiny" });
end
self.keyboardState:commit();
self.mouseState:commit();
self._tkContext:finish();
end,
visit = function(self, widget)
if ( widget == nil or widget.data.visible == false ) then
return
end
widget:updatePositionInParent(self._tkContext);
-- Slightly annoying that we do mouse stuff here (and it's not completely separate)
-- but it's more efficient to only walk the tree of workspaces once...
self.mouseState:visitWidget(self._tkContext, widget);
self.keyboardState:visitWidget(self._tkContext, widget);
widget:update(self._tkContext, self._deltaTime);
self._tkContext:setWorkspaceOpacity(widget.data.opacity);
widget:draw(self._tkContext);
self._tkContext:pushWorkspace(widget.data.position, widget.data.size);
for _,child in ipairs(widget._children) do
self:visit(child);
end
self._tkContext:popWorkspace();
end,
addImage = function(self, args)
local image = { };
if ( (args.name == nil) or (args.path == nil) or (args.size == nil)) then
Console.warn("[addImage] requires at least a name, a path and a size.");
end
if ( args.path:sub(-4):lower() ~= ".tga" ) then
Console.warn("[addImage] '" .. args.path .. "' does not end in .tga");
end
if ( args.path:find("//") ) then
Console.warn("[addImage] '" .. args.path .. "' conains '//' which WILL FAIL when exported!");
end
if ( (args.path:sub(1, 6) ~= "assets") and (args.path:sub(1, 10) ~= "mod_assets") ) then
Console.warn("[addImage] '" .. args.path .. "' does not start with assets or mod_assets." );
end
image.name = args.name;
image.path = args.path;
image.size = args.size;
image.margin = iff(args.margin ~= nil, args.margin, {8, 8, 8, 8});
image.origin = iff(args.origin ~= nil, args.origin, {0,0});
self._images[image.name] = image;
end,
getImage = function(self, name)
return self._images[name];
end,
getImageSize = function(self, name)
local image = self._images[name];
if ( image == nil ) then return {0, 0}; end
return image.size;
end,
nextAutoWidgetId = function(self)
local id = "widget_" .. self._nextAutoId;
self._nextAutoId = self._nextAutoId + 1;
return id;
end,
setFocus = function(self, widget)
if ( self._focusWidget ~= nil ) then
self:unlockKeyboard();
self:unlockParty();
self.keyboardState:finishTextEntry();
self._focusWidget:_triggerPlug("onLoseFocus");
self._focusWidget:doLoseFocus();
self._focusWidget = nil;
end
if ( widget ~= nil ) then
self:lockKeyboard();
self:lockParty();
self.keyboardState:startTextEntry(widget);
self._focusWidget = widget;
self._focusWidget:doGainFocus();
self._focusWidget:_triggerPlug("onGainFocus");
GameMode.setGameFlag("DisableKeyboardShortcuts", true);
end
end,
focusWidget = function(self)
return self._focusWidget;
end,
delayedCall = function(self, delay, param, func)
table.insert(self._delayedCalls, {delay = delay, func = func, param = param});
end,
lockParty = function(self)
self._partyLocks = self._partyLocks + 1;
GameMode.setGameFlag("DisableMovement", true);
GameMode.setGameFlag("DisableMouseLook", true);
end,
unlockParty = function(self)
self._partyLocks = self._partyLocks - 1;
if ( self._partyLocks <= 0 ) then
GameMode.setGameFlag("DisableMovement", false);
GameMode.setGameFlag("DisableMouseLook", false);
self._partyLocks = 0;
end
end,
isPartyLocked = function(self)
return self._partyLocks > 0;
end,
lockKeyboard = function(self)
self._keyboardLocks = self._keyboardLocks + 1;
GameMode.setGameFlag("DisableKeyboardShortcuts", true);
end,
unlockKeyboard = function(self)
self._keyboardLocks = self._keyboardLocks - 1;
if ( self._keyboardLocks <= 0 ) then
self._keyboardLocks = 0;
self:delayedCall(0.1, self, function(self)
if ( self._keyboardLocks == 0 ) then
GameMode.setGameFlag("DisableKeyboardShortcuts", false);
end
end);
end
end,
isKeyboardLocked = function(self)
return self._keyboardLocks > 0;
end,
freezeAI = function(self)
self._aiLocks = self._aiLocks + 1;
GameMode.setGameFlag("DisableMonsterAI", true);
end,
unfreezeAI = function(self)
self._aiLocks = self._aiLocks - 1;
if ( self._aiLocks <= 0 ) then
GameMode.setGameFlag("DisableMonsterAI", false);
self._aiLocks = 0;
end
end,
isAIFrozen = function(self)
return self._aiLocks > 0;
end,
log = function(self, message)
if ( #self._debugMessages > 50 ) then
table.remove(self._debugMessages, 1);
end
table.insert(self._debugMessages, message);
end,
clearDebug = function(self)
self._debugMessages = { };
end,
toggleDebug = function(self)
if ( self._tkContext.debugMode == false) then
self._tkContext.debugMode = true;
if ( Editor.isRunning() ) then
GTK.Debug.showDebugWindow();
end
else
self._tkContext.debugMode = false;
if ( Editor.isRunning() ) then
GTK.Debug.destroyDebugWindow();
end
end
end,
}
Re: Ask a simple question, get a simple answer
It means you're trying to index nil. Indexing operators are ., :, and []. It is happening because GTK.Core.GUI is nil at the time that line executes.
This problem arises from a weakness in GrimTK, specifically in how it converts its source files to ScriptComponents. In certain contexts, after createComponent() returns, it's already too late to call loadFile(); the source you "load" won't actually load and run, because the ScriptComponent has already compiled. As a general rule, createComponent [...] loadFile will work at dungeon initialization time, but not afterwards. So if you're spawning the grimtk object dynamically instead of placing it in the dungeon editor, for example, you will probably run into this bug.
In short, place the grimtk object using the dungeon editor instead of trying to spawn it later.
This problem arises from a weakness in GrimTK, specifically in how it converts its source files to ScriptComponents. In certain contexts, after createComponent() returns, it's already too late to call loadFile(); the source you "load" won't actually load and run, because the ScriptComponent has already compiled. As a general rule, createComponent [...] loadFile will work at dungeon initialization time, but not afterwards. So if you're spawning the grimtk object dynamically instead of placing it in the dungeon editor, for example, you will probably run into this bug.
In short, place the grimtk object using the dungeon editor instead of trying to spawn it later.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.