So, I've created this:
Code: Select all
// random listing
// entities should have their .listname set before RandomizeList is called
// TODO: allow entities to be featured in multiple lists, using float(.string fieldname, string s)RandomizeList?
.string
listname
;
.float
listcount
, listindex
, listused
;
.entity
listhead // first item
, listprev
, listcurr // current item, must be accessed from .listhead only
, listnext
;
float (string s) RandomizeList =
{
entity
e
;
float
c
, i
;
e = find (world, listname, s);
if (e == world)
return 0;
// counts items
c = 0;
while (e != world)
{
c = c + 1;
e.listused = FALSE;
e = find (e, listname, s);
}
// randomize their indexes
i = c;
do
{
e = find (world, listname, s);
if (e == world)
e = find (e, listname, s);
// if ( (e.listused == FALSE && random () <= (1 / c)) || i < 2) // todos os itens igualmente randomizados
if (e.listused == FALSE && random () <= (1 / i)) // randomização decrescente
{
e.listused = TRUE;
// fazendo assim, o listindex/listcount do primeiro item sempre será zero, e o do último item será 1
// e.listcount = c - 1; // last index (total number of items, minus this one)
// e.listindex = i - 1; // indexes starts from zero
// fazendo assim, listindex/listcount retornará 50% para o item 1 duma lista contendo 2
e.listcount = c; // total number of items
e.listindex = i; // indexes starts from 1
i = i - 1;
}
} while (i > 0);
// returns the amount of items
return c;
}
entity (string s, float c) MakeList =
{
entity
e
;
e = find (world, listname, s);
if (e != world)
{
bprint ("MakeList: list \""); bprint (s); bprint ("\" already exists\n");
}
else
{
// creates items
entity
p
;
float
i
;
i = c;
while (i)
{
e = spawn ();
e.listname = s;
e.listused = FALSE;
e.listcount = c; // total number of items
e.listindex = c - i + 1; // indexes starts from 1
if (i == c) // first item
e.listhead = e;
else
{
e.listhead = p.listhead;
e.listprev = p;
p.listnext = e;
}
p = e;
i = i - 1;
}
// last item
e.listnext = e.listhead;
e.listhead.listprev = e;
// current item
e.listhead.listcurr = e.listhead;
}
return e.listhead;
}
entity (string s) NextListItem =
{
entity
e
;
e = find (world, listname, s);
if (e == world)
{
bprint ("NextListItem: list \""); bprint (s); bprint ("\" doesn't exist\n");
}
else
{
e = e.listhead.listcurr = e.listhead.listcurr.listnext;
if (e == e.listhead)
RandomizeList (s);
}
return e;
}
The problem is that randomizing (in my case, a list of 19) items can very often make the QC loop counter exceeds the 100 (or 1000, can't remember) cycles, thus crashing the game to the console. And even when it works, it's almost always too slow, freezing the whole engine for many seconds.
For some reason I feel that there may be a way to get the same results (randomizing a whole list of sequential values, with no repetitions), without calling random() more than once. Maybe not possible in QC, but in a new built-in function.