Jump to content

Kismeting Out the Buildings


Handepsilon

Recommended Posts

  • Totem Arts Staff

Due to the lack of kismet support, I decided to make some notes about kismeting out buildings. For now I'll do the Action and Event

1. Toggleable Advanced Defense

For those who saw and played Coastal, you might be familiar with this one, so we'll take the obelisk as reference

So for the Obelisk, we'll do this

Placable building actor :

class Rx_Building_Obelisk_Toggleable extends Rx_Building_Obelisk
  placeable;

var(Toggling) bool bLaserActivated; //I forgot what this variable is for, maybe it's not needed, but we'll keep it for safety. Feel free to delete this and see what happens though

/* The Toggle action kismet node will check for actions that goes like this. Whenever there's this function, the Toggle kismet will always fire it up, if the actor is in the Target variable */

simulated function OnToggle(SeqAct_Toggle action)
{

/* We'll pass this to the laser shooter actor because through the editor, only the visual building (this actor) is selectable and assignable to the kismet. Just so to be safe, we'll also pass the toggle Kismet Node Parameter*/
Rx_Sentinel_Obelisk_Laser_Toggleable(Rx_Building_Obelisk_Internals_Toggleable(BuildingInternals).laserSentinel).OnToggle(action);
}

defaultproperties
{
   BuildingInternalsClass = Rx_Building_Obelisk_Internals_Toggleable
}

class Rx_Building_Obelisk_Internals_Toggleable extends Rx_Building_Obelisk_Internals;

Internal actor :


/* We rewrite the whole function, and change one line only
laserSentinel = Spawn(class'Rx_Sentinel_Obelisk_Laser_Toggleable',,,,,,true);
*/
function SetupLaser() 
{
local vector v,v2;

laserSentinel = Spawn(class'Rx_Sentinel_Obelisk_Laser_Toggleable',,,,,,true);
laserSentinel.SetOwner(self);
laserSentinel.Team = self.TeamID;

if(laserSentinel != none)
{
	laserSentinel.bCollideWorld = true; //Turn off collision and translate, because collision may move the Sentinel away from the ceiling when it's spawned.
	v = BuildingSkeleton.GetBoneLocation('Ob_Fire');
	v.z += 100;
	laserSentinel.setFireStartLoc(v);
	v2 = BuildingVisuals.location;
	v2.z = v.z;
	v2 = v2 + Normal(v-v2)*100;
	laserSentinel.setlocation(v2);
	Rx_Building_Obelisk(BuildingVisuals).SentinelLocation = laserSentinel.location;

	laserSentinel.Initialize();
	CrystalGlowMIC = BuildingSkeleton.CreateAndSetMaterialInstanceConstant(0);

	Rx_SentinelWeapon_Obelisk(laserSentinel.SWeapon).CrystalGlowMIC = CrystalGlowMIC;
	laserSentinel.SController.TargetWaitTime = 6.0;
	laserSentinel.SController.bSeeFriendly=false;
	laserSentinel.SController.TargetWaitTime=3.0;
	laserSentinel.SController.SightCounterInterval=0.1;

	Rx_SentinelWeapon_Obelisk(laserSentinel.SWeapon).InitAndAttachMuzzleFlashes(BuildingSkeleton, 'Ob_Fire');
}
}

And finally, the 'Sentinel' shooter actor

//=============================================================================
// Controls the Obi Laser
//=============================================================================
class Rx_Sentinel_Obelisk_Laser_Toggleable extends Rx_Sentinel_Obelisk_Laser_Base;

var bool bLaserDown; //self-explanatory

/* This is the function passed from the building

InputLinks correspond to the order of the left box in the node, or you can just check in the SeqAct_Toggle scripting
*/
simulated function OnToggle(SeqAct_Toggle action)
{
if (action.InputLinks[0].bHasImpulse)
{
	//Turn on
	bLaserDown = FALSE;
}
else if (action.InputLinks[1].bHasImpulse)
{
	//Turn off
	bLaserDown = TRUE;
}
else if (action.InputLinks[2].bHasImpulse)
{
	// basically toggle the laser
	bLaserDown = !bLaserDown;
}
}

/* Once again we rewrite this whole script (just copy and paste) but we add our LaserDown variable to check if the obelisk can fire or not
*/

function bool FireAt(Vector Spot)
{
local Vector Origin;
local bool bFired;


   Origin = GetPawnViewLocation();

//	if(RDiff(DesiredAim, CurrentAimNoRoll) <= SWeapon.GetMaxAimError())
//	{
	if(VSize(Spot - Origin) <= GetRange() && !bLaserDown)
	{
		if(SWeapon.FireAt(Origin, CurrentAim, Spot))
		{
			UpgradeManager.NotifyFired();
			bForceNetUpdate = true;
		}

		bFired = true;
	}
//	}

return bFired;
}

Example Kismet :

2gtc02q.jpg

2. Capturable MCT Event

And now we get to the new Beta 5 feature, the new Capturable MCT which.... unfortunately is not fully supported yet IMO. So now we start with actually creating an event for capturing and neutralizing it, just like the Power Node in UT3 but much simpler

So now, we start with...

Placable building actor:

class Rx_CapturableMCT_Kismet extends Rx_CapturableMCT
  placeable;


defaultproperties
{

BuildingInternalsClass  = Rx_CapturableMCT_Internals_Kismet

       //This is the essential so the actor can be assigned to this event. You can now right click and 'Create event with selected actor'
SupportedEvents.Add(class'Rx_SeqEvent_TechCapture')
}

Internal actor :

class Rx_CapturableMCT_Internals_Kismet extends Rx_CapturableMCT_Internals
notplaceable;

/* We rewrite this because we're going to insert an action. In my opinion it's much easier to tackle this way than using Super.HealDamage()
*/
function bool HealDamage(int Amount, Controller Healer, class DamageType)
{
local int RealAmount;
local float Scr;

if ((Health < HealthMax || Healer.GetTeamNum() != GetTeamNum()) && Amount > 0 && Healer != None ) {
	RealAmount = Min(Amount, HealthMax - Health);

	if (RealAmount > 0) {

		if (Health >= HealthMax && SavedDmg > 0.0f) {
			SavedDmg = FMax(0.0f, SavedDmg - Amount);
			Scr = SavedDmg * HealPointsScale;
			Rx_PRI(Healer.PlayerReplicationInfo).AddScoreToPlayerAndTeam(Scr);
		}

		Scr = RealAmount * HealPointsScale;
		Rx_PRI(Healer.PlayerReplicationInfo).AddScoreToPlayerAndTeam(Scr);
	}

	if(Healer.GetTeamNum() != GetTeamNum()) {
		Amount = -1 * Amount;
	}

	Health = Min(HealthMax, Health + Amount);

	if(Health <= 1) {
		Health = 1;	
		if(GetTeamNum() != TEAM_NOD && GetTeamNum() != TEAM_GDI) {
			if(Healer.GetTeamNum() == TEAM_NOD) {	
				`LogRx("GAME"`s "Captured;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
				BroadcastLocalizedMessage(MessageClass,NOD_CAPTURED,Healer.PlayerReplicationInfo,,self);
				ChangeTeamReplicate(TEAM_NOD,true);
			} else {
				`LogRx("GAME"`s "Captured;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
				BroadcastLocalizedMessage(MessageClass,GDI_CAPTURED,Healer.PlayerReplicationInfo,,self);
				ChangeTeamReplicate(TEAM_GDI,true);
			}

                               // TriggerEventClass will trigger out any event node of this type. We set the instigator to healer and set the output to 0. This is the Captured event
			BuildingVisuals.TriggerEventClass(Class'Rx_SeqEvent_TechCapture',Healer,0);
                               //

		} else {
			if (TeamID == TEAM_NOD)
				BroadcastLocalizedMessage(MessageClass,NOD_LOST,Healer.PlayerReplicationInfo,,self);
			else if (TeamID == TEAM_GDI)
				BroadcastLocalizedMessage(MessageClass,GDI_LOST,Healer.PlayerReplicationInfo,,self);
			`LogRx("GAME"`s "Neutralized;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
			ChangeTeamReplicate(255,true);
			Health = BuildingVisuals.HealthMax;

                               // Another trigger event with input set to 1. This is the Neutralized event
			BuildingVisuals.TriggerEventClass(Class'Rx_SeqEvent_TechCapture',Healer,1);
                               //

		}
	}
	else if (Amount < 0)
		TriggerUnderAttack();

                       // We can actually make another event here for the Under Attack event

	return True;
}

return False;
}


DefaultProperties
{
TeamID          = 255
}

The kismet node

class Rx_SeqEvent_TechCapture extends SequenceEvent;

defaultproperties
{
ObjName="Tech Building Event"
ObjCategory="Renegade X Buildings"

// There OutputLinks correspond to the TriggerEventClass function parameter. So if there should be more output links, it can be added at will

OutputLinks[0]=(LinkDesc="Captured")
OutputLinks[1]=(LinkDesc="Neutralized")
bPlayerOnly=false
MaxTriggerCount=0
}

Example Kismet Node :

e665x0.jpg

And there we have it. Feel free to ask anything.

PS : Hopefully this is included in the next patch, as well as some more Kismet nodes such as Building Destroyed, Harvester dumped and spawned from refinery (Destroyed is already tackled in the Vehicle script), add score, etc.

Link to comment
Share on other sites

Awesome coding there dude! Thanks for this. I was looking for something similair but I didnt have the time to code it myself / do some research. Thanks again!

The 1 thing I'd like to see in your code is the broadcast being changed. Right now, if we capture an MCT it says: " captured the" without a name. For example, when capturing a silo, it prompts us with:

" captured the Silo". Can't we have a string variable (name of the MCT) inside the MCT that will get displayed just like "silo" in the silo capture broadcast? Could you include this in your code?

Link to comment
Share on other sites

  • Totem Arts Staff

Unfortunately.... that's what I'm still wondering about. I believe the 'Silo' part is localized string. I remembered when I had Indo localization, the string got replaced.

But the script is still a jumble for me. Whatever it is, it might be hidden starting beneath the HealDamage

Link to comment
Share on other sites

I think this is the place to search for:

Rx_Building_Techbuilding_Internals.uc

class Rx_Building_TechBuilding_Internals extends Rx_Building_Team_Internals
notplaceable
implements(RxIfc_Capturable);

// The way the sound arrays are setup did not consider changing ownership. I ain't going to rip out the system and replace it, so I'll just use the shit that's there and be efficent at memory usage seeing as I can't do anything else 
const GDI_CAPTURED = 1;
const NOD_CAPTURED = 2;
const GDI_LOST = 3;
const NOD_LOST = 4;
const GDI_UNDERATTACK = 5;
const NOD_UNDERATTACK = 6;

`define GdiCapSound	FriendlyBuildingSounds[buildingRepaired]
`define GdiLostSound	FriendlyBuildingSounds[buildingDestroyed]
`define NodCapSound	EnemyBuildingSounds[buildingRepaired]
`define NodLostSound	EnemyBuildingSounds[buildingDestroyed]
`define GdiUnderAttackForGdiSound FriendlyBuildingSounds[buildingUnderAttack]
`define GdiUnderAttackForNodSound FriendlyBuildingSounds[buildingDestructionImminent]
`define NodUnderAttackForGdiSound EnemyBuildingSounds[buildingUnderAttack]
`define NodUnderAttackForNodSound EnemyBuildingSounds[buildingDestructionImminent]

var TEAM ReplicatedTeamID;
var repnotify TEAM FlagTeam;
var MaterialInstanceConstant MICFlag;

var Rx_CapturePoint_TechBuilding CP;

var float LastUnderAttackAnnouncement;
var float UnderAttackAnnouncementCooldown;

replication
{
if(bNetDirty || bNetInitial)
	ReplicatedTeamID,FlagTeam,CP;
}

simulated event ReplicatedEvent(name VarName)
{
if ( VarName == 'FlagTeam' ) 
	FlagChanged();
else
	super.ReplicatedEvent(VarName);
}

simulated function Init(Rx_Building Visuals, bool isDebug )
{
super.Init(Visuals, isDebug);
//SetupCapturePoint();
MICFlag = BuildingSkeleton.CreateAndSetMaterialInstanceConstant(0);
FlagChanged();
}

simulated function FlagChanged() 
{
	if(FlagTeam == TEAM_NOD)
		MICFlag.SetScalarParameterValue('FlagTeamNum', 1);
	else if(FlagTeam == TEAM_GDI)
		MICFlag.SetScalarParameterValue('FlagTeamNum', 0);
	else
		MICFlag.SetScalarParameterValue('FlagTeamNum', 2);	
}

function ChangeFlag(TEAM ToTeam)
{
FlagTeam = ToTeam;
FlagChanged();
}

function SetupCapturePoint()
{
  local Vector L;
  local Rotator R;

  BuildingSkeleton.GetSocketWorldLocationAndRotation('CapturePoint', L, R);
  CP = Spawn(class'Rx_CapturePoint_TechBuilding',self,,L,R);
}

function ChangeTeamReplicate(TEAM ToTeam, optional bool bChangeFlag=false)
{
TeamID = ToTeam;
ReplicatedTeamID = ToTeam;
if (bChangeFlag)
	ChangeFlag(ToTeam);
}

// damage is ignored, can only be captured through the MCT
function TakeDamage(int DamageAmount, Controller EventInstigator, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser);

function bool HealDamage(int Amount, Controller Healer, class DamageType)
{
local int RealAmount;
local float Scr;

if ((Health < HealthMax || Healer.GetTeamNum() != GetTeamNum()) && Amount > 0 && Healer != None ) {
	RealAmount = Min(Amount, HealthMax - Health);

	if (RealAmount > 0) {

		if (Health >= HealthMax && SavedDmg > 0.0f) {
			SavedDmg = FMax(0.0f, SavedDmg - Amount);
			Scr = SavedDmg * HealPointsScale;
			Rx_PRI(Healer.PlayerReplicationInfo).AddScoreToPlayerAndTeam(Scr);
		}

		Scr = RealAmount * HealPointsScale;
		Rx_PRI(Healer.PlayerReplicationInfo).AddScoreToPlayerAndTeam(Scr);
	}

	if(Healer.GetTeamNum() != GetTeamNum()) {
		Amount = -1 * Amount;
	}

	Health = Min(HealthMax, Health + Amount);

	if(Health <= 1) {
		Health = 1;	
		if(GetTeamNum() != TEAM_NOD && GetTeamNum() != TEAM_GDI) {
			if(Healer.GetTeamNum() == TEAM_NOD) {	
				`LogRx("GAME"`s "Captured;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
				BroadcastLocalizedMessage(MessageClass,NOD_CAPTURED,Healer.PlayerReplicationInfo,,self);
				ChangeTeamReplicate(TEAM_NOD,true);
			} else {
				`LogRx("GAME"`s "Captured;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
				BroadcastLocalizedMessage(MessageClass,GDI_CAPTURED,Healer.PlayerReplicationInfo,,self);
				ChangeTeamReplicate(TEAM_GDI,true);
			}
		} else {
			if (TeamID == TEAM_NOD)
				BroadcastLocalizedMessage(MessageClass,NOD_LOST,Healer.PlayerReplicationInfo,,self);
			else if (TeamID == TEAM_GDI)
				BroadcastLocalizedMessage(MessageClass,GDI_LOST,Healer.PlayerReplicationInfo,,self);
			`LogRx("GAME"`s "Neutralized;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s `PlayerLog(Healer.PlayerReplicationInfo) );
			ChangeTeamReplicate(255,true);
			Health = BuildingVisuals.HealthMax;
		}
	}
	else if (Amount < 0)
		TriggerUnderAttack();
	return True;
}

return False;
}

function TriggerUnderAttack()
{
if (WorldInfo.TimeSeconds < LastUnderAttackAnnouncement + UnderAttackAnnouncementCooldown)
	return;

if (TeamID == TEAM_GDI)
	BroadcastLocalizedMessage(MessageClass,GDI_UNDERATTACK,,,self);
else if (TeamID == TEAM_Nod)
	BroadcastLocalizedMessage(MessageClass,NOD_UNDERATTACK,,,self);

LastUnderAttackAnnouncement = WorldInfo.TimeSeconds;
}

function NotifyBeginCaptureBy(byte TeamIndex)
{
if (TeamIndex == TEAM_GDI)
{
	ChangeFlag(TEAM_GDI);
}
else if (TeamIndex == TEAM_Nod)
{
	ChangeFlag(TEAM_Nod);
}
}

function NotifyCapturedBy(byte TeamIndex)
{
`LogRx("GAME"`s "Captured;" `s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s"by"`s class'Rx_Game'.static.GetTeamName(TeamIndex) );
if (TeamIndex == TEAM_GDI)
	BroadcastLocalizedMessage(MessageClass,GDI_CAPTURED,,,self);
else
	BroadcastLocalizedMessage(MessageClass,NOD_CAPTURED,,,self);

if (TeamIndex == TEAM_GDI)
	ChangeTeamReplicate(TEAM_GDI);
else if (TeamIndex == TEAM_Nod)
	ChangeTeamReplicate(TEAM_Nod);
}

function NotifyBeginNeutralizeBy(byte TeamIndex)
{
TriggerUnderAttack();
}

function NotifyNeutralizedBy(byte TeamIndex, byte PreviousOwner)
{
`LogRx("GAME"`s "Neutralized;"`s class'Rx_Game'.static.GetTeamName(TeamID)$","$self.class `s "id" `s GetRightMost(self) `s "by" `s class'Rx_Game'.static.GetTeamName(TeamIndex) );
if (TeamID == TEAM_GDI)
	BroadcastLocalizedMessage(MessageClass,GDI_LOST,,,self);
else
	BroadcastLocalizedMessage(MessageClass,NOD_LOST,,,self);

ChangeTeamReplicate(255);
if (TeamIndex == TEAM_GDI)
	ChangeFlag(TEAM_GDI);
else if (TeamIndex == TEAM_Nod)
	ChangeFlag(TEAM_Nod);
}

function NotifyRestoredNeutral()
{
ChangeFlag(255);
}

function NotifyRestoredCaptured();


simulated function SoundNodeWave GetAnnouncment(int alarm, int teamNum )
{
switch ( alarm )
{
case GDI_CAPTURED:
	if (teamNum == TEAM_GDI)
		return `GdiCapSound;
	break;
case NOD_CAPTURED:
	if (teamNum == TEAM_Nod)
		return `NodCapSound;
	break;
case GDI_LOST:
	if (teamNum == TEAM_GDI)
		return `GdiLostSound;
	break;
case NOD_LOST:
	if (teamNum == TEAM_Nod)
		return `NodLostSound;
	break;
case GDI_UNDERATTACK:
	if (teamNum == TEAM_Nod)
		return `GdiUnderAttackForNodSound;
	else
		return `GdiUnderAttackForGDISound;
case NOD_UNDERATTACK:
	if (teamNum == TEAM_Nod)
		return `NodUnderAttackForNodSound;
	else
		return `NodUnderAttackForGDISound;
}
return None;
}

static function string GetLocalString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2
)
{
local string str;

if (RelatedPRI_1 != None)
{
	switch (Switch)
	{
	case GDI_CAPTURED:
	case NOD_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", RelatedPRI_1.PlayerName);
		return Repl(str, "`BuildingName`", default.BuildingName);
	case GDI_LOST:
	case NOD_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", RelatedPRI_1.PlayerName);
		return Repl(str, "`BuildingName`", default.BuildingName);
	}
}
else
{
	switch (Switch)
	{
	case GDI_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", "GDI");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case NOD_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", "Nod");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case GDI_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", "Nod");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case NOD_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", "GDI");
		return Repl(str, "`BuildingName`", default.BuildingName);
	}
}
return "";
}

DefaultProperties
{
MessageClass=class'Rx_Message_TechBuilding'

`GdiCapSound	= SoundNodeWave'RX_EVA_VoiceClips.gdi_eva.S_EVA_GDI_TechBuilding_Captured'
`GdiLostSound	= SoundNodeWave'RX_EVA_VoiceClips.gdi_eva.S_EVA_GDI_TechBuilding_Lost'

`NodCapSound	= SoundNodeWave'RX_EVA_VoiceClips.Nod_EVA.S_EVA_Nod_TechBuilding_Captured'
`NodLostSound	= SoundNodeWave'RX_EVA_VoiceClips.Nod_EVA.S_EVA_Nod_TechBuilding_Lost'

`GdiUnderAttackForGdiSound = SoundNodeWave'RX_EVA_VoiceClips.gdi_eva.S_EVA_GDI_GDISilo_UnderAttack'
`GdiUnderAttackForNodSound = SoundNodeWave'RX_EVA_VoiceClips.Nod_EVA.S_EVA_Nod_GDISilo_UnderAttack'

`NodUnderAttackForGdiSound = SoundNodeWave'RX_EVA_VoiceClips.gdi_eva.S_EVA_GDI_NodSilo_UnderAttack'
`NodUnderAttackForNodSound = SoundNodeWave'RX_EVA_VoiceClips.Nod_EVA.S_EVA_Nod_NodSilo_UnderAttack'

//AttachmentClasses.Remove(Rx_BuildingAttachment_MCT)
//AttachmentClasses.Add(Rx_BuildingAttachment_MCT_TechBuilding)

ReplicatedTeamID=255
FlagTeam=255

UnderAttackAnnouncementCooldown = 15
}

and then this specific piece of code:

static function string GetLocalString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2
)
{
local string str;

if (RelatedPRI_1 != None)
{
	switch (Switch)
	{
	case GDI_CAPTURED:
	case NOD_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", RelatedPRI_1.PlayerName);
		return Repl(str, "`BuildingName`", default.BuildingName);
	case GDI_LOST:
	case NOD_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", RelatedPRI_1.PlayerName);
		return Repl(str, "`BuildingName`", default.BuildingName);
	}
}
else
{
	switch (Switch)
	{
	case GDI_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", "GDI");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case NOD_CAPTURED:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[2], "`PlayerName`", "Nod");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case GDI_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", "Nod");
		return Repl(str, "`BuildingName`", default.BuildingName);
	case NOD_LOST:
		str = Repl(class'Rx_Message_Buildings'.default.BuildingBroadcastMessages[3], "`PlayerName`", "GDI");
		return Repl(str, "`BuildingName`", default.BuildingName);
	}
}
return "";
}

Link to comment
Share on other sites

  • 4 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...