Not sure if this will help - This code doesn't quite match what you are doing (it's written using a status bar to draw on screen instead of RenderOverlay), and I think this is more complicated than what you are wanting, but...
I created a custom player class that uses a ThinkerIterator to find all of the actors in range that meet certain criteria, then pushes those actors to an array. Then the status bar reads that array's contents from the player and does some sketchy HudMessageOnActor-derived math to calculate screen coordinates from the actor's world coordinates, and handles drawing a frame around the actor on screen.
.
ZScript here... It's a bit messy, and needs some optimization. But it works.
Code: Select all
version "2.5"
struct Coords
{
double x;
double y;
double left;
double right;
double top;
double bottom;
}
class TestPlayer : DoomPlayer
{
Array<Actor> ScannableThings;
override void Tick()
{
int Range = 1024;
int index;
ScannableThings.Clear();
ThinkerIterator Finder = ThinkerIterator.Create("Actor");
Actor mo;
while ( mo = Actor(Finder.Next()) )
{
target = mo;
if (
mo == self ||
mo.health <= 0 ||
mo.bDormant ||
mo.bInvisible ||
(mo.bNoBlockMap && !mo.bMissile) ||
Distance3d(mo) > Range ||
!IsVisible(mo, false)
) { continue; }
if (ScannableThings.Find(mo) == ScannableThings.Size())
{
ScannableThings.Push(mo);
}
}
Super.Tick();
}
}
class TestStatusBar : DoomStatusBar
{
double ScreenHeight, ScreenWidth, FOVfactor;
Vector2 hudscale;
int hudstate;
HUDFont mSmallFont;
override void Init()
{
Super.Init();
SetSize(32, 320, 200);
Font fnt = "SMALLFONT";
mSmallFont = HUDFont.Create(fnt, 1);
}
override void NewGame ()
{
Super.NewGame();
}
override void Tick()
{
Super.Tick();
}
override void Draw (int state, double TicFrac)
{
Super.Draw (state, TicFrac);
hudstate = state;
BeginHUD(1, False); // Urgh... Force overlays to always draw with fullscreen coords and under other bars.
DrawOverlays();
BeginHUD(1, True);
if (state == HUD_StatusBar)
{
BeginStatusBar(False);
DrawMainBar(TicFrac);
}
else if (state == HUD_Fullscreen)
{
BeginHUD(1, False);
DrawFullScreenStuff();
}
}
void DrawOverlays()
{
bool draw;
Coords ScreenCoords;
hudscale = GetHUDScale();
FOVFactor = CPlayer.FOV / 90;
ScreenHeight = double(Screen.GetHeight()) / hudscale.y;
ScreenWidth = ScreenHeight * 1.333; // Use standard ratio for handling offsets
for (int i = 0; i < TestPlayer(CPlayer.mo).ScannableThings.Size(); i++)
{
actor mo = TestPlayer(CPlayer.mo).ScannableThings[i];
[draw, ScreenCoords.x, ScreenCoords.y, ScreenCoords.left, ScreenCoords.right, ScreenCoords.top, ScreenCoords.bottom] = GetTargetScreenCoords(CPlayer.mo, mo, 0);
if (draw)
{
DrawFrame(CPlayer.mo, mo, ScreenCoords);
}
}
}
void DrawFrame(Actor origin, Actor mo, Coords ScreenCoords)
{
if (!origin || !mo) { return; }
if (mo.health <= 0) { return; }
if (origin.Distance3d(mo) - origin.radius - mo.radius < 32) { return; }
double drawscale = ((ScreenHeight / 2) / origin.Distance3d(mo)) / FOVFactor;
String BaseImage = "Scan";
readonly<Actor> defmobj = GetDefaultByType(mo.GetClass());
mo.bMissile |= defmobj.bMissile;
if ((mo.bIsMonster && !mo.bFriendly) || mo.bShootable || mo.bMissile) { BaseImage = "ScanA"; }
else if (mo is "Inventory") { BaseImage = "ScanB"; }
double alpha = drawscale * drawscale * drawscale * 2;
DrawString(mSmallFont, "+", (ScreenCoords.x, ScreenCoords.y), DI_ITEM_CENTER, Font.CR_PURPLE, 1.0);
DrawImage(BaseImage .. "_TL", (ScreenCoords.left, ScreenCoords.top), DI_ITEM_RIGHT_BOTTOM, alpha, (-1, -1));
DrawImage(BaseImage .. "_TR", (ScreenCoords.right, ScreenCoords.top), DI_ITEM_LEFT_BOTTOM, alpha, (-1, -1));
DrawImage(BaseImage .. "_BL", (ScreenCoords.left, ScreenCoords.bottom), DI_ITEM_RIGHT_TOP, alpha, (-1, -1));
DrawImage(BaseImage .. "_BR", (ScreenCoords.right, ScreenCoords.bottom), DI_ITEM_LEFT_TOP, alpha, (-1, -1));
}
bool, double, double, double, double, double, double GetTargetScreenCoords(Actor origin, Actor target, double zOffset = 0)
{
if (!origin || !target) { return false, 0, 0, 0, 0, 0, 0; }
double x, y, left, right, top, bottom;
double angle = origin.AngleTo(target) - origin.angle;
double dist = origin.Distance2d(target);
if (abs(origin.pitch) <= 50)
{
double radius = target.radius;
double height = target.height > 32 ? target.height : 32;
if (target.bMissile) { height = 0; }
if (zOffset == 0) { zOffset = height / 2; }
double viewpitch = VectorAngle(dist, target.pos.z + zOffset - (origin.pos.z + origin.player.viewheight));
viewpitch = viewpitch + origin.pitch;
viewpitch *= 1.5 / FOVFactor;
if ((ScreenWidth / 2) * sin(angle) != 0 && cos(angle) != 0 && (ScreenWidth / 2) * sin(viewpitch) != 0 && cos(viewpitch) != 0)
{
double xscale = ((ScreenWidth / 2) / origin.Distance3d(target)) / FOVFactor;
double yscale = ((ScreenHeight / 2) / origin.Distance3d(target)) / FOVFactor;
x = (Screen.GetWidth() / hudscale.x) / 2 - ((ScreenWidth / 2) * sin(angle) / cos(angle)) / FOVFactor;
y = ScreenHeight / 2 - ((ScreenHeight / 2) * sin(viewpitch) / cos(viewpitch));
if (screenblocks <= 10) { y -= RelTop; }
left = x - radius * xscale;
right = x + radius * xscale;
top = y - (height / 2 + 8) * yscale;
bottom = y + (height / 2 + 8) * yscale;
x = clamp(x, 0, Screen.GetWidth());
y = clamp(y, 0, Screen.GetHeight());
left = clamp(left, 0, Screen.GetWidth());
right = clamp(right, 0, Screen.GetWidth());
top = clamp(top, 0, Screen.GetHeight());
bottom = clamp(bottom, 0, Screen.GetHeight());
return true, x, y, left, right, top, bottom;
}
}
return false, x, y, left, right, top, bottom;
}
}