1.0 release. Requires 4.2.3 or higher.

- Migrated screen projection code to libeye.
- Some pickups emit light, like in Doomreal.
- Backported map revealer item from Doomreal.
- Brand new Invulnerability and Night Vision powerups.
- Add option to allow Shield Belt and armors simultaneously.
- Backported armor bonus model from Doomreal.
- Added Dual Enforcers icon for HUD.
- Changed player class names to their character names, like in Doomreal.
- Terrain splashes.
- Translocator doesn't telefrag other players in coop.
- Reduced view shake from Impact Hammer.
- Various other updates and bug fixes.
This commit is contained in:
Marisa the Magician 2019-10-21 21:57:35 +02:00
commit b79d29f071
91 changed files with 1994 additions and 511 deletions

View file

@ -1,8 +1,9 @@
Class UTArmor : Armor
{
int absorb;
int absorb, priority;
Property ArmorAbsorption : absorb;
Property AbsorptionPriority : priority;
Default
{
@ -11,6 +12,23 @@ Class UTArmor : Armor
+INVENTORY.KEEPDEPLETED;
+INVENTORY.ALWAYSPICKUP;
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
// find last armor that's better than us
Inventory found = null;
for ( Inventory i=other.Inv; i; i=i.Inv )
{
if ( !(i is 'UTArmor') || (i == self) || (UTArmor(i).priority < priority) ) continue;
found = i;
}
if ( !found ) return;
// place ourselves right after it
Inventory saved = found.Inv;
found.Inv = self;
other.Inv = Inv;
Inv = saved;
}
override void AbsorbDamage( int damage, Name damageType, out int newdamage )
{
int saved;
@ -48,6 +66,7 @@ Class UTArmorBonus : UTArmor
Inventory.MaxAmount 50;
Inventory.InterHubAmount 50;
UTArmor.ArmorAbsorption 25;
UTArmor.AbsorptionPriority 1;
Inventory.PickupMessage "$I_ARMORBONUS";
Inventory.PickupSound "misc/ut_shard";
}
@ -63,7 +82,7 @@ Class UTThighPads : UTArmor
{
override bool HandlePickup( Inventory item )
{
if ( item is 'UTThighPads' )
if ( flak_vanillaarmor && (item is 'UTThighPads') )
{
let s = Owner.FindInventory("UTShieldBelt");
if ( s )
@ -87,6 +106,7 @@ Class UTThighPads : UTArmor
Inventory.MaxAmount 50;
Inventory.InterHubAmount 50;
UTArmor.ArmorAbsorption 50;
UTArmor.AbsorptionPriority 7;
Inventory.PickupMessage "$I_THIGHPADS";
Inventory.PickupSound "misc/ut_armor";
}
@ -102,7 +122,7 @@ Class UTBodyArmor : UTArmor
{
override bool HandlePickup( Inventory item )
{
if ( item is 'UTBodyArmor' )
if ( flak_vanillaarmor && (item is 'UTBodyArmor') )
{
let s = Owner.FindInventory("UTShieldBelt");
if ( s )
@ -126,6 +146,7 @@ Class UTBodyArmor : UTArmor
Inventory.MaxAmount 100;
Inventory.InterHubAmount 100;
UTArmor.ArmorAbsorption 75;
UTArmor.AbsorptionPriority 7;
Inventory.PickupMessage "$I_BODYARMOR";
Inventory.PickupSound "misc/ut_armor";
}
@ -156,7 +177,7 @@ Class UTShieldBelt : UTArmor
}
override bool HandlePickup( Inventory item )
{
if ( (item is 'UTBodyArmor') || (item is 'UTThighPads') )
if ( flak_vanillaarmor && ((item is 'UTBodyArmor') || (item is 'UTThighPads')) )
{
// sum up current amounts
let a = Owner.FindInventory("UTBodyArmor");
@ -177,8 +198,11 @@ Class UTShieldBelt : UTArmor
override bool Use( bool pickup )
{
// removes thigh pads and body armor like in UT
Owner.TakeInventory("UTThighPads",50);
Owner.TakeInventory("UTBodyArmor",150);
if ( flak_vanillaarmor )
{
Owner.TakeInventory("UTThighPads",50);
Owner.TakeInventory("UTBodyArmor",150);
}
return false;
}
Default
@ -190,6 +214,7 @@ Class UTShieldBelt : UTArmor
Inventory.MaxAmount 150;
Inventory.InterHubAmount 150;
UTArmor.ArmorAbsorption 100;
UTArmor.AbsorptionPriority 10;
Inventory.PickupMessage "$I_SHIELDBELT";
Inventory.PickupSound "belt/pickup";
Inventory.RespawnTics 2100;

View file

@ -95,28 +95,27 @@ Class ActUDamage : UTActivatable
}
}
Class ActShieldBelt : UTActivatable
Class ActUTInvulnerability : UTActivatable
{
Default
{
Tag "$T_SHIELDBELT";
Inventory.Icon "ItemBelt";
Inventory.PickupMessage "$I_SHIELDBELT";
Tag "$T_UTINVUL";
Inventory.Icon "ItemInvl";
Inventory.PickupMessage "$I_UTINVUL";
+COUNTITEM;
+INVENTORY.BIGPOWERUP;
+INVENTORY.ISARMOR;
UTActivatable.GiveItem "UTShieldBelt";
Inventory.RespawnTics 2100;
UTActivatable.GiveItem "UTInvulnerability";
Inventory.RespawnTics 4200;
}
States
{
Spawn:
BELT A -1;
UKEY A -1;
Stop;
}
}
Class ActInvisibility : UTActivatable
Class ActUTInvisibility : UTActivatable
{
Default
{
@ -143,6 +142,33 @@ Class ActInvisibility : UTActivatable
}
}
Class ActUTNightVision : UTActivatable
{
Default
{
Tag "$T_UTVISION";
Inventory.Icon "ItemLite";
Inventory.PickupMessage "$I_UTVISION";
+COUNTITEM;
+INVENTORY.BIGPOWERUP;
UTActivatable.GiveItem "UTNightVision";
Inventory.RespawnTics 4200;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
tracer = Spawn("UTNightVisionX",pos);
tracer.angle = angle;
tracer.target = self;
}
States
{
Spawn:
UKEY A -1;
Stop;
}
}
Class ActJumpBoots : UTActivatable
{
Default
@ -161,25 +187,6 @@ Class ActJumpBoots : UTActivatable
}
}
Class ActSearchlight : UTActivatable
{
Default
{
Tag "$T_SEARCHLIGHT";
Inventory.Icon "ItemLite";
Inventory.PickupMessage "$I_SEARCHLIGHT";
+COUNTITEM;
UTActivatable.GiveItem "Searchlight";
Inventory.RespawnTics 1050;
}
States
{
Spawn:
SLIT A -1;
Stop;
}
}
// These have to be subclassed from HealthPickup for auto-use
Class UTActivatableHealth : HealthPickup
{

View file

@ -7,69 +7,6 @@
Class dt_CoordUtil
{
// projects a world point onto screen
// view matrix setup mostly pulled from gutawer's code
static Vector3 WorldToScreen( Vector3 vect, Vector3 eye, double pitch, double yaw, double roll, double vfov )
{
double ar = Screen.getWidth()/double(Screen.getHeight());
double fovr = (ar>=1.3)?1.333333:ar;
double fov = 2*atan(tan(clamp(vfov,5,170)*0.5)/fovr);
float pr = level.pixelstretch;
double angx = cos(pitch);
double angy = sin(pitch)*pr;
double alen = sqrt(angx*angx+angy*angy);
double apitch = asin(angy/alen);
double ayaw = yaw-90;
// rotations
dt_Matrix4 mRoll = dt_Matrix4.rotate((0,0,1),roll);
dt_Matrix4 mPitch = dt_Matrix4.rotate((1,0,0),apitch);
dt_Matrix4 mYaw = dt_Matrix4.rotate((0,-1,0),ayaw);
// scaling
dt_Matrix4 mScale = dt_Matrix4.identity();
mScale.set(1,1,pr);
// YZ swap
dt_Matrix4 mYZ = dt_Matrix4.create();
mYZ.set(0,0,1);
mYZ.set(2,1,1);
mYZ.set(1,2,-1);
mYZ.set(3,3,1);
// translation
dt_Matrix4 mMove = dt_Matrix4.identity();
mMove.set(3,0,-eye.x);
mMove.set(3,1,-eye.y);
mMove.set(3,2,-eye.z);
// perspective
dt_Matrix4 mPerspective = dt_Matrix4.perspective(fov,ar,5,65535);
// full matrix
dt_Matrix4 mView = mRoll.mul(mPitch);
mView = mView.mul(mYaw);
mView = mView.mul(mScale);
mView = mView.mul(mYZ);
mView = mView.mul(mMove);
dt_Matrix4 mWorldToScreen = mPerspective.mul(mView);
return mWorldToScreen.vmat(vect);
}
// converts a projected screen position to 2D canvas coords
// thanks once again to gutawer for making this thing screenblocks-aware
// [NEW] added second return value: true if the point has valid depth (i.e.: it's not behind view)
// [TODO] handle forced aspect ratio (e.g.: 320x200 scaling)
static Vector2, bool ToViewport( Vector3 screenpos, bool scrblocks = true )
{
if ( scrblocks )
{
int winx, winy, winw, winh;
[winx,winy,winw,winh] = Screen.getViewWindow();
int sh = Screen.getHeight();
int ht = sh;
int screenblocks = CVar.GetCVar("screenblocks",players[consoleplayer]).getInt();
if ( screenblocks < 10 ) ht = (screenblocks*sh/10)&~7;
int bt = sh-(ht+winy-((ht-winh)/2));
return (winx,sh-bt-ht)+((screenpos.x+1)*winw,(-screenpos.y+1)*ht)*0.5, (screenpos.z<=1.0);
}
else return ((screenpos.x+1)*Screen.getWidth(),(-screenpos.y+1)*Screen.getHeight())*0.5, (screenpos.z<=1.0);
}
// In Tim Sweeney's own words: "transform by a pitch-yaw-roll rotation"
static Vector3, Vector3, Vector3 GetUnAxes( double pitch, double yaw, double roll )
{

View file

@ -0,0 +1,58 @@
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.
//
//
/////((
@@////////// ((
(( %////((///// (( (( ((
(( @@//(((/////// ((( ////////////// ((
( /////////////////////////(((((((////////// ((
@@///@@@@@@//////////(((((((((//@@@////////// ((
(( ///// /////((((((((((((((//@@% %%@/////////// ((
//////////((((((((((((((//////((((( %%@@////////// ((
((%%//////////@@%% @@//(((((((/// ( (((( %%///////// (
(( ////////@@ ((( @@//(((((((///// (( ( @@/////////
((/////////@ (( (( @@/(((((((///// (( (( @@/((((///((
(( ///((//@@ (( (( %@@@@@@////////// (((( /((((/// ((
((/////((//%%( ////%%%@@////////// @//((((///
////(((// ///////// //@@/////////////////(((((//
( ////(((////( //////////////////////@@@@////////////(((((////((
////(((((((// //////////////////////////// %%@@///(((((((((// (
//(((((((((/////////////////// //////////// %%@//(((((((//// ((
//(((((((((((////////////((/// /////////////// @@//(((((((///
//(((((((((((////////(( /////////((//////////// ( //(((((((///////
//((((((( ((///////( ##### ////( ((/////// ( @@//(((((///////
//((((((( (((//////((( ### (((( ##### ((/////// (( //(((///////@@
//((((((((((((@@/////////(((((((((((((((((((((///// ///////////% ((
//((((((((((((%% ///////((((((((((((//////// //////////////@@%% ((
@@//(((((((((/%%(( /////////////////////// ///////////////// (((
( //(((((((((/ (( ///////////////////////
%%//(((((((///(( (( //////////////@///////%%
(( @@/((((((///(( (( //////////////@@%% /////// ((
(( /(((((((// ( //////////////@%% //////%((
(( @@//(((////(( (( /////(((((((///@@ ((( /////// (
( @@(((((// ( ////(((((((///@@% (((( /////////%%((
((%%//(((/////////(((((((//@%% //////////%% ((
((////(((((///////(((((///////////////////////% ((
( ////(((((/////(((((((((//////////((((///@@%% ((
////(((//////@//////(((((((((((((////@@@%% (
(( /////////@@%% %%@@@@@@@@@@@@@@@@@@@%% ((
(( @@@@@%% ((
(( (((
%%//%
( //((/ ((
( ///////((
( ///////((
///// ((
//
//
(( ///// ((

View file

@ -0,0 +1,150 @@
/* 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);
}
}

View file

@ -0,0 +1,125 @@
/* 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);
}
}

View file

@ -0,0 +1,193 @@
/* 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();
}
}

View file

@ -0,0 +1,141 @@
/* 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;
}
}

View file

@ -1,108 +0,0 @@
/*
Matrix 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_Matrix4
{
private double m[16];
dt_Matrix4 init()
{
int i;
for ( i=0; i<16; i++ ) m[i] = 0;
return self;
}
static dt_Matrix4 create()
{
return new("dt_Matrix4").init();
}
static dt_Matrix4 identity()
{
dt_Matrix4 o = dt_Matrix4.create();
for ( int i=0; i<4; i++ ) o.set(i,i,1);
return o;
}
double get( int c, int r )
{
return m[r*4+c];
}
void set( int c, int r, double v )
{
m[r*4+c] = v;
}
dt_Matrix4 add( dt_Matrix4 o )
{
dt_Matrix4 r = dt_Matrix4.create();
int i, j;
for ( i=0; i<4; i++ ) for ( j=0; j<4; j++ )
r.set(j,i,get(j,i)+o.get(j,i));
return r;
}
dt_Matrix4 scale( double s )
{
dt_Matrix4 r = dt_Matrix4.create();
int i, j;
for ( i=0; i<4; i++ ) for ( j=0; j<4; j++ )
r.set(j,i,get(j,i)*s);
return r;
}
dt_Matrix4 mul( dt_Matrix4 o )
{
dt_Matrix4 r = dt_Matrix4.create();
int i, j;
for ( i=0; i<4; i++ ) for ( j=0; j<4; j++ )
r.set(j,i,get(0,i)*o.get(j,0)+get(1,i)*o.get(j,1)+get(2,i)*o.get(j,2)+get(3,i)*o.get(j,3));
return r;
}
Vector3 vmat( Vector3 o )
{
double x, y, z, w;
x = get(0,0)*o.x+get(1,0)*o.y+get(2,0)*o.z+get(3,0);
y = get(0,1)*o.x+get(1,1)*o.y+get(2,1)*o.z+get(3,1);
z = get(0,2)*o.x+get(1,2)*o.y+get(2,2)*o.z+get(3,2);
w = get(0,3)*o.x+get(1,3)*o.y+get(2,3)*o.z+get(3,3);
return (x,y,z)/w;
}
static dt_Matrix4 rotate( Vector3 axis, double theta )
{
dt_Matrix4 r = dt_Matrix4.identity();
double s, c, oc;
s = sin(theta);
c = cos(theta);
oc = 1.0-c;
r.set(0,0,oc*axis.x*axis.x+c);
r.set(1,0,oc*axis.x*axis.y-axis.z*s);
r.set(2,0,oc*axis.x*axis.z+axis.y*s);
r.set(0,1,oc*axis.y*axis.x+axis.z*s);
r.set(1,1,oc*axis.y*axis.y+c);
r.set(2,1,oc*axis.y*axis.z-axis.x*s);
r.set(0,2,oc*axis.z*axis.x-axis.y*s);
r.set(1,2,oc*axis.z*axis.y+axis.x*s);
r.set(2,2,oc*axis.z*axis.z+c);
return r;
}
static dt_Matrix4 perspective( double fov, double ar, double znear, double zfar )
{
dt_Matrix4 r = dt_Matrix4.create();
double f = 1/tan(fov*0.5);
r.set(0,0,f/ar);
r.set(1,1,f);
r.set(2,2,(zfar+znear)/(znear-zfar));
r.set(3,2,(2*zfar*znear)/(znear-zfar));
r.set(2,3,-1);
return r;
}
}

View file

@ -51,7 +51,7 @@ Class BulletImpact : Actor
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
Scale 0.25;
@ -84,6 +84,8 @@ Class BulletImpact : Actor
s.vel = pvel;
}
A_PlaySound("bullet/hit",CHAN_VOICE,attenuation:3.0);
let s = Spawn("Splasher",Vec3Offset(0,0,2));
s.vel = (0,0,-4);
}
States
{
@ -93,6 +95,24 @@ Class BulletImpact : Actor
}
}
Class Splasher : Actor
{
Default
{
Radius 0.1;
Height 0;
+CORPSE;
+NOTELEPORT;
}
States
{
Spawn:
Crash:
TNT1 A 2;
Stop;
}
}
Class UTCasing : Actor
{
int deadtimer, numbounces;

View file

@ -59,7 +59,7 @@ Class ImpactHammer : UTWeapon
invoker.count = 0;
A_AlertMonsters();
}
A_QuakeEx(clamp(int(invoker.chargesize*3),0,3),clamp(int(invoker.chargesize*3),0,3),clamp(int(invoker.chargesize*3),0,3),2,0,96,"",QF_RELATIVE,rollIntensity:clamp(invoker.chargesize*0.3,0,0.3));
A_QuakeEx(clamp(int(invoker.chargesize),0,2),clamp(int(invoker.chargesize),0,2),clamp(int(invoker.chargesize),0,2),2,0,96,"",QF_RELATIVE,rollIntensity:clamp(invoker.chargesize*0.3,0,0.3));
UTMainHandler.DoSwing(self,(FRandom[Impact](-1,1),FRandom[Impact](-1,1)),invoker.chargesize*0.1,0,1,SWING_Spring);
if ( !(player.cmd.buttons&BT_ATTACK) )
{

View file

@ -105,6 +105,107 @@ Class DamageAmplifier : Powerup
}
}
Class UTInvulnerability : PowerupGiver
{
Default
{
Tag "$T_UTINVUL";
Inventory.PickupMessage "$I_UTINVUL";
+COUNTITEM;
+INVENTORY.AUTOACTIVATE;
+INVENTORY.ALWAYSPICKUP;
+INVENTORY.BIGPOWERUP;
Inventory.MaxAmount 0;
Powerup.Type "UTInvulPower";
Inventory.PickupSound "uinvul/pickup";
Inventory.RespawnTics 4200;
}
States
{
Spawn:
UKEY A -1;
Stop;
}
}
Class UTInvulLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
Args 255,238,0,80;
}
override void Tick()
{
Super.Tick();
if ( !target || !master )
{
Destroy();
return;
}
if ( target.player )
SetOrigin(target.Vec2OffsetZ(0,0,target.player.viewz),true);
else SetOrigin(target.Vec3Offset(0,0,target.height/2),true);
args[LIGHT_INTENSITY] = Random[UInvuln](10,12)*8;
bDORMANT = Powerup(master).isBlinking();
}
}
Class UTInvulPower : Powerup
{
Actor l;
int lasteffect;
Default
{
Powerup.Duration -60;
Powerup.Color "FFEE00", 0.05;
}
override void BeginPlay()
{
Super.BeginPlay();
if ( deathmatch ) EffectTics /= 2;
}
override void InitEffect()
{
Super.InitEffect();
lasteffect = int.min;
l = Spawn("UTInvulLight",Owner.pos);
l.target = Owner;
l.master = self;
}
override void DoEffect()
{
Super.DoEffect();
if ( (EffectTics == 175) || (EffectTics == 140) || (EffectTics == 105) || (EffectTics == 70) || (EffectTics == 35) )
Owner.A_PlaySound("uinvul/drain",CHAN_7,1.0,false,0.25);
}
override void EndEffect()
{
Super.EndEffect();
if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_UTINVUL"));
}
override bool isBlinking()
{
return ((EffectTics <= 175) && (EffectTics%35 >= 30));
}
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive )
{
if ( !passive || (damage <= 0) ) return;
newdamage = 0;
if ( gametic < lasteffect ) return;
Owner.A_PlaySound("uinvul/hit",CHAN_7,1.0,false,0.25);
UTMainHandler.DoFlash(Owner,Color(48,255,238,0),6);
lasteffect = gametic+5; // prevent excess flash
}
}
// Backpack that only gives ammo for valid weapons
Class UTBackpack : BackpackItem
{
@ -277,7 +378,7 @@ Class UTInvisibilityX : Actor
return;
}
Warp(target,flags:WARPF_COPYINTERPOLATION|WARPF_NOCHECKPOSITION);
bInvisible = target.bInvisible;
bInvisible = target.bInvisible||!target.InStateSequence(target.CurState,target.FindState("Spawn"));
}
States
{
@ -291,18 +392,19 @@ Class UTMapRevealer : MapRevealer
{
Default
{
Tag "$T_MAPREVEALER";
+COUNTITEM;
+INVENTORY.FANCYPICKUPSOUND;
+INVENTORY.ALWAYSPICKUP;
Inventory.MaxAmount 0;
Inventory.PickupSound "trans/pickup";
Inventory.PickupSound "misc/p_pkup";
Inventory.PickupMessage "$I_MAPREVEALER";
}
States
{
Spawn:
TRNS A -1;
Stop;
TRNS ABCDCB 6;
Loop;
}
}
@ -385,6 +487,7 @@ Class PowerJumpBoots_IronFeet : PowerIronFeet
}
override void AbsorbDamage( int damage, Name damageType, out int newdamage )
{
Inventory.AbsorbDamage(damage,damageType,newdamage);
}
override void DoEffect()
{
@ -394,159 +497,108 @@ Class PowerJumpBoots_IronFeet : PowerIronFeet
}
}
Class Searchlight : Inventory
Class UTNightVision : PowerupGiver
{
Actor lt[3];
int ticcnt;
Default
{
Tag "$T_SEARCHLIGHT";
Tag "$T_UTVISION";
Inventory.PickupMessage "$I_UTVISION";
+COUNTITEM;
+INVENTORY.UNTOSSABLE;
+INVENTORY.FANCYPICKUPSOUND;
+INVENTORY.AUTOACTIVATE;
+INVENTORY.ALWAYSPICKUP;
Inventory.Amount 200;
Inventory.MaxAmount 200;
Inventory.InterHubAmount 0;
Inventory.PickupMessage "$I_SEARCHLIGHT";
Inventory.PickupSound "lite/pickup";
Inventory.RespawnTics 1050;
+INVENTORY.BIGPOWERUP;
Inventory.MaxAmount 0;
Powerup.Type "UTVisionPower";
Inventory.PickupSound "uvision/pickup";
Inventory.RespawnTics 4200;
}
override bool Use( bool pickup )
override void PostBeginPlay()
{
if ( !lt[0] ) lt[0] = Spawn("mkLight");
lt[0].target = owner;
lt[0].master = self;
if ( !lt[1] ) lt[1] = Spawn("mkLight2");
lt[1].target = owner;
lt[1].master = self;
if ( !lt[2] ) lt[2] = Spawn("mkLight3");
lt[2].target = owner;
lt[2].master = self;
return Super.Use(pickup);
}
override void DetachFromOwner()
{
Super.DetachFromOwner();
if ( lt[0] ) lt[0].Destroy();
if ( lt[1] ) lt[1].Destroy();
if ( lt[2] ) lt[2].Destroy();
}
override void DoEffect()
{
Super.DoEffect();
if ( !Owner ) return;
if ( ticcnt++ < TICRATE ) return;
ticcnt = 0;
if ( --Amount <= 0 )
{
if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_SEARCHLIGHT"));
DepleteOrDestroy();
}
Super.PostBeginPlay();
tracer = Spawn("UTNightVisionX",pos);
tracer.angle = angle;
tracer.target = self;
}
States
{
Spawn:
SLIT A -1;
UKEY A -1;
Stop;
}
}
/* hello, Soundless Mound copypasted lights */
Class mkLight : DynamicLight
Class UTNightVisionX : UTInvisibilityX
{
int basecolor[3];
Default
{
Alpha 0.3;
}
States
{
Spawn:
UKEY A -1 Bright;
Stop;
}
}
Class UTVisionLight : DynamicLight
{
Default
{
DynamicLight.Type "Point";
+DynamicLight.SPOT;
+DynamicLight.ATTENUATE;
+DynamicLight.DONTLIGHTSELF;
args 255,224,160,300;
DynamicLight.SpotInnerAngle 20;
DynamicLight.SpotOuterAngle 35;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
basecolor[0] = args[LIGHT_RED];
basecolor[1] = args[LIGHT_GREEN];
basecolor[2] = args[LIGHT_BLUE];
+DYNAMICLIGHT.SPOT;
DynamicLight.SpotInnerAngle 60;
DynamicLight.SpotOuterAngle 90;
Args 224,238,255,800;
}
override void Tick()
{
Super.Tick();
if ( !target || !Inventory(master) )
if ( !target || !master )
{
Destroy();
return;
}
if ( target.player ) SetOrigin(target.Vec2OffsetZ(0,0,target.player.viewz),true);
else SetOrigin(target.vec3Offset(0,0,target.height*0.75),true);
A_SetAngle(target.angle,SPF_INTERPOLATE);
A_SetPitch(target.pitch,SPF_INTERPOLATE);
args[LIGHT_RED] = int(basecolor[0]*clamp(Inventory(master).amount/40.,0.,1.));
args[LIGHT_GREEN] = int(basecolor[1]*clamp(Inventory(master).amount/40.,0.,1.));
args[LIGHT_BLUE] = int(basecolor[2]*clamp(Inventory(master).amount/40.,0.,1.));
bDORMANT = (target.health <= 0);
if ( Inventory(target) && target.bInvisible ) bDORMANT = true;
// alert monsters hit by the light
if ( GetClass() != "mkLight" ) return;
if ( !bDORMANT && target.player && (target.health > 0) )
{
BlockThingsIterator bt = BlockThingsIterator.Create(target,args[LIGHT_INTENSITY]);
while ( bt.Next() )
{
if ( !bt.Thing || (Distance3D(bt.Thing) > args[LIGHT_INTENSITY]) ) continue;
Vector3 aimdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
Vector3 reldir = Vec3To(bt.Thing).unit();
if ( (acos(aimdir dot reldir) < SpotOuterAngle+5) && bt.Thing.CheckSight(target) ) bt.Thing.LastHeard = target;
}
}
Vector3 x, y, z, origin;
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
if ( target.player )
origin = target.Vec2OffsetZ(0,0,target.player.viewz);
else origin = target.Vec3Offset(0,0,target.height/2);
SetOrigin(origin,true);
angle = target.angle;
pitch = target.pitch;
args[LIGHT_INTENSITY] = Random[UVision](40,48)*20;
bDORMANT = ((players[consoleplayer].Camera != target)||Powerup(master).isBlinking());
}
}
Class mkLight2 : mkLight
Class UTVisionPower : Powerup
{
Default
{
args 128,112,96,300;
DynamicLight.SpotInnerAngle 0;
DynamicLight.SpotOuterAngle 50;
}
}
Class mkLight3 : DynamicLight
{
int basecolor[3];
Actor l;
Default
{
DynamicLight.Type "Point";
+DynamicLight.ATTENUATE;
args 32,28,24,0;
Powerup.Duration -90;
Powerup.Color "AAEEFF", 0.05;
}
override void PostBeginPlay()
override void BeginPlay()
{
Super.PostBeginPlay();
basecolor[0] = args[LIGHT_RED];
basecolor[1] = args[LIGHT_GREEN];
basecolor[2] = args[LIGHT_BLUE];
Super.BeginPlay();
if ( deathmatch ) EffectTics /= 2;
}
override void Tick()
override void InitEffect()
{
Super.Tick();
if ( !target || Inventory(target) || !Inventory(master) )
{
Destroy();
return;
}
args[LIGHT_RED] = int(basecolor[0]*clamp(Inventory(master).amount/40.,0.,1.));
args[LIGHT_GREEN] = int(basecolor[1]*clamp(Inventory(master).amount/40.,0.,1.));
args[LIGHT_BLUE] = int(basecolor[2]*clamp(Inventory(master).amount/40.,0.,1.));
SetOrigin(target.vec3Offset(0,0,target.height*0.5),true);
Super.InitEffect();
l = Spawn("UTVisionLight",Owner.pos);
l.target = Owner;
l.master = self;
}
override void EndEffect()
{
Super.EndEffect();
if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_UTVISION"));
}
}

View file

@ -143,6 +143,8 @@ Class Razor2 : Actor
}
Goto Spawn;
Death:
TNT1 A 0 A_RazorHit();
XDeath:
TNT1 A 1 A_StopSound(CHAN_VOICE);
Stop;
}

View file

@ -442,6 +442,15 @@ Class Translocator : UTWeapon
A_AlertMonsters();
// squeeze down new z if ceiling is in the way
if ( (newpos.z+height > moduleceilingz) ) newpos.z = max(modulefloorz,moduleceilingz-height);
// temporarily disable telefragging for all allies
bool oldnotele[MAXPLAYERS];
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
oldnotele[i] = players[i].mo.bNOTELEFRAG;
if ( !IsFriend(players[i].mo) ) continue;
players[i].mo.bNOTELEFRAG = true;
}
if ( Warp(self,newpos.x,newpos.y,newpos.z,flags:WARPF_ABSOLUTEPOSITION|WARPF_TESTONLY) && TeleportMove(newpos,true) )
{
SpawnTeleportFog(oldpos,true,false);
@ -454,6 +463,11 @@ Class Translocator : UTWeapon
player.fov = min(175,player.desiredfov+60);
}
else A_PlaySound("transloc/return",CHAN_WEAPON);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
players[i].mo.bNOTELEFRAG = oldnotele[i];
}
if ( bBroken )
{
UTMainHandler.DoFlash(self,Color(255,255,255,255),50);

View file

@ -139,13 +139,21 @@ Class UTPlayer : DoomPlayer
if ( giveall || (name ~== "armor") )
{
// Doom Tournament just gives the player a shield belt and maximum bonuses
let belt = Inventory(Spawn("UTShieldBelt"));
belt.ClearCounters();
if ( !belt.CallTryPickup(self) ) belt.Destroy();
let bonus = Inventory(Spawn("UTArmorBonus"));
bonus.ClearCounters();
bonus.Amount = bonus.MaxAmount;
if ( !bonus.CallTryPickup(self) ) bonus.Destroy();
// in non-vanilla mode, also gives body/thigh armor
Class<Inventory> which[] =
{
"UTShieldBelt", "UTArmorBonus",
"UTBodyArmor", "UTThighpads"
};
Inventory inv;
int mx = flak_vanillaarmor?2:4;
for ( int i=0; i<mx; i++ )
{
inv = Inventory(Spawn(which[i]));
inv.ClearCounters();
inv.Amount = inv.MaxAmount;
if ( !inv.CallTryPickup(self) ) inv.Destroy();
}
if ( !giveall ) return;
}
if ( giveall || (name ~== "keys") )
@ -1274,23 +1282,71 @@ Class UTUnderSound : Actor
}
}
// TODO terrain stuff
// terrain splashes
Class UTTerrainFX : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
}
States
{
Spawn:
TNT1 A 1;
SPSH ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_FadeOut(1./26.);
Stop;
}
}
Class UTWaterSplish : UTTerrainFX
{
override void PostBeginPlay()
{
Super.PostBeginPlay();
let r = Spawn("WaterRing",pos);
r.scale *= 0.3;
}
}
Class UTWaterSplash : UTTerrainFX
{
override void PostBeginPlay()
{
Super.PostBeginPlay();
Spawn("WaterRing",pos);
}
}
Class WaterRing : Actor
{
Default
{
RenderStyle "Add";
Scale 0.15;
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
alpha -= 1./20.;
}
States
{
Spawn:
RNGX ABCDEF 5 Bright;
Stop;
}
}
Class UTBloodSplish : UTTerrainFX
@ -1311,18 +1367,112 @@ Class UTSlimeSplash : UTTerrainFX
Class UTNukageSplish : UTTerrainFX
{
Default
{
+BRIGHT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
int numpt = Random[Terrain](3,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](2,5);
let s = Spawn("BioSpark",pos);
s.vel = pvel;
}
numpt = Random[Terrain](1,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](.6,1.2);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
s.scale *= 0.7;
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
if ( Random[Terrain](0,1) ) s.SetShade("40FF60");
else s.SetShade("60FF40");
}
}
}
Class UTNukageSplash : UTTerrainFX
{
Default
{
+BRIGHT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
int numpt = Random[Terrain](8,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](3,12);
let s = Spawn("BioSpark",pos);
s.vel = pvel;
}
numpt = Random[Terrain](3,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](1.2,2.4);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
s.scale *= 2;
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
if ( Random[Terrain](0,1) ) s.SetShade("40FF60");
else s.SetShade("60FF40");
}
}
}
Class UTLavaSplish : UTTerrainFX
{
Default
{
+BRIGHT;
}
}
Class UTLavaSplash : UTTerrainFX
{
Default
{
+BRIGHT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
Spawn("FlameExplosion",pos);
}
}
Class FlameExplosion : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
Scale 0.8;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_PlaySound("ut/lavaex",CHAN_VOICE);
Spawn("SlugSmoke",pos);
Spawn("SlugLight",pos);
}
States
{
Spawn:
FEXP ABCDEFGHIJ 2 BRIGHT;
Stop;
}
}
Class UTNitroSplish : UTTerrainFX
@ -2775,25 +2925,31 @@ Class UTMainHandler : EventHandler
else if ( Random[Replacements](0,1) ) e.Replacement = 'MiniAmmo';
else e.Replacement = 'RifleAmmo';
}
else if ( e.Replacee == 'InvulnerabilitySphere' ) e.Replacement = (!deathmatch||flak_dmsshock)?'EnhancedShockRifle':'UDamage';
else if ( e.Replacee == 'InvulnerabilitySphere' ) e.Replacement = 'UTInvulnerability';
else if ( e.Replacee == 'Berserk' ) e.Replacement = 'UDamage';
else if ( (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiEgg') ) e.Replacement = 'ActUDamage';
else if ( e.Replacee == 'Soulsphere' ) e.Replacement = 'UTHealthPack';
else if ( e.Replacee == 'ArtiSuperHealth' ) e.Replacement = 'ActHealthPack';
else if ( e.Replacee == 'Megasphere' ) e.Replacement = 'UTShieldBelt';
else if ( e.Replacee == 'ArtiInvulnerability' ) e.Replacement = 'ActShieldBelt';
else if ( e.Replacee == 'ArtiInvulnerability' ) e.Replacement = 'ActUTInvulnerability';
else if ( (e.Replacee == 'Allmap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'UTMapRevealer';
else if ( e.Replacee == 'BlurSphere' ) e.Replacement = 'UTInvisibility';
else if ( e.Replacee == 'ArtiInvisibility' ) e.Replacement = 'ActInvisibility';
else if ( e.Replacee == 'Infrared' ) e.Replacement = 'Searchlight';
else if ( e.Replacee == 'ArtiTorch' ) e.Replacement = 'ActSearchlight';
else if ( e.Replacee == 'ArtiInvisibility' ) e.Replacement = 'ActUTInvisibility';
else if ( e.Replacee == 'Infrared' ) e.Replacement = 'UTNightVision';
else if ( e.Replacee == 'ArtiTorch' ) e.Replacement = 'ActUTNightVision';
else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UTJumpBoots';
else if ( e.Replacee == 'ArtiFly' ) e.Replacement = 'ActJumpBoots';
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') ) e.Replacement = 'UTBackpack';
else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') ) e.Replacement = 'UTArmorBonus';
else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'UTHealthBonus';
else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'Silvershield') ) e.Replacement = 'UTThighPads';
else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'UTBodyArmor';
else if ( e.Replacee == 'GreenArmor' ) e.Replacement = 'UTThighPads';
else if ( e.Replacee == 'Silvershield' )
{
if ( Random[Replacements](0,1) ) e.Replacement = 'UTThighPads';
else e.Replacement = 'UTBodyArmor';
}
else if ( e.Replacee == 'BlueArmor' ) e.Replacement = 'UTBodyArmor';
else if ( e.Replacee == 'EnchantedShield' ) e.Replacement = 'UTShieldBelt';
else if ( e.Replacee == 'Stimpack' ) e.Replacement = 'UTMedBox';
else if ( e.Replacee == 'Medikit' ) e.Replacement = 'UTHealthBox';
else if ( e.Replacee == 'ArtiHealth' )
@ -2804,9 +2960,7 @@ Class UTMainHandler : EventHandler
else if ( e.Replacee == 'ArtiTeleport' )
{
// I have no idea what to replace this with, so just have some random stuff
if ( Random[Replacements](0,1) ) e.Replacement = 'UTBackpack';
else if ( Random[Replacements](0,1) ) e.Replacement = 'ActShieldBelt';
else if ( Random[Replacements](0,1) ) e.Replacement = 'ActHealthPack';
if ( Random[Replacements](0,1) ) e.Replacement = 'ActHealthPack';
else e.Replacement = 'ActUDamage';
}
else if ( e.Replacee == 'RedCard' ) e.Replacement = 'UTRedKey';

View file

@ -69,6 +69,7 @@ Class UTBloodDrop : Actor
RenderStyle "Translucent";
+MISSILE;
+NOTELEPORT;
+DONTSPLASH;
+THRUACTORS;
+FORCEXYBILLBOARD;
}

View file

@ -23,7 +23,7 @@ Class ViewTracer : LineTracer
Class UTHud : BaseStatusBar
{
TextureID AmmoBar, Boxes[4], Keys[5], BigNum[12], Flash, Slots[10], Icons[14], Uses[14], Man[5], Woman[5], Boss[5], WeaponBox, IconTloc2, UseTloc2, IconSaw2, UseSaw2, ItemBox, ItemSel, ItemFlash, ItemArrow[2], LastItem, FacePanel[3];
TextureID AmmoBar, Boxes[4], Keys[5], BigNum[12], Flash, Slots[10], Icons[14], Uses[14], Man[5], Woman[5], Boss[5], WeaponBox, IconTloc2, UseTloc2, IconSaw2, UseSaw2, IconAuto2, UseAuto2, ItemBox, ItemSel, ItemFlash, ItemArrow[2], LastItem, FacePanel[3];
Class<Weapon> IconClasses[14];
double HScale;
Color tintcolor, bgcolor;
@ -34,6 +34,7 @@ Class UTHud : BaseStatusBar
int lastseentic;
bool showweapons, showfrags, showammo, showstatus, showinfo;
double hudsize, weaponsize, statussize;
bool justselected;
HUDFont mUTFont12, mUTFont40;
@ -167,6 +168,8 @@ Class UTHud : BaseStatusBar
UseTloc2 = TexMan.CheckForTexture("UseTrn2",TexMan.Type_Any);
IconSaw2 = TexMan.CheckForTexture("IconSaw2",TexMan.Type_Any);
UseSaw2 = TexMan.CheckForTexture("UseSaw2",TexMan.Type_Any);
IconAuto2 = TexMan.CheckForTexture("IconAut2",TexMan.Type_Any);
UseAuto2 = TexMan.CheckForTexture("UseAut2",TexMan.Type_Any);
FacePanel[0] = TexMan.CheckForTexture("FacePnl",TexMan.Type_Any);
FacePanel[1] = TexMan.CheckForTexture("FacePnlA",TexMan.Type_Any);
FacePanel[2] = TexMan.CheckForTexture("Static1",TexMan.Type_Any);
@ -252,7 +255,9 @@ Class UTHud : BaseStatusBar
if ( !(w is IconClasses[i]) ) continue;
if ( use )
{
if ( (i == 10) && flak_sawammo )
if ( (i == 1) && (w.Amount > 1) )
UTDrawTintedTex(UseAuto2,sx,opacity+7);
else if ( (i == 10) && flak_sawammo )
UTDrawTintedTex(UseSaw2,sx,opacity+7);
else if ( (i == 11) && flak_transloc2k4 )
UTDrawTintedTex(UseTloc2,sx,opacity+7);
@ -260,7 +265,9 @@ Class UTHud : BaseStatusBar
}
else
{
if ( (i == 10) && flak_sawammo )
if ( (i == 1) && (w.Amount > 1) )
UTDrawTintedTex(IconAuto2,sx,opacity,halftint);
else if ( (i == 10) && flak_sawammo )
UTDrawTintedTex(IconSaw2,sx,opacity,halftint);
else if ( (i == 11) && flak_transloc2k4 )
UTDrawTintedTex(IconTloc2,sx,opacity,halftint);
@ -433,11 +440,14 @@ Class UTHud : BaseStatusBar
CurY = 0;
Color dollcolor = tintcolor;
DamageAmplifier d;
UTInvulPower p;
UTJumpBoots j;
d = DamageAmplifier(CPlayer.mo.FindInventory("DamageAmplifier"));
p = UTInvulPower(CPlayer.mo.FindInventory("UTInvulPower"));
j = UTJumpBoots(CPlayer.mo.FindInventory("UTJumpBoots"));
if ( d && !d.isBlinking() ) dollcolor = d.BlendColor;
int dolltype = 0;
if ( p && !p.isBlinking() ) dollcolor = LerpColor(GoldColor,dollcolor,((gametic+fractic)/15)%1.);
if ( CPlayer.mo is 'UTPlayer' ) dolltype = UTPlayer(CPlayer.mo).DollType;
else
{
@ -1036,6 +1046,25 @@ Class UTHud : BaseStatusBar
LastItem = CPlayer.mo.InvSel.Icon;
LastAmount = CPlayer.mo.InvSel.Amount-1;
}
if ( CPlayer.inventorytics >= (5*Thinker.TICRATE)-1 )
{
if ( CPlayer.mo.InvSel )
{
PickupMsg = CPlayer.mo.InvSel.GetTag();
PickupMsgTic = gametic+50;
S_Sound("misc/hud_sel",CHAN_UI);
}
}
if ( CPlayer.inventorytics > 0 ) justselected = false;
else
{
if ( !justselected && CPlayer.mo.InvSel )
{
Console.Printf(StringTable.Localize("$M_ISELECT"),CPlayer.mo.InvSel.GetTag());
S_Sound("misc/hud_sel",CHAN_UI);
}
justselected = true;
}
if ( deathmatch||teamplay )
{
if ( CPlayer.fragcount != lastfragcnt ) lastfrag = level.time;

View file

@ -444,6 +444,13 @@ Class RedeemerHUD : HUDMessageBase
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;
RedeemerHUD Init()
{
@ -460,15 +467,45 @@ Class RedeemerHUD : HUDMessageBase
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() )
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();
@ -479,11 +516,11 @@ Class RedeemerHUD : HUDMessageBase
{
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 wpos = ViewPos+tdir;
Vector3 spos = dt_CoordUtil.WorldToScreen(wpos,ViewPos,ViewPitch,ViewAngle,ViewRoll,players[consoleplayer].FOV);
if ( spos.z > 1.0 ) continue;
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
TargetActor te = new("TargetActor");
te.vpos = dt_CoordUtil.ToViewport(spos);
te.vpos = viewport.SceneToWindow(npos);
te.diststr = String.Format("%f",tdir.length());
te.diststr.Replace(".","");
ta.Push(te);
@ -496,10 +533,15 @@ 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() )
if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() && can_project )
{
if ( CVar.GetCVar('flak_redeemerreadout_perframe',players[consoleplayer]).GetBool() )
{
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();
@ -510,11 +552,11 @@ Class RedeemerHUD : HUDMessageBase
{
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 wpos = ViewPos+tdir;
Vector3 spos = dt_CoordUtil.WorldToScreen(wpos,ViewPos,ViewPitch,ViewAngle,ViewRoll,players[consoleplayer].FOV);
if ( spos.z > 1.0 ) continue;
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
TargetActor te = new("TargetActor");
te.vpos = dt_CoordUtil.ToViewport(spos);
te.vpos = viewport.SceneToWindow(npos);
te.diststr = String.Format("%f",tdir.length());
te.diststr.Replace(".","");
ta.Push(te);