Page 1 of 1

Checking if a value of a variable is one of a list of values

Posted: Tue Nov 30, 2021 2:00 pm
by Kzer-Za
Sorry for the newbie question, but how do I do separate a list of values from the logic?

Currently, if I need to compare a variable to a list of values, I do something like this:

Code: Select all

string TextureName;
TextureName = TexMan.GetName(foo);
if (TextureName == "WOOD1" || TextureName == "WOOD2" || TextureName == "WOOD3" || TextureName == "WOOD3")
I would like to do something like:

Code: Select all

string TextureName;
bar = ... // a list of strings like WOOD1, WOOD2, etc
TextureName = TexMan.GetName(foo);
if (TextureName == bar) // where bar is any value from the list
Ideally would be if the list could be taken from a TEXTURES file. As you know, there can be multiple TEXTURES files in the mod folder, and I have organized my textures into separate files like TEXTURES.wood.txt, TEXTURES.ice.txt, TEXTURES.stone.txt, etc. So, ideally would be to read file TEXTURES.wood.txt, get all lines that begin with "Texture" (for example: Texture "WOOD1", 512, 512), and get the names in quotes (in the example above: WOOD1) from there.

But if it is impossible, or too complicated, I would be content with just defining a list of values within the zscript file itself, just separately from the logic.

Re: Checking if a value of a variable is one of a list of va

Posted: Tue Nov 30, 2021 2:21 pm
by Logan MTM
With my programer skills lvl 0, this is what i usually do:

Code: Select all

if ( TextureName == "WOOD1"
  || TextureName == "WOOD2"
  || TextureName == "WOOD3"
  || TextureName == "WOOD3"
  ...
  ...
  ...
  || TextureName == "WOOD999")
Well, looks like a list! :mrgreen:

Re: Checking if a value of a variable is one of a list of va

Posted: Tue Nov 30, 2021 3:27 pm
by Caligari87
@Logan: That literally is exactly what Kzer-Za is doing right now, just formatted differently. The question was how NOT to do that ("how do I do separate a list of values from the logic?"). Please have something of value to contribute when posting.

@ Kzer-Za: There's a couple things you can do.

If you know the texture names will be consistent (WOOD1, WOOD2, etc) you can compare just the first part of the string. This is the simplest option, but the least flexible if texture names aren't rigidly enforced.

Code: Select all

if (TextureName.left(4) ~== "WOOD") // Do stuff
If the names are NOT going to be consistent like that, the other way is to use an array and loop through it until finding a match. This is a slightly more complex but also a bit more flexible option, as the array can be easily modified in some central place instead of fiddling with the if conditional, and can be easily re-used in multiple places without copy-pasting the entire list.

Code: Select all

array<string> TextureList;
TextureList.push("WOOD1");
TextureList.push("WOOD2");
TextureList.push("WOOD3");

for (int i=0; i<TextureList.size(); i++) {
  if (TextureName ~== TextureList[i]) // do stuff
}
You can either fill in the array by hand, OR fill it in automatically from the TEXTURES lump using FindLump and ReadLump. The latter is the most complicated but also correspondingly most robust if implemented well.

Unfortunately I barely understand how to use lump reading myself, so I'm providing only a brief overview here. The gist of it is that the FindLump returns a lump number as an integer if it exists and matches the name provided. You may need to iterate over names if multiple lumps have the same name. Then ReadLump(lumpnumber) returns a string containing the entire text of the lump. You need to process this with [wiki]String[/wiki] functions, most likely using string.Split() to build an array of lines or other tokens and extract the strings you want from that, into your texture name array.

8-)

Re: Checking if a value of a variable is one of a list of va

Posted: Tue Nov 30, 2021 3:49 pm
by TheRatCircus
To expand on what Caligari posted:

Code: Select all

int lump = -1, next = 0;

do
{
	lump = Wads.FindLump("TEXTURES", next, Wads.GLOBALNAMESPACE);
	if (lump == -1) break;
	next = lump + 1;

	Array<string> lines;
	Wads.ReadLump(lump).Split(lines, "\n");

	for (uint i = 0; i < lines.Size(); i++)
	{
		if (lines[i].IndexOf("Texture") != 0) continue;
		if (lines[i].IndexOf("//") != -1) continue; // Skip comments
		int lqi = lines[i].IndexOf("\""), rqi = lines[i].RightIndexOf("\"");
		Console.Printf(lines[i]);
		Console.Printf(lines[i].Mid(lqi + 1, rqi - lqi - 1));
	}
}
while (true);
This ignores many possible edge cases so it isn't enormously robust (let me know if it does anything expected and I'll post a fix), but when used on gzdoom.pk3 it outputs:

Code: Select all

Texture "ts_frame_top_right", 64, 64
ts_frame_top_right
Texture "ts_frame_bottom_left", 64, 64
ts_frame_bottom_left
Texture "ts_frame_bottom_right", 64, 64
ts_frame_bottom_right
Texture "ts_framr_top_right", 64, 64
ts_framr_top_right
Texture "ts_framr_bottom_left", 64, 64
ts_framr_bottom_left
Texture "ts_framr_bottom_right", 64, 64
ts_framr_bottom_right
Texture "tscp_graph_scaled", 878, 514
tscp_graph_scaled
For defining the data in code, you can also use a static constant array in a class definition instead of a dynamic `Array<string>`, if you know what all of the names will be ahead of time.

Code: Select all

static const string TEXTURE_NAMES[] = {
  "WOOD1",
  "WOOD2",
  "WOOD3"
};

Re: Checking if a value of a variable is one of a list of va

Posted: Tue Nov 30, 2021 3:53 pm
by Caligari87
Highly recommending the static array option if you decide to pre-fill, yes. That's going to be slightly more efficient than a dynamic array I believe.

8-)

Re: Checking if a value of a variable is one of a list of va

Posted: Wed Dec 01, 2021 5:27 am
by Kzer-Za
Thanks for the replies!

For starters I decided to try a static array, but iterating through it in a cycle increases nesting of the code, so I wanted to separate in into a function. So as to use a condition not like this:

Code: Select all

for (int i=0; i<TextureList.size(); i++) {
  if (TextureName ~== TextureList[i])
  {
    //do stuff
  }
}
But like this:

Code: Select all

if (IsNameInArray(TexName, ArrayName))
{
  //do stuff
}
For that, of course, I need a function that takes the texture name and the array of names. This is where I ran into a problem. I defined it like this:

Code: Select all

bool IsNameInArray(string Name, Array<string> NameList)
{
	for (int i=0; i < NameList.size(); i++)
	{
		if (Name ~== NameList[i])
			return true;
	}
	return false;
}
However, when I try to launch GZDoom with this mod, it returns this error:

Code: Select all

Type mismatch in reference argument IsNameInArray
As far as I understand, it's because my IsNameInArray is supposed to accept an argument of "array<string>" type, but a constant array has some other type. What type should I use for it, please?

Re: Checking if a value of a variable is one of a list of va

Posted: Wed Dec 01, 2021 3:17 pm
by TheRatCircus
To my knowledge there's no way to pass a static array to a function in ZScript. Marrub's documentation says the same thing (https://zdoom-docs.github.io/staging/ZS ... rray-types), and I've never found a way to circumvent it. The only solution would be to iteratively copy the contents of the static array to the dynamic array before passing the latter but then there wouldn't be any reason to use the static array in the first place.

Re: Checking if a value of a variable is one of a list of va

Posted: Thu Dec 02, 2021 4:37 am
by Kzer-Za
TheRatCircus wrote:To my knowledge there's no way to pass a static array to a function in ZScript. Marrub's documentation says the same thing (https://zdoom-docs.github.io/staging/ZS ... rray-types), and I've never found a way to circumvent it. The only solution would be to iteratively copy the contents of the static array to the dynamic array before passing the latter but then there wouldn't be any reason to use the static array in the first place.
Thanks!

I guess for this use I will prefer a manually created dynamic array then. But for the future I may need also the means of reading the lumps that you described above.

However, I don't quite understand how to use it. I thought it reads from a given lump (TEXTURES in that case) that is available, i.e. from the basic game (e.g. Doom) + a lump from the loaded mod (if it has one).

So for testing I just made a mod that is only supposed to execute your code (which is supposed to only print the available textures, right?) when a map is loaded:

Code: Select all

class TestWorld2 : EventHandler
{
	override void WorldLoaded(WorldEvent event)
	{
		Console.Printf("Test mod 2 is working");
		
		int lump = -1, next = 0;
		
		do
		{
			lump = Wads.FindLump("TEXTURES", next, Wads.GLOBALNAMESPACE);
			if (lump == -1) break;
			next = lump + 1;
		
			Array<string> lines;
			Wads.ReadLump(lump).Split(lines, "\n");
		
			for (uint i = 0; i < lines.Size(); i++)
			{
				if (lines[i].IndexOf("Texture") != 0) continue;
				if (lines[i].IndexOf("//") != -1) continue; // Skip comments
				int lqi = lines[i].IndexOf("\""), rqi = lines[i].RightIndexOf("\"");
				Console.Printf(lines[i]);
				Console.Printf(lines[i].Mid(lqi + 1, rqi - lqi - 1));
			}
		}
		while (true);
	}
}
However, I only get the message "Test mod 2 is working", and then nothing. Could you tell me what I misunderstood, please?

Re: Checking if a value of a variable is one of a list of va

Posted: Thu Dec 02, 2021 11:36 pm
by TheRatCircus
The code that I posted didn't consider the casing of the "Texture" prefix and didn't cover textures whose names aren't enclosed in quotation marks; this is a little bit more all-encompassing, and might fix the problem. However, I don't have much experience with TEXTURES lumps, so if you could post the files you intend to use this on (or at least a small sample), that would help me get a better idea of how to fix the problem if it persists after this.

Code: Select all

int lump = -1, next = 0;

do
{
	lump = Wads.FindLump("TEXTURES", next, Wads.GLOBALNAMESPACE);
	if (lump == -1) break;
	next = lump + 1;

	Array<string> lines;
	Wads.ReadLump(lump).Split(lines, "\n");

	for (uint i = 0; i < lines.Size(); i++)
	{
		if (!(lines[i].Left(7) ~== "texture")) continue;
		if (lines[i].IndexOf("//") == 0) continue; // Skip comments
		int lqi = lines[i].IndexOf("\""), rqi = lines[i].RightIndexOf("\"");

		if (lqi == -1 || rqi == -1)
		{
			// Likely no quotes. Try again with whitespaces, commas
			lqi = lines[i].IndexOf(" ");
			rqi = lines[i].IndexOf(",");
		}
				
		Console.Printf(lines[i]);
		Console.Printf(lines[i].Mid(lqi + 1, rqi - lqi - 1));
	}
}
while (true);

Re: Checking if a value of a variable is one of a list of va

Posted: Fri Dec 03, 2021 12:38 am
by Kzer-Za
Now everything is working, thanks!