Getting a list of ALL classes (with package) that are a subclass of a class

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

Getting a list of ALL classes (with package) that are a subclass of a class

Post by 1337GameDev » Sat Jan 29, 2022 7:30 am

I am curious of iterating through all LOADED classes, that are a subclass of another.

The easiest example is

Code: Select all

Engine.Weapon
, as that's present in the Ut99 menu of

Code: Select all

Mod -> Weapon Priority -> Advanced Priority....
I see this code that seems "kind of" promising, but unsure of how it works, or the limits and such:

Code: Select all

UMenu.UMenuWeaponPriorityListBox.uc
----------------------------------------------------

var string WeaponClassParent;

function Created() {
	local name PriorityName;
	local string WeaponClassName;
	local class<Weapon> WeaponClass;
	local int WeaponNum, i;
	local UMenuWeaponPriorityList L;
	local PlayerPawn P;

	Super.Created();

	SetHelpText(WeaponPriorityHelp);

	P = GetPlayerOwner();

        //!!!!!!!!!!!!!!!!!!!! CODE OF INTEREST !!!!!!!!!!!!!!!!!!!!!!!!!!!
	// Load weapons into the list
	for(i=0;i<ArrayCount(P.WeaponPriority);i++)
	{
		PriorityName = P.WeaponPriority[i];
		if(PriorityName == 'None') break;
		L = UMenuWeaponPriorityList(Items.Insert(ListClass));
		L.PriorityName = PriorityName;
		L.WeaponName = "(unk) "$PriorityName;
	}

	WeaponNum = 1;
	
	//!!!!!!!!!!!!!!!!!!!! CODE OF INTEREST !!!!!!!!!!!!!!!!!!!!!!!!!!!
	//My guess is this gets the first int file defined in this list? (index = 0?)
	WeaponClassName = P.GetNextInt(WeaponClassParent, 0);
	
	while( WeaponClassName != "" && WeaponNum < 50 )
	{
		for(L = UMenuWeaponPriorityList(Items.Next); L != None; L = UMenuWeaponPriorityList(L.Next))
		{
			if( string(L.PriorityName) ~= P.GetItemName(WeaponClassName) )
			{
				L.WeaponClassName = WeaponClassName;
				L.bFound = True;
				if( L.ShowThisItem() )
				{
				        //!!!!!!!!!!!!!!!!!!!! CODE OF INTEREST !!!!!!!!!!!!!!!!!!!!!!!!!!!
				        //  My guess is this loads the class at runtime based on the string
					WeaponClass = class<Weapon>(DynamicLoadObject(WeaponClassName, class'Class'));
					ReadWeapon(L, WeaponClass);
				}
				else
					L.bFound = False;
				break;
			}
		}
                
                //!!!!!!!!!!!!!!!!!!!! CODE OF INTEREST !!!!!!!!!!!!!!!!!!!!!!!!!!!
                //My guess is this iterates and finds the next INT file in the list loaded, of the class parent string, and given the index in WeaponNum
		WeaponClassName = P.GetNextInt(WeaponClassParent, WeaponNum);
		WeaponNum++;
	}
}

defaultproperties {
     //This seems to control which class to fetch using Actor.GetNextInt
     WeaponClassParent="Engine.Weapon"
}


I see

Code: Select all

PlayerPawn.WeaponPriority
(array of max capacity of 50), and

Code: Select all

WeaponClassName = P.GetNextInt(WeaponClassParent, WeaponNum)
as interesting code.

I am unsure what

Code: Select all

Actor.GetNextInt
does (does this iterate through the ".int" files in the System directory?) Also, this code seems limited to a max of 50 weapons.... ?

I originally considered:

Code: Select all

        local string ClassListing;
	ClassListing= (consoleCommand("obj classes"));
But this seems to just list all classes, without any package prefixes. So that seems out of the question.

I added comments to denote code i believe are of interest and my thoughts. Can anybody give some insight and clarify some things?

Buggie
Godlike
Posts: 1766
Joined: Sat Mar 21, 2020 5:32 am

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by Buggie » Sat Jan 29, 2022 8:56 am

anth wrote:
Thu Jan 20, 2022 11:05 am

Code: Select all

ClassesList = Level.ConsoleCommand("obj list class=class");
ClassTree   = Level.ConsoleCommand("obj classes")$" ";
Use one of this.

For both cases need additional work: parse class name to class variable via trick: viewtopic.php?p=133705#p133705

After that check class against desired one for subclassing.

1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by 1337GameDev » Sat Jan 29, 2022 2:39 pm

Buggie wrote:
Sat Jan 29, 2022 8:56 am
anth wrote:
Thu Jan 20, 2022 11:05 am

Code: Select all

ClassesList = Level.ConsoleCommand("obj list class=class");
ClassTree   = Level.ConsoleCommand("obj classes")$" ";
Use one of this.

For both cases need additional work: parse class name to class variable via trick: viewtopic.php?p=133705#p133705

After that check class against desired one for subclassing.
This seems convoluted to extract away the class prefix and then the size and whitespace.

How does the advanced priority menu do it? Where is that file for that menu? (I only see the regular priority menu uc file). Hmm, maybe it's a mod.... ?

Buggie
Godlike
Posts: 1766
Joined: Sat Mar 21, 2020 5:32 am

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by Buggie » Sat Jan 29, 2022 3:58 pm

Usually weapon list read int files as you stated above. So you not able set priority for any weapon which come from server if not installed appropriate int file.

IDK what custom menu you talk about but suspect it use predefined list of classes which periodically fill by simple search Weapon objects on level.

1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by 1337GameDev » Sat Jan 29, 2022 7:10 pm

It's the menu for ut99, when you see the main UT logo.

In Mod -> Weapon Priority -> Advanced priority....

Is that menu natively included in ut99? I'd appreciate if you would quickly check your 469b install.

And is there any limit to reading these int files? Are these file system reads (eg: actually looking in ut99/system folder) or just any "int file definition" the engine knows?

I tried iterating through this list in a mutator and it only gave me unreal1 weapons.

EDIT:

I found the solution I believe:

I was from a mod: Wormbo's Advanced Priority List & Mod Menu Extensions -- a 3rd party mod

http://www.koehler-homepage.de/Wormbo/download.html#GUI

I looked at the code in
EIWeaponIncludeCW.uc
and see this code:

Code: Select all

// load weapons defined in *.int files and the player's priority list
function LoadMutators()
{
	local string NextMutator, NextDesc, NextMod;
	local EIWeaponIncludeList I;
	local int WeaponNum, k;
	local class<Weapon> WClass;
	local PlayerPawn P;
	
	P = GetPlayerOwner();
	// load weapons declared with MetaClass="Botpack.TournamentWeapon" (all UT weapons)
	P.GetNextIntDesc(MutatorBaseClass, 0, NextMutator, NextDesc);
	while( NextMutator != "" && WeaponNum < 200 ) {
		I = EIWeaponIncludeList(Exclude.Items.Append(class'EIWeaponIncludeList'));
		I.MutatorClass = NextMutator;
		NextMod = ModNameFor(I);
		k = InStr(NextDesc, ",");
		if ( k == -1 ) {
			I.MutatorName = NextMod $ NextDesc;
			I.HelpText = "";
		} else {
			I.MutatorName = NextMod $ Left(NextDesc, k);
			I.HelpText = Mid(NextDesc, k + 1);
		}
		WClass = class<Weapon>(DynamicLoadObject(NextMutator, class'Class'));
		if ( WClass != None ) {
			I.PriorityName = WClass.Name;
			if ( I.MutatorName == NextMod && WClass.Default.ItemName != "" )
				I.MutatorName = NextMod $ WClass.Default.ItemName;
			// EnhancedWeapon can store a name to be displayed outside normal gameplay
			if ( class<EnhancedWeapon>(WClass) != None && class<EnhancedWeapon>(WClass).Default.MenuName != "" )
				I.MutatorName = NextMod $ class<EnhancedWeapon>(WClass).Default.MenuName;
		}
		else {
			I.PriorityName = 'None';
			I.MutatorName = "(-)" @ I.MutatorName;
		}
		
		WeaponNum++;
		P.GetNextIntDesc(MutatorBaseClass, WeaponNum, NextMutator, NextDesc);
	}
	// load weapons declared with MetaClass="Engine.Weapon"
	// (basically Unreal weapons, normally not used in priority)
	WeaponNum = 0;
	P.GetNextIntDesc("Engine.Weapon", 0, NextMutator, NextDesc);
	while( NextMutator != "" && WeaponNum < 200 ) {
		I = EIWeaponIncludeList(Exclude.Items.Append(class'EIWeaponIncludeList'));
		I.MutatorClass = NextMutator;
		NextMod = ModNameFor(I);
		k = InStr(NextDesc, ",");
		if ( k == -1 ) {
			I.MutatorName = "(*)" @ NextMod $ NextDesc;
			I.HelpText = "";
		} else {
			I.MutatorName = "(*)" @ NextMod $ Left(NextDesc, k);
			I.HelpText = Mid(NextDesc, k + 1);
		}
		WClass = class<Weapon>(DynamicLoadObject(NextMutator, class'Class'));
		if ( WClass != None ) {
			I.PriorityName = WClass.Name;
			if ( I.MutatorName == ("(*) " $ NextMod) && WClass.Default.ItemName != "" )
				I.MutatorName = "(*)" @ NextMod $ WClass.Default.ItemName;
			if ( class<EnhancedWeapon>(WClass) != None && class<EnhancedWeapon>(WClass).Default.MenuName != "" )
				I.MutatorName = "(*)" @ NextMod $ class<EnhancedWeapon>(WClass).Default.MenuName;
		}
		else {
			I.PriorityName = 'None';
			I.MutatorName = "(-)" @ I.MutatorName;
		}
		
		WeaponNum++;
		P.GetNextIntDesc("Engine.Weapon", WeaponNum, NextMutator, NextDesc);
	}
	log(WeaponNum@NextMutator);

	For ( WeaponNum = 0; WeaponNum < 50; WeaponNum++ ) {
		NextMutator = string(P.WeaponPriority[WeaponNum]);
		
		if ( NextMutator == "" || NextMutator ~= "None" )
			continue;
		
		I = EIWeaponIncludeList(EIWeaponIncludeList(Exclude.Items).FindMutator(NextMutator));
		if ( I != None ) {
			I.Remove();
			Include.Items.AppendItem(I);
		}
		else {
			I = EIWeaponIncludeList(Include.Items.Append(class'EIWeaponIncludeList'));
			I.MutatorClass = NextMutator;
			I.PriorityName = P.WeaponPriority[WeaponNum];
			I.MutatorName = "(?)"@NextMutator;
		}
	}
	Exclude.Sort();
}

function string ModNameFor(EIWeaponIncludeList L)
{
	local int i, Num, PNum;
	local string NextClass, NextDesc, PackageName;
	local PlayerPawn P;
	
	P = GetPlayerOwner();
	i = Instr(L.MutatorClass, ".");
	if ( i > -1 )
		PackageName = Left(L.MutatorClass, i);
	else
		return "";
	
	// check saved mod names first
	while (PNum < ArrayCount(SavedPackages) && SavedPackages[PNum] != "") {
		if ( SavedPackages[PNum] ~= PackageName )
			return SavedModNames[PNum]$": ";
		
		PNum++;
	}
	
	P.GetNextIntDesc("Engine.Info", Num, NextClass, NextDesc);
	while (NextClass != "") {
		i = Instr(NextDesc, ",");
		if ( i > -1 && Left(NextDesc, i) ~= PackageName ) {
			// extract the mod's short (or long) name out of "Package,Long Name,Short Name"
			NextDesc = Mid(NextDesc, i + 1);
			i = Instr(NextDesc, ",");
			if ( i > -1 )
				NextDesc = Mid(NextDesc, i + 1);
			
			// save package and mod name
			if ( PNum < ArrayCount(SavedPackages) ) {
				SavedPackages[PNum] = PackageName;
				SavedModNames[PNum] = NextDesc;
			}
			return NextDesc$": ";
		}
		P.GetNextIntDesc("Engine.Info", ++Num, NextClass, NextDesc);
	}
	return "";
}
I'll reply when I isolate a base generic example function.

Buggie
Godlike
Posts: 1766
Joined: Sat Mar 21, 2020 5:32 am

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by Buggie » Sun Jan 30, 2022 4:30 am

> In Mod -> Weapon Priority -> Advanced priority....

I do not have such item. It is something custom. Search over .int files in System dir. Also can view code inside .u.

AFAIK no any limits. expect there files must be. iNT files not send from servers so even if extract other files from Cache - INT need make manually.

v469b able generate some INT files from .u files, but some information be missed (because not store in .u file) and not sure if this auto generation handle all possible cases. Anyway it generate Weapon items AFAIK.

Automatically merged

Well in fact, parse INT is only one option because obj list class=class and obj classes list only LOADED stuff. Not just dropped or installed in UT - LOADED. So if you really need all stuff - you need firstly run ucc against each package from system, without int. After that use code from above for collect all info from generated INT files in super huge list.

This if you want handle ALL possible weapons.

If you need less - good way be use special stored list which extend each call by loaded list of classes. Enter to map with new set of weapons - open priority and there among old appear new weapons.
Such approach must be enough for wisely use.

1337GameDev
Skilled
Posts: 198
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by 1337GameDev » Thu Feb 03, 2022 5:11 am

Yeah, i figured out that it essentially looks at all INT files, and filters based on meta class defined in the INT file.

I found a method that seems to work:

Code: Select all

ChaosWeapons.int
-------------------------
[Public]
; the ChaosUT weapons as ChaosUT never defined an INT for these
Object=(Name=ChaosUT.ch_WarHeadLauncher,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Chaos Redeemer")
Object=(Name=ChaosUT.Crossbow,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Crossbow")
Object=(Name=ChaosUT.poisoncrossbow,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Poison Crossbow")
Object=(Name=ChaosUT.explosivecrossbow,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Explosive Crossbow")
Object=(Name=ChaosUT.Sniper2,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Chaos Sniper Rifle")
Object=(Name=ChaosUT.sniper_rpb,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Chaos RPB Sniper Rifle")
Object=(Name=ChaosUT.Flak2,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="The Claw")
Object=(Name=ChaosUT.ProxyArm,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Proxy Mine")
Object=(Name=ChaosUT.Sword,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Bastard Sword")
Object=(Name=ChaosUT.TurretLauncher,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Turret Launcher")
Object=(Name=ChaosUT.VortexArm,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="Chaos Vortex")

Code: Select all

WeaponLoopTest.uc
--------------------------
class WeaponLoopTest extends Actor;

var string ClassStrings[255];
var class<Weapon> WeaponClasses[255];

static function GetClassesLoadedFromIntFiles(string IntMetaClassToCompareTo, optional bool LoadClasses, optional int MaxClassIntNum){
    local int ClassIntNum;
    local class<Actor> LoadedClass;
    local string NextClassNameToLoad, NextClassNameToLoadDescr;

    if(MaxClassIntNum == 0){
        MaxClassIntNum = 255;
    }

    if(IntMetaClassToCompareTo == ""){
        IntMetaClassToCompareTo = "Botpack.TournamentWeapon";
    }

    ClassIntNum = 0;
    self.GetNextIntDesc(IntMetaClassToCompareTo, 0, NextClassNameToLoad, NextClassNameToLoadDescr);

	while((NextClassNameToLoad != "") && (ClassIntNum < MaxClassIntNum)) {
	    if(LoadClasses) {
	        LoadedClass = class<Weapon>(DynamicLoadObject(NextClassNameToLoad, class'Class'));
	    } else {
	        LoadedClass = None;
	    }

           self.ClassStrings[ClassIntnum] = NextClassNameToLoad;
           self.WeaponClasses[ClassIntNum] = LoadedClass;
          
           Log("WeaponLoopTest - GetClassesLoadedFromIntFiles - NextClassNameToLoad:"$NextClassNameToLoad$" - LoadedClass:"$LoadedClass);   

	   ClassIntNum++;
	   self.GetNextIntDesc(IntMetaClassToCompareTo, ClassIntNum, NextClassNameToLoad, NextClassNameToLoadDescr);
	}
}

Buggie
Godlike
Posts: 1766
Joined: Sat Mar 21, 2020 5:32 am

Re: Getting a list of ALL classes (with package) that are a subclass of a class

Post by Buggie » Mon Mar 07, 2022 1:52 pm

There code which can be useful for this task:
https://github.com/SeriousBuggie/XVehic ... iclesMH.uc

Code: Select all

class XVehiclesMH expands Mutator;

var class<Actor> DynClass;

event PreBeginPlay()
{	
	local class<Vehicle> cl;
	local int i, s, total;
	local string str, list;
	
	list = Caps(Level.ConsoleCommand("OBJ CLASSES"));
	total = Len(list);
	i = InStr(list, " VEHICLE ");
	if (i == -1)
	{
		Warn(self @ "There no Vehicle class in list!");
		return;
	}
	while (true)
	{
		while (Asc(Mid(list, i, 1)) <= 32 && i < total)
			i++;
		if (i >= total)
			break;
		s = 0;
		while (Asc(Mid(list, i + s, 1)) > 32 && i + s < total)
			s++;
		str = Mid(list, i, s);
		i += s;
		SetPropertyText("DynClass", "Class'" $ str $ "'");
		cl = Class<vehicle>(DynClass);
		if (cl == None)
			break;
		SetupVehicleClass(cl);
		//log(cl);
	}
}
In short, I get tree of all loaded classes, goest to class "vehicle" and iterate over all loaded subclasses with call "SetupVehicleClass" function on each class.

Note: it can be slow if tree huge. I use search for move to know class, and stop loop on first class which not subclass of it, for reduce performance impact.