The three virtues of a programmer: Laziness, Impatience, and Hubris. – Larry Wall
Legacy:CaptureVolume
Contents
Linking Volumes and Control Points[edit]
Or, how I learned to stop worrying and love my volume!
Goes without saying, but if you sample any of this code please give me credit somewhere in your script. Also, note that I'm not the cleanest coder in the world, so be sure to check and double check! Share the love baby!
Props go to the Adster. Visit his page @ http://www.speakerprojects.com/index.html and tell him he's sexy.
The origin of this page comes from a project I'm currently working. For the mod, it was necessary to track player position and then have an icon fade in and out on the hud. I'm not going to go into the fades right now since that's a whole monster to itself. What I'm going to focus on is how to link a control point to a capture volume and, using the two, get it to display icons onto the HUD.
Writing The Code[edit]
You need to create some new UnrealScript classes. You can either:
- Create a subclass in UnrealEd for each of the following scripts,
- or create a package containing all of these classes.
Note: Using UnrealEd is not recommended. Instead, use any of the suggested text editors.
CaptureVolume[edit]
The first thing you want to do is create a subclass of the volume class. The volume will be used to track when a player enters (or leaves) a given space.
/* Extended Volume Class Graham Ross 9/21/04 An extended volume class to help display the names of rooms on the HUD */ //Graham Ross (thanks Adster!!!) //An interactive volume to trigger elements on the HUD // 10/13/04 //============================================================================= class captureVolume extends PhysicsVolume; var( ) Int captureVolumeNumber; var CapturePoint ourCapturePoint; var int volumeNumber; var bool inVolume; simulated function PostBeginPlay( ) { local NavigationPoint nP; Super.PostBeginPlay( ); for( nP = Level.NavigationPointList; nP != None; nP = nP.NextNavigationPoint ) { if( !nP.IsA( 'CapturePoint' ) ) continue; if( CapturePoint( nP ).capturePointNumber == captureVolumeNumber ) { ourCapturePoint = CapturePoint( nP ); ourCapturePoint.SetMaster( self ); break; } } } event PawnEnteredVolume( Pawn other ) { if( ( other.IsPlayerPawn() ) && ( ourCapturePoint != None ) && (other.PlayerReplicationInfo != none)) //is the problem that the capt. point is none? { if( customPawn( other ) != none ) rscPRI(other.PlayerReplicationInfo).cVolumeNumber = captureVolumeNumber; ourCapturePoint.Touched( other); } } event PawnLeavingVolume( Pawn other ) { if(( other != none )&& ( ourCapturePoint != None )&& (other.PlayerReplicationInfo != none)) { if( customPawn( other ) != none ) rscPRI(other.PlayerReplicationInfo).cVolumeNumber = 0; ourCapturePoint.Untouched( other); } } defaultproperties { captureVolumeNumber=255 volumeNumber = -1; }
So you'll notice there are a few things I'm keeping track of. First, in the PostBeginPlay function, I'm cycling through all the NavigationPoints in the level. If it is a CapturePoint (or a subclass of it), I proceed forward and match up the number of capture points to the number of the volume, two attributes I've manually set from within the level. You'll also notice I have two checks for whether the pawn enters or leaves a volume. This is the most important part of the code. When a player enters the volume, it calls the Touched and Untouched functions of the capture point. We'll get into that part shortly.
Note: I've set captureVolumeNumber and Volume number to 255 and -1, two out of range types.
CapturePoint[edit]
Now we move on to creating a custom CapturePoint for our new CaptureVolumes to link to.
// Graham Ross (Thanks Adster!!!) // class for altering the function of GameObjective. // //============================================================================= class CapturePoint extends GameObjective Placeable; var( ) localized String captureName; var( ) Name captureEvent; var( ) Int capturePointNumber; var CaptureVolume captureVolume; simulated function PostBeginPlay( ) { Super.PostBeginPlay( ); } function SetMaster( CaptureVolume ourCaptureVolume ) { if( ourCaptureVolume != None ) captureVolume = ourCaptureVolume; } function Touched( Pawn other) { } function Untouched( Pawn other) { } function ResetPoint( Bool bEnabled ) { } defaultproperties { bHidden=False capturePointNumber=-1 captureName="A Capture Point" bUseCylinderCollision=True RemoteRole=ROLE_SimulatedProxy bAlwaysRelevant=True bStasis=False bStatic=False bNoDelete=True bNetNotify=True CollisionRadius=60.000000 CollisionHeight=40.000000 DrawScale=0.60000 bCollideActors=True bBlockActors=False bBlockPlayers=False NetUpdateFrequency=8 ObjectiveName="Capture Point" }
You'll notice there's not much there. Basically the only thing I want is the touched and untouched function. This will allow me to interact easily with the HUD and draw upon it. I personally wasn't able to find a way to interact solely using the volume and the HUD. It's also helpful to note that the triggering is not done by the CapturePoint, but done by the CaptureVolume which then interacts with their specified CapturePoint.
Note: You must create a custom Player Replication Info for your player to hold a single variable that will track the volume number.
Custom PlayerReplicationInfo[edit]
// Graham Ross // 10/13/04 // // Custom PRI to use with my volume and my hud class rscPRI extends xPlayerReplicationInfo; var int cVolumeNumber; defaultproperties { cVolumeNumber = 0 }
The last bit of code will do the drawing on the hud for us. I'm using a HUD interaction here, in my opinion the best way to draw on the HUD.
At the top, declare something like this:
var enum volumeNo { VN_None, VN_Bal1, VN_Bal2 } VNStatus;
Later, do something like this...
simulated function checkGlowVariable(canvas c) { switch(rscPRI(pOwner.PlayerReplicationInfo).cVolumeNumber) { case 0: VNStatus = VN_None; fadeout = true; break; case 1: VNStatus = VN_Bal1; fadeout = true; break; case 2: VNStatus = VN_Bal2; fadeout = true; break; default: } }
In the DrawHud function (or separate function that will be called) put in the following code:
simulated function checkGlowInfo() { local byte tempStyle; local color tempColor; glowMoveX = shiftX; glowMoveY = ShiftY; tempStyle = Cv.Style; tempcolor = Cv.DrawColor; cv.SetDrawColor(255, 255, 255, 0); cv.SetPos((cv.ClipX - xpos * HUDscaleX), (cv.ClipY - ypos * HUDScaley)); cv.DrawIcon( LastRoom, sizeIcon * (hudScaleX + hudScaleY)); cv.SetDrawColor(255, 255, 255, fade); cv.SetPos((cv.ClipX - xpos * HUDscaleX), (cv.ClipY - ypos * HUDScaley)); cv.DrawIcon( pLastRoom, sizeIcon * (hudScaleX + hudScaleY)); cv.SetDrawColor(255, 255, 255, mapOp); cv.SetPos((cv.CLipX - (xGlow-glowMoveX) * HUDScaleX), (cv.ClipY - (yGlow-glowMoveY) * HUDScaleY)); cv.DrawIcon( pLastGlow, sizeGlow * (hudScaleX + hudScaleY)); switch (VNStatus) { case VN_None: resetfade(); break; case VN_Bal1: plastRoom = blank; lastVolume = 12; pLastGlow = Bal1_g; ShiftX = 5; ShiftY = 0; break; case VN_Bal2: plastRoom = blank; lastVolume = 13; pLastGlow = Bal2_g; ShiftX = -5; ShiftY = 0; break; default: pLastRoom = blank ; pLastGlow = Hall_g ; resetfade(); break; } }
Please note that the reason I'm doing it this way is becuase of my fades. I faked my fades by having them come in on top of each other, so I needed to track the last icon that came up, then draw it and have it stay to give the appearance of a cross-fade. You could easily track player position and then have it just pop instantly onto the screen...
Linking Through UnrealEd[edit]
Okay, hopefully you can just hand this off to a level designer and say "Link the numbers slave of GUI!". But for those less fortunate of us, here's how you link the two together.
-
- If you've created a package, after you've compiled all your scripts, go to the actor browser and load the package under the actor classes tab. This will bring your scripts into UEd and allow you to access them and place them on your level.
- If you've created classes within UnrealEd, they should already be in the actor browser once you compile in he Script Editor window.
- Add an Actor >> NavigationPoint >> JumpDest >> GameObjective >> CapturePoint.
- Add a Volume of the new class, CaptureVolume.
- Now comes the tricky part. Select your capturepoint. Open the actor ptoperites window and set the capturePointNum to a valid number, like 1. The, go under the events rollout and give it a tag, like MED_CP or something.
- Select the corresponding volume. Give the volume the name number as the capture point (1) and then under the events rollout set the event name to the tag of the capture point (MED_CP). This will effectively link the two. Here's a brief overview on how to better match these tags
That's about it. If you have problems, make sure your tags match between the two. Also, another trick I've found is to set the draw coords to somehting like 500 by 500, so you can make sure it's actually drawing and not getting covered by something.
Confused about what 'trigger' means? Here's a good definition of what triggered means.