Skip to content

Instantly share code, notes, and snippets.

@SeriousBuggie
Last active September 24, 2023 17:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SeriousBuggie/eb1533b0b7a4d46046f0bd65a5b1909f to your computer and use it in GitHub Desktop.
Save SeriousBuggie/eb1533b0b7a4d46046f0bd65a5b1909f to your computer and use it in GitHub Desktop.
Useful helper functions for Uscript for UT99

Get screen X Y coordinates for specified Location:

simulated function float getXY(Canvas C, vector location, out int screenX, out int screenY) {
	local vector X, Y, Z, CamLoc, TargetDir, Dir, XY;
	local rotator CamRot;
	local Actor Camera;
	local float TanFOVx, TanFOVy;
	local float ret;

	C.ViewPort.Actor.PlayerCalcView(Camera, CamLoc, CamRot);

	TanFOVx = Tan(C.ViewPort.Actor.FOVAngle / 114.591559); // 360/Pi = 114.5915590...
	TanFOVy = (C.ClipY / C.ClipX) * TanFOVx;
	GetAxes(CamRot, X, Y, Z);

	TargetDir = Location - CamLoc;

	ret = X dot TargetDir;

	if (ret > 0) {
		Dir = X * (X dot TargetDir);
		XY = TargetDir - Dir;

		screenX = C.ClipX * 0.5 * (1.0 + (XY dot Y) / (VSize(Dir) * TanFOVx));
		screenY = C.ClipY * 0.5 * (1.0 - (XY dot Z) / (VSize(Dir) * TanFOVy));
	}

	return ret;
}

if return value (which is projection of vector from camera to desired point to line of view) less or equal zero, then point behind camera and coordinates can not set.

Draw text with outline around it:

simulated function DrawTextClipped(Canvas C, int X, int Y, string text, Color outline) {
	local Color old;

	old = C.DrawColor;
	C.DrawColor = outline;

	C.SetPos(X - 1, Y - 1);
	C.DrawTextClipped(text, False);
	C.SetPos(X + 1, Y + 1);
	C.DrawTextClipped(text, False);
	C.SetPos(X - 1, Y + 1);
	C.DrawTextClipped(text, False);
	C.SetPos(X + 1, Y - 1);
	C.DrawTextClipped(text, False);

	C.DrawColor = old;
	C.SetPos(X, Y);
	C.DrawTextClipped(text, False);
}

Draw line between two points on screen with one pixel texture:

Slow! Don't use often. Better use another approach.

simulated function DrawLine(Canvas Canvas, int x1, int y1, int x2, int y2) {
	const PenTex = Texture'UWindow.WhiteTexture';
	local int i, j, n, dx, dy;
	if (x1 == 0 && y1 == 0) return;
	if (x2 == 0 && y2 == 0) return;
	dx = x2 - x1;
	dy = y2 - y1;
	if (Abs(dx) > Abs(dy)) {		
		if (dx == 0) return;
		if (dx > 0) j = 1; else j = -1;
		n = Min(Canvas.ClipX, Max(0, x2));
		i = Min(Canvas.ClipX, Max(0, x1));
		if (i != n)
			do {
				canvas.CurX = i;
				canvas.CurY = y1 + dy*(i - x1)/dx;
				canvas.DrawTile(PenTex, 1, 1, 0, 0, PenTex.USize, PenTex.VSize);
				i += j;
			} until (i == n);
	} else {
		if (dy == 0) return;
		if (dy > 0) j = 1; else j = -1;
		n = Min(Canvas.ClipY, Max(0, y2));
		i = Min(Canvas.ClipY, Max(0, y1));
		if (i != n)
			do {
				canvas.CurX = x1 + dx*(i - y1)/dy;
				canvas.CurY = i;
				canvas.DrawTile(PenTex, 1, 1, 0, 0, PenTex.USize, PenTex.VSize);
				i += j;
			} until (i == n);
	}
}

Get value from Actor (work with network play):

simulated function string getVal(Actor actor, string prop) {
	local string ret;

	Root.Console.ViewPort.Actor.MyHUD.Role = actor.Role; // hack for avoid Enum set issues, here always ROLE_Authority before that
	actor.Role = actor.ENetRole.ROLE_Authority;
	ret = actor.GetPropertyText(prop);
	actor.Role = Root.Console.ViewPort.Actor.MyHUD.Role;
	Root.Console.ViewPort.Actor.MyHUD.Role = actor.ENetRole.ROLE_Authority;
	return ret;
}

Quick Sort implementation

Taken from http://www.unreal.ut-files.com/3DEditing/Tutorials/unrealwiki-offline/quicksort.html

And optimized for usage with CoopTranslocator but can be easly modified for your needs:

// http://www.unreal.ut-files.com/3DEditing/Tutorials/unrealwiki-offline/quicksort.html
static Function SortPlayers(CoopTranslocator trans, Int Low, Int High) { //Sortage
//  low is the lower index, high is the upper index
//  of the region of array a that is to be sorted
	local Int i,j;
	local String x;
	Local Pawn tmp;

	i = Low;
	j = High;
	x = trans.player[(Low+High)/2].PlayerReplicationInfo.PlayerName;

	do { //  partition
		while (trans.player[i].PlayerReplicationInfo.PlayerName < x) i += 1; 
		while (trans.player[j].PlayerReplicationInfo.PlayerName > x) j -= 1;
		if (i <= j) {
			tmp = trans.player[j];
			trans.player[j] = trans.player[i];
			trans.player[i] = tmp;
			
			i += 1; 
			j -= 1;
		}
	} until (i > j);

	//  recursion
	if (low < j) SortPlayers(trans, low, j);
	if (i < high) SortPlayers(trans, i, high);
}

Replace first occurrence of substring with another substring. Useful for make placeholders in messages.

static function string Replace(coerce string source, coerce string search, coerce string replace) {
	var int pos;
	
	pos = InStr(source, search);
	if (pos >= 0) {
		source = Left(source, pos) $ replace $ Mid(source, pos + Len(search));
	}
	
	return source;
}

Draw 3D Line properly, even if one point is going to back of camera.

simulated function DrawLine3D( Canvas C, vector P1, vector P2, float R, float G, float B ) {
	local int x1, y1, x2, y2;
	local float a1, a2;

	a1 = getXY(C, P1, x1, y1);	
	a2 = getXY(C, P2, x2, y2);
	if (a1 <= 0 && a2 <= 0) return;
	if (a1 <= 0) {
		P1 -= P2;
		P1 *= a2/(a2 - a1);
		P1 -= Normal(P1);
		P1 += P2;
		getXY(C, P1, x1, y1);
	} else if (a2 <= 0) {
		P2 -= P1;
		P2 *= a1/(a1 - a2);
		P2 -= Normal(P2);
		P2 += P1;
		getXY(C, P2, x2, y2);
	}
	if (R >= 0)
		SetColor(C, R, G, B);
	DrawLine(C, x1, y1, x2, y2);
}
@smcl
Copy link

smcl commented Aug 20, 2021

Nice! I recently noticed that the way the Redeemer's altfire calculates screen co-ords to draw the red crosshair by players is a bit off (see here), and I wondered if it was deliberate - but your equivalent (getXY) is absolutely spot-on 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment