stinger_m/zscript/peacemaker.zsc
Marisa Kirisame c6a81479ca Release Candidate 3:
- Fix inventory items not removing themselves when fully drained and no more
  copies are held.
- Fixed Minigun playing the unwind animation on player death.
- Fixed main hand Automag still firing when out of ammo dual wielding.
- Corrected name clash between two explosion sounds.
- Fixed suits still protecting from elemental damage when depleted.
- Add option to wear all suits simultaneously.
- Stunner now consumes Stinger ammo to recharge, this is more in line with the
  Unreal Bible.
- Max damage per explosion capped to 100 for Stinger. Prevents ludicrous
  map-clearing explosions with an amplified asmd combo.
- Shoot-through lines can now be activated by hitscan/beam weapons thanks to
  new DT "bullet trail" feature. No more softlocks in custom maps.
- Added option to make Peacemaker missiles not seek owner and allies.
- Peacemaker missiles start seeking targets much earlier, making it more viable
  indoors.
- Autocannon has had its damage increased again.
- Adjusted swingers for many weapons to feel a bit more natural. Still far from
  perfect.
- Reverted changes to Flamethrower projectile density, and simply made it have
  less dynamic lights.
- Adjustments to armors. Suit elemental resistances now take priority (as
  intended). [please redownload your DT devbuild for full effect]
- Added ring effect for 6-rocket tight wad. Completely forgot this was a thing.
- Fixed flashlight not clearing its dynlights when depleted and still having
  copies.
2019-10-10 09:32:29 +02:00

568 lines
14 KiB
Text

Class PeaceAmmo : Ammo
{
Default
{
Inventory.Icon "I_Peace";
Inventory.Amount 1;
Inventory.MaxAmount 1;
Ammo.BackpackAmount 0;
Ammo.BackpackMaxAmount 2;
}
override bool TryPickup( in out Actor toucher )
{
if ( !sting_proto ) return false; // not allowed
return Super.TryPickup(toucher);
}
override void Tick()
{
Super.Tick();
if ( sting_proto ) return;
if ( Owner ) Owner.RemoveInventory(self);
Destroy();
}
}
Class PeaceLight : RocketLight
{
Default
{
Args 255,128,112,32;
}
}
Class PeaceTrail : Actor
{
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.25;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("PeaceLight",pos);
l.target = self;
}
override void Tick()
{
Super.Tick();
if ( !target || !target.bMISSILE )
{
Destroy();
return;
}
Vector3 dir = (cos(target.angle)*cos(target.pitch),sin(target.angle)*cos(target.pitch),-sin(target.pitch));
SetOrigin(level.Vec3Offset(target.pos,-dir*3),true);
}
States
{
Spawn:
PFLA A -1 Bright;
Stop;
}
}
Class PeaceRocket : Actor
{
Vector3 Acceleration;
int ticcnt;
Default
{
Obituary "$O_PEACE";
DamageType 'PeaceDeath';
Radius 2;
Height 2;
Speed 3.5;
PROJECTILE;
+SKYEXPLODE;
+EXPLODEONWATER;
+SEEKERMISSILE;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+INTERPOLATEANGLES;
+HITOWNER;
ReactionTime 9;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("PeaceTrail",pos);
l.target = self;
A_PlaySound("peace/fly",CHAN_VOICE,1.0,true,2.5,pitch:0.8);
Acceleration = vel*1.67;
}
action void A_CheckForTargets()
{
let bi = BlockThingsIterator.Create(self,500);
double mindist = double.infinity;
while ( bi.Next() )
{
if ( !bi.Thing || (!bi.Thing.bISMONSTER && !bi.Thing.player) || (bi.Thing.Health <= 0) || (Distance3D(bi.Thing) > 500) || !CheckSight(bi.Thing) ) continue;
if ( sting_peacehome && ((bi.Thing == target) || (target && target.IsFriend(bi.Thing))) ) continue;
double dist = Distance3D(bi.Thing);
if ( dist > mindist ) break;
tracer = bi.Thing;
mindist = dist;
}
}
action void A_SeekTargets()
{
if ( !tracer || (tracer.Health <= 0) ) return;
double mag = vel.length();
vel = mag*(level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2)).unit()*mag*6./TICRATE+vel).unit();
}
action void A_PeaceExplode()
{
bFORCEXYBILLBOARD = true;
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("RocketBlast",50);
A_NoGravity();
A_SetScale(1.1);
UTMainHandler.DoBlast(self,200,70000);
A_Explode(100,200);
A_QuakeEx(3,3,3,8,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:0.2);
A_PlaySound("utrl/explode",CHAN_VOICE);
A_AlertMonsters();
Spawn("RocketExplLight",pos);
int numpt = Random[Peace](15,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](1,3);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
}
numpt = Random[Peace](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](2,6);
let s = Spawn("UTSpark",pos);
s.vel = pvel;
}
numpt = Random[Peace](35,70);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](2,12);
let s = Spawn("UTChip",pos);
s.vel = pvel;
s.scale *= FRandom[Peace](0.9,2.7);
}
}
States
{
Spawn:
PEAR A 1
{
roll += 5.;
A_SeekTargets();
vel += Acceleration/TICRATE;
double mag = vel.length();
if ( mag > 0. )
{
Vector3 dir = vel/mag;
if ( mag > 16 ) vel = vel.unit()*16;
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
mag = Acceleration.length();
Acceleration = dir*mag;
}
for ( int i=0; i<3; i++ )
{
let s = Spawn("UTSmoke",pos);
s.vel = (FRandom[Peace](-0.2,0.2),FRandom[Peace](-0.2,0.2),FRandom[Peace](-0.2,0.2));
s.vel += vel*0.1;
s.SetShade(Color(1,1,1)*Random[Peace](32,48));
}
special1++;
if ( special1 > 35 )
{
special1 = 0;
A_CheckForTargets();
A_CountDown();
}
// early check
if ( GetAge() == 5 ) A_CheckForTargets();
}
Wait;
Death:
TNT1 A 0 A_PeaceExplode();
SSMX ABCDEFGHIJ 2 Bright;
Stop;
}
}
Class PeaceFragment : SentryFragment
{
Default
{
Scale 0.5;
}
}
Class PeaceBarrel : Actor
{
action void A_AlignSelf()
{
// find closest 3d floor for its normal
F3DFloor ff = null;
for ( int i=0; i<floorsector.Get3DFloorCount(); i++ )
{
if ( !(floorsector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = floorsector.Get3DFLoor(i);
break;
}
Vector3 normal;
if ( ff ) normal = -ff.top.Normal;
else normal = floorsector.floorplane.Normal;
// bless Gutawer
Vector2 angv = (cos(angle),sin(angle));
Vector3 x;
if ( ff ) x = (angv,ff.top.ZAtPoint(pos.xy+angv)-pos.z).unit();
else x = (angv,floorsector.floorplane.ZAtPoint(pos.xy+angv)-pos.z).unit();
pitch = -asin(x.z);
roll = asin(normal.x*angv.y-normal.y*angv.x);
}
action void A_FireRocket( int n )
{
A_PlaySound("flak/altfire",CHAN_AUTO);
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 dir = z*1.3;
Vector3 origin;
switch ( n )
{
case 0:
// front right
dir = (dir+x+y).unit();
origin = level.Vec3Offset(pos,z*4+x*4+y*4);
break;
case 1:
// front left
dir = (dir-x+y).unit();
origin = level.Vec3Offset(pos,z*4-x*4+y*4);
break;
case 2:
// back right
dir = (dir+x-y).unit();
origin = level.Vec3Offset(pos,z*4+x*4-y*4);
break;
case 3:
// back left
dir = (dir-x-y).unit();
origin = level.Vec3Offset(pos,z*4-x*4-y*4);
break;
}
let r = Spawn("PeaceRocket",origin);
r.angle = atan2(dir.y,dir.x);
r.pitch = asin(-dir.z);
r.vel = dir*r.speed;
r.target = target;
}
action void A_BlowUp()
{
bFORCEXYBILLBOARD = true;
SetOrigin(Vec3Offset(0,0,16),false);
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("RocketBlast",50);
A_NoGravity();
A_SetScale(bAMBUSH?1.8:1.2);
A_Explode(bAMBUSH?500:125,bAMBUSH?200:100);
A_PlaySound("sentry/explode",CHAN_BODY,pitch:0.6);
A_PlaySound("utrl/explode",CHAN_VOICE,pitch:0.8);
A_AlertMonsters();
A_QuakeEx(4,4,4,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:0.2);
UTMainHandler.DoBlast(self,bAMBUSH?200:100,50000);
double ang, pt;
for ( int i=0; i<16; i++ )
{
let f = Spawn("PeaceFragment",Vec3Offset(FRandom[EFrag](-8,8),FRandom[EFrag](-8,8),FRandom[EFrag](4,12)));
ang = FRandom[EFrag](0,360);
pt = FRandom[EFrag](-90,90);
f.vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[EFrag](4,20);
f.vel.z += 3.;
}
int numpt = Random[ExploS](20,35);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,9);
let s = Spawn("UTSmoke",pos);
s.vel = pvel;
s.scale *= 2.;
}
numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
let s = Spawn("UTSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
let s = Spawn("UTChip",pos);
s.vel = pvel;
s.scale *= FRandom[ExploS](0.9,2.7);
}
Spawn("FlareXLight",pos);
}
Default
{
Obituary "$O_PEACEMK";
Radius 6;
Height 9;
DamageType 'PeaceBarrelDeath';
PROJECTILE;
-NOGRAVITY;
+SKYEXPLODE;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+MOVEWITHSECTOR;
+CANBOUNCEWATER;
+BOUNCEAUTOOFF;
+BOUNCEAUTOOFFFLOORONLY;
+USEBOUNCESTATE;
+INTERPOLATEANGLES;
Speed 8;
BounceType "Hexen";
BounceFactor 0.3; // otherwise it's too damn bouncy
BounceSound "transloc/bounce";
WallBounceFactor 0.8;
Gravity 0.35;
}
States
{
Spawn:
PEAM A -1;
Stop;
Bounce:
PEAM A 0
{
if ( BlockingFloor ) A_AlignSelf();
else pitch = roll = 0;
if ( vel.length() < 2. ) ClearBounce();
}
Goto Spawn;
Death:
PEAM A 1 A_CheckFloor(1);
Wait;
PEAM A 4 A_AlignSelf();
PEAM A 35
{
A_PlaySound((special1<=0)?"utrl/seeklost":"utrl/seeklock",CHAN_AUTO);
A_AlertMonsters();
return A_JumpIf(--special1<0,1);
}
Wait;
PEAM A 0 A_JumpIf(bAMBUSH,"Detonate");
PEAM A 3 A_PlaySound("peace/open",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
PEAM B 3;
PEAM C 3 A_PlaySound("peace/open",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
PEAM D 3 A_PlaySound("transloc/bounce",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
PEAM E 3 A_PlaySound("peace/open",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
PEAM F 3
{
A_PlaySound("transloc/bounce",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
A_PlaySound("transloc/bounce",CHAN_AUTO,.5,pitch:FRandom[Peace](0.8,1.2));
}
PEAM G 3 A_PlaySound("peace/open",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
PEAM H 3
{
A_PlaySound("transloc/bounce",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
A_PlaySound("transloc/bounce",CHAN_AUTO,.5,pitch:FRandom[Peace](0.8,1.2));
}
PEAM I 3;
PEAM J 3
{
A_PlaySound("transloc/bounce",CHAN_AUTO,.8,pitch:FRandom[Peace](0.8,1.2));
A_PlaySound("transloc/bounce",CHAN_AUTO,.5,pitch:FRandom[Peace](0.8,1.2));
}
PEAM K 3 A_PlaySound("utrl/rotate",CHAN_AUTO,.1);
PEAM L 3 A_PlaySound("transloc/bounce",CHAN_AUTO,.5,pitch:FRandom[Peace](0.8,1.2));
PEAM M 3;
PEAM N 3 A_PlaySound("utrl/rotate",CHAN_AUTO,.1);
PEAM OPQ 3;
PEAM R 3 A_PlaySound("utrl/rotate",CHAN_AUTO,.1);
PEAM STU 3;
PEAM V 3 A_PlaySound("utrl/rotate",CHAN_AUTO,.1);
PEAM XYZ[\] 3;
PEAM ] 35;
PEAM ] 0 A_FireRocket(0);
PEAL A 20;
PEAL A 0 A_FireRocket(1);
PEAL B 20;
PEAL B 0 A_FireRocket(2);
PEAL C 20;
PEAL C 0 A_FireRocket(3);
PEAL D 20;
Detonate:
PEAM A 20;
BlowUp:
TNT1 A 0 A_BlowUp();
SSMX ABCDEFGHIJ 2 Bright;
Stop;
}
}
Class Peacemaker : UnrealWeapon
{
override bool TryPickup( in out Actor toucher )
{
if ( !sting_proto ) return false; // not allowed
return Super.TryPickup(toucher);
}
override void Tick()
{
Super.Tick();
if ( sting_proto ) return;
if ( Owner ) Owner.RemoveInventory(self);
Destroy();
}
action void A_PeacemakerThrow( bool bAlt = false )
{
let weap = Weapon(invoker);
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
invoker.FireEffect();
A_PlaySound("peace/throw",CHAN_WEAPON);
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*5-z*3);
let p = Spawn("PeaceBarrel",origin);
if ( bAlt ) p.bAMBUSH = true;
p.special1 = invoker.special2;
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.z += 2.;
p.target = self;
if ( weap.Ammo1.Amount <= 0 ) player.SetPSprite(PSP_WEAPON,invoker.FindState("EmptyIdle"));
else
{
invoker.PlayUpSound(self);
player.SetPSprite(PSP_WEAPON,invoker.FindState("Ready"));
}
}
action void A_StartCount()
{
invoker.special1 = invoker.special2 = 0;
A_PlaySound("peace/set",CHAN_ITEM,.4);
}
action void A_CountUp( Statelabel next )
{
invoker.special1++;
if ( invoker.special1 > 20 )
{
invoker.special2++;
if ( invoker.special2 <= 9 )
{
A_PlaySound("peace/set",CHAN_ITEM,.4);
player.FindPSprite(PSP_WEAPON).frame = invoker.special2;
}
invoker.special1 = 0;
}
if ( (invoker.special2 >= 10) || !(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)) )
{
invoker.special2 = min(9,invoker.special2);
player.SetPSprite(PSP_WEAPON,invoker.FindState(next));
}
}
Default
{
Tag "$T_PEACE";
Inventory.PickupMessage "$I_PEACE";
Weapon.UpSound "peace/select";
Weapon.SlotNumber 8;
Weapon.SelectionOrder 1050;
Weapon.SlotPriority 0.9;
Weapon.AmmoType "PeaceAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "PeaceAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 1;
UTWeapon.DropAmmo 1;
}
States
{
Spawn:
PEAP A -1;
Stop;
PEAP B -1;
Stop;
Select:
PEAS A 1 A_Raise(int.max);
Wait;
Ready:
PEAS ABCDEFGHIJ 2 A_WeaponReady(WRF_NOFIRE);
PEAS K 0 A_PlaySound("peace/up",CHAN_ITEM,.4);
PEAS KLMNOPQRST 2 A_WeaponReady(WRF_NOFIRE);
Idle:
PEAI A 1
{
A_CheckReload();
A_WeaponReady();
}
Wait;
EmptyIdle:
TNT1 A 1
{
let weap = Weapon(invoker);
if ( weap.Ammo1.Amount > 0 )
return ResolveState("Ready");
A_CheckReload();
A_WeaponReady(WRF_NOFIRE);
return ResolveState(null);
}
Wait;
Fire:
PEAC A 1 A_StartCount();
PEAC # 1 A_CountUp(1);
Wait;
PEAF ABCD 2;
PEAF E 0
{
A_PlaySound("peace/down",CHAN_ITEM,.4);
UTMainHandler.DoSwing(self,(FRandom[Peace](-0.1,-0.04),FRandom[Peace](0.4,0.6)),3,0,7,SWING_Spring,3,0.8);
}
PEAF EFG 2;
PEAF H 0
{
UTMainHandler.DoSwing(self,(FRandom[Peace](0.08,0.12),FRandom[Peace](-1.2,-0.9)),4,0,6,SWING_Spring,3,1.5);
}
PEAF HI 2; // hello
PEAF I -1 A_PeacemakerThrow();
Stop;
AltFire:
PEAC A 1 A_StartCount();
PEAC # 1 A_CountUp(1);
Wait;
PEAF ABCD 2;
PEAF E 0
{
A_PlaySound("peace/down",CHAN_ITEM,.4);
UTMainHandler.DoSwing(self,(FRandom[Peace](-0.1,-0.04),FRandom[Peace](0.4,0.6)),3,0,7,SWING_Spring,3,0.8);
}
PEAF EFG 2;
PEAF H 0
{
UTMainHandler.DoSwing(self,(FRandom[Peace](0.08,0.12),FRandom[Peace](-1.2,-0.9)),4,0,6,SWING_Spring,3,1.5);
}
PEAF HI 2; // howdy
PEAF I -1 A_PeacemakerThrow(true);
Stop;
Deselect:
PEAD A 0 A_JumpIfNoAmmo("EmptyDeselect");
PEAD ABCDEFGHI 1;
PEAD J 1 A_Lower(int.max);
EmptyDeselect:
TNT1 A 1 A_Lower(int.max);
Wait;
}
}