Jump to content

Randomising Enemy Unit Stats


lamaros

Recommended Posts

Back in a different direction, on structs. If I understand this correctly you use this to create a single array, with each entry in the array having three variables.

 

How do I populate this array though? By making these variables arrays, in effect just using the struct to bring them together into a single array?

 

Is there any good guide to unreal script anywhere? I feel like I'm missing so many basic concepts that I'm hitting the wall too often...

struct RandomChange
{
  var array<UnitName> Target;
  var array<UnitStat> StatType;
  var array<UnitRandAmount> Amount;
}

var array<RandomChange> RandomChanges;
Link to comment
Share on other sites

Ok i got it working, here's the code:

 

Main Code for randomising

 

 

function RandomEnemyStats(XComGameState_Unit Unit)
{
	local array<int> EnemyCosts;
	local array<ECharStatType> Stats;
	local array<int> EnemyStatChanges;
	local array<int> BaseModifiers;
	local int points;
	local bool PointCheck;
	local int j;
	local int currentStat;
	local float tempF;
	local array<int> RandomStats;
	local X2CharacterTemplate UnitTemplate;
	if(bIs_Epigenetics_Activated)
	{
		EnemyCosts=GetCosts();
		Stats=GetStats(); 
		 do
			{
				`log("");
				points=TotalPoints_Enemy;
				PointCheck=true;
				for(j=0;j<10;j=j++)
				{	
				
					currentStat=GetRandonStat(Unit,Stats[j]);
					points-=GetDefaultPoints(Unit,currentStat,EnemyCosts[j],Stats[j]);
					RandomStats.addItem(currentStat);
				
				}
				 `log("Points:"@points);
				//`log("out of forloop");
				if(ABS(points)>=TOLERANCE)
				{
					RandomStats.remove(0,RandomStats.Length);
					PointCheck=false;
					//`log("PointCheck=false" @points);
				}

			} Until(PointCheck==true);




		Unit.setBaseMaxStat(eStat_HP,RandomStats[0]);
		Unit.setBaseMaxStat(eStat_Offense,RandomStats[1]);
		Unit.setBaseMaxStat(eStat_Defense,RandomStats[2]);
		Unit.setBaseMaxStat(eStat_Mobility,RandomStats[3]);
		Unit.setBaseMaxStat(eStat_Will,RandomStats[4]);
		Unit.setBaseMaxStat(eStat_Dodge,RandomStats[5]);
		Unit.setBaseMaxStat(eStat_ArmorMitigation,RandomStats[6]);
		Unit.setBaseMaxStat(eStat_ArmorChance,100.00f);
		Unit.setBaseMaxStat(eStat_SightRadius,RandomStats[7]);
		Unit.setBaseMaxStat(eStat_PsiOffense,RandomStats[8]);
		Unit.setBaseMaxStat(eStat_FlankingCritChance,RandomStats[9]);

		Unit.setCurrentStat(eStat_HP,RandomStats[0]);
		Unit.setCurrentStat(eStat_Offense,RandomStats[1]);
		Unit.setCurrentStat(eStat_Defense,RandomStats[2]);
		Unit.setCurrentStat(eStat_Mobility,RandomStats[3]);
		Unit.setCurrentStat(eStat_Will,RandomStats[4]);
		Unit.setCurrentStat(eStat_Dodge,RandomStats[5]);
		Unit.setCurrentStat(eStat_ArmorMitigation,RandomStats[6]);
		Unit.setCurrentStat(eStat_ArmorChance,100.00f);
		Unit.setBaseMaxStat(eStat_SightRadius,RandomStats[7]);
		Unit.setBaseMaxStat(eStat_PsiOffense,RandomStats[8]);
		Unit.setBaseMaxStat(eStat_FlankingCritChance,RandomStats[9]);

		Unit.UnitSize=(0.0006*points*points)+(0.025*points)+1;

		`log("Stat:HP" @RandomStats[0]);
		`log("Stat:Aim" @RandomStats[1]);
		`log("Stat:Defense" @RandomStats[2]);
		`log("Stat:Mobility" @RandomStats[3]);
		`log("Stat:Will" @RandomStats[4]);
		`log("Stat:Dodge" @RandomStats[5]);
		`log("Stat:Armor" @RandomStats[6]);
		`log("Stat:SightRadius" @RandomStats[7]);
		`log("Stat:PsiOffense" @RandomStats[8]);
		`log("Stat:FlankCritChance" @RandomStats[9]);
		
	}
}
 

 

 

All the actual randomisation

 

 

function int GetRandomSign()
{
	local int i;

	i=Rand(2);
	//`log ("got a random"@i);
	if(i==1)
		return 1;
	else
		return -1;	
}
function float GetUnitStatModifier(XComGameState_Unit Unit,ECharStatType Stat)
{
	local array<int> BaseStats;
	BaseStats=GetBaseModifiers();
	if(Stat==eStat_HP)
		return (Unit.GetBaseStat(Stat)/BaseStats[0]);
	else if(Stat==eStat_Offense)
		return (Unit.GetBaseStat(Stat)/BaseStats[1]);
	else if(Stat==eStat_Defense)
		return (Unit.GetBaseStat(Stat)/BaseStats[2]);
	else if(Stat==eStat_Mobility)
		return (Unit.GetBaseStat(Stat)/BaseStats[3]);
	else if(Stat==eStat_Will)
		return (Unit.GetBaseStat(Stat)/BaseStats[4]);
	else if(Stat==eStat_Dodge)
		return (Unit.GetBaseStat(Stat)/BaseStats[5]);
	else if(Stat==eStat_ArmorMitigation)
		return (Unit.GetBaseStat(Stat)/BaseStats[6]);
	else if(Stat==eStat_SightRadius)
		return (Unit.GetBaseStat(Stat)/BaseStats[7]);
	else if(Stat==eStat_PsiOffense)
		return (Unit.GetBaseStat(Stat)/BaseStats[8]);
	else if(Stat==eStat_FlankingCritChance)
		return (Unit.GetBaseStat(Stat)/BaseStats[9]);
	
}

function int GetRandonStat(XComGameState_Unit Unit,ECharStatType Stat)
{
	local int tempRet;
	if(Stat==eStat_HP)
		tempRet=int( FMAX(2,Round(Rand(1+HP_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));	
	else if(Stat==eStat_Mobility)
		tempRet= int(FMAX(10,Round(Rand(1+Mobility_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_Offense)
		tempRet= int(FMAX(1,Round(Rand(1+Offense_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_Defense)
		tempRet= int(FMAX(0,Round(Rand(1+Defense_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_Will)
		tempRet= int(FMAX(0,Round(Rand(1+Will_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));	
	else if(Stat==eStat_ArmorMitigation)
		tempRet= int(FMAX(0,Round(Rand(1+ArmorMitigation_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_SightRadius)
		tempRet= int(FMAX(10,Round(Rand(1+SightRadius_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_PsiOffense)
		tempRet= int(FMAX(0,Round(Rand(1+PsiOffense_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));
	else if(Stat==eStat_FlankingCritChance)
		tempRet= int(FMIN(100,FMAX(0,Round(Rand(1+FlankCrit_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat)))));
	else if(Stat==eStat_Dodge)
		tempRet= int(FMAX(0,Round(Rand(1+Dodge_Range)*GetRandomSign()*GetUnitStatModifier(Unit,Stat) + Unit.GetBaseStat(Stat))));

	Return tempRet;
}  

 

 

Utility functions

 

 

function int GetDefaultPoints(XComGameState_Unit Unit,int CurrentStat,int StatCost,ECharStatType Stat)
{
	`log(Stat @"Cost:" @(CurrentStat-Unit.GetBaseStat(Stat))*StatCost); 
	return (CurrentStat-Unit.GetBaseStat(Stat))*StatCost;
}
function array<int>GetCosts()
{
	local array<int> ToRet;
	ToRet.AddItem(HP_cost_Enemy);	
	ToRet.AddItem(Offense_cost_Enemy);	
	ToRet.AddItem(Defense_cost_Enemy);	
	ToRet.AddItem(Mobility_cost_Enemy);	
	ToRet.AddItem(Will_cost_Enemy);	
	ToRet.AddItem(Dodge_cost_Enemy);	
	ToRet.AddItem(ArmorMitigation_cost_Enemy);	
	ToRet.AddItem(SightRadius_cost_Enemy);	
	ToRet.AddItem(PsiOffense_cost_Enemy);
	ToRet.AddItem(FlankCrit_cost_Enemy);
	return ToRet;	
}
function array<int>GetBaseModifiers()
{
	local array<int> ToRet;
	ToRet.AddItem(8);	
	ToRet.AddItem(80);	
	ToRet.AddItem(10);	
	ToRet.AddItem(12);	
	ToRet.AddItem(50);	
	ToRet.AddItem(20);	
	ToRet.AddItem(1);	
	ToRet.AddItem(27);	
	ToRet.AddItem(120);
	ToRet.AddItem(33);
	return ToRet;	
}
function array<ECharStatType>GetStats()
{
	local array<ECharStatType> ToRet;
	ToRet.AddItem(eStat_HP);	
	ToRet.AddItem(eStat_Offense);	
	ToRet.AddItem(eStat_Defense);	
	ToRet.AddItem(eStat_Mobility);	
	ToRet.AddItem(eStat_Will);	
	ToRet.AddItem(eStat_Dodge);	
	ToRet.AddItem(eStat_ArmorMitigation);	
	ToRet.AddItem(eStat_SightRadius);	
	ToRet.AddItem(eStat_PsiOffense);
	ToRet.AddItem(eStat_FlankingCritChance);
	return ToRet;	
} 

 

 

Edited by Guest
Link to comment
Share on other sites

Good work, I think I mostly follow. I'm stuck on working out how to ask XComGameState_Unit what the unit type/name is for if i wanted to alter this to modify different ones in custom ways, though.

 

Going to run myself through some java lessons so I get a better understanding of how the OOP bit work.

Link to comment
Share on other sites

I've been poking about your code ElaDdv. Have you used a different way to get the random stats for enemies function to run than you did for the soldier stats?

 

Under the Downloadablecontent info you're still only going with:

	foreach `XCOMHISTORY.IterateByClassType(class'XComGameState_Unit', Unit )
	{
		`log("Mod Foreach");
		 SecondWaveObject.RandomStats(Unit);
	}

It's just brought in by the UIAvengerHUD_Listener?

function EventListenerReturn Epigenetics(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameState_SecondWave Randomiser;
	Randomiser= new class 'XComGameState_SecondWave';
	if(XComGameState_Unit(EventSource).GetTeam() == eTeam_Alien)
	{
		`log("An Enemy Unit was NCEd");
		Randomiser.RandomEnemyStats(XComGameState_Unit(EventSource));
	}
	return ELR_NoInterrupt;
}
Link to comment
Share on other sites

 

I've been poking about your code ElaDdv. Have you used a different way to get the random stats for enemies function to run than you did for the soldier stats?

 

Under the Downloadablecontent info you're still only going with:

	foreach `XCOMHISTORY.IterateByClassType(class'XComGameState_Unit', Unit )
	{
		`log("Mod Foreach");
		 SecondWaveObject.RandomStats(Unit);
	}

It's just brought in by the UIAvengerHUD_Listener?

function EventListenerReturn Epigenetics(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameState_SecondWave Randomiser;
	Randomiser= new class 'XComGameState_SecondWave';
	if(XComGameState_Unit(EventSource).GetTeam() == eTeam_Alien)
	{
		`log("An Enemy Unit was NCEd");
		Randomiser.RandomEnemyStats(XComGameState_Unit(EventSource));
	}
	return ELR_NoInterrupt;
}

Yeah i did, since soldier stats are permanent i can change them on game launch and when they are created(like the end of each month or when you get rookies from a scan). Now Enemies are created on individual missions so i have to check for their creation and there is no simpler way to do that then with a listener for 'OnUnitBeginPlay' and then sort by eTeam.

Link to comment
Share on other sites

Ok, so I've hit a wall with this. My current understanding is not up to solving, so I'll have to learn more and return to it in the future.

 

For others who might be interested here is what we (almost all EladDv's code) have at the moment, stripped back to try and debug.

 

Here is XComGameState_NCE.uc

class XComGameState_NCE extends Object Config(NCE);

//-----NCE Vars-----

var int vfiLength;
var int points;

//-----Config Vars Enemy-----

var config bool bIs_EnemyNCE_Activated;
var config int EnemyHP_randNCE;
var config int EnemyHP_cost;
var config int EnemyArmorMitigation_randNCE;
var config int EnemyArmorMitigation_cost;
var config int EnemyTotalPoints;
var config int EnemyTolerance;

//-----NCE Enemy Units-----

function RandomEnemyStats(XComGameState_Unit Unit)
{
		local array<ECharStatType> statGroup;
		
		local int Costs[2];
		local int ValuesFromINI[2];
		local int BaseEnemyValues[2];
		local int vfiLength;
		
		local int points;
		local bool PointCheck;
		local int j,Count;
		
		local int currentStat;
		local array<int> RandomStats;
		
		local int testing;
		local int testing2;
		local int testing3;
		
		testing = Unit.GetMyTemplate().CharacterBaseStats[eStat_HP];
		testing2 = Unit.getCurrentStat(eStat_HP);
		testing3 = Unit.getBaseStat(eStat_HP);

		statGroup.AddItem(eStat_HP);	
		statGroup.AddItem(eStat_ArmorMitigation);

		`log("Has Vanilla Stats? " @HasVanillaStats(Unit));
			
		if(bIs_EnemyNCE_Activated&&HasVanillaStats(Unit))
		{
				`log("NCE Enemy Activated");
				`log("HP template at start " @testing);
				`log("HP current stat at start " @testing2); 
				`log("HP base stat at start " @testing3); 
				
				Costs[0]=EnemyHP_cost;
				Costs[1]=EnemyArmorMitigation_cost;

				ValuesFromINI[0]=EnemyHP_randNCE;
				ValuesFromINI[1]=EnemyArmorMitigation_randNCE;

				BaseEnemyValues[0]=Unit.getCurrentStat(eStat_HP)-(ValuesFromINI[0]/2);
				BaseEnemyValues[1]=Unit.getCurrentStat(eStat_ArmorMitigation);

				vfiLength=2;
		
				do
				{
					points=EnemyTotalPoints;
					PointCheck=true;

					for(j=0;j<vfiLength;j=j+1)
					{		
						currentStat=(Rand((ABS(ValuesFromINI[j]))+1));
						points-=(currentStat*Costs[j]);
						RandomStats.addItem(BaseEnemyValues[j]+currentStat);
					}
				
					if(ABS(points)>=EnemyTolerance&&Count<75)
					{
						RandomStats.remove(0,RandomStats.Length);
						PointCheck=false;
						Count++;
					}

					else if(Count>75)
					{
					`log("too much counting");
					}

				} Until(PointCheck==true);
		
				for (j=0;j<vfiLength;j=j+1)
				{
					Unit.setBaseMaxStat(statGroup[j],RandomStats[j]);
					Unit.setCurrentStat(statGroup[j],RandomStats[j]);
				}

				Unit.setBaseMaxStat(eStat_ArmorChance,100.00f);
				Unit.setCurrentStat(eStat_ArmorChance,100.00f);

				testing = Unit.GetMyTemplate().CharacterBaseStats[eStat_HP];
				testing2 = Unit.getCurrentStat(eStat_HP);
                                testing3 = Unit.getBaseStat(eStat_HP);
				
				`log("HP template stat at end " @testing);
				`log("HP current stat at end " @testing2); 
				`log("HP base stat at end " @testing3); 
				`log("Enemy Stats Reported");
		}

		else if (!(bIs_EnemyNCE_Activated&&HasVanillaStats(Unit)))
		{
			`log("NCE Enemy Not Active");
			`log("HP template stat not active at " @testing);
			`log("HP current stat not active at " @testing2);
			`log("HP base stat not active at " @testing3);
		}

}

//-----Support Functions-----

function array<ECharStatType>GetStats()
{
	local array<ECharStatType> ToRet;
	ToRet.AddItem(eStat_HP);	
	ToRet.AddItem(eStat_ArmorMitigation);	
	return ToRet;
}

function bool HasVanillaStats(XComGameState_Unit Unit)
{
	local int tempStat;
	local int baseStat;
		
	local int i;
	local int count;
	local array<ECharStatType> Stats;

	count = 0;
	Stats = GetStats();
	tempStat = Unit.GetMyTemplate().CharacterBaseStats[Stats[i]];
	baseStat = Unit.getBaseStat(Stats[i]);

	for(i=0;i<Stats.length;i++)
	{
		if(tempStat==baseStat)
		{
			count++;
		}
	}

	if (count>1) return true;
	else return false;

}

Here is XComNCE.ini

;Not Created Equal Configuration
;---------------------------------------

[ExtendedFights.XComGameState_NCE]

;-----Enemy Configuration-----

bIs_EnemyNCE_Activated=True; Is Not Created Equally Active?

;Randomise Negative and Positive, values are the total range
EnemyHP_randNCE=2
EnemyHP_cost=10

;Randomise Positive Only, values are the total range
EnemyArmorMitigation_randNCE=1
EnemyArmorMitigation_cost=20

EnemyTotalPoints=20 ; Total Points allocated to stats.
EnemyTolerance=0

And here is where it's trying to hook in with UIAvengerHUD_Listener_NCE.uc

class UIAvengerHUD_Listener_NCE extends UIScreenListener Config(NCE);

// Set this value in the defaultproperties to filter UI signals based on class
var class<UIScreen> ScreenClass;

event OnInit(UIScreen Screen)
{
	local Object ThisObj;
	ThisObj=self;
	`XEVENTMGR.RegisterForEvent(ThisObj, 'OnUnitBeginPlay', Epigenetics);
}

function EventListenerReturn Epigenetics(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameState_NCE Randomiser;
	Randomiser= new class 'XComGameState_NCE';

	`log("NCE Enemy Start");

	if(XComGameState_Unit(EventSource).GetTeam() == eTeam_Alien)
	{
		`log("Trying to randomise enemy unit");
		Randomiser.RandomEnemyStats(XComGameState_Unit(EventSource));
	}
	return ELR_NoInterrupt;
}

defaultproperties
{
	// Leaving this assigned to none will cause every screen to trigger its signals on this class
	ScreenClass = UIAvengerHUD;
}

And here is what the debug log is saying: http://imgur.com/a/YegYx (that the base stats and current stats have been changed)

 

Yet despite this all enemy units have default stats.

Edited by lamaros
Link to comment
Share on other sites

So, under the X2Character Template we have,

function XComGameState_Unit CreateInstanceFromTemplate(XComGameState NewGameState)
{
    local XComGameState_Unit Unit;

    Unit = XComGameState_Unit(NewGameState.CreateStateObject(class'XComGameState_Unit'));    
    Unit.OnCreation(self);    

    return Unit;
}

Is this of any relevance? I wish I knew what I was doing...

 

And this this tracks back to XComAISpawnManager?

private event XComGameState_Unit CreateUnitInternal( Vector Position, X2CharacterTemplate CharacterTemplate, ETeam Team, XComGameState NewGameState, bool bRollForLoot, bool PerformAIUpdate, XComGameState_Unit ReanimatedFromUnit, optional string CharacerPoolName )
{
	local XComGameStateHistory History;
	local XComGameState_Unit UnitState;
	local XComGameState_Player PlayerState;
	local bool bUsingStartState, bFoundExistingPlayerState;
	local TTile UnitTile;

	History = `XCOMHISTORY;

	bUsingStartState = (NewGameState == History.GetStartState());

	// find the player matching the requested team ID... 
	foreach History.IterateByClassType(class'XComGameState_Player', PlayerState)
	{
		if( PlayerState.GetTeam() == Team )
		{
			bFoundExistingPlayerState = true;
			break;
		}
	}

	// ... or create one if it does not yet exist
	if( !bFoundExistingPlayerState )
	{
		PlayerState = class'XComGameState_Player'.static.CreatePlayer(NewGameState, Team);
		NewGameState.AddStateObject(PlayerState);
	}
	
	// create the unit
	UnitTile = `XWORLD.GetTileCoordinatesFromPosition(Position);
	UnitState = CharacterTemplate.CreateInstanceFromTemplate(NewGameState);
	UnitState.PostCreateInit(NewGameState, CharacterTemplate, PlayerState.ObjectID, UnitTile, Team == eTeam_Alien, bUsingStartState, PerformAIUpdate, ReanimatedFromUnit, CharacerPoolName);

	// Auto loot
	UnitState.bAutoLootEnabled = bRollForLoot;

	//// Timed Loot
	//if( bRollForTimedLoot )
	//{
	//	UnitState.RollForTimedLoot();
	//}

	if( !CharacterTemplate.bIsCosmetic && `PRES.m_kUnitFlagManager != none )
	{
		`PRES.m_kUnitFlagManager.AddFlag(UnitState.GetReference());
	}

	return UnitState;
}

Not sure what X2StrategyGame_SimCombat does, but doesn't seem as relevant?

Edited by lamaros
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...