UE2 - UT2kX Replacing pawn?

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

ROSoldier

New Member
Feb 1, 2015
29
0
1
Hi guys. Can someone tell me how can I replace the class that is marked with red color? Basically I want to replace the pawn that controls the player character.
Its for ROOST (Unreal engine 2.5)
I tried creating a mutator by reading a Killing Floor mutator tutorial but it did not work.

Code:
class ROSU_Winter_RKKA extends ROSU_RKKA_Units
	abstract;

defaultproperties
{
     Models(0)="R_RKKAWinter1"
     Models(1)="R_RKKAWinter2"
     Models(2)="R_RKKAWinter3"
     Models(3)="R_RKKAWinter4"
     Models(4)="R_RKKAWinter5"
     SleeveTexture=Texture'Weapons1st_tex.Arms.RussianSnow_Sleeves'
     DetachedArmClass=Class'ROEffects.SeveredArmSovSnow'
     DetachedLegClass=Class'ROEffects.SeveredLegSovSnow'
     RolePawnClass="[COLOR="Red"]RORoles.RUWinterPawn[/COLOR]"
}
 

VendorX

Member
Aug 2, 2010
231
7
18
BXL/Paris
What exactly you want to do? Replace pawn class with existing one or your custom pawn class ..?

Anyway, this in red means:
PackageName.ClassName

BTW: Can you provide a link to this game home site?
 
Last edited:

ROSoldier

New Member
Feb 1, 2015
29
0
1
What exactly you want to do? Replace pawn class with existing one or your custom pawn class ..?

Anyway, this in red means:
PackageName.ClassName

BTW: Can you provide a link to this game home site?

I have a pawn that I want to use instead of the pawn marked with red.
I just need to replace the standard Red Orchestra pawn with another one. In this game there are "RoleInfos" (roles on maps (rifleman, machine gunner, squad leader and so on) and classes like the one I quoted say what pawn is used depending on the soldiers.
In the example I have you its a Soviet (Russian) soldier from the RKKA (workers and peasants Red Army) with winter uniform...going in detail for those of you who are curious.
 
Last edited:

VendorX

Member
Aug 2, 2010
231
7
18
BXL/Paris
So, where is the problem? Replace this pawn class with yours and compile the script ...

As for mutator: I don't know how RO deal with pawn assignment so I can't tell you for sure if it's possible - find part of code responsible for it, then maybe we can do something.
 
Last edited:

ROSoldier

New Member
Feb 1, 2015
29
0
1
So, where is the problem? Replace this pawn class with yours and compile the script ...

As for mutator: I don't know how RO deal with pawn assignment so I can't tell you for sure if it's possible - find part of code responsible for it, then maybe we can do something.

No I can't do this it wont do the job. I need to replace the pawn in the existing class. Of course I would extend the class and recompile it if it was possible....theres a reason why I am looking for a mutator. By the way it does not have to be a mutator I can also do it in my custom gametype.

And I already tried compiling my own mutator from scratch but it did not work.

The mutator I compiled is just replacing the default property and I did it so that It will also work on clients, but it simply does not work.

Heres the mutator I created and tried:

Code:
class 12345 extends Mutator;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
	if (ROSU_Winter_RKKA(Other) != None)
	{
		ROSU_Winter_RKKA(Other).RolePawnClass="[COLOR="red"]MyRoles.MyRiflemanPawn[/COLOR]";
	}

	return Super.CheckReplacement(Other, bSuperRelevant);
}






defaultproperties
{
    bAddToServerPackages=True
    GroupName="12345"
    FriendlyName="12345"
    Description="12345"
    RemoteRole=ROLE_SimulatedProxy
	bAlwaysRelevant=true
	}
 

meowcat

take a chance
Jun 7, 2001
803
3
18
My 2 cents:
Since the 'ROSU_Winter_RKKA' class appears to be an abstract information holding class (set as a "level specific property actor" variable placed in each map?), and therefore would never be spawned (and therefore never passed to the 'CheckReplacement' function), you may need to try something more un-orthodox like:
Code:
...
function PreBeginPlay(){
....
     class'ROSU_Winter_RKKA'.default.RolePawnClass = "MyRoles.MyRiflemanPawn";
...

Probably a good idea to toss in a few logging statements to let you know whether your code was able to access any of those variables.

[EDIT] Another option to consider/test. Here is some sample code from Mysterial's "Super Bots" mutator that changed the default bot class for pawns in a way that worked since it was hard to access the class' properties before they were used (NOTE: a special 'Interaction' also had to be spawned to restore the class setting afterwards when the level changed)
Code:
class BotMutator extends Mutator;
.....
function PostBeginPlay()
{
	local Object O;

    	foreach AllObjects(class'object', O)
    		if (ClassIsChildOf(Class(O), class'Pawn'))
    			if (class<Pawn>(O).default.ControllerClass == class'xBot')
    				class<Pawn>(O).default.ControllerClass = class'CustomBot';
	Super.PostBeginPlay();

    // Change other stuff etc
    ....
}

Something like the above ^ code could search for the 'ROSU_Winter_RKKA' class and then modify its properties (assuming that the rest of the Pawn spawning code (whether it be in the GameInfo subclass OR the Controller's 'PawnClass' variable) actually refers to the class directly to extract what type of pawn to spawn and does not store it in a separate variable.

Maybe, rather than intercepting the 'ROSU_Winter_RKKA' class and modify it, just check the controller's pawnclass, and adjust if necessary. Something like this in the 'ModifyPlayer' function (I made some tweaks to the below code from my own mod for the case you are describing so there are probably going to be some errors...):
Code:
function ModifyPlayer(Pawn Other){
....   // Other is the pawn instance passed to the function, C is the pawn's controller, C=Other.Controller...
       if(YourCustomPawn(other) == none && C.PawnClass==class'RORoles.RUWinterPawn'){
	         //log("Modified A pawn "$Other$" and controller "$C); // DEBUG LOG
             C.SetPawnClass("YourPackage.YourCustomPawn", "");// no upl string
             C.PawnDied(Other);
	         Other.UnPossessed();
	         Other.Reset();
	         Level.game.RestartPlayer(C);
        }
.....
}
 
Last edited:

ROSoldier

New Member
Feb 1, 2015
29
0
1
Thank you thank you thank you for posting in this topic!!! I have tried fourc mutators I have compiled today with no luck. And please note that I am not experienced in this. I will try to do what you said Meowcat. I am giving you more information on how Roles work in this game: There is a RoleInfo class (a "parent" class like most of the UE guys call it. And all of the "Roles" Extend this class. Of course each role has its default properties, like primary secondary weapons, and so on. When you place such actor in a map you can edit these properties to change the soldiers load out.
And you may have already noted that I don't need to change the players skins (face skin body skin or load outs at least for now. I just need to change the Pawn class that controls the solider.

Heres and example of a Soviet soldier wearing a summer uniform from 1935

//=============================================================================
// ROSURiflemanM35RKKA
//=============================================================================
// Defines the characteristics of a given role
//=============================================================================
// Red Orchestra Source
// Copyright (C) 2003-2004 Erik Christensen
//=============================================================================

Code:
class ROSURiflemanM35RKKA extends ROSU_Spring_RKKA;

//=============================================================================
// Functions
//=============================================================================

//-----------------------------------------------------------------------------
// GetHeadgear
//-----------------------------------------------------------------------------

function class<ROHeadgear> GetHeadgear()
{
    if (FRand() < 0.2)
    {
        if (FRand() < 0.5)
            return Headgear[2];
        else
            return Headgear[1];
    }
    else
    {
        return Headgear[0];
    }
}

//=============================================================================
// defaultproperties
//=============================================================================

defaultproperties
{
     MyName="Rifleman"
     AltName="Strelok"
     Article="a "
     PluralName="Riflemen"
     InfoText="Strelok - Difficulty: Medium||The Soviet Strelok was the main-stay of the infantry platoon. His job is the key role of taking and holding ground. The standard rifle will reach out accurately a good distance, although longer ranges are best left to the Sniper. The Strelok is expected to close with the enemy, with rifle fire, grenade, bayonet and whatever other close-combat weapons come to hand and defeat him.||Loadout: MN 91/30, grenades"
     menuImage=Texture'InterfaceArt_tex.SelectMenus.Strelok'
     PrimaryWeapons(0)=(Item=Class'ROInventory.MN9130Weapon',Amount=15,AssociatedAttachment=Class'ROInventory.ROMN9130AmmoPouch')
     PrimaryWeapons(1)=(Item=Class'ROInventory.M38Weapon',Amount=15,AssociatedAttachment=Class'ROInventory.ROMN9130AmmoPouch')
     Grenades(0)=(Item=Class'ROInventory.F1GrenadeWeapon',Amount=2)
     Headgear(0)=Class'ROInventory.ROSovietSidecap'
     Headgear(1)=Class'ROInventory.ROSovietHelmet'
     Headgear(2)=Class'ROInventory.ROSovietHelmetTwo'
And here is the class from which all RKKA soldiers originate from:


Code:
// Russian RKKA Spring Uniforms

class ROSU_Spring_RKKA extends ROSU_RKKA_Units
    abstract;

defaultproperties
{
     Models(0)="R_RKKASpring1"
     Models(1)="R_RKKASpring2"
     Models(2)="R_RKKASpring3"
     Models(3)="R_RKKASpring4"
     Models(4)="R_RKKASpring5"
     RolePawnClass="RORoles.RURiflemanPawn"
}
And as we go up in the hierarchy:

Code:
class ROSU_RKKA_Units extends ROSURole
    abstract;

defaultproperties
{
}
The Soviet Union is Team Allies


Code:
//=============================================================================
// ROSURole.
//=============================================================================
// Defines the characteristics of a given role
//=============================================================================
// Red Orchestra Source
// Copyright (C) 2003-2004 Erik Christensen
//=============================================================================

class ROSURole extends RORoleInfo
    abstract;

//=============================================================================
// defaultproperties
//=============================================================================

defaultproperties
{
VoiceType="ROGame.RORussian1Voice"
AltVoiceType="ROGame.RORussian1Voice"
SleeveTexture=Texture'Weapons1st_tex.Arms.russian_sleeves'
DetachedArmClass=Class'ROEffects.SeveredArmSovTunic'
DetachedLegClass=Class'ROEffects.SeveredLegSovTunic'
Side=[COLOR=indigo]SIDE_Allies[/COLOR]
}


And here it is the RI

Code:
//=============================================================================
// RORoleInfo.
//=============================================================================
// Defines the characteristics of a given role
//=============================================================================
// Red Orchestra Source
// Copyright (C) 2003-2004 Erik Christensen
//=============================================================================

class RORoleInfo extends Actor
    hidecategories(Object,Movement,Collision,Lighting,LightColor,Karma,Force,Events,Display,Advanced,Sound)
    placeable
    abstract;

//=============================================================================
// Variables
//=============================================================================

// Strings
var        localized    string            MyName;                        // Names to use in-game for this role
var        localized    string            AltName;
var        localized    string            Article;
var        localized    string            PluralName;
var        localized    string            InfoText;

// Assorted properties
var        Material                    MenuImage;                    // texture to use on the menus
var        array<string>                Models;                        // Player models to use
var        string                        VoiceType;                    // Player's voice type
var        string                        AltVoiceType;                // Player's English voice type
var        bool                        bIsLeader;                    // Enable special leader capabilites?
var     bool                        bIsGunner;                  // Enable player to request MG resupply
var        int                            ObjCaptureWeight;        // How many people is this person worth in a capture zone?
var        float                        PointValue;                    // Used for scoring
var        Material                    SleeveTexture;                // The texture this role should use for thier first person sleeves

// Gore
var class <SeveredAppendage>        DetachedArmClass;            // class of detached arm to spawn for this role.
var class <SeveredAppendage>        DetachedLegClass;            // class of detached arm to spawn for this role.

struct ItemData
{
    var()    class<Inventory>        Item;
    var()    int                        Amount;
    var()    class<ROAmmoPouch>        AssociatedAttachment;
};

var()    ItemData                    PrimaryWeapons[3];            // Primary weapons available
var()    ItemData                    SecondaryWeapons[3];        // Secondary weapons available
var()    ItemData                    Grenades[3];                // Grenade types available
var()    array<string>                GivenItems;                    // Other items always given to the player
var        array<class<ROHeadgear> >    Headgear;                    // Headgear classes used
var        array<string>                UnusableItems;                // List of items this person is NOT allowed to pickup at all
var()    bool                        bCarriesMGAmmo;                // carries a load of MG ammo

// Have to do a default pawn class, since it seems to be the only way to guarantee the proper mesh
// is set immediately after a player spawns in laggy conditions
var        string                        RolePawnClass;                // The default pawn class for this role

// New stuff
enum EWeaponType
{
    WT_Rifle,
    WT_SMG,
    WT_LMG,
    WT_Sniper,
    WT_SemiAuto,
    WT_Assault,
    WT_PTRD,
};

var()   EWeaponType                    PrimaryWeaponType;            // Role will recieve bonuses based on thier primary weapontype

// Weapon abilities
var        bool                        bEnhancedAutomaticControl;    // True if this person has extra experience in controlling automatic weapons( not including MG's)

var     bool                        bCanBeTankCrew;             // Qualified to operate tanks
var     bool                        bCanBeTankCommander;

// New stuff
enum ESide
{
    SIDE_Axis,
    SIDE_Allies,
};

var()    ESide                Side;                // Side that can use this role
var()    int                    Limit;                // How many people can be this role?  (0 = no restriction)
var()   int                 Limit33to44;        // How many people can be this role on a 33 to 44 player server?
var()   int                 LimitOver44;        // How many people can be this role on a server with MaxPlayers more than 44?

//=============================================================================
// replication
//=============================================================================

replication
{
    reliable if (bNetInitial && Role == ROLE_Authority)
        Limit, PrimaryWeapons, SecondaryWeapons, Grenades;
}

//=============================================================================
// Functions
//=============================================================================

//-----------------------------------------------------------------------------
// PostBeginPlay - Add this role to the list
//-----------------------------------------------------------------------------

function PostBeginPlay()
{
    if (ROTeamGame(Level.Game) != None)
        ROTeamGame(Level.Game).AddRole(self);

    HandlePrecache();
}

//-----------------------------------------------------------------------------
// PostNetBeginPlay - Initiate precache
//-----------------------------------------------------------------------------

simulated function PostNetBeginPlay()
{
    if (Role < ROLE_Authority)
        HandlePrecache();
}

//-----------------------------------------------------------------------------
// HandlePrecache - Try to add necessary precache materials
//-----------------------------------------------------------------------------

simulated function HandlePrecache()
{
    local int i;
    local xUtil.PlayerRecord PR;

    for (i = 0; i < ArrayCount(PrimaryWeapons); i++)
    {
        if (PrimaryWeapons[i].Item == None)
            continue;

        if (PrimaryWeapons[i].Item.default.PickupClass != None)
            PrimaryWeapons[i].Item.default.PickupClass.static.StaticPrecache(Level);

        if (PrimaryWeapons[i].AssociatedAttachment != None)
            PrimaryWeapons[i].AssociatedAttachment.static.StaticPrecache(Level);
    }

    for (i = 0; i < ArrayCount(SecondaryWeapons); i++)
    {
        if (SecondaryWeapons[i].Item == None)
            continue;

        if (SecondaryWeapons[i].Item.default.PickupClass != None)
            SecondaryWeapons[i].Item.default.PickupClass.static.StaticPrecache(Level);

        if (SecondaryWeapons[i].AssociatedAttachment != None)
            SecondaryWeapons[i].AssociatedAttachment.static.StaticPrecache(Level);
    }

    for (i = 0; i < ArrayCount(Grenades); i++)
    {
        if (Grenades[i].Item == None)
            continue;

        if (Grenades[i].Item.default.PickupClass != None)
            Grenades[i].Item.default.PickupClass.static.StaticPrecache(Level);

        if (Grenades[i].AssociatedAttachment != None)
            Grenades[i].AssociatedAttachment.static.StaticPrecache(Level);
    }

    for (i = 0; i < default.Headgear.Length; i++)
        default.Headgear[i].static.StaticPrecache(Level);

    for (i = 0; i < default.Models.Length; i++)
    {
        PR = class'xUtil'.static.FindPlayerRecord(default.Models[i]);
        DynamicLoadObject(PR.MeshName, class'Mesh');
        Level.ForceLoadTexture(Texture(DynamicLoadObject(PR.BodySkinName, class'Material')));
        Level.ForceLoadTexture(Texture(DynamicLoadObject(PR.FaceSkinName, class'Material')));
    }

    if (default.VoiceType != "")
        DynamicLoadObject(default.VoiceType, class'Class');

    if (default.AltVoiceType != "")
        DynamicLoadObject(default.AltVoiceType, class'Class');

    if (default.DetachedArmClass != none)
        default.DetachedArmClass.static.PrecacheContent(Level);

    if (default.DetachedLegClass != none)
        default.DetachedLegClass.static.PrecacheContent(Level);
}

//-----------------------------------------------------------------------------
// GetDummyAttachments - Returns all the ammo pouches required of this role
//-----------------------------------------------------------------------------

function GetAmmoPouches(out array<class<ROAmmoPouch> > OutArray, int Prim, int Sec, int Gren)
{
    if (Prim >= 0 && PrimaryWeapons[Prim].AssociatedAttachment != None)
        OutArray[OutArray.Length] = PrimaryWeapons[Prim].AssociatedAttachment;

    if (Sec >= 0 && SecondaryWeapons[Sec].AssociatedAttachment != None)
        OutArray[OutArray.Length] = SecondaryWeapons[Sec].AssociatedAttachment;

    if (Gren >= 0 && Grenades[Gren].AssociatedAttachment != None)
        OutArray[OutArray.Length] = Grenades[Gren].AssociatedAttachment;
}

//-----------------------------------------------------------------------------
// CanPickUp
//-----------------------------------------------------------------------------

function bool CanPickUp(Inventory Item)
{
    local int i;

    for (i = 0; i < UnusableItems.Length; i++)
    {
        if (string(Item.Class) ~= UnusableItems[i])
            return false;
    }

    return true;
}

//-----------------------------------------------------------------------------
// GetModel
//-----------------------------------------------------------------------------

static function string GetModel()
{
    if (default.Models.Length == 0)
        return "";

    return default.Models[Rand(default.Models.Length)];
}


//-----------------------------------------------------------------------------
// GetPawnClass
//-----------------------------------------------------------------------------

static function string GetPawnClass()
{
    return default.RolePawnClass;
}

//-----------------------------------------------------------------------------
// GetHeadgear
//-----------------------------------------------------------------------------

function class<ROHeadgear> GetHeadgear()
{
    if (Headgear.Length == 0)
        return None;

    return Headgear[0];
}

static function Material GetSleeveTexture()
{
    return default.SleeveTexture;
}

static function class<SeveredAppendage> GetArmClass()
{
    return default.DetachedArmClass;
}

static function class<SeveredAppendage> GetLegClass()
{
    return default.DetachedLegClass;
}

//-----------------------------------------------------------------------------
// GetLimit
//-----------------------------------------------------------------------------

simulated function int GetLimit(byte MaxPlayers)
{
    //Use a higher limit if server's MaxPlayers is over 32
    if (MaxPlayers > 32)
    {
        if (MaxPlayers <= 44)
            return Max(Limit, Limit33to44);
        else
            return Max(Limit, LimitOver44);
    }

    return Limit;
}

//=============================================================================
// defaultproperties
//=============================================================================

defaultproperties
{
     InfoText="No information provided."
     ObjCaptureWeight=1
     PointValue=1.000000
     bStatic=True
     bHidden=True
     bReplicateMovement=False
     bSkipActorPropertyReplication=True
     NetUpdateFrequency=10.000000
}
 
Last edited:

meowcat

take a chance
Jun 7, 2001
803
3
18
Hi ROSoldier,
Thanks for posting the above code, that clears some stuff up (since it looks like the Role class info are instantiated in the level....). I (and VendorX too if he's checking this thread) need one more piece of info that he mentions earlier. Since the 'RORoleInfo' class has the 'GetPawnClass()' function, I need to see the code snippets where that function is called as this will tell us how the game uses that variable to spawn the player's pawn. Do a text search of the source code classes (I'm assuming you have access to them) and see where that function is called, I'm guessing it will be in a subclass of GameInfo or the RO PlayerController.

Based on the info you provided above I'm guessing the below code *might* work (though the replication aspect might tricky... you may have to duplicate the same allactors check in the PostNetBeginPlay() function too). Note that the 'CheckReplacement' mutator function only gets called on "dynamic" actors (bStatic=false) so you usually have to use an AllActors (or Objects) iterator call to find and modify those items (also noting that changes to those don't usually get replicated after the initial replication, if any occurs, at the beginning of a client connection to the server):
Code:
class ROPawnSwapper extends Mutator;

simulated function BeginPlay(){
    local ROSU_Winter_RKKA RoleInfoInst;

    // search for and change the RolePawnClass string ... may not get reliably called on the client...
    foreach AllActors(class'ROSU_Winter_RKKA', RoleInfoInst, ){
          Log("Found a instantiated subclass of ROSU_Winter_RKKA, trying to swap!",'RORole_mutator'); 
         RoleInfoInst.default.RolePawnClass="MyRoles.MyRiflemanPawn";
    }
    Super.BeginPlay();
}

defaultproperties
{
    bAddToServerPackages=True
    GroupName="ROPawnSwapper"
    FriendlyName="ROPawnSwapper"
    Description="Swap ROPawn CCCC with VVVV"
    RemoteRole=ROLE_SimulatedProxy
	bAlwaysRelevant=true
}

If the bots or players are spawned immediately (or "pick their roles" immediately) when the level loads (as opposed to after a short prematch wait time), then the mutator might not be executed fast enough (or in code order) on clients to change the string var before it is used...
 

ROSoldier

New Member
Feb 1, 2015
29
0
1
Hi man thanx again! Still haven't got time to test anything you gave me, I will try to do it tomorrow morning when server is empty.

Here is the SetPawnClass function found in the default vanilla BotClass:

Code:
function ChangeCharacter(string newCharacter, optional string inClass)
{
	if( inClass != "")
	{
		SetPawnClass(inClass, newCharacter);
	}
	else
	{
		SetPawnClass(string(PawnClass), newCharacter);
	}

	UpdateURL("Character", newCharacter, true);
	//SaveConfig();
}

// Overriden to allow for setting the correct RO-specific pawn class
function SetPawnClass(string inClass, string inCharacter)
{
	local class<ROPawn> pClass;

	pClass = class<ROPawn>(DynamicLoadObject(inClass, class'Class'));
	if (pClass != None)
		PawnClass = pClass;

	PawnSetupRecord = class'xUtil'.static.FindPlayerRecord(inCharacter);
	PlayerReplicationInfo.SetCharacterName(PawnSetupRecord.DefaultName);
}


And here it is in the Default vanilla Player controller:

Code:
//-----------------------------------------------------------------------------
// ChangeCharacter - Subclassed to allow for setting the correct team or role specific model in ROTeamgame - Ramm
//-----------------------------------------------------------------------------
function ChangeCharacter(string newCharacter, optional string inClass)
{
	if( inClass != "")
	{
		SetPawnClass(inClass, newCharacter);
	}
	else
	{
		SetPawnClass(string(PawnClass), newCharacter);
	}

	UpdateURL("Character", newCharacter, true);
	SaveConfig();

	// Send the info to the client now!
	NetUpdateTime = Level.TimeSeconds - 1;
}

// Overriden to allow for setting the correct RO-specific pawn class
function SetPawnClass(string inClass, string inCharacter)
{
    local class<ROPawn> pClass;

    if ( inClass != "" )
	{
		pClass = class<ROPawn>(DynamicLoadObject(inClass, class'Class'));
		if ( (pClass != None) && pClass.Default.bCanPickupInventory )
			PawnClass = pClass;
	}

    PawnSetupRecord = class'xUtil'.static.FindPlayerRecord(inCharacter);
    PlayerReplicationInfo.SetCharacterName(inCharacter);
}
 

VendorX

Member
Aug 2, 2010
231
7
18
BXL/Paris
It seems that PlayerReplicationInfo is holding player current Role, which can be called from multiple places and which - I suppose (I don't have entire code) - is chosen after the player login, so probably meowcat last snippet should work.
 

ROSoldier

New Member
Feb 1, 2015
29
0
1
It looks like its going to be more complicated than I thought....
I may also need to replace or even add more "Models"
The problem is that the uniforms to be used have variations of the texture, plus I have to tell the game to use all the 15 faces that are used from the stock uniforms (so that soldiers will not have same faces)

This means some sort of permutation of all uniform types per Unit type (for example for RKKA unit) and 15 faces. So a huge Playerrecord up file should be written and numerous Mod files must be available on the server for client download.

Is there a way to just write down all the uniforms, and then let the game choose the face randomly?

I forgot to say that I have done custom uniforms and I didn't have problem doing it the normal way. But this is more complicated because of the tex variations....
For those of you who can't understand: until now soldiers would spawn with same uniform texture(for example all the german soldiers from the SS unit would be the same), and different face depending on what face array you have chosen to write down. What I am aiming for is the same, but only the mesh texture or the face texture will be "hard" written in the uniform code, and the other one will be chosen randomly. This will save us from making hundreds of model files to be downloaded by clients and the endless player character file that must be written.....

So I was thinking something like:

Code:
class ROSU_Winter_RKKA extends ROSU_RKKA_Units
abstract;

defaultproperties
{
Models(0)="R_RKKAWinter1"
Models(1)="R_RKKAWinter2"
Models(2)="R_RKKAWinter3"
Models(3)="R_RKKAWinter4"
Models(4)="R_RKKAWinter5"
[COLOR=royalblue]Models(5)="R_RKKAWinterVariation1"
Models(6)="R_RKKAWinterVariation2"[/COLOR]
[COLOR=red]SleeveTexture=Texture'Weapons1st_tex.Arms.RussianSnow_Sleeves'[/COLOR]
}

Where the blue models are added with a mutator.
Sleeve texture is nasty too, since sleeves must be replaced depending on the texture that is loaded at the moment.

The question is how to write the variation models, so that they will use random faceskin?

And another very important question:
can the standard and the "variation" models be written in a separate *pl file or they must reside in the same file?