A double purpose code for teleporters

Discussions about Coding and Scripting
Post Reply
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

A double purpose code for teleporters

Post by sektor2111 »

Some people don't know what trash is a disabled teleporter - still pointing routes through, others might know this.
What I mean here ?
While I was working at some patch file tweaking/creating navigation in some map, I could see a sort of spot working like a bOneWay for some reason, perhaps BSP problem, to hell with it. I went to another solution by using a teleporter from said last spot in order to gain next objectives (MH in stage) because all Pawn hunter was stuck in the area with no way back - strange crap.
What is with teleporter then ?
Perhaps a new teleporter will have bSpecialCost being locked if is not enabled and having no handling from any way. If teleporter is enabled it should follow "RULEZ" for bPlayerOnly rather than teleporting everything touched... Engine has no deal here with it's own properties - LOL.
A Probing Code

Code: Select all

event int SpecialCost(Pawn Seeker)
{
	if ( !bEnabled )
		return 100000000;
	else
	{
		if ( bPlayerOnly ) //I don't know if these aren't already natively handled, but... it would be nice to make sure about.
		{
			if ( Seeker.bIsPlayer )
				return 0;
			else
				return 100000000;
		}
		else
			return 0;
	}
	return 100000000; //for any sudden exception above - should not happen
}

event Actor SpecialHandling(Pawn Other)
{
	local int i;
	local vector Dist2D;

// This is called when pawn is about to come here directly - reachable so to speak
// When a teleporter is not active it should work as a blocked path...
	if ( !bEnabled )
		return None; //or Return a Moron type coding...

	if ( bEnabled && (Other.RouteCache[1] != None)
		&& Other.RouteCache[1].IsA('Teleporter') && (string(Other.RouteCache[1].tag)~=URL) )
	{
		if ( Abs(Location.Z - Other.Location.Z) < CollisionHeight + Other.CollisionHeight )
		{
			Dist2D = Location - Other.Location;
			Dist2D.Z = 0;
			if ( VSize(Dist2D) < CollisionRadius + Other.CollisionRadius )
				Touch(Other);
		}
		return self;
	}

	if (TriggerActor == None)
	{
		FindTriggerActor();
		if (TriggerActor == None)
			return None;
	}

	if ( (TriggerActor2 != None) 
		&& (VSize(TriggerActor2.Location - Other.Location) < VSize(TriggerActor.Location - Other.Location)) )
		return TriggerActor2;

	return TriggerActor;
}

function Touch( actor Other )
{
	local Teleporter Dest;
	local int i;
	local Actor A;

	if ( bEnabled )
	{
		if ( bPlayerOnly && Pawn(Other) != None && Pawn(Other).Health > 0 && !Pawn(Other).bIsPlayer )
			return;
		if ( Pawn(Other) != None && Pawn(Other).Health > 0 ) //No bullshit here
		if( Other.bCanTeleport && Other.PreTeleport(Self)==false )
		{
			if( (InStr( URL, "/" ) >= 0) || (InStr( URL, "#" ) >= 0) )
			{
				// Teleport to a level on the net.
				if( (Role == ROLE_Authority) && (PlayerPawn(Other) != None) )
					Level.Game.SendPlayer(PlayerPawn(Other), URL);
			}
			else
			{
				// Teleport to a random teleporter in this local level, if more than one pick random.

				foreach AllActors( class 'Teleporter', Dest )
					if( string(Dest.tag)~=URL && Dest!=Self )
						i++;
				i = rand(i);
				foreach AllActors( class 'Teleporter', Dest )
					if( string(Dest.tag)~=URL && Dest!=Self && i-- == 0 )
						break;
				if( Dest != None )
				{
					// Teleport the actor into the other teleporter.
					if ( Other.IsA('Pawn') )
						PlayTeleportEffect( Pawn(Other), false);
					Dest.Accept( Other, self );
					if( (Event != '') && (Other.IsA('Pawn')) )
						foreach AllActors( class 'Actor', A, Event )
							A.Trigger( Other, Other.Instigator );
				}
				else if ( Role == ROLE_Authority )
					Pawn(Other).ClientMessage( "Teleport destination for "$self$" not found!" );
			}
		}
	}
}
I'm going to track what does this do because probably a mapping version is good as a portable tweaking version too, and then I will inform you if this is a good idea or not. Until that moment I will figure what you think, in here.
To add SpecialHandling for NonPlayer deal ? Is that even needed ? Monsters are morons after all but I'll try to catch if they are causing issues. Definitely monster from area is not allowed to roam out of guarding spot.
Code is not compatible to be conformed with stock because of new function needed and forgotten by Epic. Using a child Teleporter might be much recommended.
Function touch is not simulated here because player has no purpose to mess touching based on prediction specific to On-Line latency. Player location it's decided by server and NOT by client.
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: A double purpose code for teleporters

Post by sektor2111 »

Said teleporter went to some needs if it's getting combined with a kicker for preventing borks when Bot decides to camp. It should get kicked away. In other cases, Bot will attempt to go at this teleporter if is done with way back and... looping like an ass through teleporters. Solution is simple screwing another function as follows:
function Accept

Code: Select all

simulated function bool Accept( actor Incoming, Actor Source )
{
	local rotator newRot, oldRot;
	local int oldYaw;
	local float mag;
	local vector oldDir;
	local pawn P;

	// Move the actor here.
	Disable('Touch');
	//log("Move Actor here "$tag);
	newRot = Incoming.Rotation;
	if (bChangesYaw)
	{
		oldRot = Incoming.Rotation;
		newRot.Yaw = Rotation.Yaw;
		if ( Source != None )
			newRot.Yaw += (32768 + Incoming.Rotation.Yaw - Source.Rotation.Yaw);
	}

	if ( Pawn(Incoming) != None )
	{
		//tell enemies about teleport
		if ( Role == ROLE_Authority )
		{
			P = Level.PawnList;
			While ( P != None )
			{
				if (P.Enemy == Incoming)
					P.LastSeenPos = Incoming.Location; 
				P = P.nextPawn;
			}
		}
		Pawn(Incoming).SetLocation(Location);
		if ( (Role == ROLE_Authority) 
			|| (Level.TimeSeconds - LastFired > 0.5) )
		{
			Pawn(Incoming).SetRotation(newRot);
			Pawn(Incoming).ViewRotation = newRot;
			LastFired = Level.TimeSeconds;
		}
		Pawn(Incoming).MoveTimer = -1.0;
		if (Pawn(Incoming).RouteCache[2] != None
		 && FastTrace(Self.Location,Pawn(Incoming).RouteCache[2].Location) )
			Pawn(Incoming).MoveTarget = Pawn(Incoming).RouteCache[2];
		else if (Pawn(Incoming).RouteCache[1] != None 
		 && FastTrace(Self.Location,Pawn(Incoming).RouteCache[1].Location))
			Pawn(Incoming).MoveTarget = Pawn(Incoming).RouteCache[1];
		else
			Pawn(Incoming).MoveTarget = self;
		PlayTeleportEffect( Incoming, false);
	}
	else
	{
		if ( !Incoming.SetLocation(Location) )
		{
			Enable('Touch');
			return false;
		}
		if ( bChangesYaw )
			Incoming.SetRotation(newRot);
	}

	Enable('Touch');

	
	if (bChangesVelocity)
		Incoming.Velocity = TargetVelocity;
	else
	{
		if ( bChangesYaw )
		{
			if ( Incoming.Physics == PHYS_Walking )
				OldRot.Pitch = 0;
			oldDir = vector(OldRot);
			mag = Incoming.Velocity Dot oldDir;		
			Incoming.Velocity = Incoming.Velocity - mag * oldDir + mag * vector(Incoming.Rotation);
		} 
		if ( bReversesX )
			Incoming.Velocity.X *= -1.0;
		if ( bReversesY )
			Incoming.Velocity.Y *= -1.0;
		if ( bReversesZ )
			Incoming.Velocity.Z *= -1.0;
	}	

	// Play teleport-in effect.

	return true;
}
Explained changes: Pawn is sent to a traceable RouteCache[2] as long [1] is teleporter kicking pawn away, before changing its destination. Else 1 can be different if PickDestination was already called or kicker too wick... so it's about probing if these are possible targets and NOT teleporter bringing pawn in area. Pawn is allowed to fallback when it needs and not looping. Kicker has to be smaller for reacting only when pawn is coming through and not when it's about to use teleporter. Perhaps next version will be a mix: teleporter-kicker attempting to prevent those collision crashes as much as possible.
Post Reply