I've just coded some prototype. This just lays down a few Lift* NavigationPoints in PreBeginPlay. I hope they get linked in the end
But I fear it won't work since bNoDelete is true for LiftCenter and LiftExit. Would doing a subclass of those with bNoDelete false work then?
Code: Select all
//=============================================================================
// BotMovers.
//=============================================================================
class BotMovers expands Info;
struct KeyPath
{
var() int KeyBegin;
var() byte KeyOffset;
var() name PathTag;
};
/*
Automating pathing might be still far from being true.
But here is a possible solution for automating LIFT
pathing.
By Gustavo6046
*/
var(Pathing) Vector CenterOffset;
var(Pathing) float WalkOffDistance;
var(Pathing) float WalkHeight;
var(Pathing) int NumPathes;
var(Pathing) KeyPath KeyPathes[8];
var(Pathing) float MaxWalkingPitch;
var(Pathing) bool bDebug;
var(Pathing) byte ExitDirs;
var(Pathing) name TriggerTag;
var(Pathing) bool bRetagMover;
// DegToRot = 182.0444444444444444444
// RotToDeg = 0.0054931640625
final operator(25) float # (Actor A, Actor B)
{
return VSize(Location - A.Location) - VSize(Location - B.Location);
}
function Vector FullOffs()
{
local Vector V;
V = CenterOffset;
V.Z += WalkHeight;
return V;
}
function bool MoverPathable(Mover Other)
{
return Other.bBlockActors;
}
function Mover GetMover()
{
local Mover m, cm;
if ( Tag != '' )
foreach AllActors(class'Mover', m)
if ( m.Tag == Tag && MoverPathable(m) )
return m;
else
{
foreach AllActors(class'Mover', m)
if ( MoverPathable(m) && (cm != None || ((m # cm) < 0)) )
cm = m;
return cm;
}
return None;
}
function Vector MoverLocWithOffs(Mover Other, optional byte MoverKey)
{
local Vector ML;
ML = Other.KeyPos[MoverKey];
return ML + FullOffs();
}
function Vector MyMoverLocWithOffs(optional byte MoverKey)
{
local Vector ML;
ML = GetMover().KeyPos[MoverKey];
return ML + FullOffs();
}
function bool SurfaceWalkable(vector HitNormal)
{
return abs(Rotator(HitNormal).Pitch) / 16384.0 < MaxWalkingPitch;
}
function bool TraceWalk(out Vector HitLoct, out Vector HitNorm, vector Start, float YawDeg) // helper function for other modders
{
local Rotator FinalDir;
local Vector DirOff;
FinalDir.Yaw += YawDeg * 182.044444444444444444444444444;
DirOff = Vector(FinalDir);
DirOff *= WalkOffDistance;
DirOff.Z -= WalkHeight;
return Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None;
}
function bool TraceWalk2(out Vector HitLoct, out Vector HitNorm, vector Start, float YawStep, optional int Steps)
{
local Rotator FinalDir;
local Vector DirOff;
if ( Steps == 0 )
Steps = 8;
FinalDir.Yaw += YawStep * (65536 / Steps);
DirOff = Vector(FinalDir);
DirOff *= WalkOffDistance;
DirOff.Z -= WalkHeight;
return Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None;
}
function ProcessPath(Mover Other, KeyPath Path)
{
local Vector InPos, OutPos, OutNrm;
local LiftCenter LC;
local LiftExit LE;
local byte st;
if ( bRetagMover )
Other.Tag = Path.PathTag;
InPos = MoverLocWithOffs(Other, Path.KeyBegin);
LC = Spawn(class'LiftCenter', self,, InPos);
LC.LiftTag = Path.PathTag;
LC.LiftTrigger = TriggerTag;
LC.bHidden = !bDebug;
for ( st = 0; st < ExitDirs; st++ )
{
TraceWalk2(OutPos, OutNrm, InPos, st, ExitDirs);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight;
LE = Spawn(class'LiftExit', self,, OutPos);
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
InPos = MoverLocWithOffs(Other, Path.KeyBegin);
for ( st = 0; st < ExitDirs; st++ )
{
TraceWalk2(OutPos, OutNrm, InPos, st, ExitDirs);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight;
LE = Spawn(class'LiftExit', self,, OutPos);
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
}
function PathMover()
{
local Mover MyMover;
local int i;
local KeyPath KP;
MyMover = GetMover();
for ( i = 0; i < NumPathes; i++ )
{
ProcessPath(MyMover, KeyPathes[i]);
KP.KeyBegin = KeyPathes[i].KeyOffset;
KP.KeyOffset = KeyPathes[i].KeyBegin;
ProcessPath(MyMover, KP);
}
}
function PreBeginPlay()
{
PathMover();
}
And a subclass which sets the offsets automatically:
Code: Select all
//=============================================================================
// QuickBotMover.
//=============================================================================
class QuickBotMover expands BotMovers;
var(Pathing) bool bResetWalkHeight;
// This class deprecates CenterOffset (and if desired WalkHeight) by using this actor's relative position to the mover instead.
function PreBeginPlay()
{
local Mover MyMover;
local Vector Offset;
MyMover = GetMover();
Offset = Location - MyMover.Location;
if ( bResetWalkHeight )
WalkHeight = Offset.Z;
CenterOffset = Offset;
if ( bResetWalkHeight )
CenterOffset.Z = 0;
Super.PreBeginPlay();
}
I should do sanity checks later, but this is a prototype and I'm testing it
SO yeah, I just speedcoded
EDIT: Seems to work now:
Code: Select all
//=============================================================================
// BotMovers.
//=============================================================================
class BotMovers expands Info;
/*
Automating pathing might be still far from being true.
But here is a possible solution for automating LIFT
pathing.
By Gustavo6046
*/
struct KeyPath
{
var() int KeyBegin;
var() byte KeyOffset;
var() name PathTag;
};
var(Pathing) Vector CenterOffset;
var(Pathing) float WalkOffDistance;
var(Pathing) float WalkHeight;
var(Pathing) int NumPathes;
var(Pathing) KeyPath KeyPathes[8];
var(Pathing) float MaxWalkingPitch;
var(Pathing) bool bDebug;
var(Pathing) byte ExitDirs;
var(Pathing) name TriggerTag;
var(Pathing) bool bRetagMover;
// DegToRot = 182.0444444444444444444
// RotToDeg = 0.0054931640625
final operator(25) float # (Actor A, Actor B)
{
if ( A == None || B == None )
return 0;
return VSize(Location - A.Location) - VSize(Location - B.Location);
}
function Vector FullOffs()
{
local Vector V;
V = CenterOffset;
V.Z += WalkHeight;
return V;
}
function bool MoverPathable(Mover Other)
{
return Other.bBlockActors;
}
function Mover GetMover()
{
local Mover m, cm;
if ( Tag != 'None' )
foreach AllActors(class'Mover', m)
if ( m.Tag == Tag && MoverPathable(m) )
return m;
else
{
foreach AllActors(class'Mover', m)
if ( MoverPathable(m) && (cm != None || ((m # cm) < 0)) )
cm = m;
return cm;
}
return None;
}
function Vector MoverLocWithOffs(Mover Other, optional byte MoverKey)
{
local Vector ML;
ML = Other.KeyPos[MoverKey];
return ML + FullOffs();
}
function Vector MyMoverLocWithOffs(optional byte MoverKey)
{
local Vector ML;
ML = GetMover().KeyPos[MoverKey];
return ML + FullOffs();
}
function bool SurfaceWalkable(vector HitNormal)
{
return abs(Rotator(HitNormal).Pitch) / 16384.0 < MaxWalkingPitch;
}
function bool TraceWalk(out Vector HitLoct, out Vector HitNorm, vector Start, float YawDeg) // helper function for other modders
{
local Rotator FinalDir;
local Vector DirOff;
FinalDir.Yaw += YawDeg * 182.044444444444444444444444444;
DirOff = Vector(FinalDir);
DirOff *= WalkOffDistance;
DirOff.Z -= WalkHeight;
return Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None;
}
function bool TraceWalk2(out Vector HitLoct, out Vector HitNorm, vector Start, float YawStep, optional int Steps)
{
local Rotator FinalDir;
local Vector DirOff;
if ( Steps == 0 )
Steps = 8;
FinalDir.Yaw += YawStep * (65536 / Steps);
DirOff = Vector(FinalDir);
DirOff *= WalkOffDistance;
DirOff.Z -= WalkHeight;
return Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None;
}
function bool ProcessPath(Mover Other, KeyPath Path)
{
local Vector InPos, OutPos, OutNrm;
local LiftCenter LC;
local LiftExit LE;
local byte st;
if ( bRetagMover )
Other.Tag = Path.PathTag;
InPos = MoverLocWithOffs(Other, Path.KeyBegin);
LC = Spawn(class'AutoLiftCenter', self,, InPos);
if ( LC == None )
return False;
LC.LiftTag = Path.PathTag;
LC.LiftTrigger = TriggerTag;
LC.bHidden = !bDebug;
for ( st = 0; st < ExitDirs; st++ )
{
TraceWalk2(OutPos, OutNrm, InPos, st, ExitDirs);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight;
LE = Spawn(class'LiftExit', self,, OutPos);
if ( LE == None )
return False;
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
InPos = MoverLocWithOffs(Other, Path.KeyBegin);
for ( st = 0; st < ExitDirs; st++ )
{
TraceWalk2(OutPos, OutNrm, InPos, st, ExitDirs);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight;
LE = Spawn(class'LiftExit', self,, OutPos);
if ( LE == None )
return False;
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
return True;
}
function PathMover()
{
local Mover MyMover;
local int i;
local KeyPath KP;
MyMover = GetMover();
for ( i = 0; i < NumPathes; i++ )
{
ProcessPath(MyMover, KeyPathes[i]);
KP.KeyBegin = KeyPathes[i].KeyOffset;
KP.KeyOffset = KeyPathes[i].KeyBegin;
ProcessPath(MyMover, KP);
}
}
function PreBeginPlay()
{
if ( GetMover() == None )
return;
PathMover();
}
And AutoLiftCenter is just an empty LiftCenter subclass with like no changes other than bNoDelete = false.
I could also do class'LiftCenter'.default.bNoDelete = false; before and class'LiftCenter'.default.bNoDelete = true; after. But that would be hackish
EDIT: Damnit, I didn't see LiftExit bStatic! I'm doing such an awful job :C
EDIT: I just did this, but those LiftExit's just won't spawn in the ground where the lifts start! >.<
Code: Select all
//=============================================================================
// BotMovers.
//=============================================================================
class BotMovers expands Info;
/*
Automating pathing might be still far from being true.
But here is a possible solution for automating LIFT
pathing.
By Gustavo6046
*/
struct KeyPath
{
var() int KeyBegin;
var() byte KeyOffset;
var() name PathTag;
var() float InDistance;
var() float OutDistance;
};
var(Pathing) Vector CenterOffset;
var(Pathing) float WalkHeight;
var(Pathing) int NumPathes;
var(Pathing) KeyPath KeyPathes[8];
var(Pathing) float MaxWalkingPitch;
var(Pathing) bool bDebug;
var(Pathing) byte ExitDirs;
var(Pathing) name TriggerTag;
var(Pathing) bool bRetagMover;
var(Pathing) float MoverHeight;
// DegToRot = 182.0444444444444444444
// RotToDeg = 0.0054931640625
function CheckLog(string s)
{
if ( bDebug )
Log(s);
}
final operator(25) float # (Actor A, Actor B)
{
if ( A == None || B == None )
return 0;
return VSize(Location - A.Location) - VSize(Location - B.Location);
}
function Vector FullOffs(optional bool bAddHeight)
{
local Vector V;
V = CenterOffset;
V.Z += WalkHeight;
if ( bAddHeight )
V.Z += MoverHeight;
return V;
}
function bool MoverPathable(Mover Other)
{
return Other.bBlockActors;
}
function Mover GetMover()
{
local Mover m, cm;
foreach AllActors(class'Mover', m, Tag)
if ( MoverPathable(m) && (cm == None || ((m # cm) < 0)) )
cm = m;
return cm;
}
function Vector MoverLocWithOffs(Mover Other, optional byte MoverKey, optional bool bAddHeight)
{
local Vector ML;
ML = Other.KeyPos[MoverKey];
return ML + Other.Location + FullOffs(bAddHeight);
}
function Vector MyMoverLocWithOffs(optional byte MoverKey, optional bool bAddHeight)
{
local Vector ML;
local Mover M;
M = GetMover();
ML = M.KeyPos[MoverKey];
return ML + M.Location + FullOffs(bAddHeight);
}
function bool SurfaceWalkable(vector HitNormal)
{
return abs(Rotator(HitNormal).Pitch) / 16384.0 < MaxWalkingPitch;
}
function bool TraceWalk(out Vector HitLoct, out Vector HitNorm, vector Start, float WalkDistance, float YawDeg) // helper function for other modders
{
local Rotator FinalDir;
local Vector DirOff;
FinalDir.Yaw += YawDeg * 182.044444444444444444444444444;
DirOff = Vector(FinalDir);
DirOff *= WalkDistance;
DirOff.Z -= WalkHeight * 2 + MoverHeight;
return Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None;
}
function bool TraceWalk2(out Vector HitLoct, out Vector HitNorm, vector Start, float WalkDistance, float YawStep, optional int Steps)
{
local Rotator FinalDir;
local Vector DirOff;
local bool b;
if ( Steps == 0 )
Steps = 8;
FinalDir.Yaw = YawStep * (65536 / Steps);
DirOff = Vector(FinalDir);
DirOff.X *= WalkDistance;
DirOff.Y *= WalkDistance;
DirOff.Z -= WalkHeight * 2 + MoverHeight;
b = Trace(HitLoct, HitNorm, Start + DirOff, Start, false) != None || !FastTrace(Start + DirOff, Start);
CheckLog(Start@+@DirOff@=@Start + DirOff@o@FinalDir.Yaw@^@Rotator(HitNorm).Pitch@->@HitLoct@==@b);
return b;
}
function ProcessPath(Mover Other, KeyPath Path)
{
local Vector InPos, OutPos, OutNrm, _, v1, v2;
local LiftCenter LC;
local LiftExit LE;
local byte st;
if ( bRetagMover )
Other.Tag = Path.PathTag;
InPos = MoverLocWithOffs(Other, Path.KeyBegin, true);
LC = Spawn(class'AutoLiftCenter', self,, InPos);
LogActor(LC);
if ( LC == None )
return;
LC.LiftTag = Path.PathTag;
LC.LiftTrigger = TriggerTag;
LC.bHidden = !bDebug;
for ( st = 0; st < ExitDirs; st++ )
{
InPos = MoverLocWithOffs(Other, Path.KeyBegin, false);
if ( !TraceWalk2(OutPos, OutNrm, InPos, Path.InDistance, st, ExitDirs) )
continue;
v1 = OutPos;
v1.z -= 24;
v2 = OutPos;
v2.z += WalkHeight;
Trace(_, OutNrm, v1, v2, false);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight * 2 + class'AutoLiftExit'.default.CollisionHeight;
LE = Spawn(class'AutoLiftExit', self,, OutPos);
LogActor(LE);
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
InPos = MoverLocWithOffs(Other, Path.KeyBegin + Path.KeyOffset, true);
LC = Spawn(class'AutoLiftCenter', self,, InPos);
LogActor(LC);
if ( LC == None )
return;
LC.LiftTag = Path.PathTag;
LC.LiftTrigger = TriggerTag;
LC.bHidden = !bDebug;
for ( st = 0; st < ExitDirs; st++ )
{
InPos = MoverLocWithOffs(Other, Path.KeyBegin + Path.KeyOffset, false);
if ( !TraceWalk2(OutPos, OutNrm, InPos, Path.OutDistance, st, ExitDirs) )
continue;
v1 = OutPos;
v1.z -= 24;
v2 = OutPos;
v2.z += WalkHeight;
Trace(_, OutNrm, v1, v2, false);
if ( !SurfaceWalkable(OutNrm) )
continue;
OutPos.Z += WalkHeight * 2 + class'AutoLiftExit'.default.CollisionHeight;
LE = Spawn(class'AutoLiftExit', self,, OutPos);
LogActor(LE);
LE.LiftTag = Path.PathTag;
LE.LiftTrigger = TriggerTag;
LE.bHidden = !bDebug;
}
}
function string LogActor(Actor Other)
{
local string res;
if ( Other == None )
return "";
res = "<"$Other.Class.Name@'$Other$"' at x="$Other.Location.X$",y="$Other.Location.Y$",z="$Other.Location.Z$">";
CheckLog(res);
return res;
}
function PathMover()
{
local Mover MyMover;
local int i;
local KeyPath KP;
MyMover = GetMover();
for ( i = 0; i < NumPathes; i++ )
{
ProcessPath(MyMover, KeyPathes[i]);
}
}
function PreBeginPlay()
{
if ( GetMover() == None )
return;
PathMover();
}