Spell – rune order
Spell – rune order
hi there
Is it possible to get notified if a rune is selected?
Is it possible to change the rune-tile?
In other words: is a DM-like behaviour doable where the runes you'd able to add change with every rune selected?
Is it possible to get notified if a rune is selected?
Is it possible to change the rune-tile?
In other words: is a DM-like behaviour doable where the runes you'd able to add change with every rune selected?
Re: Spell – rune order
I believe that it is ~possible, but that it is not built in behavior, and that you would have to script the entire system for this; including re-implementing the drag-path behavior on the runes, if you wanted it, and all of the other graphics involved. It might not be possible to make it perfect.
Re: Spell – rune order
Lets say I want this tiles: how would you do it? Can someone post a simple example? Creating other graphipcs on request is no problem.
,
,
,
,
Re: Spell – rune order
No... (That's the point... it won't be simple at all...).rhavin wrote:Can someone post a simple example?
The runes are nice. What you are asking, I don't think many (if any) have tried. What it entails is scripting a feature that reads the mouse input over the spell caster's attack panel, and uses the graphic drawing functions to write your bitmaps to the screen; while tracking the state of your (more complicated) rune panel; and casting the spells that come of the interaction with it. It is possible to indicate and cast spells with rune combinations that are not adjacent or consecutive, like the ones in the game. If your script can discern the mouse position and clicks that occur in each attack panel, then you can animate the menu and call these spells to be cast.
I do not have any ready-made examples, but you will need to have a decent command of the LUA scripting language, and to use the gui.context drawing functions. Those can be seen here: https://github.com/JKos/log2doc/wiki/Ob ... icscontext
The button drawing function creates a region anywhere on the screen, and detects clicks within the region. You would use that to detect clicks on your tiles, and trigger changes to the rune panel layout; to reveal new runes, and remove unrelated ones, etc...
The idea is to write a function that uses these functions to create the UI panel. Your function gets called every frame, and has to decide what to draw—each frame —each time it is called. Of particular note is the party.onDrawAttackPanel() hook, seen here:
https://github.com/JKos/log2doc/wiki/Co ... -component
This hook can be set to call your function, and provide gui context, and the screen region of the (specific) attack panel being drawn. The game draws four attack panels, and provides enough information to detect which champion is being affected by the current call.
I have an example of generating a menu; it does not use the attack panel, but the premise and function calls are more or less the same, and illustrate the process. What this does, is composite a custom menu using a three state button texture, and remembers what button was pressed.
https://www.dropbox.com/s/h359z7ayvv0r1 ... o.zip?dl=0
**The video recorded an incoming Skype notification... so expect it when played.
Re: Spell – rune order
Thanx for your code, i already have a working panel now with mouse hover highlighting and automatic change of tiles that can combine my runes and craft spells.
Now - how do I hook into the spell-panel? Basically, I want my panel to pop up instead of the games spell-panel or at least above (before) it.
Or - even better: how do i completely disable the games spell-panel so that a right-click on the hands always become either a weapon or a unarmed attack? I'd prefer to have my spell-panel above the character icons. Of course, a third way to do it would be a third plane for the character hands. Is that possible?
Now - how do I hook into the spell-panel? Basically, I want my panel to pop up instead of the games spell-panel or at least above (before) it.
Or - even better: how do i completely disable the games spell-panel so that a right-click on the hands always become either a weapon or a unarmed attack? I'd prefer to have my spell-panel above the character icons. Of course, a third way to do it would be a third plane for the character hands. Is that possible?
Re: Spell – rune order
Minmay seems to have the most experience with the internal behavior of the engine—short of Petri himself. He's really the one to ask.
Probably the easiest way to add a custom spell menu is through an equipable item, but if you want the right-click panel, then from what I've seen, just remove the 'hand_caster' trait from the definitions for the character classes that have it, and none should be able to activate it, except through items. There might be a much better way.
The hand_caster trait is special, but if you define your own trait named 'hand_caster', then you can add it or remove it from PCs during the game; provided their class didn't have it in the definition. Not having the hand_caster trait means they attack with empty hands, instead of open a rune panel. This can be tested for. Presumably one could test for (and cancel) unarmed attacks by any PC with an alternative (user defined) caster trait present (to mark them as spell casters), and then open a custom spell panel on right-clicks to that PC's attack panel.
Testing for unarmed attacks is not as straightforward as one would think, because for some reason they don't trigger the onAttack party hook. The only way I know of to test for unarmed attacks is by using one of the Compute hooks found in the definitions for skills, traits, and conditions.
**It does come to mind that a more complex spell panel could come from having greater skill, and/or some new magic tome. As such the normal spell panel could work for novice casters, and the advanced panel for an arch-mage. This might be possible by using a tome item, or by removing the hand_caster trait from all classes, then restoring it (at-load) to any class with spell-skills; and later changing it to a 'master_caster' trait once their skill level is high enough. (The specific caster trait determining what spell menu is used.)
Probably the easiest way to add a custom spell menu is through an equipable item, but if you want the right-click panel, then from what I've seen, just remove the 'hand_caster' trait from the definitions for the character classes that have it, and none should be able to activate it, except through items. There might be a much better way.
The hand_caster trait is special, but if you define your own trait named 'hand_caster', then you can add it or remove it from PCs during the game; provided their class didn't have it in the definition. Not having the hand_caster trait means they attack with empty hands, instead of open a rune panel. This can be tested for. Presumably one could test for (and cancel) unarmed attacks by any PC with an alternative (user defined) caster trait present (to mark them as spell casters), and then open a custom spell panel on right-clicks to that PC's attack panel.
Testing for unarmed attacks is not as straightforward as one would think, because for some reason they don't trigger the onAttack party hook. The only way I know of to test for unarmed attacks is by using one of the Compute hooks found in the definitions for skills, traits, and conditions.
**It does come to mind that a more complex spell panel could come from having greater skill, and/or some new magic tome. As such the normal spell panel could work for novice casters, and the advanced panel for an arch-mage. This might be possible by using a tome item, or by removing the hand_caster trait from all classes, then restoring it (at-load) to any class with spell-skills; and later changing it to a 'master_caster' trait once their skill level is high enough. (The specific caster trait determining what spell menu is used.)
Re: Spell – rune order
Update: I have been tinkering around with this, and had some neat progress, but every idea seems to come with bad side effects. I had forgotten that (though detectable)—afaik, unarmed attacks cannot be canceled.
I'm beginning to think that (barring new information), that the menu idea will almost have to come in the form of a magical inventory item, that opens the menu on use.
If there is ever another Glögg session (and there most likely won't be ), I will ask for a dedicated means to disable the PC without decorations or UI side effects; to more easily detect (and/or cancel) unarmed attacks, and also a way to limit or occlude UI interaction when desired... because it accepts attacks and draws spells underneath custom menu graphics.
I'm beginning to think that (barring new information), that the menu idea will almost have to come in the form of a magical inventory item, that opens the menu on use.
If there is ever another Glögg session (and there most likely won't be ), I will ask for a dedicated means to disable the PC without decorations or UI side effects; to more easily detect (and/or cancel) unarmed attacks, and also a way to limit or occlude UI interaction when desired... because it accepts attacks and draws spells underneath custom menu graphics.
Re: Spell – rune order
First concept Demo, just in the upper left corner without scaling, call skript function showPanel() to activate. This is my first time coding in Lua, so any hints appreciated
you'll need those files:
http://doc.rhavin.de/dmf/runes.dds
http://doc.rhavin.de/dmf/runes_h.dds
http://doc.rhavin.de/dmf/runes_s.dds
you'll need those files:
http://doc.rhavin.de/dmf/runes.dds
http://doc.rhavin.de/dmf/runes_h.dds
http://doc.rhavin.de/dmf/runes_s.dds
Code: Select all
function showPanel()
party.party:addConnector('onDrawGui', self.go.id, "renderRunePanel")
end
function hidePanel()
party.party:removeConnector('onDrawGui', self.go.id, "renderRunePanel")
end
-- definition of spells
QDMF_Spell = {}
QDMF_Spell.__index = QDMF_Spell
-- definition of runes
QDMF_Rune = {}
QDMF_Rune.__index = QDMF_Rune
QDMF_Rune.tiers = 3
QDMF_Rune.ways = 9
-- the spellphrase-pseudoclass-ctor
function QDMF_PhraseCreate(runestring)
local phrase = {}
--[[
# clear this phrase ]]--
function phrase.clear(self)
self.name = ""
self.align = ""
self.runes = {}
self.power = 0
self.cost = 0
self.minimum = 0
self._length = 0
self._valid = false
self._tested = false
self.string = ""
end
--[[
# add a rune to the phrase, invalidating it ]]--
function phrase.add(self, rune, pos)
local word
if (rune == nil) then
return false end
if (type(rune) ~= "table") then
rune = QDMF_Rune[rune]
if (rune == nil) then return false end
end
if self:hasRuneIndex(rune.index) then
return false end
if (rune.tier == 0) then
self.power = rune.way + 1 -- need 0 for "no powerrune selected"
return true end
if (pos ~= nil and pos > 0) then
self.runes[pos - 1] = rune
else
self.runes[self._length] = rune
self._length = self._length + 1
end
self._tested = false
self._valid = false
end
--[[
# get effective length including optional power rune ]]--
function phrase.getLength(self)
if (self.power == 0) then
return self._length
else
return self._length + 1
end
end
--[[
# get the phrases rune-tier by index ]]--
function phrase.getTierAt(self, index)
local rune = self:getRuneAt(index)
if (rune == nil) then
return -1
end
return rune.tier
end
--[[
# get the phrases rune by index ]]--
function phrase.getRuneAt(self, index)
if (self.power > 0) then
index = index - 1 end
if (index < -1) then
return nil end
if (index == -1) then
return QDMF_Rune[self.power - 1] end
if (index < self._length) then
return self.runes[index] end
return nil
end
--[[
# check whether a certain rune is part of the spell ]]--
function phrase.hasRuneIndex(self, idx)
if (self == nil or idx == nil) then return nil end
-- a power rune?
if idx < QDMF_Rune.ways then
if self.power == idx + 1 then
return true
else
return false
end
end
for k,v in pairs(self.runes) do
if v.index == idx then return true end
end
return false
end
--[[
# check if the phrase is a valid spell and if: update name and alignment ]]--
function phrase.isValid(self)
if (self._tested) then return self._valid end
self.string = ""
self.cost = 0
if (self.power) > 0 then
self.string = QDMF_Rune[self.power - 1]
end
for k,v in pairs(self.runes) do
if (self.string ~="") then
self.string = self.string .. " "
end
self.string = self.string .. v.name
self.cost = self.cost + v.cost
end
local spell = QDMF_Spell[self.string]
self._tested = true
if spell == nil then
self.name = ""
self.align = ""
self._valid = false
return false
end
self.name = spell.name
self.aligh = spell.align
self._valid = true
return true
end
--[[
# convert the phrase to a full string with power rune ]]--
function phrase.toString(self)
local str = "", rune
if self.power > 0 and self.power <= QDMF_Rune.ways then
rune = QDMF_Rune[self.power - 1]
if rune ~=nil then
str = QDMF_Rune[self.power - 1].name .. " "
end
end
return str .. self.string
end
function phrase.getNextTier(self)
if (self.power == 0) then return 0 end
if (self._length > 5) then return -1 end
return (self._length % 3) + 1
end
phrase:clear()
if runestring == nil then
return phrase end
for i in string.gmatch(runestring, "%S+") do
phrase:add(QDMF_Rune[i])
end
return phrase
end
function QDMF_Rune:new(name,tier,way,cost,effect)
local rune = {}
rune.name = name
rune.tier = tier
rune.way = way
rune.index = tier * QDMF_Rune.ways + way
rune.cost = cost
-- make accessible by name and position, in lua, this
-- is automatically a ref to the object, not a copy
QDMF_Rune[name] = rune
QDMF_Rune[rune.index] = rune
return rune
end
--[[
# Definition of a spell.
# minimum : the least possible power-rune to cast this spell
# ]]--
function QDMF_Spell:new(name, align, runestring, minimum)
local spell = QDMF_PhraseCreate(runestring)
spell.name = name
spell.align = align
QDMF_Spell[name] = spell
QDMF_Spell[spell.string] = spell
return spell
end
QDMF_Rune:new("Sha", 0,0,3, "Neophyte")
QDMF_Rune:new("Lo", 0,1,3, "Novice")
QDMF_Rune:new("Beth", 0,2,3, "Apprentice")
QDMF_Rune:new("Um", 0,3,3, "Journeyman")
QDMF_Rune:new("On", 0,4,3, "Craftsman")
QDMF_Rune:new("Ee", 0,5,3, "Artisan")
QDMF_Rune:new("Pal", 0,6,3, "Adept")
QDMF_Rune:new("Mon", 0,7,3, "Expert")
QDMF_Rune:new("Arch", 0,8,3, "Master")
QDMF_Rune:new("Fri", 1,0,3, "Cold")
QDMF_Rune:new("Ya", 1,1,3, "Earth")
QDMF_Rune:new("Vi", 1,2,3, "Water")
QDMF_Rune:new("Oh", 1,3,3, "Air")
QDMF_Rune:new("Ful", 1,4,3, "Fire")
QDMF_Rune:new("Eck", 1,5,3, "Energy")
QDMF_Rune:new("Man", 1,6,3, "Mind")
QDMF_Rune:new("Des", 1,7,3, "Void")
QDMF_Rune:new("Zo", 1,8,3, "Negation")
QDMF_Rune:new("Sta", 2,0,3, "Unity")
QDMF_Rune:new("Ven", 2,1,3, "Time")
QDMF_Rune:new("Des", 2,2,3, "Life")
QDMF_Rune:new("Kath", 2,3,3, "Expand")
QDMF_Rune:new("Ir", 2,4,3, "Flight")
QDMF_Rune:new("Het", 2,5,3, "Twist")
QDMF_Rune:new("Bro", 2,6,3, "Peace")
QDMF_Rune:new("Trans", 2,7,3, "Transit")
QDMF_Rune:new("Gor", 2,8,3, "War")
QDMF_Rune:new("Lyr", 3,0,3, "Artistry")
QDMF_Rune:new("Ku", 3,1,3, "Strenght")
QDMF_Rune:new("Ros", 3,2,3, "Dexterity")
QDMF_Rune:new("Dain", 3,3,3, "Magic")
QDMF_Rune:new("Neta", 3,4,3, "Belief")
QDMF_Rune:new("Ing", 3,5,3, "Insight")
QDMF_Rune:new("Ra", 3,6,3, "Order")
QDMF_Rune:new("Sar", 3,7,3, "Chaos")
QDMF_Rune:new("Woo", 3,8,3, "Balace")
QDMF_Spell:new("Fireball", "Mage", "Ful Ir", 0)
file_runes = "mod_assets/textures/runes.dds"
file_runes_h = "mod_assets/textures/runes_h.dds"
file_runes_s = "mod_assets/textures/runes_s.dds"
function renderRunePanel(self,context)
runePanel:render(context)
end
function runePanelCreate(runestring)
local panel = {}
panel.phrase = QDMF_PhraseCreate(runestring)
panel.runesize = 30
panel.phrase_h = 20
panel.X = 0
panel.Y = 0
panel.clickcycle = false
panel.click = -1
panel.select = -1
panel.hover = -1
panel.tier = panel.phrase:getNextTier()
panel.base = panel.tier * QDMF_Rune.ways
function panel.checkHover(self,context)
for r = 0, self.phrase:getLength() do
if (context.button("", self.X + r*self.phrase_h, self.Y, self.phrase_h, self.phrase_h) ~= nil) then
self.hover = r
return
end
end
local yy = self.Y + self.phrase_h
for r = 0, 8 do
y = math.floor(r / 3)
x = self.X + (r - (y * 3)) * self.runesize
y = yy + y * self.runesize
if (context.button("", x, y, self.runesize, self.runesize) ~= nil) then
self.hover = r + 50 + self.base
return
end
end
self.hover = -1
end
function panel.drawPhrase(self, context, x, y)
local len = self.phrase:getLength()
for r = 0, len do
rune = self.phrase:getRuneAt(r)
if (rune ~= nil) then
if (self.click == r or self.select == r) then
file = file_runes_s
elseif (self.hover == r) then
file = file_runes_h
else
file = file_runes
end
context.drawImage2(file, x + r * self.phrase_h, y, rune.way * 100 + 10,
rune.tier * 100 + 10, 80, 80, self.phrase_h, self.phrase_h)
end
end
end
function panel.drawTiles(self, context, x, y)
if (self.tier == -1) then
return
end
local hover = self.hover - 50
local click = self.click - 50
local rx = 0
local ry = 0
local rr
for r = 0, 8 do
ry = math.floor(r / 3)
rx = x + (r - (ry * 3)) * self.runesize
ry = y + ry * self.runesize
rr = r + self.base
if (hover == rr) then
if (click == rr) then
file = file_runes_s
else
file = file_runes_h
end
elseif (self.phrase:hasRuneIndex(rr)) then
file = file_runes_s
else
file = file_runes
end
context.drawImage2(file, rx, ry, r * 100, self.tier * 100, 100, 100, self.runesize, self.runesize)
end
end
function panel.render(self, context)
self:checkHover(context)
if (context.mouseDown(0)) then
-- mousebutton pressed, save click position if valid
if (not self.clickcycle and self.hover > -1) then
self.clickcycle = true
self.click = self.hover
end
else
-- mousebutton released, act if position is still the same
if (self.clickcycle) then
self.clickcycle = false
if (self.hover == self.click) then
if (self.click > -1 and self.click < 50) then
-- click into spell-phrase
self.select = self.click
self.tier = self.phrase:getTierAt(self.click)
self.base = self.tier * QDMF_Rune.ways
elseif (self.click > 49 and self.click < 100) then
-- click into tile-zone
if (self.select > -1 and self.select < 50) then
self.phrase:add(self.click - 50, self.select)
else
self.phrase:add(self.click - 50)
end
self.tier = self.phrase:getNextTier()
self.base = self.tier * QDMF_Rune.ways
self.select = -1
end
end
self.click = -1
end
end
self:drawPhrase(context, self.X, self.Y)
self:drawTiles(context, self.X, self.Y + self.phrase_h)
end
return panel
end
runePanel = runePanelCreate()
Last edited by .rhavin on Mon Oct 16, 2017 5:15 pm, edited 1 time in total.
Re: Spell – rune order
The script itself looks great. I pasted it *as copied*, and connected a floor_trigger to it; when triggered, nothing happened. The same (nothing) happened when I called it direct from the console. I double checked my copy from your post, and called showPanel() both ways. No menu
So my first question is (the obvious one)... Does it also not work for anyone else but me?
Then... Is there any chance it was miss-copied from the editor when posted?
Also what screen resolution do you use? Is there anything else needed besides just calling showPanel(), and having the three textures @ "mod_assets/textures/" ?
Can you post a screenshot of it?
**As a side note to regulars here: I would have sworn that DDS textures had to be referenced in scripts as TGA files, but the runes show (via GameMode), and are included in the exported dat. When did they change (patch) that historical quirk?
EDIT: This is definitely something on my end that is not working.
Now that I've seen it in action, it reminds me a little bit of Arx Fatalis, at least in principle (not with mouse gestures). It's very impressive; doubly so, being a first use of Lua. (So what do you normally use? )
So my first question is (the obvious one)... Does it also not work for anyone else but me?
Then... Is there any chance it was miss-copied from the editor when posted?
Also what screen resolution do you use? Is there anything else needed besides just calling showPanel(), and having the three textures @ "mod_assets/textures/" ?
Can you post a screenshot of it?
**As a side note to regulars here: I would have sworn that DDS textures had to be referenced in scripts as TGA files, but the runes show (via GameMode), and are included in the exported dat. When did they change (patch) that historical quirk?
EDIT: This is definitely something on my end that is not working.
Now that I've seen it in action, it reminds me a little bit of Arx Fatalis, at least in principle (not with mouse gestures). It's very impressive; doubly so, being a first use of Lua. (So what do you normally use? )
- Zo Kath Ra
- Posts: 937
- Joined: Sat Apr 21, 2012 9:57 am
- Location: Germany
Re: Spell – rune order
Another side note:
Clicking "Select all" above the code sample should select the entire code, but it takes me to the beginning of the thread instead.
Tested on Firefox and Chromium.
Clicking "Select all" above the code sample should select the entire code, but it takes me to the beginning of the thread instead.
Tested on Firefox and Chromium.