• Please make sure you are familiar with the forum rules. You can find them here: https://forums.tripwireinteractive.com/index.php?threads/forum-rules.2334636/

Code Change ROObjective values...

-=THOR=-

Grizzled Veteran
Sep 20, 2011
1,050
50
Hi,

I'm trying to create a mutator that will allow to scale the CountdownTime value of ROObjective, because I want to modify the default value hard-coded in the maps. ROGameInfoCountdown uses ROObjective.CountodwnTime to assign WorldInfo.GRI.RemainingTime. ROObjective instances are stored in an array named Objectives. That array can be found in ROGameInfoTerritories, and ROGameReplicationInfo.

At first I tried this code:
Code:
for ( i = 0; i < ROGameInfoTerritories(self.WorldInfo.Game).Objectives.Length; i++ )
{
  if ( ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i] != none )
  {
    ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime = 2*ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime;
  }
}

Then this:
Code:
for ( i = 0; i < ROGameReplicationInfo(WorldInfo.GRI).Objectives.Length; i++ )
{
  if ( ROGameReplicationInfo(WorldInfo.GRI).Objectives[i] != none )
  {
    ROGameReplicationInfo(WorldInfo.GRI).Objectives[i].CountdownTime = 2*ROGameReplicationInfo(WorldInfo.GRI).Objectives[i].CountdownTime;
  }
}

The problem is that only the current "active" objective gets updated. Any idea why? And how does replication work? Thanks.

P.S.: Sorry for the cast that repeats.
 
Last edited:
Hi,

I'm trying to create a mutator that will allow to scale the CountdownTime value of ROObjective, because I want to modify the default value hard-coded in the maps. ROGameInfoCountdown uses ROObjective.CountodwnTime to assign WorldInfo.GRI.RemainingTime. ROObjective instances are stored in an array named Objectives. That array can be found in ROGameInfoTerritories, and ROGameReplicationInfo.

At first I tried this code:
Code:
for ( i = 0; i < ROGameInfoTerritories(self.WorldInfo.Game).Objectives.Length; i++ )
{
  if ( ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i] != none )
  {
    ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime = 2*ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime;
  }
}

Then this:
Code:
for ( i = 0; i < ROGameReplicationInfo(WorldInfo.GRI).Objectives.Length; i++ )
{
  if ( ROGameReplicationInfo(WorldInfo.GRI).Objectives[i] != none )
  {
    ROGameReplicationInfo(WorldInfo.GRI).Objectives[i].CountdownTime = 2*ROGameReplicationInfo(WorldInfo.GRI).Objectives[i].CountdownTime;
  }
}

The problem is that only the current "active" objective gets updated. Any idea why? And how does replication work? Thanks.

P.S.: Sorry for the cast that repeats.

The countdown time in ROGameInfoTerritories will probably be the source for the countdown times in ROGameReplicationInfo. If it is, then try to find a hook in Mutator, Actor or Object class that will be fired before the values are passed to the replication object. I'm not sure if that hook exists, but in general you can overwrite any of the public functions in any of those 3 classes. If it doesn't, then you will probably need to derive a class from ROGameReplicationInfo and use it to replace the WorldInfo.GRI object and also the WordInfo.Game.ReplicationInfo object (yes there are 2 of them, but they refer to the same instance). In your class you overwrite the ReplicationEvent function and catch the CountdownTime property and do with it whatever you want.

P.S.: This will work too:
Code:
ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime *= 2;
 
Upvote 0
Actually, I want to override the "source" of the countdown time, which is the CountdownTime value of each Objective. There's a function called SetCountDownTime in ROGameInfoCountdown (its base class is ROGameInfoTerritories). That function does this:

Code:
for ( i = 0; i < Objectives.Length; i++ )
{
  if ( Objectives[i] != none && Objectives[i].bActive )
  {
    WorldInfo.GRI.RemainingTime = Objectives[i].CountdownTime;
  }
  WorldInfo.GRI.RemainingMinute = WorldInfo.GRI.RemainingTime;
}

Basically, it searches for the current active objective, takes its time, and sets it to the RemainingTime. So I thought that applying a scaling multiplier to CountdownTime of every objective before the game starts would do the trick. For the moment, I use a Mutate command, during a round. But only the current objective countdown time is changed: when the next round kicks in, WorldInfo.GRI.RemainingTime is twice larger, but only for one objective. However I'm looping over the whole list of objectives.

So my question is, why is only the 'active' objective modified, although (as you can see in the first post) i'm looping over the whole list of objectives. Shouldn't they all be modified?
 
Last edited:
Upvote 0
I think I found out... although the comments say "A list of all objectives on the current map", only the active objective is stored in ROGameInfoTerritories.Objectives and ROGameReplicationInfo_Objectives. All the other objectives equal "none" (i suppose it's UDK way to mean null?). I'll have to find where the objectives are.
 
Last edited:
Upvote 0
If it doesn't, then you will probably need to derive a class from ROGameReplicationInfo and use it to replace the WorldInfo.GRI object and also the WordInfo.Game.ReplicationInfo object (yes there are 2 of them, but they refer to the same instance). In your class you overwrite the ReplicationEvent function and catch the CountdownTime property and do with it whatever you want.

I wish Unreal Script would use C#-style events. You register a function pointer (delegate) to the event. When the event is fired, everyone registered is called. You don't have to override stuff...
 
Last edited:
Upvote 0
I wish Unreal Script would use C#-style events. You register a function pointer (delegate) to the event. When the event is fired, everyone registered is called. You don't have to override stuff...

But then the original event handler will also still be called and you don't have direct control over the chain order. Your mutator might then work or maybe it won't. In that case it comes back to deriving the base class and overload the original event handler. And that's the same as how it's done in US. And no, I'm certainly not an advocate of US, but it's what we have to use.
 
Upvote 0
Actually, I want to override the "source" of the countdown time, which is the CountdownTime value of each Objective. There's a function called SetCountDownTime in ROGameInfoCountdown (its base class is ROGameInfoTerritories). That function does this:

Code:
for ( i = 0; i < Objectives.Length; i++ )
{
  if ( Objectives[i] != none && Objectives[i].bActive )
  {
    WorldInfo.GRI.RemainingTime = Objectives[i].CountdownTime;
  }
  WorldInfo.GRI.RemainingMinute = WorldInfo.GRI.RemainingTime;
}
Basically, it searches for the current active objective, takes its time, and sets it to the RemainingTime. So I thought that applying a scaling multiplier to CountdownTime of every objective before the game starts would do the trick. For the moment, I use a Mutate command, during a round. But only the current objective countdown time is changed: when the next round kicks in, WorldInfo.GRI.RemainingTime is twice larger, but only for one objective. However I'm looping over the whole list of objectives.

So my question is, why is only the 'active' objective modified, although (as you can see in the first post) i'm looping over the whole list of objectives. Shouldn't they all be modified?

I noticed that you are using this cast:
Code:
ROGameInfoTerritories(self.WorldInfo.Game).Objectives[i].CountdownTime
AFAIK, ROObjective.CountdownTime isn't used for territory game type. It's only used for countdown and firefight. The lockdown in territory mode is implemented with 3 timers. They are:
Code:
ROObjective.LockDownTime16
ROObjective.LockDownTime32
ROObjective.LockDownTime64
 
Upvote 0
Yeah, I'm trying to modify CD first. After I'll try to modify other stuff like lockdown timers, spawn delays, for all other gametypes... Eventually I'd like to end-up with a mutator that allows to scale most of timing properties of the maps that cannot already be scaled.
 
Last edited:
Upvote 0
I wish Unreal Script would use C#-style events. You register a function pointer (delegate) to the event. When the event is fired, everyone registered is called. You don't have to override stuff...
UnrealScript does have this ability, but not by default. It has to be explicitly defined.

For what you want to do, there shouldn't be any need to override anything, though. Just tweaking numbers only requires a reference to the objects you want to tweak. If the Objectives are being pulled from the map and it's just the GameInfo list that's not populated, then you could just generate your own list with one of the Actor search iterators. If the Objectives are being created during play, then simply iterating through a list at map start will, clearly, not work...however, the ROObjectives are a child of Actor, so you should be able to catch them spawning with mutator.IsRelevant() and modify them that way.
 
Last edited:
Upvote 0
Thanks for the input. I've tried overriding these functions (plus CheckReplacement), and none of them see ROObjective objects:

IsRelevant:
Code:
function bool IsRelevant(Actor Other)
{
    `Log("IsRelevant", true, 'TimingOverride');
	if (ROObjective(Other) != None)
	{
        `Log("ROObjective Assigned", true, 'TimingOverride');
        ROObjective(Other).CountdownTime *= 2;
	}
    
    return Super.IsRelevant(Other);
}

CheckRelevance
Code:
function bool CheckRelevance(Actor Other)
{
    `Log("CheckRelevance", true, 'TimingOverride');
	if (ROObjective(Other) != None)
	{
        `Log("ROObjective Assigned", true, 'TimingOverride');
        ROObjective(Other).CountdownTime *= 2;
	}
    
    return Super.CheckRelevance(Other);
}

AlwaysKeep
Code:
function bool AlwaysKeep(Actor Other)
{
    `Log("AlwaysKeep", true, 'TimingOverride');
	if (ROObjective(Other) != None)
	{
        `Log("ROObjective Assigned", true, 'TimingOverride');
        ROObjective(Other).CountdownTime *= 2;
	}
    
    return Super.AlwaysKeep(Other);
}

Creating my own list might do the trick, I'll try it and let you know if it worked.
 
Upvote 0
Conceptually, yes, you would expect that the purpose of having an objective array in the gameinfo would be to hold a complete list of objective objects. However, since that approach didn't work, that's clearly not what the game uses it for.

You'll get an awful lot of headaches while modding RO2 if you get in the habit of making assumptions about how something functions. Sometimes even the comments can't be trusted. Unless you've traced so deeply through the code paths that you're utterly certain that you know how something is being used, you're better off doing things in as broad and general manner as you can.
 
Upvote 0