Using Enums From another class

Discussions about Coding and Scripting
1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Using Enums From another class

Post by 1337GameDev »

I am currently wanting to have an enum as a parameter to a method of a class, and am running into issues calling this method from another class, passing in the enum.

For example, lets assume I have the below:

Code: Select all

class ClassA extends Actor;
enum Enum1 {
    VALUE1, VALUE2, VALUE3
};
static function DoSomething(Enum1 e1) {
    Log("e1:"$string(e1));
};

class ClassB extends Actor;
var class'ClassA'.Enum1 MyEnum1Value;
function Foo(){
    class'ClassA'.static.DoSomething(class'ClassA'.Enum1.VALUE1);
    class'ClassA'.static.DoSomething(MyEnum1Value);
}

defaultproperties {
    MyEnum1Value=class'ClassA'.Enum1.VALUE2
}
This won't work, as compiling will yield an error saying:

Code: Select all

Unrecognized member 'Enum1' in class 'Class' 
In reference to trying to get a value of the enum as an argument to the function, for it's specified parameter.

What's the correct way? Just reference it like ClassA.Enum1? That doesn't seem to work either when calling the function....

But when I add that in the defaultproperties block of ClassB, it seems to work? Why is that? Or does it take on an uninitialized value, of 0? (as enums are essentially bytes behind the scene). Is there a max enum entry list as well (because a byte is only 8 bits so 2^8 or 256 values).
User avatar
ExpEM
Adept
Posts: 298
Joined: Wed Nov 09, 2016 1:48 am

Re: Using Enums From another class

Post by ExpEM »

Enums can be called as integers, value1 in your code would be 0.
Signature goes here.
1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev »

ExpEM wrote: Wed Jan 27, 2021 7:59 am Enums can be called as integers, value1 in your code would be 0.
Well.... how would I pass this as an argument? And I want NAMED options, as to strongly enforce valid values.....

I don't want to use a "hack" and pass an arbitrary raw value.
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Using Enums From another class

Post by ANUBITEK »

You could pass the name of the value as a string, then check the characters in said string in the receiving class' function. On mobile and getting some issues, but something like

ClassB:
class'ClassA'.static.DoSomething( "VALUE1")

ClassA:
static function DoSomething( str val_string): {
if val_string == "VALUE1": [code here...]
}


It has been a while so I forget if it is Str, str, String, or string but you get the idea hopefully.
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev »

ANUBITEK wrote: Thu Jan 28, 2021 6:13 am You could pass the name of the value as a string, then check the characters in said string in the receiving class' function. On mobile and getting some issues, but something like

ClassB:
class'ClassA'.static.DoSomething( "VALUE1")

ClassA:
static function DoSomething( str val_string): {
if val_string == "VALUE1": [code here...]
}


It has been a while so I forget if it is Str, str, String, or string but you get the idea hopefully.
This still seems super fragile....

I see enum info here:
https://web.archive.org/web/20201023050 ... .com/Enums

and in this post:
viewtopic.php?t=6482

But... this guide specifies to never have enums as a parameter, but to use a byte.
viewtopic.php?t=6372

This excerpt specifies this:
Enums are stored internally as a byte. Don't use enums as parameters in functions.
If an outside class attempts to call that function, you will get a compiler error even if
both classes have the same definition. Use a byte instead:
> Right: function keyEvent(Byte key);
> Wrong: function keyEvent(EInputKey key);
I simply want a very defined way of passing a valid option, and give users of my class an EASY list of valid options when calling a function.

How is this done?
I know I can pass an int as a byte, and then use a switch statement on the parameter.... But then how do i get an int for a given enum value from another class? :???:

I'm kind of lost here :?
User avatar
sektor2111
Godlike
Posts: 6410
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Using Enums From another class

Post by sektor2111 »

I don't get this hard-way. When something doesn't for for me I simply switch methods. Why not using a simple static array and adding a function for translating INT into other corresponding strings - like attitude code:
Pawn.uc

Code: Select all

var(AI) enum EAttitude  //important - order in decreasing importance
{
	ATTITUDE_Fear,		//will try to run away
	ATTITUDE_Hate,		// will attack enemy
	ATTITUDE_Frenzy,	//will attack anything, indiscriminately
	ATTITUDE_Threaten,	// animations, but no attack
	ATTITUDE_Ignore,
	ATTITUDE_Friendly,
	ATTITUDE_Follow 	//accepts player as leader
} AttitudeToPlayer;	//determines how creature will react on seeing player (if in human form)
then it comes Bot:

Code: Select all

function eAttitude AttitudeTo(Pawn Other)
{
	local byte result;

	if ( Level.Game.IsA('DeathMatchPlus') )
	{
		result = DeathMatchPlus(Level.Game).AssessBotAttitude(self, Other);
		Switch (result)
		{
			case 0: return ATTITUDE_Fear;
			case 1: return ATTITUDE_Hate;
			case 2: return ATTITUDE_Ignore;
			case 3: return ATTITUDE_Friendly;
		}
	}

	if ( Level.Game.bTeamGame && (PlayerReplicationInfo.Team == Other.PlayerReplicationInfo.Team) )
		return ATTITUDE_Friendly; //teammate

	return ATTITUDE_Hate;
}
I don't recall more situations when I could not deal with these from outside - game-type DeathMatchPlus also defines attitude stuff read by Bot itself and making coder to setup this as required by game-type.

Light has enums - I changed lights from outside class
Mover has enums - I always fix these as long as the mostly are WRONG set.
But all of those are... BYTES not INT variables.

Edit: Shorter
Using Enums From another class
DeathMatchPlus uses Enum from Pawn through Bot class.
User avatar
ANUBITEK
Adept
Posts: 261
Joined: Sun Dec 28, 2014 1:10 am
Location: Anubitek

Re: Using Enums From another class

Post by ANUBITEK »

Sektor has the better idea. I want to add that if you're worried about users not using this properly in code, you can make comments and add an example case in the comments.
<<| http://uncodex.ut-files.com/ |>>

Code reference for UGold, UT99, Unreal2, UT2k3, UT3
Additional Beyond Unreal Wiki Links
wiki.beyondunreal.com/Legacy:Console_Bar
wiki.beyondunreal.com/Exec_commands#Load
wiki.beyondunreal.com/Legacy:Exec_Directive#Loading_Other_Packages
wiki.beyondunreal.com/Legacy:Config_Vars_And_.Ini_Files
wiki.beyondunreal.com/Legacy:INT_File
User avatar
Feralidragon
Godlike
Posts: 5493
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: Using Enums From another class

Post by Feralidragon »

It's been a (looong) while since I looked into using enums from different classes, but you can at least cast them to and from "byte" directly, if I recall correctly (not 100% sure anymore).
So you can maybe have something like:

Code: Select all

class ClassA extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};

static function DoSomething(Enum1 e1)
{
    Log("e1:"$string(e1));
};

//-------------------------------------------------------------------------------------

class ClassB extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};
var Enum1 MyEnum1Value;

function Foo()
{
    class'ClassA'.static.DoSomething(Byte(VALUE1));
}
or

Code: Select all

class ClassA extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};

static function DoSomething(coerce Enum1 e1)
{
    Log("e1:"$string(e1));
};

//-------------------------------------------------------------------------------------

class ClassB extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};
var Enum1 MyEnum1Value;

function Foo()
{
    class'ClassA'.static.DoSomething(VALUE1);
}
But then again, I am not sure if this will compile and work, and I do seem to recall a way to do what you want directly without these workarounds, but I could be wrong.

I may have to validate a few ways I have in mind with an actual compiler over the weekend, since in week days the computer I use does not have UT installed, so I cannot validate right now, but I have stuff in mind like ClassA.Enum1 directly (which I recall you already tried?) and maybe even class'ClassA'.enum.Enum1 or class'ClassA'.static.Enum1 and others.

Judging how the Canvas object class defines Style as a byte instead of the actual enum it aims to represent (ERenderStyle from Actor), there might not actually be a way to do that as directly as you want it to be, and you may have to define it like my suggestions above, or as a byte type directly like the Canvas class does.
1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev »

Feralidragon wrote: Thu Jan 28, 2021 2:55 pm It's been a (looong) while since I looked into using enums from different classes, but you can at least cast them to and from "byte" directly, if I recall correctly (not 100% sure anymore).
So you can maybe have something like:

Code: Select all

class ClassA extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};

static function DoSomething(Enum1 e1)
{
    Log("e1:"$string(e1));
};

//-------------------------------------------------------------------------------------

class ClassB extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};
var Enum1 MyEnum1Value;

function Foo()
{
    class'ClassA'.static.DoSomething(Byte(VALUE1));
}
or

Code: Select all

class ClassA extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};

static function DoSomething(coerce Enum1 e1)
{
    Log("e1:"$string(e1));
};

//-------------------------------------------------------------------------------------

class ClassB extends Actor;

enum Enum1 { VALUE1, VALUE2, VALUE3};
var Enum1 MyEnum1Value;

function Foo()
{
    class'ClassA'.static.DoSomething(VALUE1);
}
But then again, I am not sure if this will compile and work, and I do seem to recall a way to do what you want directly without these workarounds, but I could be wrong.

I may have to validate a few ways I have in mind with an actual compiler over the weekend, since in week days the computer I use does not have UT installed, so I cannot validate right now, but I have stuff in mind like ClassA.Enum1 directly (which I recall you already tried?) and maybe even class'ClassA'.enum.Enum1 or class'ClassA'.static.Enum1 and others.

Judging how the Canvas object class defines Style as a byte instead of the actual enum it aims to represent (ERenderStyle from Actor), there might not actually be a way to do that as directly as you want it to be, and you may have to define it like my suggestions above, or as a byte type directly like the Canvas class does.
I looked at those examples, and the conversions don't work how you expect. You CAN convert an enum to a byte through implicit conversion, but you can't convert a byte to an enum, or a string to enum (even though it's mentioned here -- but maybe I don't understand the usage): https://web.archive.org/web/20201023050 ... .com/Enums

(bottom of the page)

redefining an enum also is not optimal, as i'll have to redefine it multiple times, and keep them all in sync....

I CAN reference an enum as a type, but I cannot reference any VALUE from it....

Code: Select all

class ClassA {
    enum Enum1 {VAL1, VAL2}
}
class ClassB {
    var ClassA.Enum1 E1;

    function DoSomething(){
        Log("Value1:"$E1);
        E1 = ClassA.Enum1.VAL2;//this fails to compile
    }
}
I'll likely have to settle for passing names and just using a switch... which is absolutely gross to maintain... or just settle with integers and people looking up each value :(....

I absolutely hate that simple enums don't just work easily....Irks me something fierce....
User avatar
sektor2111
Godlike
Posts: 6410
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Using Enums From another class

Post by sektor2111 »

1337GameDev wrote: Fri Jan 29, 2021 7:20 am I absolutely hate that simple enums don't just work easily....Irks me something fierce....
Okay, let's see another sample of accessing enum vars of movers:

Code: Select all

class Mover extends Brush
	native
	nativereplication;

// How the mover should react when it encroaches an actor.
var() enum EMoverEncroachType
{
	ME_StopWhenEncroach,	// Stop when we hit an actor.
	ME_ReturnWhenEncroach,	// Return to previous position when we hit an actor.
   	ME_CrushWhenEncroach,   // Crush the poor helpless actor.
   	ME_IgnoreWhenEncroach,  // Ignore encroached actors.
} MoverEncroachType;
And an ancient mutator like UTJMH is addressing movers exactly into these enum types for preventing game-break in broken maps.

Code: Select all

		if(M.bTriggerOnceOnly && M.MoverEncroachType == ME_ReturnWhenEncroach)
		{
			M.MoverEncroachType = ME_IgnoreWhenEncroach;
		}
I don't get what exactly is the problem with enum vars.
1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev »

sektor2111 wrote: Fri Jan 29, 2021 8:03 am
1337GameDev wrote: Fri Jan 29, 2021 7:20 am I absolutely hate that simple enums don't just work easily....Irks me something fierce....
Okay, let's see another sample of accessing enum vars of movers:

Code: Select all

class Mover extends Brush
	native
	nativereplication;

// How the mover should react when it encroaches an actor.
var() enum EMoverEncroachType
{
	ME_StopWhenEncroach,	// Stop when we hit an actor.
	ME_ReturnWhenEncroach,	// Return to previous position when we hit an actor.
   	ME_CrushWhenEncroach,   // Crush the poor helpless actor.
   	ME_IgnoreWhenEncroach,  // Ignore encroached actors.
} MoverEncroachType;
And an ancient mutator like UTJMH is addressing movers exactly into these enum types for preventing game-break in broken maps.

Code: Select all

		if(M.bTriggerOnceOnly && M.MoverEncroachType == ME_ReturnWhenEncroach)
		{
			M.MoverEncroachType = ME_IgnoreWhenEncroach;
		}
I don't get what exactly is the problem with enum vars.
This essentially is doing what I want...

But i cannot get my example to compile....

Sigh... Here's a direct example I tried to compile.

----------------- ClassA ----------------------

Code: Select all

class TestEnumClassA extends Object;

enum Enum1 {
    VALUE1, VALUE2, VALUE3
};

function LogEnum(){
    Log("Enum1 1st value:"$Enum1.VALUE1);
}
----------------- ClassB ----------------------

Code: Select all

class TestEnumClassB extends Object;

var byte Enum2;

function LogEnum2(){
    Enum2 = TestEnumClassA.Enum1.VALUE2;
    Log("Enum2 value:"$Enum2);
}
The results after trying to build with ucc is:

Code: Select all

TestEnumClassB.uc(6) : Error, Bad or missing expression in '='
Critical error while compiling
Refers to the first line in the LogEnum2 function where I set the variable....

Basically, because it's outside of the scope of class A, it can't find the enum...

I tried this then too:

Code: Select all

function LogEnum2(){
    Enum2 = class'TestEnumClassA'.Enum1.VALUE2;
    Log("Enum2 value:"$Enum2);
}
for the function in class B, and tried compiling, and got this:

Code: Select all

TestEnumClassB.uc(6) : Error, Unrecognized member 'Enum1' in class 'Class'
Critical error while compiling
PS: I couldn't find the code you mentioned in MH.... I tried looking for the latest "utjmh" but couldnt extract the uc files using wotgreal....
PPS: I got the decompile to work. Found the code in "UTJMHBase" on line 256 using grepwin.

Hmm. I don't think this code should even compile. It shouldn't. I try the exact same thing, and my code complains.... Maybe it's because it's setting the enum to one declared inside the same class?

I tested exactly that....
And it worked.... but ONLY for setting an enum variable inside ClassA.... but not ClassB....

Here are the classes I tested:
------------ New ClassA ------------

Code: Select all

class TestEnumClassA extends Object;

var enum Enum1 {
    VALUE1, VALUE2, VALUE3
} Enum1Var;

function LogEnum(){
    Log("Enum1 1st value:"$Enum1.VALUE1);
}
------------ New ClassB ------------

Code: Select all

class TestEnumClassB extends Object;

var byte Enum2;

function LogEnum2(){
    local TestEnumClassA ca;
    ca = new class'TestEnumClassA';
    ca.Enum1Var = VALUE2;

    Enum2 = VALUE2;
    Log("Enum2 value:"$Enum2);
}
And the resulting compile error:

Code: Select all

TestEnumClassB.uc(10) : Error, Bad or missing expression in '='
Critical error while compiling
I decided to try and change the ClassB "byte" value... to Enum1 type... and it cannot find the type....

Code: Select all

TestEnumClassB.uc(3) : Error, Unrecognized type 'Enum1'
Critical error while compiling
The ONLY way it compiles... is when I try to set the ClassB.Enum2 type to be ClassA.Enum1.... Then it can set it...

Hmm.... I believe the error was that I was trying to have it implicitly cast types....

THIS situation seems to work:

Code: Select all

class TestEnumClassA extends Object;

enum Enum1 {
    VALUE1, VALUE2, VALUE3
};
var Enum1 Enum1Var;

function LogEnum(){
    Log("Enum1 1st value:"$Enum1.VALUE1);
}

static function LogPassed(byte e1) {
    Log("Enum1 passed:"$e1);
}

Code: Select all

class TestEnumClassB extends Object;

var TestEnumClassA.Enum1 Enum2;

function LogEnum2(){
    local TestEnumClassA ca;
    local TestEnumClassA.Enum1 e1;
    e1 = VALUE3;

    ca = new class'TestEnumClassA';
    ca.Enum1Var = VALUE2;

    Enum2 = VALUE2;
    Log("Enum2 value:"$Enum2);

    class'TestEnumClassA'.static.LogPassed(e1);
}
So, the rules seem to be this:
If you want to declare an enum type from another class / package - FULL QUALIFY the type. EG:

Code: Select all

local TestEnumClassA.Enum1 e1;
If you want to pass enums around, and cast to a byte... You must do so implicitly, but ONLY after you store the wanted value in a type of the fully qualified enum. EG:

Code: Select all

local TestEnumClassA.Enum1 e1;
local byte EnumByte;

e1 = VALUE3; // any valid value of that enum
EnumByte = e1;
You CANNOT directly reference an enum value, EXCEPT if youre storing it into a fully qualified (or in-scope) variable of that enum type. You CANNOT pass it directly to a function, unless that function has a FULL QUALIFIED argument for that enum.

Otherwise it acts as if it cannot find the type / the enum is an invalid expression.

Really frustrating... but seems to work now.

Thanks secktor2111... Your example lead me to the proper limitations of enums, and NEEDING to store the value i intend to pass, in a temp variable of the FULLY QUALIFIED enum type. I'm over the moon that I figured this out (with your help). Was so perplexing...

NEVERMIND - I found out WHY it worked.

It's based on COMPILATION order. In later UnrealEngines, there is a

Code: Select all

dependson
keyword to build a dependency order for compilation....

If TestEnumClassB compiles BEFORE TestEnumClassA, then it fails in ALL cases, even if I full qualify the enum. Dammit. This is why they try to use BYTE.....

The only reason why they can get away with that in the MH mutator... is because MOVER is already in the compilation context as it's a base class in the game files, already built.

Dammit. I cannot do what I want with UnrealEngine1.... Unless I force a compilation order, via relying on names or something with ucc.

I'll have to find a way to get an enum value via SetPropertyText, or just pass an integer blindly, and rely on the caller to lookup the index of that value.... as we also cannot declare values for enums.... (eg: if i add a new enum value to the beginning all enum values are now off by 1).

Sigh... I'll have to settle for an integer if i also care about performance as name/string lookups are expensive in any language (even if they use a hashtable and string hashing).

Sigh....

:facepalm: :wth:
User avatar
sektor2111
Godlike
Posts: 6410
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Using Enums From another class

Post by sektor2111 »

1337GameDev wrote: Sat Jan 30, 2021 6:08 am PS: I couldn't find the code you mentioned in MH.... I tried looking for the latest "utjmh" but couldnt extract the uc files using wotgreal....
PPS: I got the decompile to work. Found the code in "UTJMHBase" on line 256 using grepwin.

Hmm. I don't think this code should even compile. It shouldn't. I try the exact same thing, and my code complains.... Maybe it's because it's setting the enum to one declared inside the same class?
Then I'm sorry, dude, but this code is compiled already X times in X MH2 versions, here syntax was a problem not accessing vars is problem when UCC doesn't even compile these. And then, a MH compilation needs a clean MH package for coding not the original one and it works as it always worked...
I don't even recall how many times I was addressing enum vars from various actors.
The hard way for me was working with "invisible" stuff (like reachSpecs - unseen objects) which I wanted to morph into a user friendly format, because I think those are enum vars too but in another Level, UScript is not really having functions for screwing this data - here XC_Engine has the power, in essence when something goes limited with UScript it's time for C+, but C++ it's not my domain.

I have to admit that working with "strings" is expensive generally. I'm stating on this based on MapVoteLA type, major hit - those attempting to sort 2047 maps and iterating to the death, there I turned a function into a state code letting engine to work more slower and breaking iterations with latent "Sleep" else MapVoteLA++ types addressing more than 1500 maps would be myths as some of them are useless because... they weren't tested and are lagging game during maps reload. During this "slow" sorting process in my machine tick-rate which is sitting at 24/25 is going to 13/25. It's heavy to wrap 1705 names after all...

Edit: Let me introduce a fresh code which I used a few time ago.
It's addressing enum from LevelInfo class:

Code: Select all

class LevelInfo extends ZoneInfo
	native
	nativereplication;
...
var() enum ENetMode
{
	NM_Standalone,        // Standalone game.
	NM_DedicatedServer,   // Dedicated server, no local client.
	NM_ListenServer,      // Listen server.
	NM_Client             // Client only, no local server.
} NetMode;
Then... happily embedding in some EMB map a sort of Teleporter which will work depending on settings - visible or not - it's self-morphing in desired environment:

Code: Select all

...
simulated function PostBeginPlay()
{
	Super.PostBeginPlay();
	if ( bIAmVisible )
	if ( URL != "" && URL != "None" && Level.NetMode != NM_DedicatedServer )
	{
		bHidden = False;
		Mesh = LodMesh'BotPack.Tele2';
		DrawType = DT_Mesh;
		Texture = None;
		LoopAnim('Teleport', 2.0, 0.0);
		T = spawn(class'UTTeleeffect');
		if ( T != None )
		{
			T.RemoteRole = ROLE_None;
			T.lifespan = 0.0;
		}
	}
	bSpecialCost = True;
}
....
Only a woman can be so beautiful as this code... :D
User avatar
Barbie
Godlike
Posts: 2802
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Using Enums From another class

Post by Barbie »

Maybe an ucc-preprocessor can help to eliminate the mistake of different definitions of enums (and similar). Doesn't have the ucc helper such?
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
sektor2111
Godlike
Posts: 6410
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Using Enums From another class

Post by sektor2111 »

It does a 500 server error, so it has no use...
User avatar
OjitroC
Godlike
Posts: 3613
Joined: Sat Sep 12, 2015 8:46 pm

Re: Using Enums From another class

Post by OjitroC »

UCCHelper1.0_Setup.zip
(1.18 MiB) Downloaded 11 times
Easy to find if you know where to look (joke :P ).
Post Reply