Class Tier3Ammo : RandomSpawner2 replaces Shell { Default { DropItem "BioAmmo2", 255, 1; DropItem "ShockAmmo2", 255, 1; } } Class Tier3Ammo2 : RandomSpawner2 replaces ShellBox { Default { DropItem "BioAmmo", 255, 1; DropItem "ShockAmmo", 255, 1; } } Class Tier3Weapon : RandomSpawner2 replaces Shotgun { Default { DropItem "BioRifle", 255, 1; DropItem "ShockRifle", 255, 1; } } Class Tier3Weapon2 : Tier3Weapon replaces SuperShotgun {} Class BioAmmo : Ammo { Default { Tag "Biosludge Ammo"; Inventory.PickupMessage "You picked up the Biosludge Ammo."; Inventory.Amount 25; Inventory.MaxAmount 100; Ammo.BackpackAmount 50; Ammo.BackpackMaxAmount 200; Ammo.DropAmount 10; } States { Spawn: BIOA A -1; Stop; } } Class BioAmmo2 : BioAmmo // small variant { Default { Tag "Biosludge Ammo"; Inventory.PickupMessage "You picked up the Small Biosludge Ammo."; Inventory.Amount 10; Ammo.DropAmount 5; Scale 0.5; } States { Spawn: BIOA A -1; Stop; } } Class BioSpark : Actor { Default { RenderStyle "Add"; Radius 2; Height 2; +NOBLOCKMAP; +FORCEXYBILLBOARD; +MISSILE; +MOVEWITHSECTOR; +THRUACTORS; +ROLLSPRITE; +ROLLCENTER; +NOTELEPORT; +DONTSPLASH; -BOUNCEAUTOOFF; +BOUNCEAUTOOFFFLOORONLY; BounceType "Doom"; BounceFactor 0.5; WallBounceFactor 0.5; Gravity 0.2; Scale 0.04; } override void PostBeginPlay() { Super.PostBeginPlay(); frame = Random[GES](0,4); roll = FRandom[GES](0,360); } States { Spawn: GBLB # 1 Bright A_FadeOut(FRandom[GES](0.005,0.015)); Wait; Death: GBLB # 1 Bright A_FadeOut(FRandom[GES](0.04,0.06)); Wait; Dummy: GBLB ABCDE -1; Stop; } } Class BioHitbox : Actor { Default { Radius 3; Height 6; +SHOOTABLE; +NOGRAVITY; +NOCLIP; +DONTSPLASH; +NOBLOOD; } override void PostBeginPlay() { Super.PostBeginPlay(); if ( target ) A_SetSize(1.5*target.scale.x,3*target.scale.y); } override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) { if ( inflictor == target ) return 0; if ( target && !target.InStateSequence(target.CurState,target.ResolveState("XDeath")) ) { target.bAMBUSH = true; target.ExplodeMissile(null,inflictor); } return 0; } override void Tick() { Super.Tick(); if ( !target ) { Destroy(); return; } SetOrigin(target.pos-(0,0,height*0.5),true); } override bool CanCollideWith( Actor other, bool passive ) { return !target.bNoGravity; // don't "intercept" while flying (doesn't seem to work, but at least I tried) } } Class BioLight : DynamicLight { Default { DynamicLight.Type "Point"; Args 64,255,48,8; } override void Tick() { Super.Tick(); if ( !target ) { Destroy(); return; } args[LIGHT_INTENSITY] = int(8*target.Scale.x); } } Class BioXLight : DynamicLight { double lifetime; Default { DynamicLight.Type "Point"; Args 64,255,48,30; } override void PostBeginPlay() { Super.PostBeginPlay(); lifetime = 1.0; } override void Tick() { Super.Tick(); if ( globalfreeze || level.frozen ) return; args[LIGHT_RED] = int(64*lifetime); args[LIGHT_GREEN] = int(255*lifetime); args[LIGHT_BLUE] = int(48*lifetime); lifetime -= 0.05; if ( lifetime <= 0 ) Destroy(); } } Class BioGel : Actor { Actor l, b; enum EHitType { HIT_NONE, HIT_WALL, HIT_CEILING, HIT_FLOOR }; int hittype; int deadtimer, dttimer; Line atline; int atside; int atpart; int atplane; Sector atsector; double atz; double rollvel, pitchvel, yawvel; Vector3 normal; override void PostBeginPlay() { Super.PostBeginPlay(); vel.z += 6; deadtimer = -1; l = Spawn("BioLight",pos); l.target = self; b = Spawn("BioHitbox",pos); b.target = self; rollvel = FRandom[GES](10,30)*RandomPick[GES](-1,1); pitchvel = FRandom[GES](10,30)*RandomPick[GES](-1,1); yawvel = FRandom[GES](10,30)*RandomPick[GES](-1,1); } override int SpecialMissileHit( Actor victim ) { if ( victim == b ) return 1; if ( (victim is 'BioHitbox') && ((victim.target == master) || (victim.target.master == master)) ) return 1; return -1; } override void Tick() { Super.Tick(); if ( globalfreeze || level.frozen ) return; if ( !bNOGRAVITY ) { A_SetRoll(roll+rollvel,SPF_INTERPOLATE); A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE); A_SetPitch(pitch+yawvel,SPF_INTERPOLATE); if ( waterlevel > 0 ) { vel.xy *= 0.98; rollvel *= 0.98; pitchvel *= 0.98; yawvel *= 0.98; } } if ( deadtimer > -2 ) { if ( atline ) // attempt to follow the movement of the line { if ( atpart == 1 ) { if ( atline.flags&Line.ML_DONTPEGTOP ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(1)),true); else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside?0:1].sector.GetPlaneTexZ(1)),true); } else if ( atpart == -1 ) { if ( atline.flags&Line.ML_DONTPEGBOTTOM ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(0)),true); else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside?0:1].sector.GetPlaneTexZ(0)),true); } else if ( atline.flags&Line.ML_DONTPEGBOTTOM ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(0)),true); else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(1)),true); if ( (pos.z > ceilingz) || (pos.z < floorz) ) deadtimer = min(deadtimer,0); } else if ( atsector ) // attempt to follow the movement of the plane { SetOrigin(Vec2OffsetZ(0,0,atz+cursector.GetPlaneTexZ(atplane)),true); if ( ceilingz-floorz <= 2 ) deadtimer = min(deadtimer,0); } } if ( !InStateSequence(CurState,FindState("XDeath")) && ((!bNOGRAVITY && !Random[GES](0,2)) || !Random[GES](0,10)) ) { int numpt = Min(20,int(Scale.x*2))+Random[GES](-1,1); for ( int i=0; i= pos.z-4*Scale.x)) ) deadtimer = 0; } if ( deadtimer-- <= 0 ) { deadtimer = -1; SetStateLabel("XDeath"); } } // align self to what surface was hit, currently does not support 3d floors + slopes properly virtual void AlignSelf() { A_NoGravity(); A_Stop(); if ( bAMBUSH ) { SetStateLabel("XDeath"); return; } FLineTraceData d; A_SetSize(0.1,0); if ( BlockingLine ) { atline = BlockingLine; normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit(); atside = 1; if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) ) { atside = 0; normal *= -1; } Vector3 orig = (BlockingLine.v1.p.x,BlockingLine.v1.p.y,0); Vector3 onwall = pos-(normal dot (pos-orig))*normal; SetOrigin(onwall+normal*0.5,false); // attempt to guess line part (upper/mid/lower) if ( !atline.sidedef[1] ) atpart = 0; // mid else if ( atline.sidedef[atside?0:1].sector.ceilingplane.ZAtPoint(pos.xy) < pos.z ) atpart = 1; // upper else if ( atline.sidedef[atside?0:1].sector.floorplane.ZAtPoint(pos.xy) > pos.z ) atpart = -1; // lower else atpart = 0; if ( atpart == 1 ) { if ( atline.flags&Line.ML_DONTPEGTOP ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(1); else atz = pos.z-atline.sidedef[atside?0:1].sector.GetPlaneTexZ(1); } else if ( atpart == -1 ) { if ( atline.flags&Line.ML_DONTPEGBOTTOM ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(0); else atz = pos.z-atline.sidedef[atside?0:1].sector.GetPlaneTexZ(0); } else if ( atline.flags&Line.ML_DONTPEGBOTTOM ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(0); else atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(1); angle = atan2(normal.y,normal.x); pitch = 0; roll = 180; // otherwise it slides upwards (UT changes roll like this too) if ( waterlevel > 0 ) hittype = HIT_FLOOR; else hittype = HIT_WALL; } else if ( pos.z <= floorz+4 ) { atsector = cursector; atplane = 0; normal = cursector.floorplane.Normal; pitch = asin(-normal.z); angle = atan2(normal.y,normal.x); roll = FRandom[GES](0,360); SetOrigin((pos.x,pos.y,floorz)+normal*0.5,false); atz = pos.z-cursector.GetPlaneTexZ(0); hittype = HIT_FLOOR; } else if ( pos.z >= ceilingz-8 ) { atsector = cursector; atplane = 1; normal = cursector.ceilingplane.Normal; pitch = asin(-normal.z); angle = atan2(normal.y,normal.x); roll = FRandom[GES](0,360); SetOrigin((pos.x,pos.y,ceilingz)+normal*0.5,false); atz = pos.z-cursector.GetPlaneTexZ(1); if ( waterlevel > 0 ) hittype = HIT_FLOOR; else if ( normal dot (0,0,-1) > 0.7 ) hittype = HIT_CEILING; else hittype = HIT_FLOOR; } else { SetStateLabel("XDeath"); return; } A_PlaySound("ges/hit"); A_SprayDecal("BioSplat",-172); int numpt = Min(100,int(Scale.x*10))+Random[GES](-5,5); for ( int i=0; i= 5.1 ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; invoker.charge = min(5.1,invoker.charge+0.5); } override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) { if ( bCharging ) return true; return Super.CheckAmmo(fireMode,autoSwitch,requireAmmo,ammocount); } Default { Tag "GES Bio Rifle"; Inventory.PickupMessage "You got the GES BioRifle."; Weapon.UpSound "ges/select"; Weapon.SlotNumber 3; Weapon.SelectionOrder 7; Weapon.AmmoType "BioAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "BioAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 25; UTWeapon.DropAmmo 5; } States { Spawn: BIOP A -1; Stop; BIOP B -1; Stop; Ready: BIOS ABCDEFGHIJKLMNOPQRSTUV 1 A_WeaponReady(WRF_NOFIRE); Idle: BIOI A 1 { invoker.bCharging = false; A_CheckReload(); A_WeaponReady(); } Goto Idle; Fire: BIOF A 1 A_BioFire(); BIOF BCDEFGHI 1; Goto Idle; AltFire: BIOC A 4 A_BeginCharge(); BIOC B 5 A_ChargeUp(); BIOC CD 5; BIOC E 0 A_Refire(1); Goto AltRelease; BIOC E 5 A_ChargeUp(); BIOC FG 5; BIOC H 0 A_Refire(1); Goto AltRelease; BIOC H 5 A_ChargeUp(); BIOC IJ 5; BIOC K 0 A_Refire(1); Goto AltRelease; BIOC K 5 A_ChargeUp(); BIOC LM 5; BIOC N 0 A_Refire(1); Goto AltRelease; BIOC N 5 A_ChargeUp(); BIOC OP 5; BIOC Q 0 A_Refire(1); Goto AltRelease; BIOC Q 5 A_ChargeUp(); BIOC RS 5; BIOC T 0 A_Refire(1); Goto AltRelease; BIOC T 5 A_ChargeUp(); BIOC UV 5; BIOC W 0 A_Refire(1); Goto AltRelease; BIOC W 5 A_ChargeUp(); BIOC XY 5; BIOC Z 0 A_Refire(1); Goto AltRelease; BIOC Z 5 A_ChargeUp(); BIC2 AB 5; BIC2 C 0 A_Refire(1); Goto AltRelease; BIC2 C 5 A_ChargeUp(); BIC2 DE 5; BIOM A 0 A_Refire(1); Goto AltRelease; AltHeld: BIOM A 1; BIOM A 0 A_Refire("AltHeld"); AltRelease: BIOE A 1 { invoker.charge = min(5.1,invoker.charge+0.1); } BIOF A 2 A_BioFire(true); BIOF BCDEFGHI 2; Goto Idle; Select: BIOS A 1 A_Raise(int.max); Wait; Deselect: BIOD ABCDEFGHIJ 1; BIOD J 1 A_Lower(int.max); Wait; } }