Page 7 of 14

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 12:37 pm
by thomson
No worries, Xanathar. I hope you had great time off-line.

There's actually still a lot of things to do here. Current code is what was referred at one point a low level API. The code still doesn't support displaying images, you can't use relative positions ('bottom', 'middle' etc.), It is not possible to specify alpha-channel, so the display is a bit transparent. Also, JKos pointed out that the event triggering mechanism may be time consuming as we are calling entitiesAt() every frame.

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 1:10 pm
by Xanathar
as we are calling entitiesAt() every frame
is there any reason not to have them on onMove/onTurn events ? (apart from the engine bug where you can turn without triggering onTurn if you use the free view mode ?)

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 2:20 pm
by thomson
Xanathar wrote:
as we are calling entitiesAt() every frame
is there any reason not to have them on onMove/onTurn events ? (apart from the engine bug where you can turn without triggering onTurn if you use the free view mode ?)
I think it's doable, just less reliable. onTurn is not needed as you need to step onto specific grid to enable event, no matter what direction the party is looking. What if you teleport into event location or fall on it through a pit? Will onMove work? JKos suggested a timer set to small value (0.2 second?).

If onMove is called when you teleport of fall, then it solves the problem nicely. If not, I think the timer would be a better way to go.

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 2:46 pm
by Xanathar
You are right about the teleport / pit problems (I would find it weird to have them trigger an npc but well some might find them cool): a solution could be having the main entity do a find of all "triggers" at startup, and create on the fly and bind a pressure plate to them. (this is what I do in grimq for auto-printers and auto-secrets)

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 4:54 pm
by Diarmuid
Xanathar wrote:You are right about the teleport / pit problems (I would find it weird to have them trigger an npc but well some might find them cool): a solution could be having the main entity do a find of all "triggers" at startup, and create on the fly and bind a pressure plate to them. (this is what I do in grimq for auto-printers and auto-secrets)
Yes, I've developped a marker code based on that to set zones in my dungeon (and then filter my timers, spawners and various functions depending on where the player is):

Code: Select all

zone = "mainStairs"
subZone = nil
zoneLocations = {}

function setZoneLocations()
	local markers = grimq.fromAllEntitiesInWorld()
				:where("name", "zone_marker")
				:toIterator()
				
	for zMarker in markers do
		local level, x, y = zMarker.level, zMarker.x, zMarker.y
		local zKey = level.."."..x.."."..y
		zoneLocations[zKey] = zMarker.zone
		spawn("pressure_plate_hidden", level, x, y, 0)
			:setTriggeredByParty(true)
			:setTriggeredByMonster(false)
			:setTriggeredByItem(false)
			:setSilent(true)
			:addConnector("activate", "coreScript", "setZone")
	end
end

function setZone()
	local level, x, y = party.level, party.x, party.y
	local zKey = level.."."..x.."."..y
	if zoneLocations[zKey] ~= nil then
		local zParts = help.split(zoneLocations[zKey],'.')
		zone = zParts[1]
		if zParts[2] ~= nil then
			subZone = zParts[2]
		else
			subZone = nil
		end
		--print(zone,subZone)
	end
end
zone_marker is a clone of a script_entity, so you can set up variables in it that the search decodes. It's like having a function() object directly in the editor, in this case, something like a onParty.setZone(zone). For example, I place the zone_marker object in the editor and write in the script window: zone="templeOfLight.altarRoom". That's it, and then when the party steps/teleports/falls on the square, zone will be set to "templeOfLight" and subZone will be "altarRoom". I also used a gfxIcon of a pit ceiling, so the zone_marker appears as a thin outline, very non-intrusive.

EDIT: Altough not necessary, as you see I also store the zone data in a table indexed by level.x.y, in case I need to access this also from a script somewhere. Originally I used a quick timer which checked if the index [party.level, party.x, party.y] existed. That's still faster than an allEntities...

The advantage of this approach is that you can easily set triggers directly in the editor. Disadvantage is that it's not dynamic and cannot be changed afterwards, as you cannot destroy script_entities.

An alternative way would be to use wall_texts, as these you can "move" by destroying the text object and the pressure plate, and respawning it again. (You would need to keep pressure plate ids when initializing).

The advantage of that would be that you could just type "templeOfLight.altarRoom" in the wall text. Disadvantage would be that if you want to store different variables, like " npcName = 'John', npcAction = 'dialogue1' ", then you would need to code a parser to get this into usable code. Centaur Soldier should really step in here, he's developping a system for saving/restoring data in wall texts for our LotNR project.

Re: New GUI scripting concepts and foundation

Posted: Mon Jan 07, 2013 9:17 pm
by Diarmuid
Ok, I pushed the concept a bit:

markerScript entity:

Code: Select all

-- Table of markers data
md = {}

function getMarkersData()
	local markers = grimq.fromAllEntitiesInWorld()
				:where("name", "onParty_marker")
				:toIterator()
				
	for m in markers do
		-- variables to look for in markers
		local data = {}
		if m.var1 then
			data.var1 = m.var1
		end
		if m.var2 then
			data.var2 = m.var2
		end
		if m.var3 then
			data.var3 = m.var3
		end
		setMarker(m.id, m.level, m.x, m.y, data)
	end
end

function moveMarker(id, level, x, y)
	local data = md[id]
	md[id] = nil
	findEntity(id..".trigger"):destroy()
	setMarker(id, level, x, y, data)
end

function setMarker(id, level, x, y, data)
	md[id] = {}
	md[id].level = level
	md[id].x = x
	md[id].y = y
	for i, v in pairs(data) do
		md[id][i] = v
	end
	if findEntity(id..".trigger") then
		findEntity(id..".trigger"):destroy()
	end
	spawn("pressure_plate_hidden", level, x, y, 0, id..".trigger")
		:setTriggeredByParty(true)
		:setTriggeredByMonster(false)
		:setTriggeredByItem(false)
		:setSilent(true)
		:addConnector("activate", "markerScript", "onParty")
		:addConnector("deactivate", "markerScript", "onUnparty")
end

function removeMarker(id)
	md[id] = nil
	findEntity(id..".trigger"):destroy()
end

function onParty(trigger)
	id = string.sub(trigger.id, 0, -9)
	if md[id] then
		--do anything you want here, calling other functions using stored data. For example:
		print("on:",id)
	end
end

function onUnparty(trigger)
	id = string.sub(trigger.id, 0, -9)
	if md[id] then
		--do anything you want here, calling other functions using stored data. For example:
		print("off:",id)
	end
end

function autoexec()
	getMarkersData()
end
and this in an .lua:

Code: Select all

cloneObject {
	name = "onParty_marker",
	baseObject = "script_entity",
	editorIcon = 104,
}
So now it's really modular:
1. It scans onParty_marker objects in the dungeon and stores in the md table all predefined variables to look for.
2. Then, when the party enters the marker square, it calls onParty, from where you can do anything with that data.
3. When the party leaves the square, it call onUnparty.

And there's a moveMarker function, I realized that since we don't rely on the original onParty_marker object to get data, but rather on the position index of the md table, we can move that freely, just changing the index and respawning the pressure plate.

So you can use this to define an npc encounter with any amount of variables, and have it move around too.

EDIT: How it looks in the editor:

Image

I think it makes defining events very user-friendly.

EDIT 2: Updated the code with a setMarker and removeMarker functions to allow those markers to be also generated dynamically from a script. Also added an onUparty trigger. If you just want to check what's the current id the party stepped on from onDrawGui, you could always call something like gw.setCurrentEventId(md[mIndex].markerId) from onParty and gw.setCurrentEventId(nil) from onUnparty.

EDIT 3: Realized all the level.x.y key was not necessary as I can get the id from the trigger plate. Code is much simpler now. The only problem is that you need to predefine which variables can be put in the marker. I think that using a wallText object instead of a script_entity object, we could parse it to get any variables from it without predefining them.

Re: New GUI scripting concepts and foundation

Posted: Tue Jan 08, 2013 8:03 pm
by Xanathar
Done a commit:
  • Added a 3D button object, which mimics "motif" style buttons, with hover highlight and bevel borders
  • Added onClick event which mimics the click semantics in GUIs. If you press the mouse, the button is "down" but the click event is raised when the mouse gets up again if - and only if - the cursor is still on the button (allowing cancelling an action moving the mouse away)
  • As far as I know, transparency should be managed ok, but I didn't test really
  • Added some utility methods: rgb2yuv, normalizeColorComponent, yuv2rgb, changeBrightness, isPointInBox, setColor, resetColor, drawBevel, defaultValue (nitpicking - it's more YCbCr than YUV, and is in whatever range it is - more than enough for the bevel purpose, we are not doing movies or broadcast tv here :) )
Still to do: comments

My biggest issue so far: working in one big scripting entity and everyone changing dungeon.lua does not scale IMHO.

For grimq I did split it into many files, having a batch "compiling" it all into one big file and copying the text to the clipboard so that workflow is:
  • Work on lua files in the src directory
  • Click build.cmd
  • Go to dungeon editor, paste over the code
  • Test, go to bullet 1 until ready
This would allow for splitting the source in logical modules and easier merge/diff of the changes.

What do you think ? Any other idea ?

Re: New GUI scripting concepts and foundation

Posted: Tue Jan 08, 2013 8:23 pm
by Komag
modular is good for everyone and long term usage, better to invest the effort to set it up that way from the beginning stages in my opinion

Re: New GUI scripting concepts and foundation

Posted: Tue Jan 08, 2013 9:22 pm
by JKos
Cool stuff Xanathar and welcome back. I have a another idea: If we include my framework to this project we can split it to several script entities and files and still keep the installation fairly easy. I think we have to include it anyway if we are going to implement freeze time.
I could remove all unecessary optional modules from it and leave only the core modules for this project, and it includes grimq too so we could use auto functions for events and all other awesome stuff.

gw could for example contain only the public api, core functionality and utility functions.
Then we could move the implementation of the elements to separate script entity/entities.
But it's just a suggestion and I'm sure that your batch compiling method is good enough too. Even though this is my suggestion I'm not sure if I like it so I will leave the decission to you, it's your project ;)

Edit: I could make a quick conceptual demo to another branch so we could test how it works. It should't be much work.

Re: New GUI scripting concepts and foundation

Posted: Wed Jan 09, 2013 12:02 am
by Xanathar
Hi JKos,
about the framework inclusion, I think this whole project is targeted towards complex mods (if your gui is just a button you are probably served better with a DYI solution), which would probably benefit of having the fw anyway, so I wouldn't worry too much about that. Plus that would reinforce the idea of having the framework as a somewhat standard asset which would be an additional benefit.

Just as a clarification: do you mean using the same initialization as the framework currently does (using and staying with separate files) ?
I will leave the decission to you, it's your project
No no, please, it's our project (our = of everyone who contributes) :) Let's share the blame ehm I mean, the decisions :lol: