So, this is the situation in short:
I have:
1. a set of sprites in the sprite editor.
2. a random selected sprite (int spritenum) (this happens in the global script, in the always_execute function).
3. a gui that shows the random selected sprite for x seconds.
4. a menu where you can activate/dis-activate categories (about 20)
What I'm looking for is this:
for each category I do know the collection of sprites that does fit within each category.
Now, after a random sprite has been selected, I'd like to check if this specific chosen sprite does exist in each category.
if so (the category does contain this random sprite number): show sprite
if not (the category does not contain this random sprite number): choose another random sprite
So, is there something like a data collection I can create where you can store a bunch of, in this case, numbers and check the random sprite (int spritenum) against such data collection?
What about usign arrays?
//10 sprites per category for example
#define MAX_SPRITES_PER_CATEGORY 10
//As far as I know bidimensional arrays ar not implemented in AGS so the best option would be:
int sprites[20*MAX_SPRITES_PER_CATEGORY];
//You would have to fill it manually at some point in the script (game_start maybe).
int category=0;
sprites[category*MAX_SPRITES_PER_CATEGORY]=23;//23 would be the fisrt spriteID in category 0
sprites[category*MAX_SPRITES_PER_CATEGORY+1]=45;//45 would be the 2nd spriteID in category 0
sprites[category*MAX_SPRITES_PER_CATEGORY+2]=63;
//...
category=2;
sprites[category*MAX_SPRITES_PER_CATEGORY]=65;
//...
//and then wherever you want to check if the sprite is in the wanted category:
int i=0;
bool found=false;
while(i<MAX_SPRITES_PER_CATEGORY)
{
if(sprites[selected_category*MAX_SPRITES_PER_CATEGORY+i]==randomly_chosen_sprite)
found=true;
i++;
}
if(found==true)
your_show_sprite_function();//
else
choose_another_sprite();//or wherever you have scripted
EDIT: I posted a better solution below!!
Thanx Joe! I'll check this out tomorrow.
questions:
Any reason spriteID 23 is written out twice for category 0?
EDIT:
After having a good look at it: this seems to be exactly what I was looking for, thanx again!
No problem!
1Q) The reason for having a 'max sprites...' is that it's the easiest way. You can define it as 100 or 1000... On modern PC's you won't ever have memory overflows issues.
2Q) No sorry, just copied the line and forgot to modify it.
Nevertheless, looking at your 1stQ I've thought an easier way where you don't need to define a max_sprites_per_category:
int sprite_category[MAX_AGS_SPRITES];//this array will store the category number instead of the sprite ID
//NOTE: I don't know if MAX_AGS_SPRITES is already defined in AGS
//at game_start
int i=0;
while(i<MAX_AGS_SPRITES)
{
sprite_category[i]=-1;//Fill the whole array with -1 which will mean null category
i++; //The reason for doing this is that there will surely be some sprite IDs which will not be used for the category purpose
}
//then fill the array manually:
sprite_category[23]=1;//The sprite whose ID is 23 is in category 1;
sprite_category[31]=1;//The sprite whose ID is 31 is in category 1;
sprite_category[11]=2;//The sprite whose ID is 11 is in category 2;
//...
//and then whenever you want to check if the sprite is in the wanted category:
if(sprite_category[randomly_chosen_spriteID]==selected_category)
your_show_sprite_function();
else
choose_another_sprite();
Looks perfect, thanx.
Drat, can't seem to implement your code.
Would probably need to rewrite the whole part I already have...
So, the whole thing was already working except for 1 thing:
As you can see in Line 80 and 81, I now have to write out an 'if' statement for each available category. (written out to of them now: exterior & nature)
I guess there's a way better way to do such a check for all categories.
The categories are set from line 182. (again, only exterior & nature)
This is what I already have (sorry for the not so well done coding of me...):
// main global script file
bool menu_is_shown; //if menu is shown, stop running the gui with images
int countdown;
int ran; //randomizer
bool playlist_Exterior = true; //for enable/Disable category Exterior (in the menu)
bool playlist_Nature = true; //for enable/Disable category Nature (in the menu)
int prevsprite;int prevsprite;//to store the previous sprite to avoid showing 1 sprite twice
struct Sprite { //struct with arrays as categories for each sprite
String name;
bool cat_Nature;
bool cat_Interior;
bool cat_Exterior;
import void Copy(int spriteID);
};
Sprite sprites[14]; //set number of sprites
function repeatedly_execute_always(){
//set Sprite Info
sprites[0].name = "info0";
sprites[1].name = "info1";
sprites[2].name = "info2";
sprites[3].name = "info3";
sprites[4].name = "info4";
sprites[5].name = "info5";
sprites[6].name = "info6";
sprites[7].name = "info7";
sprites[8].name = "info8";
sprites[9].name = "info9";
sprites[10].name = "info10";
sprites[11].name = "info11";
sprites[12].name = "info12";
sprites[13].name = "info13";
//set multiple categories per sprite on/off:
sprites[0].cat_Exterior = true;
sprites[1].cat_Exterior = true;
sprites[2].cat_Exterior = true;
sprites[3].cat_Exterior = false;
sprites[4].cat_Exterior = false;
sprites[5].cat_Exterior = false;
sprites[6].cat_Exterior = true;
sprites[6].cat_Nature = true;
sprites[7].cat_Exterior = true;
sprites[7].cat_Nature = true;
sprites[8].cat_Exterior = true;
sprites[8].cat_Nature = true;
sprites[9].cat_Exterior = true;
sprites[9].cat_Nature = false;
sprites[10].cat_Exterior = false;
sprites[10].cat_Nature = false;
sprites[11].cat_Exterior = false;
sprites[12].cat_Exterior = false;
sprites[13].cat_Exterior = false;
if (countdown > 0)
{
countdown--; // decrease countdown by 1 and reloop
}
else if (menu_is_shown == true) //do nothing when menu is shown and reloop
{
//reloop
}
else if (countdown == 0)
{
Bblitz.Visible=false;//set gui button invisible
Lblitznfo.Visible=false;//set gui label invisible
ran=Random((12)+1); //pick a random sprite
if ((ran < 6) || (ran == prevsprite))//reloop if random selected sprite_id is 5 or lower or current random sprite is same previous random sprite
{
//reloop
}
else if (ran >= 6) //random selected sprite_id is 6 or higher, sprite_id 0 to 6 aren't used
{
Bblitz.Visible=false;//set gui button invisible
int spritenum = (ran);
if (((sprites[(spritenum)].cat_Exterior==true) && (playlist_Exterior==true)) &&
((sprites[(spritenum)].cat_Nature==true) && (playlist_Nature==true))) //go if sprite cat exterior is true en playlistexterior is choosen in menu
{
prevsprite =(ran);
Bblitz.NormalGraphic=(ran); //set button normalgrahp with random sprite within range
Bblitz.X = ((1024 - Game.SpriteWidth[ran])/2);//center gui button that shows sprite
Bblitz.Visible=true; //set gui buttonvisible
Lblitznfo.Visible=true;//set gui label visible
Lblitznfo.Text = (sprites[(spritenum)].name); //the sprite information shown on the label on the gui
countdown=60;//reset countdown (40=1sec)
}
}
}
}
//These are buttons in the menu to set a category on or of:
function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
if (Bcatexterior.Text == "exterior is on"){
Bcatexterior.Text = "exterior is off";
playlist_Exterior = false;
}
else if (Bcatexterior.Text == "exterior is off"){
Bcatexterior.Text = "exterior is on";
playlist_Exterior = true;
}
}
function Bnature_OnClick(GUIControl *control, MouseButton button)
{
if (Bnature.Text == "nature is on"){
Bnature.Text = "nature is off";
playlist_Nature = false;
}
else if (Bnature.Text == "nature is off"){
Bnature.Text = "nature is on";
playlist_Nature = true;
}
}
1st of all. Why initalize sprites in rep_ex func?? I think you should initialize them in game_start function.
I could appreciate that 1 sprite can have several categories and you can also have more than one category selected. Am I wrong?
In that case I wouldn't create several properties for handling categories.
I would do the foloowing:
//Declare each category as binary code. Note they can only be powers of 2
#define NATURE 1 //00000001
#define EXTERIOR 2 //00000010
#define INTERIOR 4 //00000100
#define RELAX 8 //00001000
struct Sprite { //struct with arrays as categories for each sprite
String name;
int category;
import void Copy(int spriteID);
};
//Also define only 1 variable for your playlist_category
int playlist_category;
//then at game _start:
sprites[0].category = EXTERIOR;
sprites[1].category = EXTERIOR;
sprites[2].category = EXTERIOR;
sprites[6].category = EXTERIOR | NATURE;//this will result 00000011 which will mean exterior+nature
sprites[6].category = EXTERIOR | NATURE;
//...
//Whenever you select 1 category:
playlist_category=NATURE|INTERIOR;//Or what ever you have selected
//Whenever you want to compare(your 80 and 81 lines):
if(sprites[spritenum].category & playlist_category) //Note it's only one & and not 2 since it's bitwise operation
//This condition will return true if any of the sprite categories fits with any of the playlist categories
{
//...your stuff
}
//Whenever you select 1 category:
playlist_category=NATURE|INTERIOR;//Or what ever you have selected
That's for when you click a category button in the menu, I suppose?
If so, I can never know what other categories has been set, so I can't create a combi like NATURE|INTERIOR.
But it wouldn't surprise me if I completely misunderstoot this specific line of code of yours.
In my case multiple categories can be set on or off.
And yes, I complete misplaced parts of the code in rep_exec instead of the game_start function.
That wasn't a smart move indeed.
You intrepeted it just right. I'll try to fit that line in your code:
function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
if (Bcatexterior.Text == "exterior is on"){
Bcatexterior.Text = "exterior is off";
playlist_category ^= EXTERIOR; //This will toggle EXTERIOR bit ...
}
else if (Bcatexterior.Text == "exterior is off"){
Bcatexterior.Text = "exterior is on";
playlist_category |= EXTERIOR; //this will add the EXTERIOR bit without modifying other bits so you can have combiantions
}
}
I think this looks like the solution I was looking for.
But I don't know why line 5 & 9 from your code gives me a parse error:
GlobalScript.asc(635): Error (line 635): PE04: parse error at 'playlist_category'
I'm not really familiar with using operators like XOR this way :(
Maybe its not supported in AGS, try this mode:
playlist_category = playlist_category ^ EXTERIOR;
playlist_category = playlist_category | EXTERIOR;
EDIT: If you dont feel good with using this AND, OR, XOR operators and need an explanation of their operating mode please tell, I think I can explain it in detail.
Yup, you nailed it, much appriciated!!
Quote from: Joe on Fri 20/06/2014 22:08:37
If you dont feel good with using this AND, OR, XOR operators and need an explanation of their operating mode please tell, I think I can explain it in detail.
Yeh, I'm not really familiar with these bitwise operators, so it would be quite helpful for me to see an explanation of their operating mode.
Very kind of you.
Quote from: Joe on Fri 20/06/2014 21:16:49
You intrepeted it just right. I'll try to fit that line in your code:
function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
if (Bcatexterior.Text == "exterior is on"){
Bcatexterior.Text = "exterior is off";
playlist_category ^= EXTERIOR; //This will toggle EXTERIOR bit ...
}
else if (Bcatexterior.Text == "exterior is off"){
Bcatexterior.Text = "exterior is on";
playlist_category |= EXTERIOR; //this will add the EXTERIOR bit without modifying other bits so you can have combiantions
}
}
Support for yer' new fangled assignment operators is hopefully coming in the next release. For now, yeah, you need to use the long form.
Quote from: Gurok on Fri 20/06/2014 23:39:50
Support for yer' new fangled assignment operators is hopefully coming in the next release. For now, yeah, you need to use the long form.
Nice to know.
Quote from: Arj0n on Fri 20/06/2014 22:40:44
Yeh, I'm not really familiar with these bitwise operators, so it would be quite helpful for me to see an explanation of their operating mode.
Very kind of you.
I guess you can understand logic operations, anyway here is a link for reference: http://en.wikibooks.org/wiki/Digital_Circuits/Logic_Operations
Bitwise operations are made bit by bit. If you type:
result=var1|var2;
It will do the OR operation between var1's first bit and var2's first bit and the result will be stored in result's 1st bit and so on with the rest of bits
An example:
00101101 = var1
10101000 = var2
============ OR
10101101 = result
In our case we want to be able to store several categories in 1 variable so if we type:
sprites[10].category = EXTERIOR | NATURE | INTERIOR;
The operation would be the following:
00000010
00000001
00000100
====== OR
00000111
Note im just typing 8 bits but they are actually 32. So you can store your 20 categories.
Also when we are checking if our sprite fits with any of the selected categories:
if(sprites[spritenum].category & playlist_category)
Let's guess spritenum=10 so the
category value is 00000111. If the
playlist_category value is for example 00001100 (INTERIOR + RELAX) the condition should return true (note any value !=0 is considered as true) since INTERIOR category is selected. The operation would be the following:
00000111
00001100
======= AND
00000100 -------> this is !=0 so it's true as we expected
However if the
playlist_category value was 00001000 then the operation should return false:
00000111
00001000
======= AND
00000000 -------> 0 = false since none of the sprite categories fit with the selected categories
The XOR operation used in the selection is for toggling the corresponding bit. Actually you can use the XOR operator in both cases (selection and deselection) since it will always toggle the bit. The only reason I used the OR operator in the selection part
was to make sure the bit is set to 1. But I really dont think there would be any problem if you use the XOR operator.
Hope you understand my testament ;)
Wow, thanx for the comprehensive testament Joe!
I'm gonna have a good read now first :)
EDIT:
While reading it I do remember I've actually already been through this bitwise operations material some years ago for software testing techniques.
But it's good to read such a clear explanation like yours.
I'm gonna save your testament in a text file on my hd now for sure :)