PDA

View Full Version : How do custom dice?



Munacra
July 25th, 2008, 12:36
So I'm trying to implement a Descent: Journeys into the Dark ruleset into FG. It is a boardgame, so it requires some funky looking dice (https://www.streakingegyptian.com/descentdice/roller.htm):

Basically, it is a D6, but the faces are custom. On a result of a 2, the meaning is 2 damage, 1 range, for example. The values also change depending on the die color, so essentially you have a green d6 and a red d6, and they are not the same die.

Is there a way to get those dice to work inside FG? I was thinking of just rolling d6's and then just cross referencing them by hand, but I figured that would quickly get tedious. I'd like FG to roll the dice and calculate the results. (throw red 2d6 and yellow d6, script crossreferences it to a table and gives me the result = 8 damage, 3 range, 2 surges) I'm assuming a LUA script would work, but if anyone could give me a tip on where/how to start, that would be super.

Valarian
July 25th, 2008, 12:54
I think you're right that it'll need Lua scripting ... it goes beyond what a simple custom die will be able to produce. I don't think you'll be able to get the pictorial representation in FGII.

I'll try and describe what I think may be needed:
1. Create custom dice based on the d6 model for the different colours (e.g. dR = Red die, dY = Yellow die)
Example: https://www.fantasygrounds.com/forums/showpost.php?p=59981&postcount=14
2. Create custom onDiceLanded() handler for the custom dice in the chat_chat.lua file.
e.g. if "dR" then .... do red die stuff

The custom dice would allow you to use the /die command to roll the dice (e.g. /die 2dR+1dY). The onDiceLanded function in the chat window would then produce the expected results based on the die results.

Sorontar
July 25th, 2008, 13:01
Such good fun playing this game, took me back to playing Heroquest as a kid ....I can't help but best of luck.

Munacra
July 25th, 2008, 17:12
Thanks. :) Let me cook up something and I'll update when I get stuck.

Munacra
July 25th, 2008, 20:35
Ok, so the easy stuff is done.


<customdie name="dR">
<model>d6</model>
<menuicon>customdice</menuicon>
</customdie>

<customdie name="dB">
<model>d6</model>
<menuicon>customdice</menuicon>
</customdie>

<customdie name="dW">
<model>d6</model>
<menuicon>customdice</menuicon>
</customdie>

That makes the custom dice. Now the hard part, I think.

So this is kind of what I want to achieve. In a 1dR, the result of 1 will be 4damage/0range/0surges. On a result of a 2, the dRwill be 1 damage/1 range/1 surge. Etcetera.

so I think that will require a table some pattern matching.


string.gsub (s, pattern, repl [, n])
Returns a copy of s in which all (or the first n, if given) occurrences of the pattern have been replaced by a replacement string specified by repl, which may be a string, a table, or a function. gsub also returns, as its second value, the total number of matches that occurred.

If repl is a string, then its value is used for replacement. The character % works as an escape character: any sequence in repl of the form %n, with n between 1 and 9, stands for the value of the n-th captured substring (see below). The sequence %0 stands for the whole match. The sequence %% stands for a single %.

If repl is a table, then the table is queried for every match, using the first capture as the key; if the pattern specifies no captures, then the whole match is used as the key.

If repl is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order; if the pattern specifies no captures, then the whole match is passed as a sole argument.

If the value returned by the table query or by the function call is a string or a number, then it is used as the replacement string; otherwise, if it is false or nil, then there is no replacement (that is, the original match is kept in the string).

Here are some examples:

x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"

x = string.gsub("hello world", "%w+", "%0 %0", 1)
--> x="hello hello world"

x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
--> x="world hello Lua from"

x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
--> x="home = /home/roberto, user = roberto"

x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
return loadstring(s)()
end)
--> x="4+5 = 9"

local t = {name="lua", version="5.1"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.1.tar.gz"


So since a table is/can be an array, then could I do this?

d6=a/b/c/d/e/f


var dRdamage = new Array(4,0,1,2,3,3)
var dRrange = new Array(0,0,1,0,1,0)
var dRsurge = new Array(0,0,2,2,1,1)
var dRmiss = new Array(0,1,0,0,0,0)

Call up the results and then add them together? Is that possible to do?

Also, the 3d representation of the dice can't be done, but what about the icons in the chatbox? Can the dR die be red and show 3d/4r/0s and the blue die show 2d/1r/3/s?

Munacra
July 25th, 2008, 21:19
oh hey I CAN do it. I'll be back with the actual code isntead of copy-pasting from that java thing.

Valarian
July 25th, 2008, 21:41
I'm afraid I'm not too up on the tables and how they work on lookups. You might be able to get it working. The longhand version would be to interrogate the dice as you go through the onDiceLanded() function



for i = 1, draginfo.getSlotCount() do
draginfo.setSlot(i);

local entry = {};
entry.text = draginfo.getDescription();
entry.font = "systemfont";
entry.dice = {};
entry.diemodifier = draginfo.getNumberData();
local dicetable = draginfo.getDieList();

local damage = 0;
local range = 0;
local surge = 0;
local miss = 0;

if dicetable then
for n = 1, table.maxn(dicetable) do
if dicetable[n].type == "dR" then
if dicetable[n].result == 1 then
damage = damage + 4;
elseif dicetable[n].result == 2 then
miss = miss + 1;
elseif dicetable[n].result == 3 then
damage = damage + 1;
range = range + 1;
surge = surge + 1;
elseif ....
elseif dicetable[n].type == "dB" then
....
elseif dicetable[n].type == "dW" then
....
else
table.insert(entry.dice, dicetable[n]);
end
end
end

if miss > 0 then
entry.text = entry.text .. " {MISS} ";
elseif damage > 0 or range > 0 or surge = 0 then
entry.text = entry.text .. " {Damage: " .. damage .. ", Range: " .. range .. ", Surge: " .. surge .. "} ";
end

if User.isHost() then
if ChatManager.getDieRevealFlag() then
entry.dicesecret = false;
end
entry.sender = GmIdentityManager.getCurrent();
else
entry.sender = User.getIdentityLabel();
end

deliverMessage(entry);

Munacra
July 25th, 2008, 22:20
Thank you Valarian. I think I'm making progress.

Foen
July 26th, 2008, 06:16
For notational/programming convenience, you could try the following:



local dRresults = {
damage={4,0,1,2,3,3},
range={0,0,1,0,1,0},
surge={0,0,2,2,1,1},
miss={0,1,0,0,0,0}};


Then to decode a roll of n (between 1 and 6), you would use:



damage=dRresults.damage[n];
range=dRresults.range[n];
surge=dRresults.surge[n];
miss=dRresults.miss[n];


Hope that helps,

Foen

Munacra
July 26th, 2008, 17:04
That helps a lot because I scoured the internet and read the lua manual over and over again but couldn't get the table to work. Thank you.

Here is what I have so far


function onDiceLanded()
for i=1, draginfo.getSlotCount() do
draginfo.setSlot(i);

localentry = {};
entry.text = draginfo.getDescription();
entry.font = "systemfont";
entry.dice = {};
entry.diemodifier = draginfo.getNumberData();
local dicetable = draginfo.getDieList();

local damage = 0;
local range = 0;
local surge = 0;
local miss = 0;
local damagerange= 0;

local dRresults = {
damage={4,0,1,2,3,3},
range={0,0,2,2,1,1},
surge={0,0,1,0,1,0},
miss={0,1,0,0,0,0}};

localdBresults = {
damage={1,0,1,2,0,2},
range={3,0,3,1,4,2},
surge={1,0,1,0,1,0},
miss{0,1,0,0,0,0};

localdWresults = {
damage={3,0,1,1,3,2},
range={1,0,3,3,1,2},
surge={1,0,1,1,1,0},
miss{0,1,0,0,0,0};

localdGresults = {
damage={2,2,2,1,3,3},
range={0,1,0,1,0,0},
surge={1,0,1,0,0,0};

localdYresults = {
damage={1,0,1,0,0,0},
range={1,3,2,3,2,2},
surge={0,0,0,0,1,1};

localdBlackresults = {
damagerange={1,1,1,0,0,0},
surge=0,0,0,1,1,0{};

localdSilverresults = {
damagerange={2,2,2,0,0,0},
surge={0,0,0,2,2,0};

localdGoldresults = {
damagerange={3,3,3,0,0,0},
surge={0,0,0,3,3,0};

I think Valarian's suggetion would work like this


if dicetable then
for n = 1, table.maxn(dicetable) do
if dicetable[n].type == "dR" then
damage=dRresults.damage[n];
range=dRresults.range[n];
surge=dRresults.surge[n];
miss=dRresults.miss[n];

If I can get that to work, that's each individual result. How do I add all the different dice up at the end?

Foen
July 26th, 2008, 18:34
First you'd need to set damage, range, surge and miss to zero each, then I think you'd do something like:



damage = damage + dRresults.damage[n];
range = range + dRresults.range[n];
surge = surge + dRresults.surge[n];
miss = miss + dRresults.miss[n];


That way, as each dice is parsed, its results are added to a running total.

Cheers

Stuart

Munacra
July 26th, 2008, 21:30
Alright guys, here's my update.


function onDiceLanded()
for i=1, draginfo.getSlotCount() do
draginfo.setSlot(i);

localentry = {};
entry.text = draginfo.getDescription();
entry.font = "systemfont";
entry.dice = {};
entry.diemodifier = draginfo.getNumberData();
local dicetable = draginfo.getDieList();

local dRresults = {
damage={4,0,1,2,3,3},
range={0,0,2,2,1,1},
surge={0,0,1,0,1,0},
miss={0,1,0,0,0,0}};

localdBresults = {
damage={1,0,1,2,0,2},
range={3,0,3,1,4,2},
surge={1,0,1,0,1,0},
miss{0,1,0,0,0,0};

localdWresults = {
damage={3,0,1,1,3,2},
range={1,0,3,3,1,2},
surge={1,0,1,1,1,0},
miss{0,1,0,0,0,0};

localdGresults = {
damage={2,2,2,1,3,3},
range={0,1,0,1,0,0},
surge={1,0,1,0,0,0};

localdYresults = {
damage={1,0,1,0,0,0},
range={1,3,2,3,2,2},
surge={0,0,0,0,1,1};

localdBlackresults = {
damagerange={1,1,1,0,0,0},
surge=0,0,0,1,1,0{};

localdSilverresults = {
damagerange={2,2,2,0,0,0},
surge={0,0,0,2,2,0};

localdGoldresults = {
damagerange={3,3,3,0,0,0},
surge={0,0,0,3,3,0};

local damage = 0;
local range = 0;
local surge = 0;
local miss = 0;
local damagerange= 0;

if dicetable then
for n = 1, table.maxn(dicetable) do
if dicetable[n].type == "dR" then
damage=dRresults.damage[n];
range=dRresults.range[n];
surge=dRresults.surge[n];
miss=dRresults.miss[n];
elseif dicetable[n].type == "dB" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dW" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dG" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dY" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dBlack" then
damagerange=dBlackresults.damage[n];
surge=dBlackresults.surge[n];
elseif dicetable[n].type =="dSilver" then
damagerange=dSilverresults.damage[n];
surge=dSilverresults.surge[n];
elseif dicetable[n].type =="dGold" then
damagerange=dGoldresults.damage[n];
surge=dGoldresults.surge[n];

Now that doesn't work, but I'm not giving up. I'm learning how this stuff reads now, so I'll probably be asking more questions as I continue descenting into the darkness shooting magic missiles.

Foen
July 27th, 2008, 06:10
If the code you posted is verbatim (a copy and paste job) then it appears you have some typos in there. Here are the ones I spotted at first glance:


You need a space after the keyword 'local', for example in 'localdBresults' should be 'local dBresults'
The tables (such as dGresults) don't all have matching closing braces: }
Some of the tables have damagerange as a sub-table (for example dBlackresults), but your code is looking for dBlackresults.damage[n]
You need to close the if, for and function control structures with an 'end' keyword each.


The code looks good, but it just needs a bit of polishing up ;)

Cheers

Stuart

Munacra
July 27th, 2008, 21:25
Ok, so newest update, and finished unworking code:


function onDiceLanded()
for i=1, draginfo.getSlotCount() do
draginfo.setSlot(i);

localentry = {};
entry.text = draginfo.getDescription();
entry.font = "systemfont";
entry.dice = {};
entry.diemodifier = draginfo.getNumberData();
local dicetable = draginfo.getDieList();

local dRresults = {
damage={4,0,1,2,3,3},
range={0,0,2,2,1,1},
surge={0,0,1,0,1,0},
miss={0,1,0,0,0,0}};

local dBresults = {
damage={1,0,1,2,0,2},
range={3,0,3,1,4,2},
surge={1,0,1,0,1,0},
miss{0,1,0,0,0,0}};

local dWresults = {
damage={3,0,1,1,3,2},
range={1,0,3,3,1,2},
surge={1,0,1,1,1,0},
miss{0,1,0,0,0,0}};

local dGresults = {
damage={2,2,2,1,3,3},
range={0,1,0,1,0,0},
surge={1,0,1,0,0,0}};

local dYresults = {
damage={1,0,1,0,0,0},
range={1,3,2,3,2,2},
surge={0,0,0,0,1,1}};

local dBlackresults = {
damagerange={1,1,1,0,0,0},
surge={0,0,0,1,1,0}};

local dSilverresults = {
damagerange={2,2,2,0,0,0},
surge={0,0,0,2,2,0}};

local dGoldresults = {
damagerange={3,3,3,0,0,0},
surge={0,0,0,3,3,0}};

local damage = 0;
local range = 0;
local surge = 0;
local miss = 0;
local damagerange= 0;

if dicetable then
for n = 1, table.maxn(dicetable) do
if dicetable[n].type == "dR" then
damage=dRresults.damage[n];
range=dRresults.range[n];
surge=dRresults.surge[n];
miss=dRresults.miss[n];
elseif dicetable[n].type == "dB" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dW" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dG" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dY" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dBlack" then
damagerange=dBlackresults.damagerange[n];
surge=dBlackresults.surge[n];
elseif dicetable[n].type =="dSilver" then
damagerange=dSilverresults.damagerange[n];
surge=dSilverresults.surge[n];
elseif dicetable[n].type =="dGold" then
damagerange=dGoldresults.damagerange[n];
surge=dGoldresults.surge[n];
else
table.insert(entry.dice, dicetable[n]);
end
end
end

damage = damage + dRresults.damage[n] + dBresults.damage[n] + dWresults.damage[n] + dGresults.damage[n] + dYresults.damage[n];
range = range + dRresults.range[n] + dBresults.range[n] + dWresults.range[n] + dGresults.range[n] + dYresults.range[n];
surge = surge + dRresults.surge + dBresults.surge[n] + dWresults.surge[n] + dGresults.surge[n] + dYresults.surge[n] + dBlackresults.surge[n] + dSilverresults.surge[n] + dGoldresults.surge[n];
miss = miss + dRresults.miss[n] + dBresults.miss[n] + dWresults.miss[n];
damagerange = damagerange + dBlackresults.damagerange[n] + dSilverresults.damagerange[n] + dGoldresults.damagerange[n];

if miss > 0 then
entry.text = entry.text .. " {MISS} ";
elseif damage > 0 or range > 0 or surge = 0 then
entry.text = entry.text .. " {Damage: " .. damage .. ", Range: " .. range .. ", Surge: " .. surge .. "} ";
end

if User.isHost() then
if ChatManager.getDieRevealFlag() then
entry.dicesecret = false;
end
entry.sender = GmIdentityManager.getCurrent();
else
entry.sender = User.getIdentityLabel();
end

deliverMessage(entry);


The problems are that I cannot roll any of the custom dice using the /die command. Also, it doesn't tally up anything, and it just simply doesn't work. But the code is written, so I'll take it as a moral victory! :)

Munacra
July 27th, 2008, 21:35
You know, the thing is that the overlord card mechanic will likely be much harder than all this, and I'm floundering as it is.

Now this is getting ahead of myself, buuuut
Since the game involves tons of card dealing, I'm thinking ok... Well, just put every card from the ei; the treasure deck inside a table, and use a math.random function to draw it from the treasure deck table. Since the cards go back into the deck anyway, that is simple enough.

But what do I do with for example, the overlock deck? In this deck, you are required to draw from a deck, keep the cards you want, discard the ones you dont, and play the ones you want putting them in the discard pile. Also, to shuffle when the overlord draw deck is exhausted using the discarded cards.

makes my mind go ugh.

Foen
July 27th, 2008, 21:57
Hi Munacra!

I have had another look through your code, and include an edited version below:



function onDiceLanded()
for i=1, draginfo.getSlotCount() do
draginfo.setSlot(i);

local entry = {};
entry.text = draginfo.getDescription();
entry.font = "systemfont";
entry.dice = {};
entry.diemodifier = draginfo.getNumberData();
local dicetable = draginfo.getDieList();

local dRresults = {
damage={4,0,1,2,3,3},
range={0,0,2,2,1,1},
surge={0,0,1,0,1,0},
miss={0,1,0,0,0,0}};

local dBresults = {
damage={1,0,1,2,0,2},
range={3,0,3,1,4,2},
surge={1,0,1,0,1,0},
miss{0,1,0,0,0,0}};

local dWresults = {
damage={3,0,1,1,3,2},
range={1,0,3,3,1,2},
surge={1,0,1,1,1,0},
miss{0,1,0,0,0,0}};

local dGresults = {
damage={2,2,2,1,3,3},
range={0,1,0,1,0,0},
surge={1,0,1,0,0,0}};

local dYresults = {
damage={1,0,1,0,0,0},
range={1,3,2,3,2,2},
surge={0,0,0,0,1,1}};

local dBlackresults = {
damagerange={1,1,1,0,0,0},
surge={0,0,0,1,1,0}};

local dSilverresults = {
damagerange={2,2,2,0,0,0},
surge={0,0,0,2,2,0}};

local dGoldresults = {
damagerange={3,3,3,0,0,0},
surge={0,0,0,3,3,0}};

local damage = 0;
local range = 0;
local surge = 0;
local miss = 0;
local damagerange= 0;

if dicetable then
for n = 1, table.maxn(dicetable) do
if dicetable[n].type == "dR" then
damage=dRresults.damage[n];
range=dRresults.range[n];
surge=dRresults.surge[n];
miss=dRresults.miss[n];
elseif dicetable[n].type == "dB" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dW" then
damage=dBresults.damage[n];
range=dBresults.range[n];
surge=dBresults.surge[n];
miss=dBresults.miss[n];
elseif dicetable[n].type =="dG" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dY" then
damage=dGresults.damage[n];
range=dGresults.range[n];
surge=dGresults.surge[n];
elseif dicetable[n].type =="dBlack" then
damagerange=dBlackresults.damagerange[n];
surge=dBlackresults.surge[n];
elseif dicetable[n].type =="dSilver" then
damagerange=dSilverresults.damagerange[n];
surge=dSilverresults.surge[n];
elseif dicetable[n].type =="dGold" then
damagerange=dGoldresults.damagerange[n];
surge=dGoldresults.surge[n];
else
table.insert(entry.dice, dicetable[n]);
end
end
end

damage = damage + dRresults.damage[n] + dBresults.damage[n] + dWresults.damage[n] + dGresults.damage[n] + dYresults.damage[n];
range = range + dRresults.range[n] + dBresults.range[n] + dWresults.range[n] + dGresults.range[n] + dYresults.range[n];
surge = surge + dRresults.surge + dBresults.surge[n] + dWresults.surge[n] + dGresults.surge[n] + dYresults.surge[n] + dBlackresults.surge[n] + dSilverresults.surge[n] + dGoldresults.surge[n];
miss = miss + dRresults.miss[n] + dBresults.miss[n] + dWresults.miss[n];
damagerange = damagerange + dBlackresults.damagerange[n] + dSilverresults.damagerange[n] + dGoldresults.damagerange[n];

if miss > 0 then
entry.text = entry.text .. " {MISS} ";
elseif damage > 0 or range > 0 or surge = 0 then
entry.text = entry.text .. " {Damage: " .. damage .. ", Range: " .. range .. ", Surge: " .. surge .. "} ";
end

if User.isHost() then
if ChatManager.getDieRevealFlag() then
entry.dicesecret = false;
end
entry.sender = GmIdentityManager.getCurrent();
else
entry.sender = User.getIdentityLabel();
end

deliverMessage(entry);
end
end


You were missing two end statements (I've color-coded them), and one of your 'local' statements didn't have a space before the variable name (I've emboldened that one).

As regards not working, you might want to add print() commands into the code to help you debug it, and/or give a bit more detail on the kind of error messages your are getting.

The print() command writes its argument to the console, which you can open using /console from the chat window.

On the wider topic of overlord card mechanics, you have me completely stumped. I'm not at all familiar with the game system your are talking about.

Cheers

Stuart

Munacra
July 27th, 2008, 22:22
ok Foen your myhero. Now the dice are rolling, but I'm getting this error in the console

attempt to call global 'miss' a nil value.

I fixed a typo in the datatable for the miss values of dR and DB and DW that didn't have an "=". But I'm still getting that error

Foen
July 27th, 2008, 22:30
If the code is inline (in a file with a .xml extension) instead of a separate code file (with a .lua extension) then you need to change 'if miss > 0 then' to 'if miss &gt; 0 then' because an xml file cannot have a > sign in it.

If the code is in a separate (.lua) file, then it should give you the line number of the error, which will help pin it down.

Cheers

Stuart