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

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

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

Post 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.
User avatar
Logan MTM
Posts: 678
Joined: Mon Jan 16, 2006 8:53 pm
Location: Rio de Janeiro - Brazil

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

Post 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:
User avatar
Caligari87
Admin
Posts: 6236
Joined: Thu Feb 26, 2004 3:02 pm
Preferred Pronouns: He/Him
Contact:

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

Post 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-)
User avatar
TheRatCircus
Posts: 15
Joined: Mon Aug 02, 2021 1:58 am
Operating System Version (Optional): Linux Mint 21.3
Graphics Processor: nVidia with Vulkan support
Location: Ontario, Canada
Contact:

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

Post 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"
};
User avatar
Caligari87
Admin
Posts: 6236
Joined: Thu Feb 26, 2004 3:02 pm
Preferred Pronouns: He/Him
Contact:

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

Post 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-)
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

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

Post 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?
User avatar
TheRatCircus
Posts: 15
Joined: Mon Aug 02, 2021 1:58 am
Operating System Version (Optional): Linux Mint 21.3
Graphics Processor: nVidia with Vulkan support
Location: Ontario, Canada
Contact:

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

Post 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.
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

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

Post 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?
User avatar
TheRatCircus
Posts: 15
Joined: Mon Aug 02, 2021 1:58 am
Operating System Version (Optional): Linux Mint 21.3
Graphics Processor: nVidia with Vulkan support
Location: Ontario, Canada
Contact:

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

Post 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);
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

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

Post by Kzer-Za »

Now everything is working, thanks!
Post Reply

Return to “Scripting”