1.2 update w/ GZDoom 4.9 features:
- Changeable player skins. - Ammo LEDs are now 1:1 with UT by using canvas textures. - Integrate some add-ons, including reskins. - Various fixes (some backported from Demolitionist). - Migrated from libeye to Gutamatics.
This commit is contained in:
parent
81ffca403d
commit
602a89cc68
1761 changed files with 4461 additions and 1597 deletions
|
|
@ -1,259 +0,0 @@
|
|||
const PULSELEDBASE = 60000.;
|
||||
const FLAKLEDBASE = 61000.;
|
||||
const MINILEDBASE = 62000.;
|
||||
const ROCKETLEDBASE = 63000.;
|
||||
|
||||
Class PulseGunLED : Actor
|
||||
{
|
||||
Actor base, digits[3], bar;
|
||||
transient PulseGun mygun;
|
||||
|
||||
Default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
Radius .1;
|
||||
Height 0;
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
SetOrigin((PULSELEDBASE+128.,PULSELEDBASE,0),false);
|
||||
SetZ(floorz);
|
||||
roll = 180;
|
||||
angle = 180;
|
||||
double halfstretch = (1.+level.pixelstretch)/2.;
|
||||
double zofs = (ceilingz>floorz)?CurSector.GetFloorTerrain(0).footclip*16.:0.;
|
||||
if ( !base ) base = Spawn("AmmoLEDScreen",(PULSELEDBASE,PULSELEDBASE-128.,pos.z+64.*halfstretch-zofs));
|
||||
for ( int i=0; i<3; i++ )
|
||||
{
|
||||
if ( !digits[i] )
|
||||
digits[i] = Spawn("LEDFont",(PULSELEDBASE+1.,PULSELEDBASE-72.+48.*i,pos.z+50.*halfstretch-zofs));
|
||||
}
|
||||
if ( !bar ) bar = Spawn("AmmoCountBar",(PULSELEDBASE+1.,PULSELEDBASE-98.,pos.z-26.*halfstretch-zofs));
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( !mygun ) return;
|
||||
base.SetState(base.SpawnState);
|
||||
int amo = flak_pulsereload?mygun.ClipCount:mygun.Ammo1.Amount;
|
||||
int mamo = flak_pulsereload?mygun.default.ClipCount:mygun.Ammo1.MaxAmount;
|
||||
digits[0].SetState(digits[0].SpawnState+1+((amo/100)%10));
|
||||
digits[1].SetState(digits[1].SpawnState+1+((amo/10)%10));
|
||||
digits[2].SetState(digits[2].SpawnState+1+(amo%10));
|
||||
if ( amo < 10 )
|
||||
{
|
||||
digits[0].SetShade(Color(255,0,0));
|
||||
digits[1].SetShade(Color(255,0,0));
|
||||
digits[2].SetShade(Color(255,0,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
digits[0].SetShade(Color(0,0,255));
|
||||
digits[1].SetShade(Color(0,0,255));
|
||||
digits[2].SetShade(Color(0,0,255));
|
||||
}
|
||||
bar.scale.x = amo/double(mamo);
|
||||
}
|
||||
}
|
||||
|
||||
Class FlakCannonLED : Actor
|
||||
{
|
||||
Actor base, digits[3];
|
||||
transient FlakCannon mygun;
|
||||
|
||||
Default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
Radius .1;
|
||||
Height 0;
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
SetOrigin((FLAKLEDBASE+64,FLAKLEDBASE,0),false);
|
||||
SetZ(floorz);
|
||||
roll = 180;
|
||||
angle = 180;
|
||||
double halfstretch = (1.+level.pixelstretch)/2.;
|
||||
double zofs = (ceilingz>floorz)?CurSector.GetFloorTerrain(0).footclip*16.:0.;
|
||||
if ( !base ) base = Spawn("AmmoLEDScreen",(FLAKLEDBASE,FLAKLEDBASE-64.,floorz+32.*halfstretch-zofs));
|
||||
for ( int i=0; i<3; i++ )
|
||||
{
|
||||
if ( !digits[i] )
|
||||
digits[i] = Spawn("LEDFont",(FLAKLEDBASE+1.,FLAKLEDBASE-30.+20.*i,floorz+16.*halfstretch-zofs));
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( !mygun ) return;
|
||||
base.SetState(base.SpawnState+1);
|
||||
int amo = mygun.Ammo1.Amount;
|
||||
digits[0].SetState(digits[0].SpawnState+11+((amo/100)%10));
|
||||
digits[1].SetState(digits[1].SpawnState+11+((amo/10)%10));
|
||||
digits[2].SetState(digits[2].SpawnState+11+(amo%10));
|
||||
digits[0].SetShade(Color(255,0,0));
|
||||
digits[1].SetShade(Color(255,0,0));
|
||||
digits[2].SetShade(Color(255,0,0));
|
||||
}
|
||||
}
|
||||
|
||||
Class MinigunLED : Actor
|
||||
{
|
||||
Actor base, digits[3];
|
||||
transient Minigun mygun;
|
||||
|
||||
Default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
Radius .1;
|
||||
Height 0;
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
SetOrigin((MINILEDBASE+32,MINILEDBASE,0),false);
|
||||
SetZ(floorz);
|
||||
roll = 180;
|
||||
angle = 180;
|
||||
double halfstretch = (1.+level.pixelstretch)/2.;
|
||||
double zofs = (ceilingz>floorz)?CurSector.GetFloorTerrain(0).footclip*16.:0.;
|
||||
if ( !base ) base = Spawn("AmmoLEDScreen",(MINILEDBASE,MINILEDBASE-32.,floorz+32.*halfstretch-zofs));
|
||||
for ( int i=0; i<3; i++ )
|
||||
{
|
||||
if ( !digits[i] )
|
||||
digits[i] = Spawn("LEDFont",(MINILEDBASE+1.,MINILEDBASE-30.+20.*i,floorz+16.*halfstretch-zofs));
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( !mygun ) return;
|
||||
base.SetState(base.SpawnState+2);
|
||||
int amo = mygun.Ammo1.Amount;
|
||||
digits[0].SetState(digits[0].SpawnState+11+((amo/100)%10));
|
||||
digits[1].SetState(digits[1].SpawnState+11+((amo/10)%10));
|
||||
digits[2].SetState(digits[2].SpawnState+11+(amo%10));
|
||||
digits[0].SetShade(Color(255,0,0));
|
||||
digits[1].SetShade(Color(255,0,0));
|
||||
digits[2].SetShade(Color(255,0,0));
|
||||
}
|
||||
}
|
||||
|
||||
Class UTRocketLauncherLED : Actor
|
||||
{
|
||||
Actor base, digits[3];
|
||||
transient UTRocketLauncher mygun;
|
||||
|
||||
Default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
Radius .1;
|
||||
Height 0;
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
SetOrigin((ROCKETLEDBASE+32,ROCKETLEDBASE,0),false);
|
||||
SetZ(floorz);
|
||||
roll = 180;
|
||||
angle = 180;
|
||||
double halfstretch = (1.+level.pixelstretch)/2.;
|
||||
double zofs = (ceilingz>floorz)?CurSector.GetFloorTerrain(0).footclip*16.:0.;
|
||||
if ( !base ) base = Spawn("AmmoLEDScreen",(ROCKETLEDBASE,ROCKETLEDBASE-32.,floorz+32.*halfstretch-zofs));
|
||||
for ( int i=0; i<3; i++ )
|
||||
{
|
||||
if ( !digits[i] )
|
||||
digits[i] = Spawn("LEDFont",(ROCKETLEDBASE+1.,ROCKETLEDBASE-30.+20.*i,floorz+16.*halfstretch-zofs));
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( !mygun ) return;
|
||||
base.SetState(base.SpawnState+3);
|
||||
int amo = mygun.Ammo1.Amount;
|
||||
digits[0].SetState(digits[0].SpawnState+11+((amo/100)%10));
|
||||
digits[1].SetState(digits[1].SpawnState+11+((amo/10)%10));
|
||||
digits[2].SetState(digits[2].SpawnState+11+(amo%10));
|
||||
digits[0].SetShade(Color(255,0,0));
|
||||
digits[1].SetShade(Color(255,0,0));
|
||||
digits[2].SetShade(Color(255,0,0));
|
||||
}
|
||||
}
|
||||
|
||||
Class LEDFont : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
RenderStyle "Shaded";
|
||||
StencilColor "FF 00 00";
|
||||
Radius .1;
|
||||
Height 0.;
|
||||
RenderRadius 131072;
|
||||
+NOBLOCKMAP;
|
||||
+NOGRAVITY;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
+WALLSPRITE;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A -1;
|
||||
LFN1 ABCDEFGHIJ -1 Bright;
|
||||
LFN2 ABCDEFGHIJ -1 Bright;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class AmmoCountBar : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius .1;
|
||||
Height 0.;
|
||||
RenderRadius 131072;
|
||||
+NOBLOCKMAP;
|
||||
+NOGRAVITY;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
+WALLSPRITE;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
AMCB A -1 Bright;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class AmmoLEDScreen : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius .1;
|
||||
Height 0.;
|
||||
RenderRadius 131072;
|
||||
+NOBLOCKMAP;
|
||||
+NOGRAVITY;
|
||||
+NOINTERACTION;
|
||||
+DONTSPLASH;
|
||||
+WALLSPRITE;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
AMLD ABCD -1 Bright;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
|
@ -647,7 +647,7 @@ Class BioGlob : BioGel
|
|||
|
||||
void SpawnSplash()
|
||||
{
|
||||
Vector3 ofs = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 ofs = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<2; i++ )
|
||||
{
|
||||
if ( numsplash-- <= 0 ) return;
|
||||
|
|
@ -659,7 +659,7 @@ Class BioGlob : BioGel
|
|||
d.scale *= FRandom[GES](0.5,0.7);
|
||||
d.angle = atan2(dir.y,dir.x);
|
||||
d.pitch = -asin(dir.z);
|
||||
d.vel = (cos(d.angle)*cos(d.pitch),sin(d.angle)*cos(d.pitch),-sin(d.pitch))*d.speed*FRandom[GES](0.4,0.6);
|
||||
d.vel = dt_Utility.Vec3FromAngle(d.angle,d.pitch)*d.speed*FRandom[GES](0.4,0.6);
|
||||
d.vel.z -= 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -724,7 +724,7 @@ Class BioRifle : UTWeapon
|
|||
else p = Spawn("BioGel",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
for ( int i=0; i<12; i++ )
|
||||
{
|
||||
|
|
@ -781,6 +781,7 @@ Class BioRifle : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 25;
|
||||
UTWeapon.DropAmmo 5;
|
||||
UTWeapon.NameColor "00 FF 00";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Class SawImpact : Actor
|
|||
Super.PostBeginPlay();
|
||||
A_SprayDecal("WallCrack",20);
|
||||
int numpt = Random[Chainsaw](5,10);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (-x+(FRandom[Chainsaw](-.8,.8),FRandom[Chainsaw](-.8,.8),FRandom[Chainsaw](-.8,.8))).unit()*FRandom[Chainsaw](0.1,1.2);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,22 @@ Class UTActivatable : Inventory
|
|||
|
||||
Class ActUDamage : UTActivatable
|
||||
{
|
||||
bool bOldSkin;
|
||||
|
||||
void A_CheckSkin()
|
||||
{
|
||||
if ( flak_oldudamage )
|
||||
{
|
||||
if ( !bOldSkin ) A_ChangeModel("",0,"models/220","UDamage_d.3d",0,"models/220","Judamage1.png");
|
||||
bOldSkin = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bOldSkin ) A_ChangeModel("",0,"models","UDamage_d.3d",0,"models","GoldSkin2.png");
|
||||
bOldSkin = false;
|
||||
}
|
||||
}
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_UDAMAGE";
|
||||
|
|
@ -107,8 +123,8 @@ Class ActUDamage : UTActivatable
|
|||
States
|
||||
{
|
||||
Spawn:
|
||||
UDAM A -1;
|
||||
Stop;
|
||||
UDAM A 1 A_CheckSkin();
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
173
zscript/dt_Gutamatics/GlobalMaths.zsc
Normal file
173
zscript/dt_Gutamatics/GlobalMaths.zsc
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
class dt_GM_GlobalMaths {
|
||||
/// Returns the sign of s.
|
||||
static int sign(double s) {
|
||||
if (s > 0) return 1;
|
||||
if (s < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Copies the sign from signSource to source.
|
||||
static int copySignInt(int source, int signSource) {
|
||||
return abs(source) * sign(signSource);
|
||||
}
|
||||
|
||||
/// Copies the sign from signSource to source.
|
||||
static double copySignDouble(double source, double signSource) {
|
||||
return abs(source) * sign(signSource);
|
||||
}
|
||||
|
||||
/// Remaps a value in a range to another range.
|
||||
static double remapRange(double value, double range1L, double range1H, double range2L, double range2H) {
|
||||
return range2L + (value - range1L) * (range2H - range1H) / (range1H - range1L);
|
||||
}
|
||||
|
||||
/// Remaps a value in a range to another range.
|
||||
static int remapRangeInt(int value, int range1L, int range1H, int range2L, int range2H) {
|
||||
return int(range2L + (value - range1L) * (range2H - range1H) / double(range1H - range1L));
|
||||
}
|
||||
|
||||
/// Returns if two values are close enough to be considered equal.
|
||||
static bool closeEnough(double a, double b, double epsilon = double.epsilon) {
|
||||
if (a == b) return true;
|
||||
return abs(a - b) <= epsilon;
|
||||
}
|
||||
|
||||
// Creates a smoothed transition between edge0 and edge1.
|
||||
static double smoothStep(double x, double edge0 = 0, double edge1 = 1) {
|
||||
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
|
||||
return x * x * (3 - 2 * x);
|
||||
}
|
||||
|
||||
// Creates a smoother transition between edge0 and edge1.
|
||||
static double smootherStep(double x, double edge0 = 0, double edge1 = 1) {
|
||||
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
|
||||
return x * x * x * (x * (x * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
/// Converts from horizontal FOV to vertical FOV, according to how GZDoom handles it.
|
||||
static double fovHToY(double fovH) {
|
||||
// this is how gzdoom does it internally, so i'm using it here
|
||||
double aspect = Screen.getAspectRatio();
|
||||
double fovratio = (aspect >= 1.3) ? 1.333333 : aspect;
|
||||
return 2 * atan(tan(clamp(fovH, 5, 170) / 2.0) / fovratio);
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two doubles, clamping the parameters.
|
||||
static double lerpDouble(double from, double to, double time) {
|
||||
time = clamp(time, 0, 1);
|
||||
return lerpUnclampedDouble(from, to, time);
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two doubles.
|
||||
static double lerpUnclampedDouble(double from, double to, double time) {
|
||||
double ret;
|
||||
double reverseTime = 1 - time;
|
||||
ret = reverseTime * from + time * to;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Converts from Normalised Device Coordinates to Viewport coordinates.
|
||||
// This is `ui` scope to safely access `screenblocks`.
|
||||
static ui Vector2 ndcToViewport(Vector3 ndcCoords, bool useScreenblocks = true) {
|
||||
if (useScreenblocks) {
|
||||
int viewwindowx, viewwindowy, viewwidth, viewheight;
|
||||
[viewwindowx, viewwindowy, viewwidth, viewheight] = Screen.getViewWindow();
|
||||
int screenHeight = Screen.getHeight();
|
||||
int height = screenHeight;
|
||||
if (screenblocks < 10) {
|
||||
height = (screenblocks * screenHeight / 10) & ~7;
|
||||
}
|
||||
int bottom = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
|
||||
return (viewwindowx, screenHeight - bottom - height) + (((ndcCoords.x + 1) * viewwidth) / 2, ((-ndcCoords.y + 1) * height) / 2);
|
||||
}
|
||||
else {
|
||||
return (((ndcCoords.x + 1) * Screen.getWidth()) / 2, ((-ndcCoords.y + 1) * Screen.getHeight()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
enum OutCode {
|
||||
OUT_Inside = 0,
|
||||
OUT_Left = 1 << 0,
|
||||
OUT_Right = 1 << 1,
|
||||
OUT_Bottom = 1 << 2,
|
||||
OUT_Top = 1 << 3
|
||||
}
|
||||
|
||||
/// Computes an outcode for a point in a rectangle.
|
||||
static OutCode computeOutcode(Vector2 point, Vector2 min, Vector2 max) {
|
||||
OutCode code = OUT_Inside;
|
||||
|
||||
if (point.x < min.x) {
|
||||
code |= OUT_Left;
|
||||
}
|
||||
else if (point.x > max.x) {
|
||||
code |= OUT_Right;
|
||||
}
|
||||
if (point.y < min.y) {
|
||||
code |= OUT_Top;
|
||||
}
|
||||
else if (point.y > max.y) {
|
||||
code |= OUT_Bottom;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/// Clips a line to a rectangle.
|
||||
static bool, Vector2, Vector2 cohenSutherlandClip(Vector2 point0, Vector2 point1, Vector2 min, Vector2 max) {
|
||||
OutCode outcode0 = computeOutCode(point0, min, max);
|
||||
OutCode outcode1 = computeOutCode(point1, min, max);
|
||||
|
||||
while (true) {
|
||||
// trivial accept - points are both on screen
|
||||
if ((outcode0 | outcode1) == 0) {
|
||||
return true, point0, point1;
|
||||
}
|
||||
// trivial reject - points are in the same region offscreen
|
||||
else if ((outcode0 & outcode1) != 0) {
|
||||
return false, point0, point1;
|
||||
}
|
||||
else {
|
||||
Vector2 new;
|
||||
OutCode outcodeOut = (outcode0 != 0) ? outcode0 : outcode1;
|
||||
|
||||
if ((outcodeOut & OUT_Bottom) != 0) {
|
||||
new.x = point0.x + (point1.x - point0.x) * (max.y - point0.y) / (point1.y - point0.y);
|
||||
new.y = max.y;
|
||||
}
|
||||
else if ((outcodeOut & OUT_Top) != 0) {
|
||||
new.x = point0.x + (point1.x - point0.x) * (min.y - point0.y) / (point1.y - point0.y);
|
||||
new.y = min.y;
|
||||
}
|
||||
else if ((outcodeOut & OUT_Right) != 0) {
|
||||
new.y = point0.y + (point1.y - point0.y) * (max.x - point0.x) / (point1.x - point0.x);
|
||||
new.x = max.x;
|
||||
}
|
||||
else if ((outcodeOut & OUT_Left) != 0) {
|
||||
new.y = point0.y + (point1.y - point0.y) * (min.x - point0.x) / (point1.x - point0.x);
|
||||
new.x = min.x;
|
||||
}
|
||||
|
||||
if (outcodeOut == outCode0) {
|
||||
point0.x = new.x;
|
||||
point0.y = new.y;
|
||||
outCode0 = computeOutCode(point0, min, max);
|
||||
}
|
||||
else {
|
||||
point1.x = new.x;
|
||||
point1.y = new.y;
|
||||
outCode1 = computeOutCode(point1, min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, (0, 0), (0, 0);
|
||||
}
|
||||
|
||||
// Normalizes an angle to (-180, 180]. Like Actor.normalize180, but callable in data scope.
|
||||
static double normalize180(double ang) {
|
||||
ang = ang % 360;
|
||||
ang = (ang + 360) % 360;
|
||||
if (ang > 180) ang -= 360;
|
||||
return ang;
|
||||
}
|
||||
}
|
||||
5
zscript/dt_Gutamatics/Include.zsc
Normal file
5
zscript/dt_Gutamatics/Include.zsc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#include "zscript/dt_Gutamatics/GlobalMaths.zsc"
|
||||
#include "zscript/dt_Gutamatics/Matrix.zsc"
|
||||
#include "zscript/dt_Gutamatics/Matrix4.zsc"
|
||||
#include "zscript/dt_Gutamatics/Quaternion.zsc"
|
||||
#include "zscript/dt_Gutamatics/VectorUtil.zsc"
|
||||
7
zscript/dt_Gutamatics/LICENSE.md
Normal file
7
zscript/dt_Gutamatics/LICENSE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright 2020 Jessica Russell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
335
zscript/dt_Gutamatics/Matrix.zsc
Normal file
335
zscript/dt_Gutamatics/Matrix.zsc
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
enum dt_GM_VectorType {
|
||||
dt_GM_Vector_Position,
|
||||
dt_GM_Vector_Direction
|
||||
}
|
||||
|
||||
class dt_GM_Matrix {
|
||||
private Array<double> values;
|
||||
private int columns;
|
||||
private int rows;
|
||||
|
||||
/// Initialises a new Matrix.
|
||||
dt_GM_Matrix init(int columns, int rows) {
|
||||
if (columns <= 0 || rows <= 0) {
|
||||
throwAbortException("Error: <%p>.init(%d, %d) - Matrix needs to be at least 1 * 1", self, columns, rows);
|
||||
}
|
||||
|
||||
self.rows = rows;
|
||||
self.columns = columns;
|
||||
values.resize(columns * rows);
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
values[i] = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Initialises a new Matrix in a static context.
|
||||
static dt_GM_Matrix create(int columns, int rows) {
|
||||
return new("dt_GM_Matrix").init(columns, rows);
|
||||
}
|
||||
|
||||
/// Returns an identity matrix.
|
||||
static dt_GM_Matrix identity(int dimension) {
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.create(dimension, dimension);
|
||||
for (int i = 0; i < dimension; i++) {
|
||||
ret.set(i, i, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a rotation matrix from euler angles.
|
||||
static dt_GM_Matrix fromEulerAngles(double yaw, double pitch, double roll) {
|
||||
dt_GM_Matrix rYaw = dt_GM_Matrix.identity(4);
|
||||
double sYaw = sin(yaw);
|
||||
double cYaw = cos(yaw);
|
||||
rYaw.set(0, 0, cYaw);
|
||||
rYaw.set(0, 1, -sYaw);
|
||||
rYaw.set(1, 0, sYaw);
|
||||
rYaw.set(1, 1, cYaw);
|
||||
|
||||
dt_GM_Matrix rPitch = dt_GM_Matrix.identity(4);
|
||||
double sPitch = sin(pitch);
|
||||
double cPitch = cos(pitch);
|
||||
rPitch.set(0, 0, cPitch);
|
||||
rPitch.set(2, 0, -sPitch);
|
||||
rPitch.set(0, 2, sPitch);
|
||||
rPitch.set(2, 2, cPitch);
|
||||
|
||||
dt_GM_Matrix rRoll = dt_GM_Matrix.identity(4);
|
||||
double sRoll = sin(roll);
|
||||
double cRoll = cos(roll);
|
||||
rRoll.set(1, 1, cRoll);
|
||||
rRoll.set(1, 2, -sRoll);
|
||||
rRoll.set(2, 1, sRoll);
|
||||
rRoll.set(2, 2, cRoll);
|
||||
|
||||
// concatenate ypr to get the final matrix
|
||||
dt_GM_Matrix ret = rYaw.multiplyMatrix(rPitch);
|
||||
ret = ret.multiplyMatrix(rRoll);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a rotation matrix from an axis and an angle.
|
||||
static dt_GM_Matrix fromAxisAngle(Vector3 axis, double angle) {
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.identity(4);
|
||||
double c = cos(angle);
|
||||
double s = sin(angle);
|
||||
double x = axis.x;
|
||||
double y = axis.y;
|
||||
double z = axis.z;
|
||||
|
||||
ret.set(0, 0, (x * x * (1.0 - c) + c));
|
||||
ret.set(0, 1, (x * y * (1.0 - c) - z * s));
|
||||
ret.set(0, 2, (x * z * (1.0 - c) + y * s));
|
||||
ret.set(1, 0, (y * x * (1.0 - c) + z * s));
|
||||
ret.set(1, 1, (y * y * (1.0 - c) + c));
|
||||
ret.set(1, 2, (y * z * (1.0 - c) - x * s));
|
||||
ret.set(2, 0, (x * z * (1.0 - c) - y * s));
|
||||
ret.set(2, 1, (y * z * (1.0 - c) + x * s));
|
||||
ret.set(2, 2, (z * z * (1.0 - c) + c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Converts back from the rotation matrix to euler angles.
|
||||
double, double, double rotationToEulerAngles() {
|
||||
if (dt_GM_GlobalMaths.closeEnough(get(2, 0), -1)) {
|
||||
double x = 90;
|
||||
double y = 0;
|
||||
double z = atan2(get(0, 1), get(0, 2));
|
||||
return z, x, y;
|
||||
}
|
||||
else if (dt_GM_GlobalMaths.closeEnough(get(2, 0), 1)) {
|
||||
double x = -90;
|
||||
double y = 0;
|
||||
double z = atan2(-get(0, 1), -get(0, 2));
|
||||
return z, x, y;
|
||||
}
|
||||
else {
|
||||
float x1 = -asin(get(2, 0));
|
||||
float x2 = 180 - x1;
|
||||
|
||||
float y1 = atan2(get(2, 1) / cos(x1), get(2, 2) / cos(x1));
|
||||
float y2 = atan2(get(2, 1) / cos(x2), get(2, 2) / cos(x2));
|
||||
|
||||
float z1 = atan2(get(1, 0) / cos(x1), get(0, 0) / cos(x1));
|
||||
float z2 = atan2(get(1, 0) / cos(x2), get(0, 0) / cos(x2));
|
||||
|
||||
if ((abs(x1) + abs(y1) + abs(z1)) <= (abs(x2) + abs(y2) + abs(z2))) {
|
||||
return z1, x1, y1;
|
||||
}
|
||||
else {
|
||||
return z2, x2, y2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static dt_GM_Matrix createTRSEuler(Vector3 translate, double yaw, double pitch, double roll, Vector3 scale) {
|
||||
dt_GM_Matrix translateMat = dt_GM_Matrix.identity(4);
|
||||
translateMat.set(0, 3, translate.x);
|
||||
translateMat.set(1, 3, translate.y);
|
||||
translateMat.set(2, 3, translate.z);
|
||||
|
||||
dt_GM_Matrix rotateMat = dt_GM_Matrix.fromEulerAngles(yaw, pitch, roll);
|
||||
|
||||
dt_GM_Matrix scaleMat = dt_GM_Matrix.identity(4);
|
||||
scaleMat.set(0, 0, scale.x);
|
||||
scaleMat.set(1, 1, scale.y);
|
||||
scaleMat.set(2, 2, scale.z);
|
||||
|
||||
dt_GM_Matrix ret = translateMat.multiplyMatrix(rotateMat);
|
||||
ret = ret.multiplyMatrix(scaleMat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static dt_GM_Matrix createTRSAxisAngle(Vector3 translate, Vector3 axis, double angle, Vector3 scale) {
|
||||
dt_GM_Matrix translateMat = dt_GM_Matrix.identity(4);
|
||||
translateMat.set(0, 3, translate.x);
|
||||
translateMat.set(1, 3, translate.y);
|
||||
translateMat.set(2, 3, translate.z);
|
||||
|
||||
dt_GM_Matrix rotateMat = dt_GM_Matrix.fromAxisAngle(axis, angle);
|
||||
|
||||
dt_GM_Matrix scaleMat = dt_GM_Matrix.identity(4);
|
||||
scaleMat.set(0, 0, scale.x);
|
||||
scaleMat.set(1, 1, scale.y);
|
||||
scaleMat.set(2, 2, scale.z);
|
||||
|
||||
dt_GM_Matrix ret = translateMat.multiplyMatrix(rotateMat);
|
||||
ret = ret.multiplyMatrix(scaleMat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a view matrix.
|
||||
static dt_GM_Matrix view(Vector3 camPos, double yaw, double pitch, double roll) {
|
||||
// all of this is basically lifted and converted from PolyRenderer::SetupPerspectiveMatrix(),
|
||||
// so credit goes to Graf Zahl/dpJudas/whoever else
|
||||
// pitch needs to be adjusted by the pixel ratio
|
||||
float pixelRatio = level.pixelstretch;
|
||||
double angx = cos(pitch);
|
||||
double angy = sin(pitch) * pixelRatio;
|
||||
double alen = sqrt(angx * angx + angy * angy);
|
||||
double adjustedPitch = asin(angy / alen);
|
||||
double adjustedYaw = yaw - 90;
|
||||
|
||||
// rotations
|
||||
dt_GM_Matrix rotR = dt_GM_Matrix.fromAxisAngle((0, 0, 1), roll);
|
||||
dt_GM_Matrix rotP = dt_GM_Matrix.fromAxisAngle((1, 0, 0), adjustedPitch);
|
||||
dt_GM_Matrix rotY = dt_GM_Matrix.fromAxisAngle((0, -1, 0), adjustedYaw);
|
||||
// pixel ratio scaling
|
||||
dt_GM_Matrix scale = dt_GM_Matrix.identity(4);
|
||||
scale.set(1, 1, pixelRatio);
|
||||
// swapping y and z
|
||||
dt_GM_Matrix swapYZ = dt_GM_Matrix.create(4, 4);
|
||||
swapYZ.set(0, 0, 1);
|
||||
swapYZ.set(1, 2, 1);
|
||||
swapYZ.set(2, 1, -1);
|
||||
swapYZ.set(3, 3, 1);
|
||||
// translation
|
||||
dt_GM_Matrix translate = dt_GM_Matrix.identity(4);
|
||||
translate.set(0, 3, -camPos.x);
|
||||
translate.set(1, 3, -camPos.y);
|
||||
translate.set(2, 3, -camPos.z);
|
||||
|
||||
// concatenate them all to get a final matrix
|
||||
dt_GM_Matrix ret = rotR.multiplyMatrix(rotP);
|
||||
ret = ret.multiplyMatrix(rotY);
|
||||
ret = ret.multiplyMatrix(scale);
|
||||
ret = ret.multiplyMatrix(swapYZ);
|
||||
ret = ret.multiplyMatrix(translate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a perspective matrix (same format as gluPerspective).
|
||||
static dt_GM_Matrix perspective(double fovy, double aspect, double zNear, double zFar) {
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.create(4, 4);
|
||||
double f = 1 / tan(fovy / 2.0);
|
||||
// x coord
|
||||
ret.set(0, 0, f / aspect);
|
||||
// y coord
|
||||
ret.set(1, 1, f);
|
||||
// z buffer coord
|
||||
ret.set(2, 2, (zFar + zNear) / (zNear - zFar));
|
||||
ret.set(2, 3, (2 * zFar * zNear) / (zNear - zFar));
|
||||
// w (homogeneous coordinates)
|
||||
ret.set(3, 2, -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a world->clip coords matrix from the passed args.
|
||||
static dt_GM_Matrix worldToClip(Vector3 viewPos, double yaw, double pitch, double roll, double FOV) {
|
||||
double aspect = Screen.getAspectRatio();
|
||||
double fovy = dt_GM_GlobalMaths.fovHToY(FOV);
|
||||
dt_GM_Matrix view = dt_GM_Matrix.view(viewPos, yaw, pitch, roll);
|
||||
// 5 & 65535 are what are used internally, so they're used here for consistency
|
||||
dt_GM_Matrix perp = dt_GM_Matrix.perspective(fovy, aspect, 5, 65535);
|
||||
dt_GM_Matrix worldToClip = perp.multiplyMatrix(view);
|
||||
return worldToClip;
|
||||
}
|
||||
|
||||
/// Gets the value at row, col.
|
||||
double get(int row, int col) const {
|
||||
return values[columns * row + col];
|
||||
}
|
||||
|
||||
/// Sets the value at row, col.
|
||||
void set(int row, int col, double val) {
|
||||
values[columns * row + col] = val;
|
||||
}
|
||||
|
||||
/// Adds two matrices and returns the result.
|
||||
dt_GM_Matrix addMatrix(dt_GM_Matrix other) const {
|
||||
if (rows != other.rows || columns != other.columns) {
|
||||
throwAbortException("Error: <%p>.addMatrix(<%p>) - Matrices need to be equal size", self, other);
|
||||
}
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.create(columns, rows);
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < columns; col++) {
|
||||
ret.set(row, col, get(row, col) + other.get(row, col));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies the matrix by a scalar and returns the result.
|
||||
dt_GM_Matrix multiplyScalar(double scalar) const {
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.create(rows, columns);
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < columns; col++) {
|
||||
ret.set(row, col, get(row, col) * scalar);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies two matrices and returns the result.
|
||||
dt_GM_Matrix multiplyMatrix(dt_GM_Matrix other) const {
|
||||
if (columns != other.rows) {
|
||||
throwAbortException("Error: <%p>.multiplyMatrix(<%p>) - Matrix A columns needs to equal Matrix B rows", self, other);
|
||||
}
|
||||
dt_GM_Matrix ret = dt_GM_Matrix.create(other.columns, rows);
|
||||
for (int row = 0; row < ret.rows; row++) {
|
||||
for (int col = 0; col < ret.columns; col++) {
|
||||
double val = 0;
|
||||
for (int i = 0; i < columns; i++) {
|
||||
val += get(row, i) * other.get(i, col);
|
||||
}
|
||||
ret.set(row, col, val);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies this Matrix by a 2D vector.
|
||||
dt_GM_Matrix multiplyVector2(Vector2 vec, dt_GM_VectorType type = dt_GM_Vector_Position) const {
|
||||
dt_GM_Matrix vec2Matrix = dt_GM_Matrix.create(1, 3);
|
||||
vec2Matrix.set(0, 0, vec.x);
|
||||
vec2Matrix.set(1, 0, vec.y);
|
||||
if (type == dt_GM_Vector_Position) vec2Matrix.set(2, 0, 1);
|
||||
else if (type == dt_GM_Vector_Direction) vec2Matrix.set(2, 0, 0);
|
||||
else throwAbortException("Error: Invalid vector type for multiplyVector2 (%d)", type);
|
||||
return multiplyMatrix(vec2Matrix);
|
||||
}
|
||||
|
||||
/// Multiplies this Matrix by a 3D vector.
|
||||
dt_GM_Matrix multiplyVector3(Vector3 vec, dt_GM_VectorType type = dt_GM_Vector_Position) const {
|
||||
dt_GM_Matrix vec3Matrix = dt_GM_Matrix.create(1, 4);
|
||||
vec3Matrix.set(0, 0, vec.x);
|
||||
vec3Matrix.set(1, 0, vec.y);
|
||||
vec3Matrix.set(2, 0, vec.z);
|
||||
if (type == dt_GM_Vector_Position) vec3Matrix.set(3, 0, 1);
|
||||
else if (type == dt_GM_Vector_Direction) vec3Matrix.set(3, 0, 0);
|
||||
else throwAbortException("Error: Invalid vector type for multiplyVector3 (%d)", type);
|
||||
return multiplyMatrix(vec3Matrix);
|
||||
}
|
||||
|
||||
/// Returns the Matrix in Vector2 form, optionally dividing by z.
|
||||
Vector2 asVector2(bool divideZ = true) const {
|
||||
if (columns != 1 || rows != 3) {
|
||||
throwAbortException("Error: <%p>.asVector2() - Matrix needs to be 1 * 3", self);
|
||||
}
|
||||
if (divideZ) return (get(0, 0), get(1, 0)) / get(2, 0);
|
||||
else return (get(0, 0), get(1, 0));
|
||||
}
|
||||
|
||||
/// Returns the Matrix in Vector3 form, optionally dividing by w.
|
||||
Vector3 asVector3(bool divideW = true) const {
|
||||
if (columns != 1 || rows != 4) {
|
||||
throwAbortException("Error: <%p>.asVector3() - Matrix needs to be 1 * 4", self);
|
||||
}
|
||||
if (divideW) return (get(0, 0), get(1, 0), get(2, 0)) / get(3, 0);
|
||||
else return (get(0, 0), get(1, 0), get(2, 0));
|
||||
}
|
||||
|
||||
/// Returns the number of columns.
|
||||
int getColumns() const {
|
||||
return columns;
|
||||
}
|
||||
|
||||
/// Returns the number of rows.
|
||||
int getRows() const {
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
319
zscript/dt_Gutamatics/Matrix4.zsc
Normal file
319
zscript/dt_Gutamatics/Matrix4.zsc
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
class dt_GM_Matrix4 {
|
||||
double values[4][4];
|
||||
|
||||
/// Initialises a new Matrix4 in a static context.
|
||||
static dt_GM_Matrix4 create() {
|
||||
return new("dt_GM_Matrix4");
|
||||
}
|
||||
|
||||
/// Returns an identity matrix.
|
||||
static dt_GM_Matrix4 identity() {
|
||||
let ret = dt_GM_Matrix4.create();
|
||||
ret.values[0][0] = 1;
|
||||
ret.values[1][1] = 1;
|
||||
ret.values[2][2] = 1;
|
||||
ret.values[3][3] = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a rotation matrix from euler angles.
|
||||
static dt_GM_Matrix4 fromEulerAngles(double yaw, double pitch, double roll) {
|
||||
dt_GM_Matrix4 rYaw = dt_GM_Matrix4.identity();
|
||||
double sYaw = sin(yaw);
|
||||
double cYaw = cos(yaw);
|
||||
rYaw.values[0][0] = cYaw;
|
||||
rYaw.values[0][1] = -sYaw;
|
||||
rYaw.values[1][0] = sYaw;
|
||||
rYaw.values[1][1] = cYaw;
|
||||
|
||||
dt_GM_Matrix4 rPitch = dt_GM_Matrix4.identity();
|
||||
double sPitch = sin(pitch);
|
||||
double cPitch = cos(pitch);
|
||||
rPitch.values[0][0] = cPitch;
|
||||
rPitch.values[2][0] = -sPitch;
|
||||
rPitch.values[0][2] = sPitch;
|
||||
rPitch.values[2][2] = cPitch;
|
||||
|
||||
dt_GM_Matrix4 rRoll = dt_GM_Matrix4.identity();
|
||||
double sRoll = sin(roll);
|
||||
double cRoll = cos(roll);
|
||||
rRoll.values[1][1] = cRoll;
|
||||
rRoll.values[1][2] = -sRoll;
|
||||
rRoll.values[2][1] = sRoll;
|
||||
rRoll.values[2][2] = cRoll;
|
||||
|
||||
// concatenate ypr to get the final matrix
|
||||
dt_GM_Matrix4 ret = rYaw.multiplyMatrix(rPitch);
|
||||
ret = ret.multiplyMatrix(rRoll);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a rotation matrix from an axis and an angle.
|
||||
static dt_GM_Matrix4 fromAxisAngle(Vector3 axis, double angle) {
|
||||
dt_GM_Matrix4 ret = dt_GM_Matrix4.identity();
|
||||
double c = cos(angle);
|
||||
double s = sin(angle);
|
||||
double x = axis.x;
|
||||
double y = axis.y;
|
||||
double z = axis.z;
|
||||
|
||||
ret.values[0][0] = (x * x * (1.0 - c) + c);
|
||||
ret.values[0][1] = (x * y * (1.0 - c) - z * s);
|
||||
ret.values[0][2] = (x * z * (1.0 - c) + y * s);
|
||||
ret.values[1][0] = (y * x * (1.0 - c) + z * s);
|
||||
ret.values[1][1] = (y * y * (1.0 - c) + c);
|
||||
ret.values[1][2] = (y * z * (1.0 - c) - x * s);
|
||||
ret.values[2][0] = (x * z * (1.0 - c) - y * s);
|
||||
ret.values[2][1] = (y * z * (1.0 - c) + x * s);
|
||||
ret.values[2][2] = (z * z * (1.0 - c) + c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Converts back from the rotation matrix to euler angles.
|
||||
double, double, double rotationToEulerAngles() {
|
||||
if (dt_GM_GlobalMaths.closeEnough(values[2][0], -1)) {
|
||||
double x = 90;
|
||||
double y = 0;
|
||||
double z = atan2(values[0][1], values[0][2]);
|
||||
return z, x, y;
|
||||
}
|
||||
else if (dt_GM_GlobalMaths.closeEnough(values[2][0], 1)) {
|
||||
double x = -90;
|
||||
double y = 0;
|
||||
double z = atan2(-values[0][1], -values[0][2]);
|
||||
return z, x, y;
|
||||
}
|
||||
else {
|
||||
float x1 = -asin(values[2][0]);
|
||||
float x2 = 180 - x1;
|
||||
|
||||
float y1 = atan2(values[2][1] / cos(x1), values[2][2] / cos(x1));
|
||||
float y2 = atan2(values[2][1] / cos(x2), values[2][2] / cos(x2));
|
||||
|
||||
float z1 = atan2(values[1][0] / cos(x1), values[0][0] / cos(x1));
|
||||
float z2 = atan2(values[1][0] / cos(x2), values[0][0] / cos(x2));
|
||||
|
||||
if ((abs(x1) + abs(y1) + abs(z1)) <= (abs(x2) + abs(y2) + abs(z2))) {
|
||||
return z1, x1, y1;
|
||||
}
|
||||
else {
|
||||
return z2, x2, y2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static dt_GM_Matrix4 createTRSEuler(Vector3 translate, double yaw, double pitch, double roll, Vector3 scale) {
|
||||
dt_GM_Matrix4 translateMat = dt_GM_Matrix4.identity();
|
||||
translateMat.values[0][3] = translate.x;
|
||||
translateMat.values[1][3] = translate.y;
|
||||
translateMat.values[2][3] = translate.z;
|
||||
|
||||
dt_GM_Matrix4 rotateMat = dt_GM_Matrix4.fromEulerAngles(yaw, pitch, roll);
|
||||
|
||||
dt_GM_Matrix4 scaleMat = dt_GM_Matrix4.identity();
|
||||
scaleMat.values[0][0] = scale.x;
|
||||
scaleMat.values[1][1] = scale.y;
|
||||
scaleMat.values[2][2] = scale.z;
|
||||
|
||||
dt_GM_Matrix4 ret = translateMat.multiplyMatrix(rotateMat);
|
||||
ret = ret.multiplyMatrix(scaleMat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static dt_GM_Matrix4 createTRSAxisAngle(Vector3 translate, Vector3 axis, double angle, Vector3 scale) {
|
||||
dt_GM_Matrix4 translateMat = dt_GM_Matrix4.identity();
|
||||
translateMat.values[0][3] = translate.x;
|
||||
translateMat.values[1][3] = translate.y;
|
||||
translateMat.values[2][3] = translate.z;
|
||||
|
||||
dt_GM_Matrix4 rotateMat = dt_GM_Matrix4.fromAxisAngle(axis, angle);
|
||||
|
||||
dt_GM_Matrix4 scaleMat = dt_GM_Matrix4.identity();
|
||||
scaleMat.values[0][0] = scale.x;
|
||||
scaleMat.values[1][1] = scale.y;
|
||||
scaleMat.values[2][2] = scale.z;
|
||||
|
||||
dt_GM_Matrix4 ret = translateMat.multiplyMatrix(rotateMat);
|
||||
ret = ret.multiplyMatrix(scaleMat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a view matrix.
|
||||
static dt_GM_Matrix4 view(Vector3 camPos, double yaw, double pitch, double roll) {
|
||||
// all of this is basically lifted and converted from PolyRenderer::SetupPerspectiveMatrix(),
|
||||
// so credit goes to Graf Zahl/dpJudas/whoever else
|
||||
// pitch needs to be adjusted by the pixel ratio
|
||||
float pixelRatio = level.pixelstretch;
|
||||
double angx = cos(pitch);
|
||||
double angy = sin(pitch) * pixelRatio;
|
||||
double alen = sqrt(angx * angx + angy * angy);
|
||||
double adjustedPitch = asin(angy / alen);
|
||||
double adjustedYaw = 90 - yaw;
|
||||
|
||||
// rotations
|
||||
let cz = cos(roll);
|
||||
let sz = sin(roll);
|
||||
let cx = cos(adjustedPitch);
|
||||
let sx = sin(adjustedPitch);
|
||||
let cy = cos(adjustedYaw);
|
||||
let sy = sin(adjustedYaw);
|
||||
|
||||
let rot = dt_GM_Matrix4.create();
|
||||
rot.values[0][0] = cz * cy - sz * sx * sy;
|
||||
rot.values[0][1] = -sz * cx;
|
||||
rot.values[0][2] = cz * sy + sz * sx * cy;
|
||||
|
||||
rot.values[1][0] = sz * cy + cz * sx * sy;
|
||||
rot.values[1][1] = cz * cx;
|
||||
rot.values[1][2] = sz * sy - cz * sx * cy;
|
||||
|
||||
rot.values[2][0] = -cx * sy;
|
||||
rot.values[2][1] = sx;
|
||||
rot.values[2][2] = cx * cy;
|
||||
|
||||
rot.values[3][3] = 1.0;
|
||||
|
||||
// pixel ratio scaling
|
||||
dt_GM_Matrix4 scale = dt_GM_Matrix4.identity();
|
||||
scale.values[1][1] = pixelRatio;
|
||||
// swapping y and z
|
||||
dt_GM_Matrix4 swapYZ = dt_GM_Matrix4.create();
|
||||
swapYZ.values[0][0] = 1;
|
||||
swapYZ.values[1][2] = 1;
|
||||
swapYZ.values[2][1] = -1;
|
||||
swapYZ.values[3][3] = 1;
|
||||
// translation
|
||||
dt_GM_Matrix4 translate = dt_GM_Matrix4.identity();
|
||||
translate.values[0][3] = -camPos.x;
|
||||
translate.values[1][3] = -camPos.y;
|
||||
translate.values[2][3] = -camPos.z;
|
||||
|
||||
// concatenate them all to get a final matrix
|
||||
dt_GM_Matrix4 ret = rot.multiplyMatrix(scale);
|
||||
ret = ret.multiplyMatrix(swapYZ);
|
||||
ret = ret.multiplyMatrix(translate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a perspective matrix (same format as gluPerspective).
|
||||
static dt_GM_Matrix4 perspective(double fovy, double aspect, double zNear, double zFar) {
|
||||
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
||||
double f = 1 / tan(fovy / 2.0);
|
||||
// x coord
|
||||
ret.values[0][0] = f / aspect;
|
||||
// y coord
|
||||
ret.values[1][1] = f;
|
||||
// z buffer coord
|
||||
ret.values[2][2] = (zFar + zNear) / (zNear - zFar);
|
||||
ret.values[2][3] = (2 * zFar * zNear) / (zNear - zFar);
|
||||
// w (homogeneous coordinates)
|
||||
ret.values[3][2] = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns a world->clip coords matrix from the passed args.
|
||||
static dt_GM_Matrix4 worldToClip(Vector3 viewPos, double yaw, double pitch, double roll, double FOV) {
|
||||
double aspect = Screen.getAspectRatio();
|
||||
double fovy = dt_GM_GlobalMaths.fovHToY(FOV);
|
||||
dt_GM_Matrix4 view = dt_GM_Matrix4.view(viewPos, yaw, pitch, roll);
|
||||
// 5 & 65535 are what are used internally, so they're used here for consistency
|
||||
dt_GM_Matrix4 perp = dt_GM_Matrix4.perspective(fovy, aspect, 5, 65535);
|
||||
dt_GM_Matrix4 worldToClip = perp.multiplyMatrix(view);
|
||||
return worldToClip;
|
||||
}
|
||||
|
||||
/// Adds two matrices and returns the result.
|
||||
dt_GM_Matrix4 addMatrix(dt_GM_Matrix4 other) const {
|
||||
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
||||
ret.values[0][0] = values[0][0] + other.values[0][0];
|
||||
ret.values[0][1] = values[0][1] + other.values[0][1];
|
||||
ret.values[0][2] = values[0][2] + other.values[0][2];
|
||||
ret.values[0][3] = values[0][3] + other.values[0][3];
|
||||
ret.values[1][0] = values[1][0] + other.values[1][0];
|
||||
ret.values[1][1] = values[1][1] + other.values[1][1];
|
||||
ret.values[1][2] = values[1][2] + other.values[1][2];
|
||||
ret.values[1][3] = values[1][3] + other.values[1][3];
|
||||
ret.values[2][0] = values[2][0] + other.values[2][0];
|
||||
ret.values[2][1] = values[2][1] + other.values[2][1];
|
||||
ret.values[2][2] = values[2][2] + other.values[2][2];
|
||||
ret.values[2][3] = values[2][3] + other.values[2][3];
|
||||
ret.values[3][0] = values[3][0] + other.values[3][0];
|
||||
ret.values[3][1] = values[3][1] + other.values[3][1];
|
||||
ret.values[3][2] = values[3][2] + other.values[3][2];
|
||||
ret.values[3][3] = values[3][3] + other.values[3][3];
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies the matrix by a scalar and returns the result.
|
||||
dt_GM_Matrix4 multiplyScalar(double scalar) const {
|
||||
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
||||
ret.values[0][0] = values[0][0] * scalar;
|
||||
ret.values[0][1] = values[0][1] * scalar;
|
||||
ret.values[0][2] = values[0][2] * scalar;
|
||||
ret.values[0][3] = values[0][3] * scalar;
|
||||
ret.values[1][0] = values[1][0] * scalar;
|
||||
ret.values[1][1] = values[1][1] * scalar;
|
||||
ret.values[1][2] = values[1][2] * scalar;
|
||||
ret.values[1][3] = values[1][3] * scalar;
|
||||
ret.values[2][0] = values[2][0] * scalar;
|
||||
ret.values[2][1] = values[2][1] * scalar;
|
||||
ret.values[2][2] = values[2][2] * scalar;
|
||||
ret.values[2][3] = values[2][3] * scalar;
|
||||
ret.values[3][0] = values[3][0] * scalar;
|
||||
ret.values[3][1] = values[3][1] * scalar;
|
||||
ret.values[3][2] = values[3][2] * scalar;
|
||||
ret.values[3][3] = values[3][3] * scalar;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies two matrices and returns the result.
|
||||
dt_GM_Matrix4 multiplyMatrix(dt_GM_Matrix4 other) const {
|
||||
dt_GM_Matrix4 ret = dt_GM_Matrix4.create();
|
||||
for (int row = 0; row < 4; row++) {
|
||||
ret.values[row][0] =
|
||||
values[row][0] * other.values[0][0] +
|
||||
values[row][1] * other.values[1][0] +
|
||||
values[row][2] * other.values[2][0] +
|
||||
values[row][3] * other.values[3][0];
|
||||
|
||||
ret.values[row][1] =
|
||||
values[row][0] * other.values[0][1] +
|
||||
values[row][1] * other.values[1][1] +
|
||||
values[row][2] * other.values[2][1] +
|
||||
values[row][3] * other.values[3][1];
|
||||
|
||||
ret.values[row][2] =
|
||||
values[row][0] * other.values[0][2] +
|
||||
values[row][1] * other.values[1][2] +
|
||||
values[row][2] * other.values[2][2] +
|
||||
values[row][3] * other.values[3][2];
|
||||
|
||||
ret.values[row][3] =
|
||||
values[row][0] * other.values[0][3] +
|
||||
values[row][1] * other.values[1][3] +
|
||||
values[row][2] * other.values[2][3] +
|
||||
values[row][3] * other.values[3][3];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Multiplies this Matrix by a 3D vector.
|
||||
Vector3 multiplyVector3(Vector3 vec, dt_GM_VectorType type = dt_GM_Vector_Position, bool divideW = true) const {
|
||||
let vecW = (type == dt_GM_Vector_Position) ? 1.0 : 0.0;
|
||||
|
||||
let ret = (
|
||||
values[0][0] * vec.x + values[0][1] * vec.y + values[0][2] * vec.z + values[0][3] * vecW,
|
||||
values[1][0] * vec.x + values[1][1] * vec.y + values[1][2] * vec.z + values[1][3] * vecW,
|
||||
values[2][0] * vec.x + values[2][1] * vec.y + values[2][2] * vec.z + values[2][3] * vecW
|
||||
);
|
||||
|
||||
if (divideW) {
|
||||
let retW = values[3][0] * vec.x + values[3][1] * vec.y + values[3][2] * vec.z + values[3][3] * vecW;
|
||||
ret /= retW;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
275
zscript/dt_Gutamatics/Quaternion.zsc
Normal file
275
zscript/dt_Gutamatics/Quaternion.zsc
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
class dt_GM_Quaternion {
|
||||
double w, x, y, z;
|
||||
|
||||
/// Initialises the Quaternion.
|
||||
dt_GM_Quaternion init(double w, double x, double y, double z) {
|
||||
self.w = w;
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.z = z;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Initialises the Quaternion in a static context.
|
||||
static dt_GM_Quaternion create(double w, double x, double y, double z) {
|
||||
return new("dt_GM_Quaternion").init(w, x, y, z);
|
||||
}
|
||||
|
||||
/// Sets up the quaternion using axis and angle.
|
||||
void setAxisAngle(Vector3 axis, double angle) {
|
||||
double lengthSquared = axis dot axis;
|
||||
// avoid a division by 0 and just return the identity
|
||||
if (dt_GM_GlobalMaths.closeEnough(lengthSquared, 0)) {
|
||||
init(1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
angle *= 0.5;
|
||||
|
||||
double sinTheta = sin(angle);
|
||||
double cosTheta = cos(angle);
|
||||
double factor = sinTheta / sqrt(lengthSquared);
|
||||
|
||||
w = cosTheta;
|
||||
x = factor * axis.x;
|
||||
y = factor * axis.y;
|
||||
z = factor * axis.z;
|
||||
}
|
||||
|
||||
/// Initialises the Quaternion using axis and angle.
|
||||
dt_GM_Quaternion initFromAxisAngle(Vector3 axis, double angle) {
|
||||
setAxisAngle(axis, angle);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Initialises the Quaternion using axis and angle in a static context.
|
||||
static dt_GM_Quaternion createFromAxisAngle(Vector3 axis, double angle) {
|
||||
return new("dt_GM_Quaternion").initFromAxisAngle(axis, angle);
|
||||
}
|
||||
|
||||
/// Sets up the quaternion using euler angles.
|
||||
void setAngles(double yaw, double pitch, double roll) {
|
||||
dt_GM_Quaternion zRotation = new("dt_GM_Quaternion").initFromAxisAngle((0, 0, 1), yaw);
|
||||
dt_GM_Quaternion yRotation = new("dt_GM_Quaternion").initFromAxisAngle((0, 1, 0), pitch);
|
||||
dt_GM_Quaternion xRotation = new("dt_GM_Quaternion").initFromAxisAngle((1, 0, 0), roll);
|
||||
dt_GM_Quaternion finalRotation = zRotation.multiplyQuat(yRotation);
|
||||
finalRotation = finalRotation.multiplyQuat(xRotation);
|
||||
copy(finalRotation);
|
||||
}
|
||||
|
||||
/// Initialises the quaternion using euler angles.
|
||||
dt_GM_Quaternion initFromAngles(double yaw, double pitch, double roll) {
|
||||
setAngles(yaw, pitch, roll);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Initialises the quaternion using euler angles in a static context.
|
||||
static dt_GM_Quaternion createFromAngles(double yaw, double pitch, double roll) {
|
||||
return new("dt_GM_Quaternion").initFromAngles(yaw, pitch, roll);
|
||||
}
|
||||
|
||||
/// Returns the euler angles from the Quaternion.
|
||||
double, double, double toAngles() {
|
||||
double singularityTest = z * x - w * y;
|
||||
double yawY = 2 * (w * z + x * y);
|
||||
double yawX = (1 - 2 * (y * y + z * z));
|
||||
|
||||
double singularityThreshold = 0.4999995;
|
||||
|
||||
double yaw = 0;
|
||||
double pitch = 0;
|
||||
double roll = 0;
|
||||
|
||||
if (singularityTest < -singularityThreshold) {
|
||||
pitch = 90;
|
||||
yaw = atan2(yawY, yawX);
|
||||
roll = dt_GM_GlobalMaths.normalize180(yaw + (2 * atan2(x, w)));
|
||||
}
|
||||
else if (singularityTest > singularityThreshold) {
|
||||
pitch = -90;
|
||||
yaw = atan2(yawY, yawX);
|
||||
roll = dt_GM_GlobalMaths.normalize180(yaw + (2 * atan2(x, w)));
|
||||
}
|
||||
else {
|
||||
pitch = -asin(2 * singularityTest);
|
||||
yaw = atan2(yawY, yawX);
|
||||
roll = atan2(2 * (w * x + y * z), (1 - 2 * (x * x + y * y)));
|
||||
}
|
||||
|
||||
return yaw, pitch, roll;
|
||||
}
|
||||
|
||||
/// Returns the conjugate of the Quaternion.
|
||||
dt_GM_Quaternion conjugate() const {
|
||||
return new("dt_GM_Quaternion").init(w, -x, -y, -z);
|
||||
}
|
||||
|
||||
/// Returns the normalised form of the Quaternion.
|
||||
dt_GM_Quaternion unit() const {
|
||||
double lengthSquared = w * w + x * x + y * y + z * z;
|
||||
if (dt_GM_GlobalMaths.closeEnough(lengthSquared, 0)) {
|
||||
return zero();
|
||||
}
|
||||
double factor = 1 / sqrt(lengthSquared);
|
||||
return new("dt_GM_Quaternion").init(w * factor, x * factor, y * factor, z * factor);
|
||||
}
|
||||
|
||||
/// Returns the inverse of the Quaternion (equal to conjugate if normalised).
|
||||
dt_GM_Quaternion inverse() {
|
||||
double norm = w * w + x * x + y * y + z * z;
|
||||
// if this is a zero quaternion, just return self
|
||||
if (dt_GM_GlobalMaths.closeEnough(norm, 0)) {
|
||||
return self;
|
||||
}
|
||||
double inverseNorm = 1/norm;
|
||||
return new("dt_GM_Quaternion").init(w * inverseNorm, x * -inverseNorm, y * -inverseNorm, z * -inverseNorm);
|
||||
}
|
||||
|
||||
/// Adds two Quaternions, returning the result.
|
||||
dt_GM_Quaternion add(dt_GM_Quaternion other) const {
|
||||
return new("dt_GM_Quaternion").init(w + other.w, x + other.x, y + other.y, z + other.z);
|
||||
}
|
||||
|
||||
/// Subtracts two Quaternions, returning the result.
|
||||
dt_GM_Quaternion subtract(dt_GM_Quaternion other) const {
|
||||
return new("dt_GM_Quaternion").init(w - other.w, x - other.x, y - other.y, z - other.z);
|
||||
}
|
||||
|
||||
/// Multiplies the Quaternion by a scalar, returning the result.
|
||||
dt_GM_Quaternion multiplyScalar(double scalar) const {
|
||||
return new("dt_GM_Quaternion").init(w * scalar, x * scalar, y * scalar, z * scalar);
|
||||
}
|
||||
|
||||
/// Multiplies two Quaternions, returning the result.
|
||||
dt_GM_Quaternion multiplyQuat(dt_GM_Quaternion other) const {
|
||||
return new("dt_GM_Quaternion").init(w * other.w - x * other.x - y * other.y - z * other.z,
|
||||
w * other.x + x * other.w + y * other.z - z * other.y,
|
||||
w * other.y + y * other.w + z * other.x - x * other.z,
|
||||
w * other.z + z * other.w + x * other.y - y * other.x );
|
||||
}
|
||||
|
||||
/// Negates the Quaternion.
|
||||
dt_GM_Quaternion negate() const {
|
||||
return new("dt_GM_Quaternion").init(-w, -x, -y, -z);
|
||||
}
|
||||
|
||||
/// Sets the values to 0 if they're close enough to 0.
|
||||
void clean() {
|
||||
if (dt_GM_GlobalMaths.closeEnough(w, 0)) w = 0;
|
||||
if (dt_GM_GlobalMaths.closeEnough(x, 0)) x = 0;
|
||||
if (dt_GM_GlobalMaths.closeEnough(y, 0)) y = 0;
|
||||
if (dt_GM_GlobalMaths.closeEnough(z, 0)) z = 0;
|
||||
}
|
||||
|
||||
/// Returns the length of the Quaternion squared.
|
||||
double lengthSquared() const {
|
||||
return (w * w + x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/// Returns the length of the Quaternion.
|
||||
double length() const {
|
||||
return sqrt(w * w + x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/// Returns whether the two Quaternions are equal.
|
||||
bool equals(dt_GM_Quaternion other) const {
|
||||
return dt_GM_GlobalMaths.closeEnough(w, other.w) && dt_GM_GlobalMaths.closeEnough(x, other.x) &&
|
||||
dt_GM_GlobalMaths.closeEnough(y, other.y) && dt_GM_GlobalMaths.closeEnough(z, other.z) ;
|
||||
}
|
||||
|
||||
/// Returns if the Quaternion is a 0 Quaternion.
|
||||
bool isZero() const {
|
||||
return dt_GM_GlobalMaths.closeEnough(w * w + x * x + y * y + z * z, 0);
|
||||
}
|
||||
|
||||
/// Returns if the Quaternion is a unit Quaternion.
|
||||
bool isUnit() const {
|
||||
return dt_GM_GlobalMaths.closeEnough(w * w + x * x + y * y + z * z, 1);
|
||||
}
|
||||
|
||||
/// Returns if the Quaternion is an identity Quaternion.
|
||||
bool isIdentity() const {
|
||||
return dt_GM_GlobalMaths.closeEnough(w, 1) && dt_GM_GlobalMaths.closeEnough(x, 0) &&
|
||||
dt_GM_GlobalMaths.closeEnough(y, 0) && dt_GM_GlobalMaths.closeEnough(z, 0) ;
|
||||
}
|
||||
|
||||
/// Returns the dot product of two Quaternions.
|
||||
double dotProduct(dt_GM_Quaternion other) const {
|
||||
return (w * other.w + x * other.x + y * other.y + z * other.z);
|
||||
}
|
||||
|
||||
/// Copies another Quaternion into this one.
|
||||
void copy(dt_GM_Quaternion other) {
|
||||
w = other.w;
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
}
|
||||
|
||||
/// Rotates a Vector3 using this Quaternion.
|
||||
Vector3 rotateVector3(Vector3 vec) const {
|
||||
dt_GM_Quaternion q = unit();
|
||||
|
||||
Vector3 u = (q.x, q.y, q.z);
|
||||
double s = q.w;
|
||||
|
||||
return 2 * (u dot vec) * u + (s * s - (u dot u)) * vec + 2 * s * u cross vec;
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two Quaternions, clamping the parameters.
|
||||
static dt_GM_Quaternion lerp(dt_GM_Quaternion from, dt_GM_Quaternion to, double time) {
|
||||
time = clamp(time, 0, 1);
|
||||
return lerpUnclamped(from, to, time);
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two Quaternions.
|
||||
static dt_GM_Quaternion lerpUnclamped(dt_GM_Quaternion from, dt_GM_Quaternion to, double time) {
|
||||
dt_GM_Quaternion ret = new("dt_GM_Quaternion");
|
||||
double reverseTime = 1 - time;
|
||||
ret.x = reverseTime * from.x + time * to.x;
|
||||
ret.y = reverseTime * from.y + time * to.y;
|
||||
ret.z = reverseTime * from.z + time * to.z;
|
||||
ret.w = reverseTime * from.w + time * to.w;
|
||||
ret = ret.unit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Spherically interpolates between two Quaternions, clamping the parameters.
|
||||
static dt_GM_Quaternion slerp(dt_GM_Quaternion from, dt_GM_Quaternion to, double time) {
|
||||
time = clamp(time, 0, 1);
|
||||
return slerpUnclamped(from, to, time);
|
||||
}
|
||||
|
||||
/// Spherically interpolates between two Quaternions.
|
||||
static dt_GM_Quaternion slerpUnclamped(dt_GM_Quaternion from, dt_GM_Quaternion to, double time) {
|
||||
dt_GM_Quaternion q3;
|
||||
double fromToDot = from.dotProduct(to);
|
||||
|
||||
if (fromToDot < 0) {
|
||||
fromToDot = -fromToDot;
|
||||
q3 = to.negate();
|
||||
}
|
||||
else {
|
||||
q3 = to;
|
||||
}
|
||||
|
||||
if (fromToDot < 0.95) {
|
||||
double angle = acos(fromToDot);
|
||||
return ((from.multiplyScalar(sin(angle * (1 - time)))).add(q3.multiplyScalar(sin(angle * time)))).multiplyScalar(1 / sin(angle));
|
||||
}
|
||||
else {
|
||||
return lerp(from, q3, time);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the 0 Quaternion.
|
||||
static dt_GM_Quaternion zero() {
|
||||
return new("dt_GM_Quaternion").init(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// Returns the identity Quaternion.
|
||||
static dt_GM_Quaternion identity() {
|
||||
return new("dt_GM_Quaternion").init(1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
29
zscript/dt_Gutamatics/VectorUtil.zsc
Normal file
29
zscript/dt_Gutamatics/VectorUtil.zsc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
class dt_GM_VectorUtil {
|
||||
/// Linearly interpolates between two Vector3s, clamping the parameters.
|
||||
static Vector3 lerpVec3(Vector3 from, Vector3 to, double time) {
|
||||
time = clamp(time, 0, 1);
|
||||
return lerpUnclampedVec3(from, to, time);
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two Vector3s.
|
||||
static Vector3 lerpUnclampedVec3(Vector3 from, Vector3 to, double time) {
|
||||
Vector3 ret;
|
||||
double reverseTime = 1 - time;
|
||||
ret = reverseTime * from + time * to;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two Vector2s, clamping the parameters.
|
||||
static Vector2 lerpVec2(Vector2 from, Vector2 to, double time) {
|
||||
time = clamp(time, 0, 1);
|
||||
return lerpUnclampedVec2(from, to, time);
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two Vector2s.
|
||||
static Vector2 lerpUnclampedVec2(Vector2 from, Vector2 to, double time) {
|
||||
Vector2 ret;
|
||||
double reverseTime = 1 - time;
|
||||
ret = reverseTime * from + time * to;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,35 @@
|
|||
/*
|
||||
Coordinate Utility helper class.
|
||||
(C)2018 Marisa Kirisame, UnSX Team.
|
||||
Released under the GNU Lesser General Public License version 3 (or later).
|
||||
See https://www.gnu.org/licenses/lgpl-3.0.txt for its terms.
|
||||
Reproduces the old UnrealScript Get(Un)Axes functions, providing XYZ axis
|
||||
vectors relative to an euler rotation (defaults to left-handed coords).
|
||||
|
||||
Copyright (c)2018-2022 Marisa the Magician, UnSX Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
Class dt_CoordUtil
|
||||
{
|
||||
// In Tim Sweeney's own words: "transform by a pitch-yaw-roll rotation"
|
||||
static Vector3, Vector3, Vector3 GetUnAxes( double pitch, double yaw, double roll )
|
||||
static Vector3, Vector3, Vector3 GetUnAxes( double pitch, double yaw, double roll, bool rhand = false )
|
||||
{
|
||||
Vector3 x = (1,0,0), y = (0,-1,0), z = (0,0,1); // y inverted for left-handed result
|
||||
Vector3 x = (1,0,0), y = (0,rhand?1:-1,0), z = (0,0,1);
|
||||
Vector3 a, b, c;
|
||||
// pitch and roll in gzdoom work in reverse compared to UE
|
||||
pitch = -pitch;
|
||||
|
|
@ -40,9 +59,9 @@ Class dt_CoordUtil
|
|||
}
|
||||
|
||||
// In Tim Sweeney's own words: "detransform by a pitch-yaw-roll rotation"
|
||||
static Vector3, Vector3, Vector3 GetAxes( double pitch, double yaw, double roll )
|
||||
static Vector3, Vector3, Vector3 GetAxes( double pitch, double yaw, double roll, bool rhand = false )
|
||||
{
|
||||
Vector3 x = (1,0,0), y = (0,-1,0), z = (0,0,1); // y inverted for left-handed result
|
||||
Vector3 x = (1,0,0), y = (0,rhand?1:-1,0), z = (0,0,1);
|
||||
Vector3 a, b, c;
|
||||
// pitch and roll in gzdoom work in reverse compared to UE
|
||||
pitch = -pitch;
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
libeye (for projection and deprojection)
|
||||
written by KeksDose / MemeDose (May 2019)
|
||||
(updated July 2019)
|
||||
|
||||
All rights etc. etc. who cares, you may reuse this as you wish and edit it, and
|
||||
leave this note intact.
|
||||
|
||||
//
|
||||
//
|
||||
/////((
|
||||
@@////////// ((
|
||||
(( %////((///// (( (( ((
|
||||
(( @@//(((/////// ((( ////////////// ((
|
||||
( /////////////////////////(((((((////////// ((
|
||||
@@///@@@@@@//////////(((((((((//@@@////////// ((
|
||||
(( ///// /////((((((((((((((//@@% %%@/////////// ((
|
||||
//////////((((((((((((((//////((((( %%@@////////// ((
|
||||
((%%//////////@@%% @@//(((((((/// ( (((( %%///////// (
|
||||
(( ////////@@ ((( @@//(((((((///// (( ( @@/////////
|
||||
((/////////@ (( (( @@/(((((((///// (( (( @@/((((///((
|
||||
(( ///((//@@ (( (( %@@@@@@////////// (((( /((((/// ((
|
||||
((/////((//%%( ////%%%@@////////// @//((((///
|
||||
////(((// ///////// //@@/////////////////(((((//
|
||||
( ////(((////( //////////////////////@@@@////////////(((((////((
|
||||
////(((((((// //////////////////////////// %%@@///(((((((((// (
|
||||
//(((((((((/////////////////// //////////// %%@//(((((((//// ((
|
||||
//(((((((((((////////////((/// /////////////// @@//(((((((///
|
||||
//(((((((((((////////(( /////////((//////////// ( //(((((((///////
|
||||
//((((((( ((///////( ##### ////( ((/////// ( @@//(((((///////
|
||||
//((((((( (((//////((( ### (((( ##### ((/////// (( //(((///////@@
|
||||
//((((((((((((@@/////////(((((((((((((((((((((///// ///////////% ((
|
||||
//((((((((((((%% ///////((((((((((((//////// //////////////@@%% ((
|
||||
@@//(((((((((/%%(( /////////////////////// ///////////////// (((
|
||||
( //(((((((((/ (( ///////////////////////
|
||||
%%//(((((((///(( (( //////////////@///////%%
|
||||
(( @@/((((((///(( (( //////////////@@%% /////// ((
|
||||
(( /(((((((// ( //////////////@%% //////%((
|
||||
(( @@//(((////(( (( /////(((((((///@@ ((( /////// (
|
||||
( @@(((((// ( ////(((((((///@@% (((( /////////%%((
|
||||
((%%//(((/////////(((((((//@%% //////////%% ((
|
||||
((////(((((///////(((((///////////////////////% ((
|
||||
( ////(((((/////(((((((((//////////((((///@@%% ((
|
||||
////(((//////@//////(((((((((((((////@@@%% (
|
||||
(( /////////@@%% %%@@@@@@@@@@@@@@@@@@@%% ((
|
||||
(( @@@@@%% ((
|
||||
(( (((
|
||||
|
||||
|
||||
|
||||
%%//%
|
||||
( //((/ ((
|
||||
( ///////((
|
||||
( ///////((
|
||||
///// ((
|
||||
//
|
||||
//
|
||||
|
||||
(( ///// ((
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
/* kd:
|
||||
|
||||
In open-gl, your screen rotates nicely and you can do mostly what you know
|
||||
to be sane. It's all about making a rotation of your view and using what
|
||||
you know about right triangles.
|
||||
|
||||
*/
|
||||
|
||||
class dtLe_GlScreen : dtLe_ProjScreen {
|
||||
protected vector3 forw_unit;
|
||||
protected vector3 right_unit;
|
||||
protected vector3 down_unit;
|
||||
|
||||
override void Reorient (vector3 world_view_pos, vector3 world_ang) {
|
||||
|
||||
// kd: Pitch is a weird gzd joke. It's probably to compensate looking
|
||||
// speed and all. After that, you see what makes this fast.
|
||||
world_ang.y = VectorAngle(
|
||||
cos(world_ang.y),
|
||||
sin(world_ang.y) * pixel_stretch);
|
||||
|
||||
|
||||
super.Reorient(world_view_pos, world_ang);
|
||||
|
||||
let cosang = cos(world_ang.x);
|
||||
let cosvang = cos(world_ang.y);
|
||||
let cosrang = cos(world_ang.z);
|
||||
let sinang = sin(world_ang.x);
|
||||
let sinvang = sin(world_ang.y);
|
||||
let sinrang = sin(world_ang.z);
|
||||
|
||||
let right_no_roll = (
|
||||
sinang,
|
||||
- cosang,
|
||||
0);
|
||||
|
||||
let down_no_roll = (
|
||||
- sinvang * cosang,
|
||||
- sinvang * sinang,
|
||||
- cosvang);
|
||||
|
||||
forw_unit = (
|
||||
cosvang * cosang,
|
||||
cosvang * sinang,
|
||||
- sinvang);
|
||||
|
||||
down_unit = cosrang * down_no_roll - sinrang * right_no_roll;
|
||||
right_unit = cosrang * right_no_roll + sinrang * down_no_roll;
|
||||
}
|
||||
|
||||
// kd: Projection handling. These get called to make stuff a little faster,
|
||||
// since you may wanna project many many times.
|
||||
protected vector3 forw_in;
|
||||
protected vector3 right_in;
|
||||
protected vector3 down_in;
|
||||
|
||||
override void BeginProjection () {
|
||||
forw_in = forw_unit;
|
||||
right_in = right_unit / tan_fov_2.x;
|
||||
down_in = down_unit / tan_fov_2.y;
|
||||
|
||||
forw_in.z *= pixel_stretch;
|
||||
right_in.z *= pixel_stretch;
|
||||
down_in.z *= pixel_stretch;
|
||||
}
|
||||
|
||||
override void ProjectWorldPos (vector3 world_pos) {
|
||||
diff = levellocals.vec3diff(view_pos, world_pos);
|
||||
proj_pos = (diff dot right_in, diff dot down_in);
|
||||
depth = diff dot forw_in;
|
||||
}
|
||||
|
||||
override void ProjectActorPos (Actor mo, vector3 offset, double t) {
|
||||
let inter_pos = mo.prev + t * (mo.pos - mo.prev);
|
||||
diff = levellocals.vec3diff(view_pos, inter_pos + offset);
|
||||
proj_pos = (diff dot right_in, diff dot down_in);
|
||||
depth = diff dot forw_in;
|
||||
}
|
||||
|
||||
override void ProjectActorPosPortal (Actor mo, vector3 offset, double t) {
|
||||
let inter_pos = mo.prev + t * levellocals.vec3diff(mo.prev, mo.pos);
|
||||
diff = levellocals.vec3diff(view_pos, inter_pos + offset);
|
||||
proj_pos = (diff dot right_in, diff dot down_in);
|
||||
depth = diff dot forw_in;
|
||||
}
|
||||
|
||||
override vector2 ProjectToNormal () const {
|
||||
return proj_pos / depth;
|
||||
}
|
||||
|
||||
override vector2 ProjectToScreen () const {
|
||||
let normal_pos = proj_pos / depth + (1, 1);
|
||||
|
||||
return 0.5 * (
|
||||
normal_pos.x * resolution.x,
|
||||
normal_pos.y * resolution.y);
|
||||
}
|
||||
|
||||
override vector2 ProjectToCustom (
|
||||
vector2 origin,
|
||||
vector2 resolution) const {
|
||||
let normal_pos = proj_pos / depth + (1, 1);
|
||||
|
||||
return origin + 0.5 * (
|
||||
normal_pos.x * resolution.x,
|
||||
normal_pos.y * resolution.y);
|
||||
}
|
||||
|
||||
// kd: Same deal but backwards-ish.
|
||||
protected vector3 forw_out;
|
||||
protected vector3 right_out;
|
||||
protected vector3 down_out;
|
||||
|
||||
override void BeginDeprojection () {
|
||||
|
||||
// kd: Same deal as above, but reversed. This time, we're compensating
|
||||
// for what we rightfully assume is a projected position.
|
||||
forw_out = forw_unit;
|
||||
right_out = right_unit * tan_fov_2.x;
|
||||
down_out = down_unit * tan_fov_2.y;
|
||||
|
||||
forw_out.z /= pixel_stretch;
|
||||
right_out.z /= pixel_stretch;
|
||||
down_out.z /= pixel_stretch;
|
||||
}
|
||||
|
||||
override vector3 DeprojectNormalToDiff (
|
||||
vector2 normal_pos,
|
||||
double depth) const {
|
||||
return depth * (
|
||||
forw_out +
|
||||
normal_pos.x * right_out +
|
||||
normal_pos.y * down_out);
|
||||
}
|
||||
|
||||
override vector3 DeprojectScreenToDiff (
|
||||
vector2 screen_pos,
|
||||
double depth) const {
|
||||
|
||||
// kd: Same thing...
|
||||
let normal_pos = 2 * (
|
||||
screen_pos.x / resolution.x,
|
||||
screen_pos.y / resolution.y) - (1, 1);
|
||||
|
||||
return depth * (
|
||||
forw_out +
|
||||
normal_pos.x * right_out +
|
||||
normal_pos.y * down_out);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/* kd:
|
||||
|
||||
This does projection stuff in Carmack / software renderer. It's conceptually
|
||||
simpler, but nonetheless a little tricky to understand if you're
|
||||
used to open-gl.
|
||||
|
||||
*/
|
||||
|
||||
class dtLe_SwScreen : dtLe_ProjScreen {
|
||||
|
||||
// kd: Less info necessary than for open-gl, but it's there.
|
||||
protected vector2 right_planar_unit;
|
||||
protected vector3 forw_planar_unit;
|
||||
|
||||
override void Reorient (vector3 world_view_pos, vector3 world_ang) {
|
||||
super.Reorient(world_view_pos, world_ang);
|
||||
|
||||
right_planar_unit = (
|
||||
sin(view_ang.x),
|
||||
- cos(view_ang.x));
|
||||
|
||||
forw_planar_unit = (
|
||||
- right_planar_unit.y,
|
||||
right_planar_unit.x,
|
||||
tan(view_ang.y));
|
||||
}
|
||||
|
||||
// kd: Projection:
|
||||
protected vector3 forw_planar_in;
|
||||
protected vector2 right_planar_in;
|
||||
|
||||
override void BeginProjection () {
|
||||
|
||||
// kd: This doesn't cause any imprecisions. It also prevents two
|
||||
// multiplications with every projection.
|
||||
right_planar_in = right_planar_unit / tan_fov_2.x;
|
||||
forw_planar_in = forw_planar_unit;
|
||||
}
|
||||
|
||||
override void ProjectWorldPos (vector3 world_pos) {
|
||||
|
||||
// kd: Your view is flat. If you pitch up or down, imagine that all the
|
||||
// actors move up and down in reality. That's effectively how it works.
|
||||
// You can see this in the addition to diff.z.
|
||||
diff = levellocals.vec3diff(view_pos, world_pos);
|
||||
depth = forw_planar_in.xy dot diff.xy;
|
||||
diff.z += forw_planar_in.z * depth;
|
||||
proj_pos = (
|
||||
right_planar_in dot diff.xy,
|
||||
- pixel_stretch * diff.z / tan_fov_2.y);
|
||||
}
|
||||
|
||||
override void ProjectActorPos (Actor mo, vector3 offset, double t) {
|
||||
let inter_pos = mo.prev + t * (mo.pos - mo.prev);
|
||||
ProjectWorldPos(inter_pos + offset);
|
||||
}
|
||||
|
||||
override void ProjectActorPosPortal (Actor mo, vector3 offset, double t) {
|
||||
let inter_pos = mo.prev + t * levellocals.vec3diff(mo.prev, mo.pos);
|
||||
ProjectWorldPos(inter_pos + offset);
|
||||
}
|
||||
|
||||
override vector2 ProjectToNormal () const {
|
||||
return proj_pos / depth;
|
||||
}
|
||||
|
||||
override vector2 ProjectToScreen () const {
|
||||
let normal_pos = proj_pos / depth + (1, 1);
|
||||
|
||||
return 0.5 * (
|
||||
normal_pos.x * resolution.x,
|
||||
normal_pos.y * resolution.y);
|
||||
}
|
||||
|
||||
override vector2 ProjectToCustom (
|
||||
vector2 origin,
|
||||
vector2 resolution) const {
|
||||
let normal_pos = proj_pos / depth + (1, 1);
|
||||
|
||||
return origin + 0.5 * (
|
||||
normal_pos.x * resolution.x,
|
||||
normal_pos.y * resolution.y);
|
||||
}
|
||||
|
||||
// kd: Just as simple. You again assume you are trying to reverse a
|
||||
// projected position from the screen back into the world.
|
||||
protected vector3 forw_planar_out;
|
||||
protected vector3 right_planar_out;
|
||||
protected vector3 down_planar_out;
|
||||
|
||||
override void BeginDeprojection () {
|
||||
forw_planar_out.xy = forw_planar_unit.xy;
|
||||
forw_planar_out.z = 0;
|
||||
right_planar_out.xy = tan_fov_2.x * right_planar_unit;
|
||||
right_planar_out.z = 0;
|
||||
down_planar_out = (
|
||||
0,
|
||||
0,
|
||||
tan_fov_2.y / pixel_stretch);
|
||||
}
|
||||
|
||||
override vector3 DeprojectNormalToDiff (
|
||||
vector2 normal_pos,
|
||||
double depth) const {
|
||||
return depth * (
|
||||
forw_planar_out +
|
||||
normal_pos.x * right_planar_out +
|
||||
- (0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
|
||||
}
|
||||
|
||||
override vector3 DeprojectScreenToDiff (
|
||||
vector2 screen_pos,
|
||||
double depth) const {
|
||||
|
||||
// kd: Same thing...
|
||||
let normal_pos = 2 * (
|
||||
screen_pos.x / resolution.x,
|
||||
screen_pos.y / resolution.y) - (1, 1);
|
||||
|
||||
return depth * (
|
||||
forw_planar_out +
|
||||
normal_pos.x * right_planar_out +
|
||||
- (0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
/* kd:
|
||||
|
||||
Here's how to do projections and deprojections. You'd use the subclasses
|
||||
to do anything worthwhile. You may project world to screen and backwards.
|
||||
|
||||
*/
|
||||
|
||||
class dtLe_ProjScreen {
|
||||
|
||||
// kd: Screen info
|
||||
protected vector2 resolution;
|
||||
protected vector2 origin;
|
||||
protected vector2 tan_fov_2;
|
||||
protected double pixel_stretch;
|
||||
protected double aspect_ratio;
|
||||
|
||||
// kd: Setup calls which you'll need to call at least once.
|
||||
void CacheResolution () {
|
||||
CacheCustomResolution((Screen.GetWidth(), Screen.GetHeight()) );
|
||||
}
|
||||
|
||||
void CacheCustomResolution (vector2 new_resolution) {
|
||||
|
||||
// kd: This is for convenience and converting normal <-> screen pos.
|
||||
resolution = new_resolution;
|
||||
|
||||
// kd: This isn't really necessary but I kinda like it.
|
||||
pixel_stretch = level.pixelstretch;
|
||||
|
||||
// kd: Get the aspect ratio. 5:4 is handled just like 4:3... I GUESS
|
||||
// this'll do.
|
||||
aspect_ratio = max(4.0 / 3, Screen.GetAspectRatio());
|
||||
}
|
||||
|
||||
double AspectRatio () const {
|
||||
return aspect_ratio;
|
||||
}
|
||||
|
||||
// kd: Once you know you got screen info, you can call this whenever your
|
||||
// fov changes. Like CacheFov(player.fov) will do.
|
||||
void CacheFov (double hor_fov = 90) {
|
||||
|
||||
// kd: This holds: aspect ratio = tan(horizontal fov) / tan(ver fov).
|
||||
// gzd always uses hor fov, but the fov only holds in 4:3 (in a 4:3 box
|
||||
// in your screen centre), so we just extend it.
|
||||
tan_fov_2.x = tan(hor_fov / 2) * aspect_ratio / (4.0 / 3);
|
||||
tan_fov_2.y = tan_fov_2.x / aspect_ratio;
|
||||
}
|
||||
|
||||
// kd: Also need some view info. Angle is yaw, pitch, roll in world format
|
||||
// so positive pitch is up. Call one of the following functions.
|
||||
protected vector3 view_ang;
|
||||
protected vector3 view_pos;
|
||||
|
||||
ui void OrientForRenderOverlay (RenderEvent event) {
|
||||
Reorient(
|
||||
event.viewpos, (
|
||||
event.viewangle,
|
||||
event.viewpitch,
|
||||
event.viewroll));
|
||||
}
|
||||
|
||||
ui void OrientForRenderUnderlay (RenderEvent event) {
|
||||
Reorient(
|
||||
event.viewpos, (
|
||||
event.viewangle,
|
||||
event.viewpitch,
|
||||
event.viewroll));
|
||||
}
|
||||
|
||||
void OrientForPlayer (PlayerInfo player) {
|
||||
Reorient(
|
||||
player.mo.vec3offset(0, 0, player.viewheight), (
|
||||
player.mo.angle,
|
||||
player.mo.pitch,
|
||||
player.mo.roll));
|
||||
}
|
||||
|
||||
virtual void Reorient (vector3 world_view_pos, vector3 world_ang) {
|
||||
view_ang = world_ang;
|
||||
view_pos = world_view_pos;
|
||||
}
|
||||
|
||||
// kd: Now we can do projections and such (position in the level, go to
|
||||
// your screen).
|
||||
protected double depth;
|
||||
protected vector2 proj_pos;
|
||||
protected vector3 diff;
|
||||
|
||||
virtual void BeginProjection () {}
|
||||
virtual void ProjectWorldPos (vector3 world_pos) {}
|
||||
virtual void ProjectActorPos (
|
||||
Actor mo,
|
||||
vector3 offset = (0,0,0),
|
||||
double t = 1) {}
|
||||
|
||||
// kd: Portal aware version.
|
||||
virtual void ProjectActorPosPortal (
|
||||
Actor mo,
|
||||
vector3 offset = (0,0,0),
|
||||
double t = 1) {}
|
||||
|
||||
virtual vector2 ProjectToNormal () const { return (0, 0); }
|
||||
virtual vector2 ProjectToScreen () const { return (0, 0); }
|
||||
|
||||
virtual vector2 ProjectToCustom (
|
||||
vector2 origin,
|
||||
vector2 resolution) const {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
bool IsInFront () const {
|
||||
return 0 < depth;
|
||||
}
|
||||
|
||||
bool IsInScreen () const {
|
||||
if( proj_pos.x < -depth || depth < proj_pos.x ||
|
||||
proj_pos.y < -depth || depth < proj_pos.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// kd: Deprojection (point on screen, go into the world):
|
||||
virtual void BeginDeprojection () {}
|
||||
|
||||
virtual vector3 DeprojectNormalToDiff (
|
||||
vector2 normal_pos,
|
||||
double depth = 1) const {
|
||||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
virtual vector3 DeprojectScreenToDiff (
|
||||
vector2 screen_pos,
|
||||
double depth = 1) const {
|
||||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
virtual vector3 DeprojectCustomToDiff (
|
||||
vector2 origin,
|
||||
vector2 resolution,
|
||||
vector2 screen_pos,
|
||||
double depth = 1) const {
|
||||
return (0, 0, 0);
|
||||
}
|
||||
|
||||
// kd: A normal position is in the -1 <= x, y <= 1 range on your screen.
|
||||
// This will be your screen no matter the resolution:
|
||||
|
||||
/*
|
||||
|
||||
(-1, -1) -- --- --- (0, -1) --- --- --- --- (1, -1)
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
(-1, 0) (0, 0) (1, 0)
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
(-1, 1) --- --- --- (0, 1) --- --- --- --- (1, 1)
|
||||
|
||||
*/
|
||||
|
||||
// So this scales such a position back into your drawing resolution.
|
||||
|
||||
vector2 NormalToScreen (vector2 normal_pos) const {
|
||||
normal_pos = 0.5 * (normal_pos + (1, 1));
|
||||
return (
|
||||
normal_pos.x * resolution.x,
|
||||
normal_pos.y * resolution.y);
|
||||
}
|
||||
|
||||
// kd: And this brings a screen position to normal. Make sure the resolution
|
||||
// is the same for your cursor.
|
||||
|
||||
vector2 ScreenToNormal (vector2 screen_pos) const {
|
||||
screen_pos = (
|
||||
screen_pos.x / resolution.x,
|
||||
screen_pos.y / resolution.y);
|
||||
return 2 * screen_pos - (1, 1);
|
||||
}
|
||||
|
||||
// kd: Other interesting stuff.
|
||||
|
||||
vector3 Difference () const {
|
||||
return diff;
|
||||
}
|
||||
|
||||
double Distance () const {
|
||||
return diff.length();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
/* kd:
|
||||
|
||||
This helps repositioning the view port for stuff like screen blocks. It's a
|
||||
little more than that, cuz it can also determine stuff like, "is this scene
|
||||
position in the viewport?" Cuz the scene doesn't necessarily match the
|
||||
viewport.
|
||||
|
||||
Well yea... see the examples. Imagine how annoying it is to even get this
|
||||
idea to begin with.
|
||||
|
||||
*/
|
||||
|
||||
struct dtLe_Viewport {
|
||||
|
||||
private vector2 scene_origin;
|
||||
private vector2 scene_size;
|
||||
|
||||
private vector2 viewport_origin;
|
||||
private vector2 viewport_bound;
|
||||
private vector2 viewport_size;
|
||||
|
||||
private double scene_aspect;
|
||||
private double viewport_aspect;
|
||||
|
||||
private double scale_f;
|
||||
private vector2 scene_to_viewport;
|
||||
|
||||
ui void FromHud () const {
|
||||
scene_aspect = Screen.GetAspectRatio();
|
||||
|
||||
vector2 hud_origin;
|
||||
vector2 hud_size;
|
||||
|
||||
[hud_origin.x, hud_origin.y, hud_size.x, hud_size.y] =
|
||||
Screen.GetViewWindow();
|
||||
|
||||
let window_resolution = (
|
||||
Screen.GetWidth(),
|
||||
Screen.GetHeight());
|
||||
|
||||
let window_to_normal = (
|
||||
1.0 / window_resolution.x,
|
||||
1.0 / window_resolution.y);
|
||||
|
||||
viewport_origin = (
|
||||
window_to_normal.x * hud_origin.x,
|
||||
window_to_normal.y * hud_origin.y);
|
||||
|
||||
viewport_size = (
|
||||
window_to_normal.x * hud_size.x,
|
||||
window_to_normal.y * hud_size.y);
|
||||
|
||||
viewport_aspect = hud_size.x / hud_size.y;
|
||||
|
||||
viewport_bound = viewport_origin + viewport_size;
|
||||
|
||||
// kd: The scene is what is actually rendered. It's not always the same
|
||||
// as the viewport. When the statusbar comes into play, the scene is
|
||||
// obscured by the viewport being too small.
|
||||
|
||||
// Example: Compare screenblocks 11 against screenblocks 10 in unmodded
|
||||
// Doom. You will notice that the scaling of the 3d world is the same,
|
||||
// but it's moved up by half the height of the statusbar.
|
||||
|
||||
// That makes this viewport stuff kinda really annoying to deal with.
|
||||
|
||||
// Also statusbar.getsomethingfromstatusbar, really really nice naming.
|
||||
|
||||
let statusbar_height =
|
||||
(window_resolution.y - Statusbar.GetTopOfStatusbar()) / window_resolution.y;
|
||||
|
||||
scale_f = hud_size.x / window_resolution.x;
|
||||
|
||||
scene_aspect = Screen.GetAspectRatio();
|
||||
|
||||
let offset = 10 < screenblocks ? 0 : 0.5 * statusbar_height;
|
||||
|
||||
scene_size = (
|
||||
scale_f,
|
||||
scale_f);
|
||||
|
||||
scene_origin = viewport_origin - (0, 0.5 * (scene_size.y - viewport_size.y));
|
||||
|
||||
scene_to_viewport = (
|
||||
viewport_size.x / scene_size.x,
|
||||
viewport_size.y / scene_size.y);
|
||||
}
|
||||
|
||||
// kd: Is the scene pos (normal, just like projected normal) inside the
|
||||
// view port? If yes, it's visible in the 3d world, even through resizing.
|
||||
bool IsInside (vector2 scene_pos) const {
|
||||
let normal_pos = scene_origin + (
|
||||
scene_size.x * 0.5 * (1 + scene_pos.x),
|
||||
scene_size.y * 0.5 * (1 + scene_pos.y));
|
||||
|
||||
if( normal_pos.x < viewport_origin.x || viewport_bound.x < normal_pos.x ||
|
||||
normal_pos.y < viewport_origin.y || viewport_bound.y < normal_pos.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// kd: Use these for drawing (and make sure the aspect ratios match).
|
||||
vector2 SceneToCustom (vector2 scene_pos, vector2 resolution) const {
|
||||
let normal_pos = 0.5 * (
|
||||
(scene_pos.x + 1) * scene_size.x,
|
||||
(scene_pos.y + 1) * scene_size.y);
|
||||
|
||||
return (
|
||||
(scene_origin.x + normal_pos.x) * resolution.x,
|
||||
(scene_origin.y + normal_pos.y) * resolution.y);
|
||||
}
|
||||
|
||||
vector2 SceneToWindow (vector2 scene_pos) const {
|
||||
return SceneToCustom(
|
||||
scene_pos,
|
||||
(Screen.GetWidth(), Screen.GetHeight()) );
|
||||
}
|
||||
|
||||
vector2 ViewportToCustom (vector2 viewport_pos, vector2 resolution) const {
|
||||
let normal_pos = 0.5 * (
|
||||
(viewport_pos.x + 1) * viewport_size.x,
|
||||
(viewport_pos.y + 1) * viewport_size.y);
|
||||
|
||||
|
||||
return (
|
||||
(viewport_origin.x + normal_pos.x) * resolution.x,
|
||||
(viewport_origin.y + normal_pos.y) * resolution.y);
|
||||
}
|
||||
|
||||
vector2 ViewportToWindow (vector2 viewport_pos) const {
|
||||
return ViewportToCustom(
|
||||
viewport_pos,
|
||||
(Screen.GetWidth(), Screen.GetHeight()) );
|
||||
}
|
||||
|
||||
double Scale () const {
|
||||
return scale_f;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
Quaternion math helper class.
|
||||
(C)2018 Marisa Kirisame, UnSX Team.
|
||||
Released under the GNU Lesser General Public License version 3 (or later).
|
||||
See https://www.gnu.org/licenses/lgpl-3.0.txt for its terms.
|
||||
*/
|
||||
|
||||
Class dt_Quat
|
||||
{
|
||||
protected double W, X, Y, Z;
|
||||
|
||||
dt_Quat init( double w, double x, double y, double z )
|
||||
{
|
||||
self.W = w;
|
||||
self.X = x;
|
||||
self.Y = y;
|
||||
self.Z = z;
|
||||
return self;
|
||||
}
|
||||
|
||||
void copy( dt_Quat q )
|
||||
{
|
||||
W = q.W;
|
||||
X = q.X;
|
||||
Y = q.Y;
|
||||
Z = q.Z;
|
||||
}
|
||||
|
||||
static dt_Quat create( double w, double x, double y, double z )
|
||||
{
|
||||
return new("dt_Quat").init(w,x,y,z);
|
||||
}
|
||||
|
||||
static dt_Quat create_axis( Vector3 axis, double theta )
|
||||
{
|
||||
double scale = axis dot axis;
|
||||
if ( scale < double.epsilon ) return dt_Quat.create(1,0,0,0);
|
||||
theta *= 0.5;
|
||||
double f = sin(theta)/sqrt(scale);
|
||||
return dt_Quat.create(cos(theta),axis.x*f,axis.y*f,axis.z*f);
|
||||
}
|
||||
|
||||
static dt_Quat create_euler( double pitch, double yaw, double roll )
|
||||
{
|
||||
dt_Quat zrot = dt_Quat.create_axis((0,0,1),yaw);
|
||||
dt_Quat yrot = dt_Quat.create_axis((0,1,0),pitch);
|
||||
dt_Quat xrot = dt_Quat.create_axis((1,0,0),roll);
|
||||
dt_Quat sum = zrot.qmul(yrot);
|
||||
sum = sum.qmul(xrot);
|
||||
return sum;
|
||||
}
|
||||
|
||||
// copied here since Actor.Normalize180 is not (yet) clearscope
|
||||
static double Normalize180( double ang )
|
||||
{
|
||||
ang = ang%360;
|
||||
ang = (ang+360)%360;
|
||||
if ( ang > 180 ) ang -= 360;
|
||||
return ang;
|
||||
}
|
||||
|
||||
double, double, double to_euler()
|
||||
{
|
||||
double stest = z*x-w*y;
|
||||
double yawY = 2*(w*z+x*y);
|
||||
double yawX = 1-2*(y*y+z*z);
|
||||
double st = 0.4999995;
|
||||
double pitch = 0;
|
||||
double yaw = 0;
|
||||
double roll = 0;
|
||||
if ( stest <= -st )
|
||||
{
|
||||
pitch = 90;
|
||||
yaw = atan2(yawY,yawX);
|
||||
roll = Normalize180(yaw+(2*atan2(x,w)));
|
||||
}
|
||||
else if ( stest > st )
|
||||
{
|
||||
pitch = -90;
|
||||
yaw = atan2(yawY,yawX);
|
||||
roll = Normalize180(yaw+(2*atan2(x,w)));
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = -asin(2*stest);
|
||||
yaw = atan2(yawY,yawX);
|
||||
roll = atan2(2*(w*x+y*z),(1-2*(x*x+y*y)));
|
||||
}
|
||||
return pitch, yaw, roll;
|
||||
}
|
||||
|
||||
dt_Quat qmul( dt_Quat q )
|
||||
{
|
||||
return dt_Quat.create(w*q.w-x*q.x-y*q.y-z*q.z,w*q.x+x*q.w+y*q.z-z
|
||||
*q.y,w*q.y+y*q.w+z*q.x-x*q.z,w*q.z+z*q.w+x*q.y-y*q.x);
|
||||
}
|
||||
}
|
||||
67
zscript/dt_utility.zsc
Normal file
67
zscript/dt_utility.zsc
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// gutamatics projection data caching and other functions from SWWM GZ
|
||||
|
||||
Struct dt_ProjectionData
|
||||
{
|
||||
dt_GM_Matrix wtc;
|
||||
int viewx, viewy, vieww, viewh;
|
||||
}
|
||||
|
||||
Class dt_Utility
|
||||
{
|
||||
// gutamatics caching
|
||||
static clearscope void PrepareProjData( out dt_ProjectionData d, Vector3 viewpos, double angle, double pitch, double roll, double fov )
|
||||
{
|
||||
double aspect = Screen.GetAspectRatio();
|
||||
// vertical fov
|
||||
double fovratio = (aspect>=1.3)?1.333333:aspect;
|
||||
double fovy = 2.*atan(tan(clamp(fov,5,170)/2.)/fovratio);
|
||||
// world→clip matrix
|
||||
dt_GM_Matrix view = dt_GM_Matrix.view(viewpos,angle,pitch,roll);
|
||||
dt_GM_Matrix perp = dt_GM_Matrix.perspective(fovy,aspect,5,65535);
|
||||
d.wtc = perp.multiplyMatrix(view);
|
||||
// screen coord data
|
||||
int sblocks = CVar.FindCVar('screenblocks').GetInt();
|
||||
int viewx, viewy, vieww, viewh;
|
||||
[viewx, viewy, vieww, viewh] = Screen.GetViewWindow();
|
||||
int sh = Screen.GetHeight();
|
||||
int h = sh;
|
||||
if ( sblocks < 10 ) h = (sblocks*sh/10)&~7;
|
||||
int bottom = sh-(h+viewy-((h-viewh)/2));
|
||||
d.viewx = viewx;
|
||||
d.viewy = sh-bottom-h;
|
||||
d.vieww = vieww;
|
||||
d.viewh = h;
|
||||
}
|
||||
|
||||
static clearscope Vector3 ProjectPoint( dt_ProjectionData d, Vector3 worldpos )
|
||||
{
|
||||
return d.wtc.multiplyVector3(worldpos).asVector3();
|
||||
}
|
||||
|
||||
static clearscope Vector2 NDCToViewport( dt_ProjectionData d, Vector3 ndc )
|
||||
{
|
||||
return (d.viewx,d.viewy)+(((ndc.x+1)*d.vieww)/2,((-ndc.y+1)*d.viewh)/2);
|
||||
}
|
||||
|
||||
// checks if a point is inside the viewport
|
||||
static clearscope bool TestScreenBounds( dt_ProjectionData d, Vector2 vpos )
|
||||
{
|
||||
return ((vpos.x == clamp(vpos.x,d.viewx,d.viewx+d.vieww))
|
||||
&& (vpos.y == clamp(vpos.y,d.viewy,d.viewy+d.viewh)));
|
||||
}
|
||||
|
||||
static clearscope Vector3 Vec3FromAngle( double angle, double pitch )
|
||||
{
|
||||
return (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
}
|
||||
|
||||
static clearscope Vector3 CircleOffset( Vector3 y, Vector3 z, double angle, double radius )
|
||||
{
|
||||
return (y*cos(angle)*radius+z*sin(angle)*radius);
|
||||
}
|
||||
|
||||
static clearscope Vector3 ConeSpread( Vector3 x, Vector3 y, Vector3 z, double angle, double spread )
|
||||
{
|
||||
return (x+y*cos(angle)*spread+z*sin(angle)*spread).unit();
|
||||
}
|
||||
}
|
||||
|
|
@ -290,35 +290,37 @@ Class UTRocketLauncher : UTWeapon
|
|||
{
|
||||
bool LockedOn;
|
||||
Actor LockedTarget;
|
||||
TextureID lockontex;
|
||||
int locktics;
|
||||
bool bSingleRocket;
|
||||
|
||||
UTRocketLauncherLED ammoled;
|
||||
ui Canvas AmmoLed;
|
||||
ui TextureID LedBase, LedFont;
|
||||
ui TextureID lockontex;
|
||||
|
||||
override void DoEffect()
|
||||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !ammoled )
|
||||
Vector2 fnt[] =
|
||||
{
|
||||
let ti = ThinkerIterator.Create("UTRocketLauncherLED");
|
||||
while ( ammoled=UTRocketLauncherLED(ti.Next()) ) break;
|
||||
if ( !ammoled ) ammoled = UTRocketLauncherLED(Spawn("UTRocketLauncherLED"));
|
||||
}
|
||||
if ( !Owner || !Owner.player || (Owner != players[consoleplayer].Camera) ) return;
|
||||
ammoled.mygun = self;
|
||||
TexMan.SetCameraToTexture(ammoled,"RAmmoLed",90);
|
||||
(0,1), (21,1), (42,1), (63,1), (84,1),
|
||||
(105,1), (126,1), (147,1), (168,1), (189,1)
|
||||
};
|
||||
if ( !AmmoLed ) AmmoLed = TexMan.GetCanvas("MiniALed");
|
||||
if ( !LedFont ) LedFont = TexMan.CheckForTexture("models/LEDFont2.png",TexMan.Type_Any);
|
||||
AmmoLed.Clear(0,0,64,64,"Black");
|
||||
int dg3 = (Ammo1.Amount/100)%10;
|
||||
int dg2 = (Ammo1.Amount/10)%10;
|
||||
int dg1 = Ammo1.Amount%10;
|
||||
AmmoLed.DrawTexture(LedFont,false,2,14,DTA_SrcX,fnt[dg3].x,DTA_SrcY,fnt[dg3].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,22,14,DTA_SrcX,fnt[dg2].x,DTA_SrcY,fnt[dg2].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,42,14,DTA_SrcX,fnt[dg1].x,DTA_SrcY,fnt[dg1].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
lockontex = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
|
||||
}
|
||||
|
||||
override void PreRender( double lbottom )
|
||||
{
|
||||
if ( LockedTarget ) Screen.DrawTexture(lockontex,false,Screen.GetWidth()*0.5,Screen.GetHeight()*0.5);
|
||||
if ( LockedTarget )
|
||||
{
|
||||
if ( !lockontex ) lockontex = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
|
||||
Screen.DrawTexture(lockontex,false,Screen.GetWidth()*0.5,Screen.GetHeight()*0.5);
|
||||
}
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
|
|
@ -395,8 +397,8 @@ Class UTRocketLauncher : UTWeapon
|
|||
{
|
||||
a = FRandom[Eightball](0,360);
|
||||
s = FRandom[Eightball](0,(num>1)?12:0);
|
||||
Vector3 dir = (x2+cos(a)*y2*s*0.004+sin(a)*z2*s*0.004).unit();
|
||||
p = Spawn("UTGrenade",level.Vec3Offset(origin,cos(a)*y*s+sin(a)*z*s));
|
||||
Vector3 dir = dt_Utility.ConeSpread(x2,y2,z2,a,s*0.004);
|
||||
p = Spawn("UTGrenade",level.Vec3Offset(origin,dt_Utility.CircleOffset(y,z,a,s)));
|
||||
p.vel = x*(vel dot x)*0.4 + dir*p.speed*FRandom[Eightball](1.0,1.2);
|
||||
p.vel.z += 3.5;
|
||||
p.target = self;
|
||||
|
|
@ -405,7 +407,7 @@ Class UTRocketLauncher : UTWeapon
|
|||
else if ( num <= 1 )
|
||||
{
|
||||
// single rocket
|
||||
p = Spawn("UTRocket",level.Vec3Offset(origin,cos(a)*y*s+sin(a)*z*s));
|
||||
p = Spawn("UTRocket",level.Vec3Offset(origin,dt_Utility.CircleOffset(y,z,a,s)));
|
||||
p.vel = x2*p.speed;
|
||||
p.target = self;
|
||||
p.tracer = invoker.LockedTarget;
|
||||
|
|
@ -418,7 +420,7 @@ Class UTRocketLauncher : UTWeapon
|
|||
s = (num>1)?6:0;
|
||||
for ( int i=0; i<num; i++ )
|
||||
{
|
||||
p = Spawn("UTRocket",level.Vec3Offset(origin,cos(a)*y*s+sin(a)*z*s));
|
||||
p = Spawn("UTRocket",level.Vec3Offset(origin,dt_Utility.CircleOffset(y,z,a,s)));
|
||||
p.vel = x2*p.speed;
|
||||
p.target = self;
|
||||
p.tracer = invoker.LockedTarget;
|
||||
|
|
@ -465,7 +467,7 @@ Class UTRocketLauncher : UTWeapon
|
|||
while ( a = Actor(t.Next()) )
|
||||
{
|
||||
if ( !a.bSHOOTABLE || (a.Health <= 0) || a.bKilled || !a.bIsMonster || a.bCorpse || (a == self) || isTeammate(a) || !CheckSight(a) ) continue;
|
||||
Vector3 viewdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 viewdir = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
Vector3 reldir = level.Vec3Diff(Vec2OffsetZ(0,0,player.viewz),a.Vec2OffsetZ(0,0,a.pos.z+a.height*0.5));
|
||||
double reldist = reldir.length();
|
||||
if ( reldist > 2000 ) continue;
|
||||
|
|
@ -502,6 +504,7 @@ Class UTRocketLauncher : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 6;
|
||||
UTWeapon.DropAmmo 3;
|
||||
UTWeapon.NameColor "FF 00 00";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ Class BulletImpact : Actor
|
|||
Super.PostBeginPlay();
|
||||
A_SprayDecal("Pock",-20);
|
||||
int numpt = int(Random[Enforcer](5,10)*scale.x*4);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Enforcer](-.8,.8),FRandom[Enforcer](-.8,.8),FRandom[Enforcer](-.8,.8))).unit()*FRandom[Enforcer](0.1,1.2);
|
||||
|
|
@ -383,7 +383,7 @@ Class Enforcer : UTWeapon
|
|||
else origin = level.Vec3Offset(origin,-z+ydir*y*4);
|
||||
double a = FRandom[Enforcer](0,360), s = FRandom[Enforcer](0,alt?invoker.altaccuracy:0.004);
|
||||
[x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
||||
Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
|
||||
Vector3 dir = dt_Utility.ConeSpread(x2,y2,z2,a,s);
|
||||
FLineTraceData d;
|
||||
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
||||
UTBulletTrail.DoTrail(self,origin,dir,10000,int((invoker.Amount+alt)**2));
|
||||
|
|
@ -485,6 +485,7 @@ Class Enforcer : UTWeapon
|
|||
Weapon.AmmoGive 30;
|
||||
Weapon.Kickback 180;
|
||||
UTWeapon.DropAmmo 10;
|
||||
UTWeapon.NameColor "C8 C8 FF";
|
||||
Enforcer.ClipCount 20;
|
||||
Enforcer.SlaveClipCount 20;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -543,10 +543,10 @@ Class FlakSlug : Actor
|
|||
p.bHITOWNER = true;
|
||||
a = FRandom[Flak](0,360);
|
||||
s = FRandom[Flak](0,0.1);
|
||||
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||||
Vector3 dir = dt_Utility.ConeSpread(x,y,z,a,s);
|
||||
p.angle = atan2(dir.y,dir.x);
|
||||
p.pitch = -asin(dir.z);
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Flak](-3,3));
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*(p.speed+FRandom[Flak](-3,3));
|
||||
p.target = target;
|
||||
}
|
||||
int numpt = Random[Flak](8,12);
|
||||
|
|
@ -637,20 +637,25 @@ Class FlakMag : UTCasing
|
|||
|
||||
Class FlakCannon : UTWeapon
|
||||
{
|
||||
FlakCannonLED ammoled;
|
||||
ui Canvas AmmoLed;
|
||||
ui TextureID LedFont;
|
||||
|
||||
override void DoEffect()
|
||||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !ammoled )
|
||||
Vector2 fnt[] =
|
||||
{
|
||||
let ti = ThinkerIterator.Create("FlakCannonLED");
|
||||
while ( ammoled=FlakCannonLED(ti.Next()) ) break;
|
||||
if ( !ammoled ) ammoled = FlakCannonLED(Spawn("FlakCannonLED"));
|
||||
}
|
||||
if ( !Owner || !Owner.player || (Owner != players[consoleplayer].Camera) ) return;
|
||||
ammoled.mygun = self;
|
||||
TexMan.SetCameraToTexture(ammoled,"FlakALed",90);
|
||||
(0,1), (21,1), (42,1), (63,1), (84,1),
|
||||
(105,1), (126,1), (147,1), (168,1), (189,1)
|
||||
};
|
||||
if ( !AmmoLed ) AmmoLed = TexMan.GetCanvas("FlakALed");
|
||||
if ( !LedFont ) LedFont = TexMan.CheckForTexture("models/LEDFont2.png",TexMan.Type_Any);
|
||||
AmmoLed.Clear(0,0,128,64,"Black");
|
||||
int dg3 = (Ammo1.Amount/100)%10;
|
||||
int dg2 = (Ammo1.Amount/10)%10;
|
||||
int dg1 = Ammo1.Amount%10;
|
||||
AmmoLed.DrawTexture(LedFont,false,34,14,DTA_SrcX,fnt[dg3].x,DTA_SrcY,fnt[dg3].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,54,14,DTA_SrcX,fnt[dg2].x,DTA_SrcY,fnt[dg2].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,74,14,DTA_SrcX,fnt[dg1].x,DTA_SrcY,fnt[dg1].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
}
|
||||
|
||||
action void A_Loading( bool first = false )
|
||||
|
|
@ -692,10 +697,10 @@ Class FlakCannon : UTWeapon
|
|||
p = Spawn("FlakChunk",level.Vec3Offset(origin,offsets[i]));
|
||||
a = FRandom[Flak](0,360);
|
||||
s = FRandom[Flak](0,0.1);
|
||||
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||||
Vector3 dir = dt_Utility.ConeSpread(x,y,z,a,s);
|
||||
p.angle = atan2(dir.y,dir.x);
|
||||
p.pitch = -asin(dir.z);
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Flak](-3,3));
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*(p.speed+FRandom[Flak](-3,3));
|
||||
p.target = self;
|
||||
}
|
||||
int numpt = Random[Flak](20,30);
|
||||
|
|
@ -737,7 +742,7 @@ Class FlakCannon : UTWeapon
|
|||
Actor p = Spawn("FlakSlug",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
int numpt = Random[Flak](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
|
|
@ -776,6 +781,7 @@ Class FlakCannon : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 10;
|
||||
UTWeapon.DropAmmo 5;
|
||||
UTWeapon.NameColor "FF 60 00";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Class HammerImpact : Actor
|
|||
Super.PostBeginPlay();
|
||||
A_SprayDecal("ImpactMark",20);
|
||||
int numpt = Random[Impact](20,40);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (-x+(FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8))).unit()*FRandom[Impact](0.1,1.2);
|
||||
|
|
@ -44,6 +44,37 @@ Class ImpactHammer : UTWeapon
|
|||
{
|
||||
double chargesize, count;
|
||||
|
||||
bool bOldSkin;
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
if ( flak_oldimpact )
|
||||
{
|
||||
if ( !bOldSkin )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models/220","JImpPick1.png");
|
||||
A_ChangeModel("",0,"","",0,"models/220","JImpactHammer1.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",1,"models/220","JImpactHammer2.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",2,"models/220","JImpactHammer3.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models/220","JImpactHammer4.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
bOldSkin = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bOldSkin )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models","JImpPick1.png");
|
||||
A_ChangeModel("",0,"","",0,"models","JImpactHammer1.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",1,"models","JImpactHammer2.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",2,"models","JImpactHammer3.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models","JImpactHammer4.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
bOldSkin = false;
|
||||
}
|
||||
}
|
||||
|
||||
override int, int, bool, bool GetClipAmount()
|
||||
{
|
||||
return (chargesize>0)?min(10,int(chargesize/.15)):-1, -1, false, false;
|
||||
|
|
@ -218,6 +249,7 @@ Class ImpactHammer : UTWeapon
|
|||
Weapon.SlotNumber 1;
|
||||
Weapon.SelectionOrder 9;
|
||||
+WEAPON.MELEEWEAPON;
|
||||
UTWeapon.NameColor "FF C0 00";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,20 +74,26 @@ Class Minigun : UTWeapon
|
|||
{
|
||||
int bcnt, tcnt;
|
||||
|
||||
MinigunLED ammoled;
|
||||
ui Canvas AmmoLed;
|
||||
ui TextureID LedBase, LedFont;
|
||||
|
||||
override void DoEffect()
|
||||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !ammoled )
|
||||
Vector2 fnt[] =
|
||||
{
|
||||
let ti = ThinkerIterator.Create("MinigunLED");
|
||||
while ( ammoled=MinigunLED(ti.Next()) ) break;
|
||||
if ( !ammoled ) ammoled = MinigunLED(Spawn("MinigunLED"));
|
||||
}
|
||||
if ( !Owner || !Owner.player || (Owner != players[consoleplayer].Camera) ) return;
|
||||
ammoled.mygun = self;
|
||||
TexMan.SetCameraToTexture(ammoled,"MiniALed",90);
|
||||
(0,1), (21,1), (42,1), (63,1), (84,1),
|
||||
(105,1), (126,1), (147,1), (168,1), (189,1)
|
||||
};
|
||||
if ( !AmmoLed ) AmmoLed = TexMan.GetCanvas("MiniALed");
|
||||
if ( !LedBase ) LedBase = TexMan.CheckForTexture("models/miniammoledbase.png",TexMan.Type_Any);
|
||||
if ( !LedFont ) LedFont = TexMan.CheckForTexture("models/LEDFont2.png",TexMan.Type_Any);
|
||||
AmmoLed.DrawTexture(LedBase,false,0,0);
|
||||
int dg3 = (Ammo1.Amount/100)%10;
|
||||
int dg2 = (Ammo1.Amount/10)%10;
|
||||
int dg1 = Ammo1.Amount%10;
|
||||
AmmoLed.DrawTexture(LedFont,false,2,14,DTA_SrcX,fnt[dg3].x,DTA_SrcY,fnt[dg3].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,22,14,DTA_SrcX,fnt[dg2].x,DTA_SrcY,fnt[dg2].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
AmmoLed.DrawTexture(LedFont,false,42,14,DTA_SrcX,fnt[dg1].x,DTA_SrcY,fnt[dg1].y,DTA_SrcWidth,20,DTA_SrcHeight,35,DTA_DestWidth,20,DTA_DestHeight,35,DTA_Color,0xFFFF0000);
|
||||
}
|
||||
|
||||
action void A_FireBullet( bool alt = false )
|
||||
|
|
@ -115,7 +121,7 @@ Class Minigun : UTWeapon
|
|||
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*2-z*2);
|
||||
double a = FRandom[Minigun](0,360), s = FRandom[Minigun](0,alt?0.05:0.02);
|
||||
[x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
||||
Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
|
||||
Vector3 dir = dt_Utility.ConeSpread(x2,y2,z2,a,s);
|
||||
FLineTraceData d;
|
||||
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
||||
UTBulletTrail.DoTrail(self,origin,dir,10000,alt?5:3);
|
||||
|
|
@ -216,6 +222,7 @@ Class Minigun : UTWeapon
|
|||
Weapon.AmmoGive 50;
|
||||
Weapon.Kickback 180;
|
||||
UTWeapon.DropAmmo 20;
|
||||
UTWeapon.NameColor "FF FF 00";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,21 @@
|
|||
Class UDamage : PowerupGiver
|
||||
{
|
||||
bool bOldSkin;
|
||||
|
||||
void A_CheckSkin()
|
||||
{
|
||||
if ( flak_oldudamage )
|
||||
{
|
||||
if ( !bOldSkin ) A_ChangeModel("",0,"models/220","UDamage_d.3d",0,"models/220","Judamage1.png");
|
||||
bOldSkin = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bOldSkin ) A_ChangeModel("",0,"models","UDamage_d.3d",0,"models","GoldSkin2.png");
|
||||
bOldSkin = false;
|
||||
}
|
||||
}
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_UDAMAGE";
|
||||
|
|
@ -16,8 +32,8 @@ Class UDamage : PowerupGiver
|
|||
States
|
||||
{
|
||||
Spawn:
|
||||
UDAM A -1;
|
||||
Stop;
|
||||
UDAM A 1 A_CheckSkin();
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,25 @@
|
|||
Class PulseAmmo : Ammo
|
||||
{
|
||||
int OldSkin;
|
||||
|
||||
void A_CheckSkin()
|
||||
{
|
||||
switch ( flak_oldpulse )
|
||||
{
|
||||
case 1:
|
||||
if ( OldSkin != 1 ) A_ChangeModel("",1,"","",1,"models/222","JPammo_01.png");
|
||||
OldSkin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if ( OldSkin != 2 ) A_ChangeModel("",1,"","",1,"models/322","JPammo_01.png");
|
||||
OldSkin = 2;
|
||||
break;
|
||||
default:
|
||||
if ( OldSkin != 0 ) A_ChangeModel("",1,"","",1,"models","JPammo_01.png");
|
||||
OldSkin = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Default
|
||||
{
|
||||
Tag "$T_PULSEAMMO";
|
||||
|
|
@ -13,8 +33,8 @@ Class PulseAmmo : Ammo
|
|||
States
|
||||
{
|
||||
Spawn:
|
||||
PAMO A -1;
|
||||
Stop;
|
||||
PAMO A 1 A_CheckSkin();
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -527,7 +547,7 @@ Class StarterBolt : PulseBolt
|
|||
A_SetPitch(target.BulletSlope());
|
||||
frame++;
|
||||
if ( frame > 4 ) frame = 0;
|
||||
CheckBeam((cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)));
|
||||
CheckBeam(dt_Utility.Vec3FromAngle(angle,pitch));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -562,24 +582,70 @@ Class PulseGun : UTWeapon
|
|||
bool ClipOut;
|
||||
double sangle;
|
||||
Actor beam;
|
||||
transient ui Font usmf;
|
||||
|
||||
Property ClipCount : clipcount;
|
||||
|
||||
PulseGunLED ammoled;
|
||||
ui Canvas AmmoLed;
|
||||
ui TextureID LedFont, LedBase, CountBar;
|
||||
|
||||
override void DoEffect()
|
||||
int OldSkin;
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !ammoled )
|
||||
Super.Tick();
|
||||
switch ( flak_oldpulse )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("PulseGunLED");
|
||||
while ( ammoled=PulseGunLED(ti.Next()) ) break;
|
||||
if ( !ammoled ) ammoled = PulseGunLED(Spawn("PulseGunLED"));
|
||||
case 1:
|
||||
if ( OldSkin != 1 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models/222","JPulsePickup_01.png");
|
||||
A_ChangeModel("",0,"","",2,"models/222","JPulseGun_02.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models/222","JPulseGun_03.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if ( OldSkin != 2 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models/322","JPulsePickup_01.png");
|
||||
A_ChangeModel("",0,"","",2,"models/322","JPulseGun_02.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models/322","JPulseGun_03.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 2;
|
||||
break;
|
||||
default:
|
||||
if ( OldSkin != 1 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models","JPulsePickup_01.png");
|
||||
A_ChangeModel("",0,"","",2,"models","JPulseGun_02.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models","JPulseGun_03.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 0;
|
||||
break;
|
||||
}
|
||||
if ( !Owner || !Owner.player || (Owner != players[consoleplayer].Camera) ) return;
|
||||
ammoled.mygun = self;
|
||||
TexMan.SetCameraToTexture(ammoled,"PAmmoLed",90);
|
||||
}
|
||||
|
||||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
Vector2 fnt[] =
|
||||
{
|
||||
(0,1), (49,1), (98,1), (147,1), (196,1),
|
||||
(0,87), (49,87), (98,87), (147,87), (196,87)
|
||||
};
|
||||
if ( !AmmoLed ) AmmoLed = TexMan.GetCanvas("PAmmoLed");
|
||||
if ( !LedFont ) LedFont = TexMan.CheckForTexture("models/LEDFont.png",TexMan.Type_Any);
|
||||
if ( !LedBase ) LedBase = TexMan.CheckForTexture("models/AmmoLedBase.png",TexMan.Type_Any);
|
||||
if ( !CountBar ) CountBar = TexMan.CheckForTexture("models/AmmoCountBar.png",TexMan.Type_Any);
|
||||
AmmoLed.DrawTexture(LedBase,false,0,0);
|
||||
int amo = flak_pulsereload?ClipCount:Ammo1.Amount;
|
||||
int mamo = flak_pulsereload?default.ClipCount:Ammo1.MaxAmount;
|
||||
int dg3 = (amo/100)%10;
|
||||
int dg2 = (amo/10)%10;
|
||||
int dg1 = amo%10;
|
||||
AmmoLed.DrawTexture(LedFont,false,56,14,DTA_SrcX,fnt[dg3].x,DTA_SrcY,fnt[dg3].y,DTA_SrcWidth,48,DTA_SrcHeight,85,DTA_DestWidth,48,DTA_DestHeight,85,DTA_Color,(amo<10)?0xFFFF0000:0xFF0000FF);
|
||||
AmmoLed.DrawTexture(LedFont,false,104,14,DTA_SrcX,fnt[dg2].x,DTA_SrcY,fnt[dg2].y,DTA_SrcWidth,48,DTA_SrcHeight,85,DTA_DestWidth,48,DTA_DestHeight,85,DTA_Color,(amo<10)?0xFFFF0000:0xFF0000FF);
|
||||
AmmoLed.DrawTexture(LedFont,false,152,14,DTA_SrcX,fnt[dg1].x,DTA_SrcY,fnt[dg1].y,DTA_SrcWidth,48,DTA_SrcHeight,85,DTA_DestWidth,48,DTA_DestHeight,85,DTA_Color,(amo<10)?0xFFFF0000:0xFF0000FF);
|
||||
AmmoLed.DrawTexture(CountBar,false,30,100,DTA_DestWidth,(min(amo,mamo)*196)/mamo,DTA_DestHeight,10);
|
||||
}
|
||||
|
||||
override int, int, bool, bool GetClipAmount()
|
||||
|
|
@ -677,12 +743,12 @@ Class PulseGun : UTWeapon
|
|||
double a;
|
||||
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-1.8*z);
|
||||
origin = level.Vec3Offset(origin,y*cos(invoker.sangle)*2+z*sin(invoker.sangle)*2);
|
||||
origin = level.Vec3Offset(origin,dt_Utility.CircleOffset(y,z,invoker.sangle,2));
|
||||
invoker.sangle += 100;
|
||||
Actor p = Spawn("PulseBall",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
for ( int i=0; i<8; i++ )
|
||||
{
|
||||
|
|
@ -749,6 +815,7 @@ Class PulseGun : UTWeapon
|
|||
Weapon.AmmoGive 60;
|
||||
PulseGun.ClipCount 50;
|
||||
UTWeapon.DropAmmo 15;
|
||||
UTWeapon.NameColor "80 FF 80";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ Class Razor2 : Actor
|
|||
A_AlertMonsters();
|
||||
A_SprayDecal("WallCrack",-20);
|
||||
int numpt = Random[Ripper](5,10);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (-x+(FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8))).unit()*FRandom[Ripper](0.1,1.2);
|
||||
|
|
@ -183,7 +183,7 @@ Class Razor2Alt : Razor2
|
|||
A_Explode(34,90,damagetype:'RipperAltDeath');
|
||||
A_QuakeEx(3,3,3,10,0,140,"",QF_RELATIVE|QF_SCALEDOWN,falloff:90,rollIntensity:0.1);
|
||||
int numpt = Random[Ripper](10,20);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (-x+(FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8))).unit()*FRandom[Ripper](0.1,1.2);
|
||||
|
|
@ -239,6 +239,7 @@ Class Ripper2 : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 10;
|
||||
UTWeapon.DropAmmo 5;
|
||||
UTWeapon.NameColor "00 FF FF";
|
||||
}
|
||||
action void A_RazorFire( bool alt = false )
|
||||
{
|
||||
|
|
@ -261,7 +262,7 @@ Class Ripper2 : UTWeapon
|
|||
else p = Spawn("Razor2",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
}
|
||||
States
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ Class ShockBeam : Actor
|
|||
if ( isFrozen() ) return;
|
||||
if ( !moving ) return;
|
||||
// step trace
|
||||
tracedir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
tracedir = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
t.ShootThroughList.Clear();
|
||||
t.Trace(pos,cursector,tracedir,1000,0);
|
||||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||||
|
|
@ -538,7 +538,7 @@ Class SuperShockBeam : Actor
|
|||
if ( isFrozen() ) return;
|
||||
if ( !moving ) return;
|
||||
// step trace
|
||||
tracedir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
tracedir = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
t.ShootThroughList.Clear();
|
||||
t.Trace(pos,cursector,tracedir,2000,0);
|
||||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||||
|
|
@ -841,7 +841,7 @@ Class ShockBall : Actor
|
|||
Spawn("ShockExplLight",pos);
|
||||
A_SetScale(1.0);
|
||||
let r = Spawn("ShockBeamRing",pos);
|
||||
Vector3 HitNormal = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 HitNormal = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
if ( BlockingLine ) HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
else if ( BlockingFloor )
|
||||
{
|
||||
|
|
@ -934,7 +934,7 @@ Class SuperShockBall : Actor
|
|||
Spawn("SuperShockExplLight",pos);
|
||||
A_SetScale(1.5);
|
||||
let r = Spawn("SuperShockBeamRing",pos);
|
||||
Vector3 HitNormal = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
Vector3 HitNormal = dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
if ( BlockingLine ) HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
else if ( BlockingFloor )
|
||||
{
|
||||
|
|
@ -1106,7 +1106,7 @@ Class ShockRifle : UTWeapon
|
|||
Actor p = Spawn("ShockBall",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
for ( int i=0; i<12; i++ )
|
||||
{
|
||||
|
|
@ -1141,6 +1141,7 @@ Class ShockRifle : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 20;
|
||||
UTWeapon.DropAmmo 5;
|
||||
UTWeapon.NameColor "80 00 FF";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
@ -1287,7 +1288,7 @@ Class EnhancedShockRifle : UTWeapon
|
|||
Actor p = Spawn("SuperShockBall",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
for ( int i=0; i<12; i++ )
|
||||
{
|
||||
|
|
@ -1327,6 +1328,7 @@ Class EnhancedShockRifle : UTWeapon
|
|||
Weapon.AmmoUse2 1;
|
||||
Weapon.AmmoGive 50;
|
||||
UTWeapon.DropAmmo 5;
|
||||
UTWeapon.NameColor "80 00 FF";
|
||||
+WEAPON.AMMO_OPTIONAL;
|
||||
+WEAPON.ALT_AMMO_OPTIONAL;
|
||||
+WEAPON.CHEATNOTWEAPON;
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ Class SniperRifle : UTWeapon
|
|||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
PlayerInfo p = players[consoleplayer];
|
||||
if ( (p.Camera != Owner) || (sniperzoom <= 1.) ) Shader.SetEnabled(p,"UTRifleScope",false);
|
||||
else Shader.SetEnabled(p,"UTRifleScope",CVar.GetCVar('flak_zoomshader',p).GetBool());
|
||||
if ( (p.Camera != Owner) || (sniperzoom <= 1.) ) PPShader.SetEnabled("UTRifleScope",false);
|
||||
else PPShader.SetEnabled("UTRifleScope",flak_zoomshader);
|
||||
}
|
||||
override void DoEffect()
|
||||
{
|
||||
|
|
@ -101,7 +101,7 @@ Class SniperRifle : UTWeapon
|
|||
FLineTraceData d;
|
||||
double pt = BulletSlope();
|
||||
LineTrace(angle,10000,pt,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
||||
UTBulletTrail.DoTrail(self,origin,(cos(angle)*cos(pt),sin(angle)*cos(pt),-sin(pt)),10000,1);
|
||||
UTBulletTrail.DoTrail(self,origin,dt_Utility.Vec3FromAngle(angle,pt),10000,1);
|
||||
if ( d.HitType == TRACE_HitActor )
|
||||
{
|
||||
int dmg = 45;
|
||||
|
|
@ -188,6 +188,7 @@ Class SniperRifle : UTWeapon
|
|||
Weapon.AmmoGive 8;
|
||||
Weapon.Kickback 250;
|
||||
UTWeapon.DropAmmo 2;
|
||||
UTWeapon.NameColor "00 00 FF";
|
||||
+NOEXTREMEDEATH;
|
||||
}
|
||||
States
|
||||
|
|
@ -247,11 +248,11 @@ Class SniperRifle : UTWeapon
|
|||
{
|
||||
if ( invoker.sniperzoom > 1. )
|
||||
{
|
||||
if ( CVar.GetCVar('flak_zoomsound',players[consoleplayer]).GetBool() && CheckLocalView() )
|
||||
if ( flak_zoomsound && CheckLocalView() )
|
||||
A_StartSound("sniper/zoomdown",CHAN_WEAPONMISC,CHANF_OVERLAP);
|
||||
return ResolveState("AltHold2");
|
||||
}
|
||||
if ( CVar.GetCVar('flak_zoomsound',players[consoleplayer]).GetBool() && CheckLocalView() )
|
||||
if ( flak_zoomsound && CheckLocalView() )
|
||||
A_StartSound("sniper/zoomup",CHAN_WEAPONMISC,CHANF_OVERLAP);
|
||||
return ResolveState(null);
|
||||
}
|
||||
|
|
@ -271,7 +272,7 @@ Class SniperRifle : UTWeapon
|
|||
Deselect:
|
||||
SRFD A 0
|
||||
{
|
||||
if ( (invoker.sniperzoom>1.0) && CVar.GetCVar('flak_zoomsound',players[consoleplayer]).GetBool() && CheckLocalView() )
|
||||
if ( (invoker.sniperzoom>1.0) && flak_zoomsound && CheckLocalView() )
|
||||
A_StartSound("sniper/zoomdown",CHAN_WEAPONMISC,CHANF_OVERLAP);
|
||||
A_ZoomFactor(invoker.sniperzoom=1.0,ZOOM_INSTANT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ Class TranslocatorModule : Actor
|
|||
States
|
||||
{
|
||||
Spawn:
|
||||
TMOD A 1;
|
||||
Wait;
|
||||
TMOD A -1;
|
||||
Stop;
|
||||
Bounce:
|
||||
TMOD A 0
|
||||
{
|
||||
|
|
@ -247,6 +247,26 @@ Class TranslocatorModule : Actor
|
|||
}
|
||||
}
|
||||
|
||||
Class OldTranslocatorModule : TranslocatorModule
|
||||
{
|
||||
States
|
||||
{
|
||||
Death:
|
||||
TMOD A -1
|
||||
{
|
||||
A_SetPitch(0);
|
||||
if ( tracer && !tracer.bACTLIKEBRIDGE )
|
||||
{
|
||||
SetOrigin(tracer.Vec2OffsetZ(0,0,pos.z),false);
|
||||
vel.xy *= 0;
|
||||
tracer = null;
|
||||
bHITTRACER = false;
|
||||
}
|
||||
}
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class TranslocatorAfterimageParticle : Actor
|
||||
{
|
||||
Default
|
||||
|
|
@ -402,7 +422,7 @@ Class Translocator : UTWeapon
|
|||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.A_SetSize(radius);
|
||||
Vector3 dir = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch));
|
||||
Vector3 dir = dt_Utility.Vec3FromAngle(p.angle,p.pitch);
|
||||
dir.z += 0.35*(1-abs(dir.z));
|
||||
p.vel = dir*p.speed;
|
||||
invoker.module = p;
|
||||
|
|
@ -559,3 +579,107 @@ Class Translocator : UTWeapon
|
|||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
Class OldTranslocator : Translocator
|
||||
{
|
||||
action void A_ThrowModuleOld()
|
||||
{
|
||||
Weapon weap = Weapon(invoker);
|
||||
if ( !weap ) return;
|
||||
A_StartSound("transloc/throw",CHAN_WEAPON);
|
||||
invoker.FireEffect();
|
||||
A_AlertMonsters();
|
||||
Vector3 x, y, z;
|
||||
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
Vector3 origin = (pos.x,pos.y,player.viewz)+15.0*x-10.0*y-4.0*z;
|
||||
let p = Spawn("OldTranslocatorModule",origin);
|
||||
p.target = self;
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.A_SetSize(radius);
|
||||
Vector3 dir = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch));
|
||||
dir.z += 0.35*(1-abs(dir.z));
|
||||
p.vel = dir*p.speed;
|
||||
invoker.module = p;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TLCP A -1;
|
||||
Stop;
|
||||
TLCP B -1;
|
||||
Stop;
|
||||
Select:
|
||||
TLCS A 1 A_Raise(int.max);
|
||||
Wait;
|
||||
Ready:
|
||||
TLCS A 0 A_JumpIf(invoker.module,"Ready2");
|
||||
TLCS ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
|
||||
TLCS O 0 A_JumpIf(invoker.module,"Idle");
|
||||
Goto Idle2;
|
||||
Ready2:
|
||||
TLS2 ABCDEFGHIJKLMNO 1 A_WeaponReady(WRF_NOFIRE);
|
||||
Goto Idle;
|
||||
Idle:
|
||||
TLCI ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
|
||||
{
|
||||
if ( !invoker.module )
|
||||
return ResolveState("PickedUp");
|
||||
A_WeaponReady();
|
||||
return ResolveState(null);
|
||||
}
|
||||
TLI2 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
|
||||
{
|
||||
if ( !invoker.module )
|
||||
return ResolveState("PickedUp");
|
||||
A_WeaponReady();
|
||||
return ResolveState(null);
|
||||
}
|
||||
TLI3 ABCDEFGH 1
|
||||
{
|
||||
if ( !invoker.module )
|
||||
return ResolveState("PickedUp");
|
||||
A_WeaponReady();
|
||||
return ResolveState(null);
|
||||
}
|
||||
Loop;
|
||||
Idle2:
|
||||
TLI4 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOSECONDARY);
|
||||
TLI5 ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOSECONDARY);
|
||||
TLI6 ABCDEFGH 1 A_WeaponReady(WRF_NOSECONDARY);
|
||||
TLI6 H 0;
|
||||
Loop;
|
||||
PickedUp:
|
||||
TLCI A 5;
|
||||
TLR2 A 0 A_ReturnModule();
|
||||
Goto Return2;
|
||||
Fire:
|
||||
TLCF A 0 A_JumpIf(invoker.module||(invoker.Ammo1&&invoker.Ammo1.Amount<=0),"Return");
|
||||
TLCF ABCDEFGHIJK 1;
|
||||
TLCF K 0 A_ThrowModuleOld();
|
||||
TLCF LMNOPQRST 1 A_WeaponReady(WRF_NOPRIMARY);
|
||||
Goto Idle;
|
||||
Return:
|
||||
TLCR ABCDEFGHIJKLMNOP 1;
|
||||
TLCR Q 1 A_ReturnModule();
|
||||
TLCR RST 1;
|
||||
Return2:
|
||||
TLR2 ABCDEFGHIJKLMNOPQRST 1;
|
||||
TLR2 T 0;
|
||||
Goto Idle2;
|
||||
AltFire:
|
||||
TLCT ABCDEFGHIJK 1;
|
||||
TLCT L 1 A_Translocate();
|
||||
TLCT MNOPQRST 1;
|
||||
Goto PickedUp;
|
||||
Deselect:
|
||||
TLCD A 0 A_JumpIf(invoker.module,"Deselect2");
|
||||
TLCD ABCDEFGIJK 1;
|
||||
TLCD K 1 A_Lower(int.max);
|
||||
Wait;
|
||||
Deselect2:
|
||||
TLD2 ABCDEFGHIJK 1;
|
||||
TLD2 K 1 A_Lower(int.max);
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1006
zscript/utcommon.zsc
1006
zscript/utcommon.zsc
File diff suppressed because it is too large
Load diff
|
|
@ -124,7 +124,7 @@ Class UTBloodSpurt : Actor
|
|||
d.SetShade(fillcolor);
|
||||
ang = angle+FRandom[Blood](-3,3)*str;
|
||||
pt = pitch+FRandom[Blood](-3,3)*str;
|
||||
Vector3 dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt));
|
||||
Vector3 dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
d.vel = dir*str*FRandom[Blood](0.4,0.8);
|
||||
d.vel.z += str*0.5;
|
||||
d.scale *= str*0.15*FRandom[Blood](0.6,1.4);
|
||||
|
|
@ -176,7 +176,7 @@ Class UTBloodTrail : Actor
|
|||
ang = FRandom[Blood](0,360);
|
||||
pt = FRandom[Blood](-90,90);
|
||||
Vector3 dir = -target.vel*0.2;
|
||||
dir += (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*0.8;
|
||||
dir += dt_Utility.Vec3FromAngle(ang,pt)*0.8;
|
||||
d.vel = dir*str*FRandom[Blood](0.8,1.2);
|
||||
d.scale *= str*0.15*FRandom[Blood](0.6,1.4);
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ Class UTGibber : Actor
|
|||
a.scale *= FRandom[Blod](1.7,2.3)*scale.x;
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.6+dir*FRandom[Blod](3.0,6.0);
|
||||
}
|
||||
for ( int i=0; i<gibsize; i++ )
|
||||
|
|
@ -257,7 +257,7 @@ Class UTGibber : Actor
|
|||
a.scale *= FRandom[Blod](1.8,2.3)*scale.x;
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
||||
}
|
||||
A_CountDown();
|
||||
|
|
@ -307,20 +307,23 @@ Class UTFemaleLegGibber : UTGibber
|
|||
firstgib = true;
|
||||
for ( int i=0; i<2; i++ )
|
||||
{
|
||||
Actor a = Spawn(parts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
||||
Vector3 pofs = (0,partofsy[i],partofsz[i]);
|
||||
pofs.xy = RotateVector(pofs.xy,angle);
|
||||
Actor a = Spawn(parts[i],level.Vec3Offset(pos,pofs));
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.6+dir*FRandom[Blod](3.0,6.0);
|
||||
}
|
||||
}
|
||||
for ( int i=0; i<gibsize; i++ )
|
||||
{
|
||||
Vector3 box = (FRandom[Blod](-8,8),FRandom[Blod](7,12),FRandom[Blod](5,18));
|
||||
let a = Spawn("UTBloodPuff",Vec3Offset(box.x*cos(angle)-box.y*sin(angle),box.x*sin(angle)+box.y*cos(angle),box.z));
|
||||
box.xy = RotateVector(box.xy,angle);
|
||||
let a = Spawn("UTBloodPuff",level.Vec3Offset(pos,box));
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
||||
}
|
||||
A_CountDown();
|
||||
|
|
@ -393,15 +396,17 @@ Class UTPlayerGibber : UTGibber
|
|||
if ( ((i == 3) || (i == 7)) && UTPlayer(Gibbed).leglessL ) continue;
|
||||
if ( ((i == 4) || (i > 7)) && UTPlayer(Gibbed).torsoless ) continue;
|
||||
Actor a;
|
||||
Vector3 pofs = (0,partofsy[i],partofsz[i]);
|
||||
pofs.xy = RotateVector(pofs.xy,angle);
|
||||
if ( i < 6 )
|
||||
{
|
||||
if ( UTPlayer(Gibbed).DollType == UTPlayer.DOLL_Boss )
|
||||
a = Spawn(bossparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
||||
a = Spawn(bossparts[i],level.Vec3Offset(pos,pofs));
|
||||
else if ( UTPlayer(Gibbed).DollType == UTPlayer.DOLL_Female )
|
||||
a = Spawn(femaleparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
||||
else a = Spawn(maleparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
||||
a = Spawn(femaleparts[i],level.Vec3Offset(pos,pofs));
|
||||
else a = Spawn(maleparts[i],level.Vec3Offset(pos,pofs));
|
||||
}
|
||||
else a = Spawn(extraparts[i-6],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
||||
else a = Spawn(extraparts[i-6],level.Vec3Offset(pos,pofs));
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
if ( a is 'UTHead' )
|
||||
|
|
@ -413,7 +418,7 @@ Class UTPlayerGibber : UTGibber
|
|||
UTHead(a).headowner = UTPlayer(Gibbed).player;
|
||||
}
|
||||
}
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.6+dir*FRandom[Blod](3.0,6.0);
|
||||
}
|
||||
}
|
||||
|
|
@ -422,7 +427,7 @@ Class UTPlayerGibber : UTGibber
|
|||
let a = Spawn("UTBloodPuff",Vec3Offset(FRandom[Blod](-0.8,0.8)*radius,FRandom[Blod](-0.8,0.8)*radius,FRandom[Blod](0.1,0.9)*height));
|
||||
ang = FRandom[Blod](0,360);
|
||||
pt = FRandom[Blod](-90,90);
|
||||
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
||||
dir = dt_Utility.Vec3FromAngle(ang,pt);
|
||||
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
||||
}
|
||||
A_CountDown();
|
||||
|
|
@ -505,7 +510,7 @@ Class UTGib : Actor
|
|||
ang = FRandom[Blood](0,360);
|
||||
pt = FRandom[Blood](-90,90);
|
||||
Vector3 dir = -vel*0.4;
|
||||
dir += (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*1.3;
|
||||
dir += dt_Utility.Vec3FromAngle(ang,pt)*1.3;
|
||||
d.vel = dir*FRandom[Blood](0.8,1.2);
|
||||
d.scale *= 0.75*FRandom[Blood](0.6,1.4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ Class UTHud : BaseStatusBar
|
|||
double CurX, CurY;
|
||||
double FracTic;
|
||||
|
||||
Weapon lastwep;
|
||||
String ntagstr;
|
||||
int ntagtic;
|
||||
Color ntagcol;
|
||||
|
||||
override void Init()
|
||||
{
|
||||
Super.Init();
|
||||
|
|
@ -179,7 +184,7 @@ Class UTHud : BaseStatusBar
|
|||
{
|
||||
Super.Draw(state,TicFrac);
|
||||
HScale = Screen.GetWidth()/1280.;
|
||||
switch ( CVar.GetCVar('flak_colorprefs',CPlayer).GetInt() )
|
||||
switch ( flak_colorprefs )
|
||||
{
|
||||
case 0:
|
||||
if ( CPlayer.GetTeam() >= Teams.Size() )
|
||||
|
|
@ -190,26 +195,26 @@ Class UTHud : BaseStatusBar
|
|||
tintcolor = CPlayer.GetColor();
|
||||
break;
|
||||
case 2:
|
||||
tintcolor = Color(CVar.GetCVar('flak_colorcustom',CPlayer).GetString());
|
||||
tintcolor = flak_colorcustom;
|
||||
break;
|
||||
}
|
||||
opacity = CVar.GetCVar('flak_opacity',players[consoleplayer]).GetInt();
|
||||
opacity = flak_opacity;
|
||||
bgcolor = Color("Black");
|
||||
showweapons = CVar.GetCVar('flak_showweapons',players[consoleplayer]).GetBool();
|
||||
showstatus = CVar.GetCVar('flak_showstatus',players[consoleplayer]).GetBool();
|
||||
showfrags = CVar.GetCVar('flak_showfrags',players[consoleplayer]).GetBool();
|
||||
showammo = CVar.GetCVar('flak_showammo',players[consoleplayer]).GetBool();
|
||||
showinfo = CVar.GetCVar('flak_showinfo',players[consoleplayer]).GetBool();
|
||||
hudsize = CVar.GetCVar('flak_hudsize',players[consoleplayer]).GetFloat();
|
||||
weaponsize = CVar.GetCVar('flak_weaponsize',players[consoleplayer]).GetFloat();
|
||||
statussize = CVar.GetCVar('flak_statussize',players[consoleplayer]).GetFloat();
|
||||
showweapons = flak_showweapons;
|
||||
showstatus = flak_showstatus;
|
||||
showfrags = flak_showfrags;
|
||||
showammo = flak_showammo;
|
||||
showinfo = flak_showinfo;
|
||||
hudsize = flak_hudsize;
|
||||
weaponsize = flak_weaponsize;
|
||||
statussize = flak_statussize;
|
||||
double lbottom = Screen.GetHeight();
|
||||
if ( showweapons )
|
||||
{
|
||||
if ( weaponsize*hudsize>=1.0 ) lbottom -= 64*hudsize*HScale;
|
||||
if ( showfrags ) lbottom -= 64*hudsize*HScale;
|
||||
}
|
||||
if ( (CPlayer.ReadyWeapon is 'UTWeapon') )
|
||||
if ( CPlayer.ReadyWeapon is 'UTWeapon' )
|
||||
UTWeapon(CPlayer.ReadyWeapon).PreRender(lbottom);
|
||||
if ( (state == HUD_StatusBar) || (state == HUD_Fullscreen) )
|
||||
{
|
||||
|
|
@ -289,7 +294,7 @@ Class UTHud : BaseStatusBar
|
|||
double flen = 3*step;
|
||||
double len = digits.length()*step;
|
||||
double alpha = clamp((opacity+7)/15.,0.0,1.0);
|
||||
for ( int i=0; i<digits.length(); i++ ) if ( digits.ByteAt(i) == 0x31 ) len -= 0.5*step;
|
||||
for ( uint i=0; i<digits.length(); i++ ) if ( digits.ByteAt(i) == 0x31 ) len -= 0.5*step;
|
||||
CurX += (flen-len)*0.5;
|
||||
if ( digits.ByteAt(0) == 0x31 ) CurX -= 0.5*step;
|
||||
if ( value < 0 )
|
||||
|
|
@ -298,7 +303,7 @@ Class UTHud : BaseStatusBar
|
|||
Screen.DrawTexture(BigNum[11],false,CurX/ss,CurY/ss,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,alpha,DTA_LegacyRenderStyle,STYLE_AddShaded,DTA_FillColor,DrawColor);
|
||||
CurX += step;
|
||||
}
|
||||
for ( int i=0; i<digits.length(); i++ )
|
||||
for ( uint i=0; i<digits.length(); i++ )
|
||||
{
|
||||
if ( opacity+7 > 15 ) Screen.DrawTexture(BigNum[digits.ByteAt(i)-0x30],false,CurX/ss,CurY/ss,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_FillColor,bgcolor);
|
||||
Screen.DrawTexture(BigNum[digits.ByteAt(i)-0x30],false,CurX/ss,CurY/ss,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,alpha,DTA_LegacyRenderStyle,STYLE_AddShaded,DTA_FillColor,DrawColor);
|
||||
|
|
@ -856,22 +861,12 @@ Class UTHud : BaseStatusBar
|
|||
}
|
||||
}
|
||||
|
||||
private bool IsAutoTaunt( String str )
|
||||
{
|
||||
// checks if this critical message is pretending to be a chat msg
|
||||
if ( str.IndexOf("\c*") == 0 ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetChatFace( String str, bool isauto = false )
|
||||
private void SetChatFace( String str )
|
||||
{
|
||||
Array<String> splitme;
|
||||
str.Split(splitme,":");
|
||||
if ( splitme.Size() < 2 ) return;
|
||||
String pname = splitme[0];
|
||||
// autotaunts have a leading chat color escape
|
||||
if ( isauto && (pname.IndexOf("\c*") == 0) ) pname.Remove(0,2);
|
||||
if ( pname.RightIndexOf("\c*") == pname.Length()-2 ) pname.Remove(pname.Length()-2,2);
|
||||
// guess the player
|
||||
int p = -1;
|
||||
for ( int i=0; i<MAXPLAYERS; i++ )
|
||||
|
|
@ -882,24 +877,33 @@ Class UTHud : BaseStatusBar
|
|||
break;
|
||||
}
|
||||
if ( (p == -1) || !(players[p].mo is 'UTPlayer') ) return;
|
||||
String path = "models/Boss";
|
||||
String skin = "boss";
|
||||
String face = "";
|
||||
switch ( UTPlayer(players[p].mo).VoiceType )
|
||||
{
|
||||
case UTPlayer.VOICE_FemaleOne:
|
||||
LastTalkFace = TexMan.CheckForTexture("Ivana",TexMan.Type_Any);
|
||||
path = "models/FCommando";
|
||||
skin = CVar.GetCVar('flak_fcmdskin',players[p]).GetString();
|
||||
face = CVar.GetCVar('flak_fcmdface',players[p]).GetString();
|
||||
break;
|
||||
case UTPlayer.VOICE_FemaleTwo:
|
||||
LastTalkFace = TexMan.CheckForTexture("Lauren",TexMan.Type_Any);
|
||||
path = "models/SGirl";
|
||||
skin = CVar.GetCVar('flak_armyskin',players[p]).GetString();
|
||||
face = CVar.GetCVar('flak_armyface',players[p]).GetString();
|
||||
break;
|
||||
case UTPlayer.VOICE_MaleOne:
|
||||
LastTalkFace = TexMan.CheckForTexture("Blake",TexMan.Type_Any);
|
||||
path = "models/Commando";
|
||||
skin = CVar.GetCVar('flak_cmdoskin',players[p]).GetString();
|
||||
face = CVar.GetCVar('flak_cmdoface',players[p]).GetString();
|
||||
break;
|
||||
case UTPlayer.VOICE_MaleTwo:
|
||||
LastTalkFace = TexMan.CheckForTexture("Brock",TexMan.Type_Any);
|
||||
break;
|
||||
case UTPlayer.VOICE_Boss:
|
||||
LastTalkFace = TexMan.CheckForTexture("Xan",TexMan.Type_Any);
|
||||
path = "models/Soldier";
|
||||
skin = CVar.GetCVar('flak_sldrskin',players[p]).GetString();
|
||||
face = CVar.GetCVar('flak_sldrface',players[p]).GetString();
|
||||
break;
|
||||
}
|
||||
LastTalkFace = TexMan.CheckForTexture(String.Format("%s/%s5%s.png",path,skin,face),TexMan.Type_Any);
|
||||
LastTalkTic = gametic+90;
|
||||
}
|
||||
|
||||
|
|
@ -923,12 +927,6 @@ Class UTHud : BaseStatusBar
|
|||
SetChatFace(outline);
|
||||
return true;
|
||||
}
|
||||
else if ( IsAutoTaunt(outline) ) // autotaunts
|
||||
{
|
||||
AppendMessage(outline,Font.CR_GREEN);
|
||||
SetChatFace(outline,true);
|
||||
return true;
|
||||
}
|
||||
else // other messages
|
||||
{
|
||||
AppendMessage(outline,Font.CR_WHITE);
|
||||
|
|
@ -1045,6 +1043,14 @@ Class UTHud : BaseStatusBar
|
|||
CurY = Screen.GetHeight()-112*HScale;
|
||||
Screen.DrawText(mUTFont40.mFont,Font.CR_WHITE,CurX/scl,CurY/scl,PickupMsg,DTA_VirtualWidthF,Screen.GetWidth()/scl,DTA_VirtualHeightF,Screen.GetHeight()/scl,DTA_KeepRatio,true,DTA_Alpha,clamp((PickupMsgTic-gametic+fractic)*0.1,0,1),DTA_LegacyRenderStyle,STYLE_Add);
|
||||
}
|
||||
// name tags
|
||||
if ( ntagtic > gametic )
|
||||
{
|
||||
double scl = HScale/2.5;
|
||||
CurX = (Screen.GetWidth()-mUTFont40.mFont.StringWidth(ntagstr)*scl)/2;
|
||||
CurY = Screen.GetHeight()-88*HScale;
|
||||
Screen.DrawText(mUTFont40.mFont,Font.CR_UNTRANSLATED,CurX/scl,CurY/scl,ntagstr,DTA_VirtualWidthF,Screen.GetWidth()/scl,DTA_VirtualHeightF,Screen.GetHeight()/scl,DTA_KeepRatio,true,DTA_Alpha,clamp((ntagtic-gametic+fractic)*0.1,0,1),DTA_LegacyRenderStyle,STYLE_AddShaded,DTA_FillColor,Color(255,ntagcol.r,ntagcol.g,ntagcol.b));
|
||||
}
|
||||
// talk face
|
||||
CurX = 0;
|
||||
CurY = 0;
|
||||
|
|
@ -1086,8 +1092,9 @@ Class UTHud : BaseStatusBar
|
|||
{
|
||||
if ( CPlayer.mo.InvSel )
|
||||
{
|
||||
PickupMsg = CPlayer.mo.InvSel.GetTag();
|
||||
PickupMsgTic = gametic+50;
|
||||
ntagstr = CPlayer.mo.InvSel.GetTag();
|
||||
ntagtic = gametic+70;
|
||||
ntagcol = Color(255,255,255);
|
||||
S_StartSound("misc/hud_sel",CHAN_BODY,CHANF_UI|CHANF_MAYBE_LOCAL);
|
||||
}
|
||||
}
|
||||
|
|
@ -1101,6 +1108,23 @@ Class UTHud : BaseStatusBar
|
|||
}
|
||||
justselected = true;
|
||||
}
|
||||
// part of gross hackery to override nametag display
|
||||
if ( CPlayer.PendingWeapon && (CPlayer.PendingWeapon != WP_NOCHANGE) && (CPlayer.PendingWeapon != lastwep) )
|
||||
{
|
||||
if ( (displaynametags&2) && (CPlayer == players[consoleplayer]) )
|
||||
{
|
||||
ntagstr = CPlayer.PendingWeapon.GetTag();
|
||||
ntagtic = gametic+70;
|
||||
ntagcol = (CPlayer.PendingWeapon is 'UTWeapon')?UTWeapon(CPlayer.PendingWeapon).NameColor:Color(255,255,255);
|
||||
}
|
||||
}
|
||||
lastwep = CPlayer.PendingWeapon;
|
||||
// make sure vanilla nametags don't display
|
||||
DetachMessageID(0x5745504e); // WEPN
|
||||
DetachMessageID(0x53494e56); // SINV
|
||||
// also try with different endianness, just in case
|
||||
DetachMessageID(0x4e504557); // WEPN
|
||||
DetachMessageID(0x564e4953); // SINV
|
||||
if ( deathmatch||teamplay )
|
||||
{
|
||||
if ( CPlayer.fragcount != lastfragcnt ) lastfrag = level.time;
|
||||
|
|
@ -1112,7 +1136,7 @@ Class UTHud : BaseStatusBar
|
|||
lastfragcnt = CPlayer.killcount;
|
||||
}
|
||||
vtracer.ignore = CPlayer.mo;
|
||||
vtracer.trace(CPlayer.mo.Vec2OffsetZ(0,0,CPlayer.viewz),CPlayer.mo.CurSector,(cos(CPlayer.mo.angle)*cos(CPlayer.mo.pitch),sin(CPlayer.mo.angle)*cos(CPlayer.mo.pitch),-sin(CPlayer.mo.pitch)),1000,0);
|
||||
vtracer.trace(CPlayer.mo.Vec2OffsetZ(0,0,CPlayer.viewz),CPlayer.mo.CurSector,dt_Utility.Vec3FromAngle(CPlayer.mo.angle,CPlayer.mo.pitch),1000,0);
|
||||
if ( vtracer.Results.HitType != TRACE_HitActor ) return;
|
||||
lastseen = vtracer.Results.HitActor;
|
||||
lastseentic = level.time;
|
||||
|
|
@ -1143,10 +1167,10 @@ Class UTHud : BaseStatusBar
|
|||
int crdefault = Font.FindFontColor('UTHUDTextLight');
|
||||
int highlight = Font.FindFontColor('UTHUDText');
|
||||
HScale = Screen.GetWidth()/1280.;
|
||||
showweapons = CVar.GetCVar('flak_showweapons',players[consoleplayer]).GetBool();
|
||||
showstatus = CVar.GetCVar('flak_showstatus',players[consoleplayer]).GetBool();
|
||||
weaponsize = CVar.GetCVar('flak_weaponsize',players[consoleplayer]).GetFloat();
|
||||
statussize = CVar.GetCVar('flak_statussize',players[consoleplayer]).GetFloat();
|
||||
showweapons = flak_showweapons;
|
||||
showstatus = flak_showstatus;
|
||||
weaponsize = flak_weaponsize;
|
||||
statussize = flak_statussize;
|
||||
double cbottom = Screen.GetHeight()*0.99;
|
||||
let scale = GetHUDScale();
|
||||
double textdist = 8./scale.Y;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,209 @@ Class OptionMenuItemUTHudPreview : OptionMenuItem
|
|||
}
|
||||
}
|
||||
|
||||
Class OptionMenuItemUTFacePreview : OptionMenuItem
|
||||
{
|
||||
String texpath[4];
|
||||
TextureID talkface[4];
|
||||
CVar mCVar[8];
|
||||
|
||||
OptionMenuItemUTFacePreview Init( String dummy )
|
||||
{
|
||||
Super.Init(dummy,'None',true);
|
||||
mCVar[0] = CVar.FindCVar('flak_cmdoskin');
|
||||
mCVar[1] = CVar.FindCVar('flak_fcmdskin');
|
||||
mCVar[2] = CVar.FindCVar('flak_sldrskin');
|
||||
mCVar[3] = CVar.FindCVar('flak_armyskin');
|
||||
mCVar[4] = CVar.FindCVar('flak_cmdoface');
|
||||
mCVar[5] = CVar.FindCVar('flak_fcmdface');
|
||||
mCVar[6] = CVar.FindCVar('flak_sldrface');
|
||||
mCVar[7] = CVar.FindCVar('flak_armyface');
|
||||
return self;
|
||||
}
|
||||
|
||||
override int Draw( OptionMenuDescriptor desc, int y, int indent, bool selected )
|
||||
{
|
||||
int x = Screen.GetWidth()/2-140*CleanXFac_1;
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
String path;
|
||||
switch ( i )
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
path = "models/Commando/"..mCVar[0].GetString().."5"..mCVar[4].GetString()..".png";
|
||||
break;
|
||||
case 1:
|
||||
path = "models/FCommando/"..mCVar[1].GetString().."5"..mCVar[5].GetString()..".png";
|
||||
break;
|
||||
case 2:
|
||||
path = "models/Soldier/"..mCVar[2].GetString().."5"..mCVar[6].GetString()..".png";
|
||||
break;
|
||||
case 3:
|
||||
path = "models/SGirl/"..mCVar[3].GetString().."5"..mCVar[7].GetString()..".png";
|
||||
break;
|
||||
}
|
||||
if ( (texpath[i] != path) || talkface[i].IsNull() )
|
||||
{
|
||||
texpath[i] = path;
|
||||
talkface[i] = TexMan.CheckForTexture(path,TexMan.Type_Any);
|
||||
}
|
||||
Screen.DrawTexture(talkface[i],false,x,y-8*CleanYFac_1,DTA_CleanNoMove_1,true);
|
||||
x += 72*CleanXFac_1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
override bool Selectable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Class OptionMenuItemUTFaceOption : OptionMenuItemOptionBase
|
||||
{
|
||||
CVar mCVar, mCVar2;
|
||||
int type;
|
||||
Array<String> faces;
|
||||
|
||||
void InitFaces()
|
||||
{
|
||||
faces.Clear();
|
||||
String path;
|
||||
switch ( type )
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
path = "models/Commando/"..mCVar2.GetString().."2";
|
||||
break;
|
||||
case 1:
|
||||
path = "models/FCommando/"..mCVar2.GetString().."4";
|
||||
break;
|
||||
case 2:
|
||||
path = "models/Soldier/"..mCVar2.GetString().."4";
|
||||
break;
|
||||
case 3:
|
||||
path = "models/SGirl/"..mCVar2.GetString().."4";
|
||||
break;
|
||||
}
|
||||
for ( int l=0; l<Wads.GetNumLumps(); l++ )
|
||||
{
|
||||
String fn = Wads.GetLumpFullName(l);
|
||||
if ( !(fn.Left(path.Length()) ~== path) ) continue;
|
||||
fn = fn.Mid(path.length());
|
||||
int ext = fn.IndexOf(".");
|
||||
if ( ext == -1 ) continue;
|
||||
fn = fn.Left(ext);
|
||||
if ( (fn == "") || (fn ~== "ice") ) continue; // empty face + special name
|
||||
faces.Push(fn);
|
||||
}
|
||||
if ( GetSelection() == -1 ) SetSelection(0);
|
||||
}
|
||||
|
||||
OptionMenuItemUTFaceOption Init( String label, Name command, Name command2, int type, CVar graycheck = null, int center = 0 )
|
||||
{
|
||||
Super.Init(label,command,'',graycheck,center);
|
||||
mCVar = CVar.FindCVar(mAction);
|
||||
mCVar2 = Cvar.FindCvar(command2);
|
||||
self.type = type;
|
||||
InitFaces();
|
||||
return self;
|
||||
}
|
||||
|
||||
override bool SetString( int i, String newtext )
|
||||
{
|
||||
if ( i == OP_VALUES )
|
||||
{
|
||||
int cnt = faces.Size();
|
||||
if ( cnt >= 0 )
|
||||
{
|
||||
mValues = newtext;
|
||||
int s = GetSelection();
|
||||
if ( (s >= cnt) || (s < 0) ) s = 0;
|
||||
SetSelection(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override int GetSelection()
|
||||
{
|
||||
int Selection = -1;
|
||||
int cnt = faces.Size();
|
||||
if ( (cnt > 0) && mCVar )
|
||||
{
|
||||
String cv = mCVar.GetString();
|
||||
for( int i=0; i<cnt; i++ )
|
||||
{
|
||||
if ( cv ~== faces[i] )
|
||||
{
|
||||
Selection = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Selection;
|
||||
}
|
||||
|
||||
override void SetSelection( int Selection )
|
||||
{
|
||||
int cnt = faces.Size();
|
||||
if ( (cnt > 0) && mCVar )
|
||||
mCVar.SetString(faces[Selection]);
|
||||
}
|
||||
|
||||
override int Draw( OptionMenuDescriptor desc, int y, int indent, bool selected )
|
||||
{
|
||||
if ( mCenter ) indent = (screen.GetWidth()/2);
|
||||
drawLabel(indent,y,selected?OptionMenuSettings.mFontColorSelection:OptionMenuSettings.mFontColor,isGrayed());
|
||||
int Selection = GetSelection();
|
||||
String text = (Selection!=-1)?faces[Selection]:"unknown";
|
||||
// capitalize first letter
|
||||
text = text.Left(1).MakeUpper()..text.Mid(1);
|
||||
drawValue(indent,y,OptionMenuSettings.mFontColorValue,text,isGrayed());
|
||||
return indent;
|
||||
}
|
||||
|
||||
override bool MenuEvent( int mkey, bool fromcontroller )
|
||||
{
|
||||
int cnt = faces.Size();
|
||||
if ( cnt > 0 )
|
||||
{
|
||||
int Selection = GetSelection();
|
||||
if ( mkey == Menu.MKEY_Left )
|
||||
{
|
||||
if ( Selection == -1 ) Selection = 0;
|
||||
else if ( --Selection < 0 ) Selection = cnt-1;
|
||||
}
|
||||
else if ( (mkey == Menu.MKEY_Right) || (mkey == Menu.MKEY_Enter) )
|
||||
{
|
||||
if ( ++Selection >= cnt ) Selection = 0;
|
||||
}
|
||||
else return OptionMenuItem.MenuEvent(mkey,fromcontroller);
|
||||
SetSelection(Selection);
|
||||
Menu.MenuSound("menu/change");
|
||||
}
|
||||
else return OptionMenuItem.MenuEvent(mkey,fromcontroller);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Class OptionMenuItemUTSkinOption : OptionMenuItemOption
|
||||
{
|
||||
OptionMenuItemUTFaceOption linked;
|
||||
|
||||
override void SetSelection( int Selection )
|
||||
{
|
||||
int oldsel = GetSelection();
|
||||
Super.SetSelection(Selection);
|
||||
int newsel = GetSelection();
|
||||
if ( oldsel == newsel ) return;
|
||||
// force face to update
|
||||
linked.InitFaces();
|
||||
}
|
||||
}
|
||||
|
||||
// changes to Tahoma
|
||||
Class UTMessageBox : MessageBoxMenu
|
||||
{
|
||||
|
|
@ -90,6 +293,20 @@ Class UTOptionMenu : OptionMenu
|
|||
{
|
||||
private String ttip;
|
||||
|
||||
override void Init( Menu parent, OptionMenuDescriptor desc )
|
||||
{
|
||||
Super.Init(parent,desc);
|
||||
// link skins and faces
|
||||
for ( int i=0; i<mDesc.mItems.Size(); i++ )
|
||||
{
|
||||
if ( !(mDesc.mItems[i] is 'OptionMenuItemUTSkinOption') )
|
||||
continue;
|
||||
// next element should be the face
|
||||
if ( !(mDesc.mItems[i+1] is 'OptionMenuItemUTFaceOption') )
|
||||
ThrowAbortException("UTSkinOption without UTFaceOption in menu.");
|
||||
OptionMenuItemUTSkinOption(mDesc.mItems[i]).linked = OptionMenuItemUTFaceOption(mDesc.mItems[i+1]);
|
||||
}
|
||||
}
|
||||
override void Ticker()
|
||||
{
|
||||
Super.Ticker();
|
||||
|
|
@ -147,4 +364,4 @@ Class UTOptionMenu : OptionMenu
|
|||
ypos += fnt.GetHeight()*CleanYFac_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,27 @@
|
|||
// wasn't placeable in UT99
|
||||
Class WarheadAmmo : Ammo
|
||||
{
|
||||
int OldSkin;
|
||||
|
||||
void A_CheckSkin()
|
||||
{
|
||||
switch ( flak_oldredeemer )
|
||||
{
|
||||
case 1:
|
||||
if ( OldSkin != 1 ) A_ChangeModel("WarheadAmmo220");
|
||||
OldSkin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if ( OldSkin != 2 ) A_ChangeModel("WarheadAmmo222");
|
||||
OldSkin = 2;
|
||||
break;
|
||||
default:
|
||||
if ( OldSkin != 0 ) A_ChangeModel("WarheadAmmo");
|
||||
OldSkin = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_WARHEADAMMO";
|
||||
|
|
@ -16,11 +37,15 @@ Class WarheadAmmo : Ammo
|
|||
States
|
||||
{
|
||||
Spawn:
|
||||
WMIS A -1;
|
||||
Stop;
|
||||
WMIS A 1 A_CheckSkin();
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
// dummies so we can copy MODELDEF
|
||||
Class WarheadAmmo220 : Actor abstract {}
|
||||
Class WarheadAmmo222 : Actor abstract {}
|
||||
|
||||
Class ShockWave : Actor
|
||||
{
|
||||
double shocksize, olddmgradius;
|
||||
|
|
@ -42,6 +67,18 @@ Class ShockWave : Actor
|
|||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
switch ( flak_oldredeemer )
|
||||
{
|
||||
case 1:
|
||||
A_ChangeModel("",0,"","",0,"models/220","Shockt1.png");
|
||||
break;
|
||||
case 2:
|
||||
A_ChangeModel("",0,"","",0,"models/222","Shockt1.png");
|
||||
break;
|
||||
default:
|
||||
// change nothing
|
||||
break;
|
||||
}
|
||||
lifespan = ReactionTime;
|
||||
A_StartSound("warhead/explode",CHAN_VOICE,attenuation:ATTN_NONE);
|
||||
A_QuakeEx(9,9,9,100,0,12000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:1200,rollIntensity:0.5);
|
||||
|
|
@ -174,7 +211,7 @@ Class WarheadLight : DynamicLight
|
|||
Destroy();
|
||||
return;
|
||||
}
|
||||
Vector3 taildir = -(cos(target.angle)*cos(target.pitch),sin(target.angle)*cos(target.pitch),-sin(target.pitch));
|
||||
Vector3 taildir = -dt_Utility.Vec3FromAngle(target.angle,target.pitch);
|
||||
SetOrigin(level.Vec3Offset(pos,taildir*20),true);
|
||||
args[LIGHT_INTENSITY] = Random[Warhead](6,8)*10;
|
||||
}
|
||||
|
|
@ -205,7 +242,7 @@ Class WarheadTrail : Actor
|
|||
alpha *= FRandom[Puff](0.5,1.5);
|
||||
ang = FRandom[Puff](0,360);
|
||||
pt = FRandom[Puff](-90,90);
|
||||
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
||||
vel += dt_Utility.Vec3FromAngle(ang,pt)*FRandom[Puff](0.2,0.8);
|
||||
roll = FRandom[Puff](0,360);
|
||||
}
|
||||
override void Tick()
|
||||
|
|
@ -227,6 +264,7 @@ Class WarShell : Actor
|
|||
{
|
||||
double destangle, destpitch;
|
||||
Actor l, b;
|
||||
int OldSkin;
|
||||
|
||||
Default
|
||||
{
|
||||
|
|
@ -274,9 +312,29 @@ Class WarShell : Actor
|
|||
else if ( vel.length() < 20 ) vel += vel.unit()*0.35;
|
||||
}
|
||||
}
|
||||
void A_CheckSkin()
|
||||
{
|
||||
switch ( flak_oldredeemer )
|
||||
{
|
||||
case 1:
|
||||
if ( OldSkin != 1 ) A_ChangeModel("Warshell220");
|
||||
OldSkin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if ( OldSkin != 2 ) A_ChangeModel("Warshell222");
|
||||
OldSkin = 2;
|
||||
break;
|
||||
default:
|
||||
if ( OldSkin != 0 ) A_ChangeModel("Warshell");
|
||||
OldSkin = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
action void A_Trail()
|
||||
{
|
||||
Vector3 taildir = -(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
invoker.A_CheckSkin();
|
||||
Vector3 taildir = -dt_Utility.Vec3FromAngle(angle,pitch);
|
||||
if ( waterlevel > 0 )
|
||||
{
|
||||
for ( int i=0; i<4; i++ )
|
||||
|
|
@ -386,15 +444,15 @@ Class GuidedWarShell : WarShell
|
|||
guideangle = lagangle2*0.95+lagangle*0.05;
|
||||
guidepitch = lagpitch2*0.95+lagpitch*0.05;
|
||||
guideroll = lagroll2*0.95+lagroll*0.05;
|
||||
dt_Quat orient = dt_Quat.create_euler(pitch,angle,roll);
|
||||
dt_Quat angles = dt_Quat.create_euler(guidepitch,guideangle,guideroll);
|
||||
orient = orient.qmul(angles);
|
||||
double npitch, nangle, nroll;
|
||||
[npitch, nangle, nroll] = orient.to_euler();
|
||||
dt_GM_Quaternion orient = dt_GM_Quaternion.createFromAngles(angle,pitch,roll);
|
||||
dt_GM_Quaternion angles = dt_GM_Quaternion.createFromAngles(guideangle,guidepitch,guideroll);
|
||||
orient = orient.multiplyQuat(angles);
|
||||
double nangle, npitch, nroll;
|
||||
[nangle, npitch, nroll] = orient.toAngles();
|
||||
angle = nangle;
|
||||
pitch = npitch;
|
||||
roll = nroll;
|
||||
vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11;
|
||||
vel = (vel+dt_Utility.Vec3FromAngle(angle,pitch)*0.8).unit()*11;
|
||||
}
|
||||
lagangle2 = lagangle2*0.95+lagangle*0.05;
|
||||
lagpitch2 = lagpitch2*0.95+lagpitch*0.05;
|
||||
|
|
@ -413,6 +471,10 @@ Class GuidedWarShell : WarShell
|
|||
}
|
||||
}
|
||||
|
||||
// dummies so we can copy MODELDEF
|
||||
Class Warshell220 : Actor abstract {}
|
||||
Class Warshell222 : Actor abstract {}
|
||||
|
||||
Class MidTracer : LineTracer
|
||||
{
|
||||
override ETraceStatus TraceCallback()
|
||||
|
|
@ -427,31 +489,17 @@ Class MidTracer : LineTracer
|
|||
}
|
||||
}
|
||||
|
||||
Class TargetActor
|
||||
{
|
||||
Vector2 vpos;
|
||||
String diststr;
|
||||
}
|
||||
|
||||
Class RedeemerHUD : HUDMessageBase
|
||||
{
|
||||
Actor Camera;
|
||||
Vector3 ViewPos;
|
||||
double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2;
|
||||
double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2, OldLagRoll2;
|
||||
TextureID reticle1, reticle2, arrow, mark, readout;
|
||||
Font whfont;
|
||||
transient ThinkerIterator t;
|
||||
transient MidTracer tr;
|
||||
Array<TargetActor> ta;
|
||||
Shape2D sshape, darrow;
|
||||
bool dodim;
|
||||
// libeye stuff
|
||||
dtLe_ProjScreen proj;
|
||||
dtLe_GLScreen gl_proj;
|
||||
dtLe_SWScreen sw_proj;
|
||||
dtLe_Viewport viewport;
|
||||
bool can_project;
|
||||
transient CVar cvar_renderer;
|
||||
dt_ProjectionData projdata;
|
||||
|
||||
RedeemerHUD Init()
|
||||
{
|
||||
|
|
@ -461,72 +509,14 @@ Class RedeemerHUD : HUDMessageBase
|
|||
mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
|
||||
readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any);
|
||||
whfont = Font.GetFont('WHFONT');
|
||||
sshape = new("Shape2D");
|
||||
sshape.PushCoord((0,0));
|
||||
sshape.PushCoord((1,0));
|
||||
sshape.PushCoord((0,1));
|
||||
sshape.PushCoord((1,1));
|
||||
sshape.PushTriangle(0,3,1);
|
||||
sshape.PushTriangle(0,2,3);
|
||||
gl_proj = new("dtLe_GLScreen");
|
||||
sw_proj = new("dtLe_SWScreen");
|
||||
PrepareProjection();
|
||||
return self;
|
||||
}
|
||||
void PrepareProjection()
|
||||
{
|
||||
if ( !cvar_renderer )
|
||||
cvar_renderer = CVar.GetCVar("vid_rendermode",players[consoleplayer]);
|
||||
if ( !cvar_renderer )
|
||||
{
|
||||
can_project = proj = gl_proj;
|
||||
return;
|
||||
}
|
||||
switch ( cvar_renderer.GetInt() )
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
proj = sw_proj;
|
||||
break;
|
||||
default:
|
||||
proj = gl_proj;
|
||||
break;
|
||||
}
|
||||
can_project = proj;
|
||||
}
|
||||
override bool Tick()
|
||||
{
|
||||
PrepareProjection();
|
||||
LagRoll = dt_Quat.Normalize180(ViewRoll-LagRoll2);
|
||||
LagRoll2 += dt_Quat.Normalize180(LagRoll-LagRoll2)*0.1;
|
||||
// shootable targetting
|
||||
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() && !CVar.GetCVar('flak_redeemerreadout_perframe',players[consoleplayer]).GetBool() && can_project )
|
||||
{
|
||||
viewport.FromHud();
|
||||
proj.CacheResolution();
|
||||
proj.CacheFov(players[consoleplayer].fov);
|
||||
proj.Reorient(ViewPos,(ViewAngle,ViewPitch,ViewRoll));
|
||||
proj.BeginProjection();
|
||||
if ( !t ) t = ThinkerIterator.Create("Actor");
|
||||
if ( !tr ) tr = new("MidTracer");
|
||||
t.Reinit();
|
||||
ta.Clear();
|
||||
Actor a;
|
||||
Vector3 vdir = (cos(ViewAngle)*cos(ViewPitch),sin(ViewAngle)*cos(ViewPitch),-sin(ViewPitch));
|
||||
while ( a = Actor(t.Next()) )
|
||||
{
|
||||
Vector3 tdir = Level.Vec3Diff(ViewPos,a.Pos+(0,0,a.Height*0.5));
|
||||
if ( !a.bSHOOTABLE || (a.Health <= 0) || ((Camera is 'GuidedWarShell') && (a == GuidedWarShell(Camera).b)) || (tdir.length() > 2000) || (acos(tdir.unit() dot vdir) > players[consoleplayer].FOV) || tr.Trace(ViewPos,Camera.CurSector,tdir.unit(),tdir.length(),0) ) continue;
|
||||
proj.ProjectWorldPos(ViewPos+tdir);
|
||||
Vector2 npos = proj.ProjectToNormal();
|
||||
if ( !proj.IsInFront() ) continue;
|
||||
TargetActor te = new("TargetActor");
|
||||
te.vpos = viewport.SceneToWindow(npos);
|
||||
te.diststr = String.Format("%f",tdir.length());
|
||||
te.diststr.Replace(".","");
|
||||
ta.Push(te);
|
||||
}
|
||||
}
|
||||
|
||||
LagRoll = Actor.Normalize180(ViewRoll-LagRoll2);
|
||||
OldLagRoll2 = LagRoll2;
|
||||
LagRoll2 += Actor.Normalize180(LagRoll-LagRoll2)*0.1;
|
||||
return !Camera;
|
||||
}
|
||||
override void Draw( int bottom, int visibility )
|
||||
|
|
@ -534,71 +524,42 @@ Class RedeemerHUD : HUDMessageBase
|
|||
if ( visibility != StatusBar.HUDMSGLayer_UnderHUD ) return;
|
||||
if ( dodim ) Screen.Dim("C8 00 00",0.2,0,0,Screen.GetWidth(),Screen.GetHeight());
|
||||
// shootable targetting
|
||||
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() && can_project )
|
||||
if ( flak_redeemerreadout )
|
||||
{
|
||||
if ( CVar.GetCVar('flak_redeemerreadout_perframe',players[consoleplayer]).GetBool() )
|
||||
dt_Utility.PrepareProjData(projdata,ViewPos,ViewAngle,ViewPitch,ViewRoll,players[consoleplayer].fov);
|
||||
if ( !t ) t = ThinkerIterator.Create("Actor");
|
||||
else t.Reinit();
|
||||
if ( !tr ) tr = new("MidTracer");
|
||||
Actor a;
|
||||
Vector3 vdir = dt_Utility.Vec3FromAngle(ViewAngle,ViewPitch);
|
||||
while ( a = Actor(t.Next()) )
|
||||
{
|
||||
viewport.FromHud();
|
||||
proj.CacheResolution();
|
||||
proj.CacheFov(players[consoleplayer].fov);
|
||||
proj.Reorient(ViewPos,(ViewAngle,ViewPitch,ViewRoll));
|
||||
proj.BeginProjection();
|
||||
if ( !t ) t = ThinkerIterator.Create("Actor");
|
||||
if ( !tr ) tr = new("MidTracer");
|
||||
t.Reinit();
|
||||
ta.Clear();
|
||||
Actor a;
|
||||
Vector3 vdir = (cos(ViewAngle)*cos(ViewPitch),sin(ViewAngle)*cos(ViewPitch),-sin(ViewPitch));
|
||||
while ( a = Actor(t.Next()) )
|
||||
{
|
||||
Vector3 tdir = Level.Vec3Diff(ViewPos,a.Pos+(0,0,a.Height*0.5));
|
||||
if ( !a.bSHOOTABLE || (a.Health <= 0) || ((Camera is 'GuidedWarShell') && (a == GuidedWarShell(Camera).b)) || (tdir.length() > 2000) || (acos(tdir.unit() dot vdir) > players[consoleplayer].FOV) || tr.Trace(ViewPos,Camera.CurSector,tdir.unit(),tdir.length(),0) ) continue;
|
||||
proj.ProjectWorldPos(ViewPos+tdir);
|
||||
Vector2 npos = proj.ProjectToNormal();
|
||||
if ( !proj.IsInFront() ) continue;
|
||||
TargetActor te = new("TargetActor");
|
||||
te.vpos = viewport.SceneToWindow(npos);
|
||||
te.diststr = String.Format("%f",tdir.length());
|
||||
te.diststr.Replace(".","");
|
||||
ta.Push(te);
|
||||
}
|
||||
}
|
||||
for ( int i=0; i<ta.Size(); i++ )
|
||||
{
|
||||
Screen.DrawTexture(mark,false,ta[i].vpos.x,ta[i].vpos.y,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(ta[i].vpos.x-whfont.StringWidth(ta[i].diststr)/2)-12,ta[i].vpos.y+8,ta[i].diststr,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
Vector3 tdir = Level.Vec3Diff(ViewPos,a.Pos+(0,0,a.Height*0.5));
|
||||
if ( !a.bSHOOTABLE || (a.Health <= 0) || ((Camera is 'GuidedWarShell') && (a == GuidedWarShell(Camera).b)) || (tdir.length() > 2000) || (acos(tdir.unit() dot vdir) > players[consoleplayer].FOV) || tr.Trace(ViewPos,Camera.CurSector,tdir.unit(),tdir.length(),0) ) continue;
|
||||
Vector3 ndc = dt_Utility.ProjectPoint(projdata,ViewPos+tdir);
|
||||
if ( ndc.z >= 1. ) continue;
|
||||
Vector2 vpos = dt_Utility.NDCToViewport(projdata,ndc);
|
||||
String diststr = String.Format("%f",tdir.length());
|
||||
diststr.Replace(".","");
|
||||
Screen.DrawTexture(mark,false,vpos.x,vpos.y,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(vpos.x-whfont.StringWidth(diststr)/2)-12,vpos.y+8,diststr,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
}
|
||||
}
|
||||
// reticle
|
||||
Vector2 vs = (640,640/Screen.GetAspectRatio());
|
||||
Vector2 mid, siz, siz2;
|
||||
[mid, siz] = Screen.VirtualToRealCoords(vs*0.5,(128,128),vs,false,false);
|
||||
[mid, siz2] = Screen.VirtualToRealCoords(vs*0.5,(8,4),vs,false,false);
|
||||
Vector2 verts[4];
|
||||
verts[0] = (-siz.x,-siz.y);
|
||||
verts[1] = (siz.x,-siz.y);
|
||||
verts[2] = (-siz.x,siz.y);
|
||||
verts[3] = (siz.x,siz.y);
|
||||
sshape.Clear(Shape2D.C_Verts);
|
||||
double rrot = -LagRoll2*2;
|
||||
for ( int i=0; i<4; i++ )
|
||||
sshape.PushVertex(mid+(verts[i].x*cos(rrot)-verts[i].y*sin(rrot),verts[i].x*sin(rrot)+verts[i].y*cos(rrot)));
|
||||
Screen.DrawShape(reticle1,false,sshape,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
sshape.Clear(Shape2D.C_Verts);
|
||||
verts[0] = (-siz2.x,siz.y+siz2.y);
|
||||
verts[1] = (siz2.x,siz.y+siz2.y);
|
||||
verts[2] = (-siz2.x,siz.y+3*siz2.y);
|
||||
verts[3] = (siz2.x,siz.y+3*siz2.y);
|
||||
for ( int i=0; i<4; i++ )
|
||||
sshape.PushVertex(mid+(verts[i].x*cos(rrot)-verts[i].y*sin(rrot),verts[i].x*sin(rrot)+verts[i].y*cos(rrot)));
|
||||
Screen.DrawShape(arrow,false,sshape,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
Screen.DrawTexture(reticle2,false,vs.x*0.5,vs.y*0.5,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
double fractic = System.GetTimeFrac();
|
||||
double rrot = -(LagRoll2*fractic+OldLagRoll2*(1.-fractic))*2;
|
||||
double scl = Screen.GetWidth()/640.;
|
||||
Screen.DrawTexture(reticle1,false,Screen.GetWidth()/2,Screen.GetHeight()/2,DTA_ScaleX,scl,DTA_ScaleY,scl,DTA_LegacyRenderStyle,STYLE_Add,DTA_Rotate,rrot);
|
||||
Vector2 apos = Actor.AngleToVector(rrot+90,140*scl);
|
||||
Screen.DrawTexture(arrow,false,Screen.GetWidth()/2+apos.x,Screen.GetHeight()/2+apos.y,DTA_ScaleX,scl,DTA_ScaleY,scl,DTA_LegacyRenderStyle,STYLE_Add,DTA_Rotate,-rrot);
|
||||
Screen.DrawTexture(reticle2,false,Screen.GetWidth()/2,Screen.GetHeight()/2,DTA_ScaleX,scl,DTA_ScaleY,scl,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
// faux assembly readout
|
||||
int numreadouts = int(vs.y/128+2);
|
||||
for ( int i=0; i<numreadouts; i++ )
|
||||
double scroll = ((gametic+fractic)*5.)%128;
|
||||
double y = -scroll*scl;
|
||||
while ( y < Screen.GetHeight() )
|
||||
{
|
||||
int scroll = (gametic*5)%128;
|
||||
Screen.DrawTexture(readout,false,0,i*128-scroll,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
Screen.DrawTexture(readout,false,0,y,DTA_ScaleX,scl,DTA_ScaleY,scl,DTA_LegacyRenderStyle,STYLE_Add);
|
||||
y += 128*scl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -621,44 +582,8 @@ Class RedeemerHUDStatic : HUDMessageBase
|
|||
override void Draw( int bottom, int visibility )
|
||||
{
|
||||
if ( visibility != StatusBar.HUDMSGLayer_UnderHUD ) return;
|
||||
double sw, sh;
|
||||
sw = 256.;
|
||||
sh = sw*(Screen.GetHeight()/double(Screen.GetWidth()));
|
||||
Screen.DrawTexture(tx,true,0,0,DTA_VirtualWidthF,sw,DTA_VirtualHeightF,sh,DTA_KeepRatio,true);
|
||||
}
|
||||
}
|
||||
|
||||
Class RedeemerHUDHandler : EventHandler
|
||||
{
|
||||
ui RedeemerHUD rhud;
|
||||
transient ui CVar deemershader;
|
||||
|
||||
override void RenderOverlay( RenderEvent e )
|
||||
{
|
||||
if ( !deemershader ) deemershader = CVar.GetCVar('flak_deemershader',players[consoleplayer]);
|
||||
if ( e.Camera is 'GuidedWarShell' )
|
||||
{
|
||||
if ( !rhud )
|
||||
{
|
||||
rhud = new("RedeemerHUD").Init();
|
||||
StatusBar.AttachMessage(rhud,0,StatusBar.HUDMSGLayer_UnderHUD);
|
||||
}
|
||||
rhud.Camera = e.Camera;
|
||||
rhud.ViewPos = e.ViewPos;
|
||||
rhud.ViewAngle = e.ViewAngle;
|
||||
rhud.ViewPitch = e.ViewPitch;
|
||||
rhud.ViewRoll = e.ViewRoll;
|
||||
rhud.dodim = !deemershader.GetBool();
|
||||
Shader.SetEnabled(players[consoleplayer],"RedeemerView",deemershader.GetBool());
|
||||
Shader.SetUniform1f(players[consoleplayer],"RedeemerView","Timer",gametic+e.fractic);
|
||||
}
|
||||
else if ( rhud )
|
||||
{
|
||||
Shader.SetEnabled(players[consoleplayer],"RedeemerView",false);
|
||||
StatusBar.DetachMessage(rhud);
|
||||
rhud.Destroy();
|
||||
StatusBar.AttachMessage(new("RedeemerHUDStatic").Init(),0,StatusBar.HUDMSGLayer_UnderHUD);
|
||||
}
|
||||
double scl = max(Screen.GetWidth()/256.,Screen.GetHeight()/256.);
|
||||
Screen.DrawTexture(tx,true,0,0,DTA_ScaleX,scl,DTA_ScaleY,scl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -666,9 +591,46 @@ Class WarheadLauncher : UTWeapon
|
|||
{
|
||||
transient CVar noswitchdeemer;
|
||||
Actor guided;
|
||||
int OldSkin;
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
switch ( flak_oldredeemer )
|
||||
{
|
||||
case 1:
|
||||
if ( OldSkin != 1 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models/220","Jwhpick1.png");
|
||||
A_ChangeModel("",0,"","",0,"models/220","Jwarhead1.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",1,"models/220","Jwarhead2.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",2,"models/220","Jwarhead3.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models/220","Jwarhead4.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if ( OldSkin != 2 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models/222","Jwhpick1.png");
|
||||
A_ChangeModel("",0,"","",0,"models/222","Jwarhead1.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",1,"models/222","Jwarhead2.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",2,"models/222","Jwarhead3.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models/222","Jwarhead4.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 2;
|
||||
break;
|
||||
default:
|
||||
if ( OldSkin != 0 )
|
||||
{
|
||||
A_ChangeModel("",1,"","",1,"models","Jwhpick1.png");
|
||||
A_ChangeModel("",0,"","",0,"models","Jwarhead1.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",1,"models","Jwarhead2.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",2,"models","Jwarhead3.png",CMDL_USESURFACESKIN);
|
||||
A_ChangeModel("",0,"","",3,"models","Jwarhead4.png",CMDL_USESURFACESKIN);
|
||||
}
|
||||
OldSkin = 0;
|
||||
break;
|
||||
}
|
||||
if ( !Owner || !Owner.player ) return;
|
||||
if ( guided ) crosshair = 99;
|
||||
else crosshair = 0;
|
||||
|
|
@ -694,7 +656,7 @@ Class WarheadLauncher : UTWeapon
|
|||
Actor p = Spawn("WarShell",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
}
|
||||
action void A_WarheadSmoke()
|
||||
|
|
@ -732,7 +694,7 @@ Class WarheadLauncher : UTWeapon
|
|||
Actor p = Spawn("GuidedWarShell",origin);
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
|
||||
p.target = self;
|
||||
p.master = invoker;
|
||||
invoker.guided = p;
|
||||
|
|
@ -774,6 +736,7 @@ Class WarheadLauncher : UTWeapon
|
|||
+INVENTORY.IGNORESKILL;
|
||||
+WEAPON.NOAUTOFIRE;
|
||||
UTWeapon.DropAmmo 1;
|
||||
UTWeapon.NameColor "FF 80 80";
|
||||
}
|
||||
States
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue