diff --git a/ItemLore.md b/ItemLore.md index b6f5904..26bb239 100644 --- a/ItemLore.md +++ b/ItemLore.md @@ -181,10 +181,21 @@ infinitely regenerating supply of both, but only one can be used at a time. These merely existed as a gimmick to show off dynamic lighting in Unreal Engine, so they're not very practical or balanced. +## Light Sentry + +Pocket turrets that shoot at any enemies standing in front of them. Easy to +carry and deploy, but once they're out in the field that's it. They will keep +going until they run out of ammo or battery, after which they will +self-destruct. + ## Minigun Sentry -A deployable little helper that will gun down any enemies that stand in its -view range. Starts with a full 200 bullets, and must be replenished by players. +A big, heavy turret that will gun down any enemies that stand in its view +range. + +Unlike the Light Sentries this one has a much wider range of view and can be +resupplied with extra ammo. Conveniently, you can also recall it to put it back +in your inventory. While carried it will slowly repair any damage it has taken. In deathmatch, the Sentry will "switch owners" to whoever last replenished it with ammunition, like the Turrets in ChaosUT. diff --git a/ProtoNotes.md b/ProtoNotes.md index cf6febc..7622c75 100644 --- a/ProtoNotes.md +++ b/ProtoNotes.md @@ -102,8 +102,8 @@ redone the whole thing. Since I didn't feel like making new textures this thing now mixes and matches a couple weapon skins. - It's composed of two models so the gun part can rotate independently. - - Thinking of making an alternate item that still uses the source model and - its frankenUVs. Will have to stitch a skin for it tho. + - Original version is available as a separate item. Gave it some stitched + textures. ### Peacemaker diff --git a/Readme.md b/Readme.md index 0374baf..0634f18 100644 --- a/Readme.md +++ b/Readme.md @@ -44,7 +44,8 @@ Doom Tournament (currently the devel branch is required). - SCUBA Gear (replaces radsuit if map has swimmable water) - Motion Detector (replaces computer map) - Light & Dark Flares - - Minigun Sentry (rare spawn in backpacks) + - Minigun Sentry (rare berserk replacement) + - Light Sentry (rare spawn in backpacks) ## In progress @@ -79,8 +80,6 @@ Doom Tournament (currently the devel branch is required). - Demolisher (slot 9) (replaces bfg9000) - Autocannon (slot 0) (replaces bfg9000) - - Original Sentry Gun (rare spawn in backpacks) - ## Known bugs - N/A diff --git a/gldefs.txt b/gldefs.txt index c2735f1..2e89414 100644 --- a/gldefs.txt +++ b/gldefs.txt @@ -350,6 +350,21 @@ Object MinigunSentry Frame "SENFQ" { light "SENTRYLIGHT2" } } +FlickerLight2 SENTRYLIGHT3 +{ + Color 1.0 0.6 0.0 + Size 40 + SecondarySize 48 + Interval 0.1 + Offset 0 16 10 +} + +Object SentryGun +{ + Frame "SENFA" { light "SENTRYLIGHT3" } + Frame "SENFC" { light "SENTRYLIGHT3" } +} + // Shaders / Brightmaps HardwareShader Texture "graphics/MenuBarr.png" { @@ -733,3 +748,7 @@ HardwareShader Texture "models/OLSMP_.png" Texture "masktex" "models/OLSMP_m.png" texture "envtex" "models/OLSMP_env.png" } +HardwareShader Texture "models/JSentry1_.png" +{ + Shader "shaders/glsl/AmbientGlow.fp" +} diff --git a/graphics/DetHud.png b/graphics/DetHud.png index b0a8c08..cb2defb 100644 Binary files a/graphics/DetHud.png and b/graphics/DetHud.png differ diff --git a/graphics/icons/I_OSntry.png b/graphics/icons/I_OSntry.png new file mode 100644 index 0000000..9a5da84 Binary files /dev/null and b/graphics/icons/I_OSntry.png differ diff --git a/language.txt b/language.txt index 1c24293..0de8ef2 100644 --- a/language.txt +++ b/language.txt @@ -33,7 +33,9 @@ O_FLAMETHROWER = "%o was thoroughly roasted by %k's Flamethrower."; O_BIGGUN = "%k blew a hole through %o with the Demolisher."; O_SMINI = "%o got blasted into steaming chunks by %k's Autocannon."; O_SENTRY = "%%o shouldn't have walked into the sights of %s."; -O_OWNSENTRY = "%o was gunned down by %p own Sentry."; +O_OWNSENTRY = "%o was gunned down by %p own Minigun Sentry."; +O_OSENTRY = "%%o took a bullet from %s."; +O_OWNOSENTRY = "%o took a bullet from %p own Light Sentry."; O_STINGERX = "%o ate flaming Tarydium death thanks to %k."; O_OLSMP = "%o didn't stand a chance against %k's SMP 7243."; /* Pickup messages */ @@ -76,7 +78,8 @@ I_FLAMEGUN = "You got the Fireblaster."; I_FLAMETHROWER = "You got the Flamethrower."; I_BIGGUN = "You picked up the Demolisher."; I_SMINI = "You got the Autocannon."; -I_SENTRY = "You picked up a Sentry."; +I_SENTRY = "You picked up a Minigun Sentry."; +I_OSENTRY = "You picked up a Light Sentry."; I_TRANSLATOR = "You picked up the Universal Translator."; I_UARMOR = "You got the Assault Vest."; I_KEVSUIT = "You picked up the Kevlar Suit."; @@ -138,8 +141,10 @@ T_FLAMEGUN = "Fireblaster"; T_FLAMETHROWER = "Flamethrower"; T_BIGGUN = "Demolisher"; T_SMINI = "Autocannon"; -T_SENTRY = "Sentry"; -T_OWNEDSENTRY = "%s's Sentry"; +T_SENTRY = "Minigun Sentry"; +T_OSENTRY = "Light Sentry"; +T_OWNEDSENTRY = "%s's Minigun Sentry"; +T_OWNEDOSENTRY = "%s's Light Sentry"; T_TRANSLATOR = "Translator"; T_UARMOR = "Assault Vest"; T_KEVSUIT = "Kevlar Suit"; @@ -186,9 +191,9 @@ D_FLASHLIGHT = "The Flashlight batteries have died."; D_SEARCHLIGHT = "The Searchlight has run out of battery."; D_DETECTOR = "The Detector has run out of battery."; D_LBOOTS = "The Jump Boots have drained."; -M_SENTRYDOWN = "Your Sentry has been destroyed."; -M_SENTRYDRY = "Your Sentry has run out of ammo."; -M_SENTRYHIJACK = "Your Sentry has been hijacked."; +M_SENTRYDOWN = "Your Minigun Sentry has been destroyed."; +M_SENTRYDRY = "Your Minigun Sentry has run out of ammo."; +M_SENTRYHIJACK = "Your Minigun Sentry has been hijacked."; M_MSNOROOM = "No room to deploy Sentry."; M_NSTOOFAR = "Cannot recall Sentry from this distance."; M_FFNOROOM = "No room to activate Force Field."; @@ -268,6 +273,8 @@ O_BIGGUN = "%k abrió un hoyo a través de %o con el Demoledor."; O_SMINI = "%o fue reventad@[ao_esp] en pedazos humeantes por el Autocañón de %k."; O_SENTRY = "%%o no debería haberse metido en el punto de mira de la %s."; O_OWNSENTRY = "%o fue abatid@[ao_esp] por su propi@[ao_esp] Torreta."; +O_OSENTRY = "%%o se llevó un tiro de la %s."; +O_OWNOSENTRY = "%o se llevó un tiro de su propi@[ao_esp] Torreta Ligera."; O_STINGERX = "%o tragó muerte ardiente de Tarydium gracias a %k."; O_OLSMP = "%o no tenía ninguna oportunidad contra el SMP 7243 de %k."; /* Pickup messages */ @@ -311,6 +318,7 @@ I_FLAMETHROWER = "Has obtenido el Lanzallamas."; I_BIGGUN = "Has recogido el Demoledor."; I_SMINI = "Has obtenido el Autocañón."; I_SENTRY = "Has recogido una Torreta."; +I_OSENTRY = "Has recogido una Torreta Ligera."; I_TRANSLATOR = "Has obtenido el Traductor Universal."; I_UARMOR = "Has obtenido la Coraza de Asalto."; I_KEVSUIT = "Has recogido el Traje de Kevlar."; @@ -370,6 +378,8 @@ T_BIGGUN = "Demoledor"; T_SMINI = "Autocañón"; T_SENTRY = "Torreta"; T_OWNEDSENTRY = "Torreta de %s"; +T_OSENTRY = "Torreta Ligera"; +T_OWNEDOSENTRY = "Torreta Ligera de %s"; T_TRANSLATOR = "Traductor"; T_UARMOR = "Coraza de Asalto"; T_KEVSUIT = "Traje de Kevlar"; diff --git a/modeldef.umisc b/modeldef.umisc index 48e6685..13efa91 100644 --- a/modeldef.umisc +++ b/modeldef.umisc @@ -673,6 +673,97 @@ Model "MinigunSentryBase" FrameIndex SENU R 0 67 } +Model "SentryGunItem" +{ + Path "models" + Model 0 "SentryM_d.3d" + SurfaceSkin 0 1 "JSentry1_.png" + AngleOffset -90 + Scale 0.12 0.12 0.144 + ZOffset 16 + + FrameIndex SENT A 0 0 +} + +Model "SentryGunX" +{ + Path "models" + Model 0 "SentryM_d.3d" + SurfaceSkin 0 0 "JSentry1.png" + AngleOffset -90 + Scale 0.12 0.12 0.144 + ZOffset 16 + DONTCULLBACKFACES + + FrameIndex SENF A 0 20 + FrameIndex SENF C 0 22 +} + +Model "SentryGun" +{ + Path "models" + Model 0 "SentryM_d.3d" + SurfaceSkin 0 1 "JSentry1.png" + AngleOffset -90 + Scale 0.12 0.12 0.144 + ZOffset 16 + + // Spawn + FrameIndex SENT A 0 0 + // Up + FrameIndex SENR A 0 0 + FrameIndex SENR B 0 1 + FrameIndex SENR C 0 2 + FrameIndex SENR D 0 3 + FrameIndex SENR E 0 4 + // Still + FrameIndex SENI A 0 5 + // Wind + FrameIndex SENW A 0 6 + FrameIndex SENW B 0 7 + FrameIndex SENW C 0 8 + FrameIndex SENW D 0 9 + FrameIndex SENW E 0 10 + FrameIndex SENW F 0 11 + FrameIndex SENW G 0 12 + FrameIndex SENW H 0 13 + FrameIndex SENW I 0 14 + FrameIndex SENW J 0 15 + FrameIndex SENW K 0 16 + FrameIndex SENW L 0 17 + FrameIndex SENW M 0 18 + FrameIndex SENW N 0 19 + // Fire + FrameIndex SENF A 0 20 + FrameIndex SENF B 0 21 + FrameIndex SENF C 0 22 + FrameIndex SENF D 0 23 + // Unwind + FrameIndex SENU A 0 24 + FrameIndex SENU B 0 25 + FrameIndex SENU C 0 26 + FrameIndex SENU D 0 27 + FrameIndex SENU E 0 28 + FrameIndex SENU F 0 29 + FrameIndex SENU G 0 30 + FrameIndex SENU H 0 31 + FrameIndex SENU I 0 32 + FrameIndex SENU J 0 33 + FrameIndex SENU K 0 34 + FrameIndex SENU L 0 35 + FrameIndex SENU M 0 36 + FrameIndex SENU N 0 37 + FrameIndex SENU O 0 38 + FrameIndex SENU P 0 39 + FrameIndex SENU Q 0 40 + // Down + FrameIndex SEND A 0 41 + FrameIndex SEND B 0 42 + FrameIndex SEND C 0 43 + FrameIndex SEND D 0 44 + FrameIndex SEND E 0 45 +} + Model "MotionDetector" { Path "models" diff --git a/models/JSentry1.png b/models/JSentry1.png new file mode 100644 index 0000000..e645b3b Binary files /dev/null and b/models/JSentry1.png differ diff --git a/models/JSentry1_.png b/models/JSentry1_.png new file mode 100644 index 0000000..e645b3b Binary files /dev/null and b/models/JSentry1_.png differ diff --git a/models/SentryM_a.3d b/models/SentryM_a.3d new file mode 100644 index 0000000..2c3d1f7 Binary files /dev/null and b/models/SentryM_a.3d differ diff --git a/models/SentryM_d.3d b/models/SentryM_d.3d new file mode 100644 index 0000000..867ff0f Binary files /dev/null and b/models/SentryM_d.3d differ diff --git a/zscript/miscitems.zsc b/zscript/miscitems.zsc index cbe530e..2536352 100644 --- a/zscript/miscitems.zsc +++ b/zscript/miscitems.zsc @@ -88,7 +88,7 @@ Class UnrealBackpack : BackpackItem replaces Backpack Super.DoPickupSpecial(toucher); if ( gameinfo.gametype&GAME_DOOMCHEX ) { - static const Class xitems[] = {"Flare", "Seeds", "SentryItem", "VoiceBox", "ForceField", "Dampener", "Peacemaker"}; + static const Class xitems[] = {"Flare", "Seeds", "SentryGunItem", "VoiceBox", "ForceField", "Dampener", "Peacemaker"}; int xitemn[7]; xitemn[0] = max(0,Random[BackpackExtra](-1,3)); xitemn[1] = max(0,Random[BackpackExtra](-1,3)); @@ -1432,6 +1432,11 @@ Class SentryItem : UnrealInventory UnrealInventory.Charge MinigunSentryBase.sentryhealth; +UNREALINVENTORY.DRAWSPECIAL; } + override bool HandlePickup( Inventory item ) + { + if ( item.GetClass() == GetClass() ) return true; // can never get more than one + return Super.HandlePickup(item); + } static void TransferOwnership( Actor newowner, Actor sentry ) { if ( sentry.master ) sentry.master.TakeInventory("SentryItem",200); @@ -1500,7 +1505,11 @@ Class SentryItem : UnrealInventory override void DoEffect() { Super.DoEffect(); - if ( !bActive ) return; + if ( !bActive ) + { + if ( !(level.maptime%10) ) Charge = min(DefaultCharge,Charge+1); + return; + } if ( !tracer ) { bActive = false; @@ -2015,7 +2024,11 @@ Class MinigunSentryBase : Actor Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); tracer.SetOrigin(level.Vec3Offset(pos,z*38),false); - if ( !deathmatch ) return; + if ( !deathmatch ) + { + if ( !master || !master.player ) bFRIENDLY = false; + return; + } if ( master && master.player ) tracer.SetFriendPlayer(master.player); else tracer.bFRIENDLY = false; } @@ -2127,3 +2140,340 @@ Class MinigunSentryBase : Actor Stop; } } + +// original fun-size stationary version +Class SentryGunItem : UnrealInventory +{ + Default + { + Tag "$T_OSENTRY"; + Inventory.Icon "I_OSntry"; + Inventory.MaxAmount 3; + Inventory.PickupMessage "$I_OSENTRY"; + Inventory.RespawnTics 1050; + } + override bool Use( bool pickup ) + { + if ( pickup ) return false; + Vector3 origin = Owner.Vec2OffsetZ(0,0,Owner.player.viewz); + FLineTraceData d; + Owner.LineTrace(Owner.angle,90,Owner.pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,data:d); + if ( d.HitType != TRACE_HitNone ) origin = d.HitLocation-d.HitDir*20; + else origin = d.HitLocation; + Owner.LineTrace(0,56,90,TRF_ABSPOSITION,origin.z,origin.x,origin.y,data:d); + origin = d.HitLocation; + let a = Spawn("SentryGun",origin); + if ( !a.TestMobjLocation() ) + { + if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$M_MSNOROOM")); + a.Destroy(); + return false; + } + tracer = a; + a.master = Owner; + a.angle = Owner.angle; + a.pitch = 0; + a.roll = 0; + return true; + } + States + { + Spawn: + SENT A -1; + Stop; + } +} + +Class SentryGunX : Actor +{ + Default + { + RenderStyle "Add"; + +NOGRAVITY; + +NOBLOCKMAP; + +INTERPOLATEANGLES; + } + override void Tick() + { + Super.Tick(); + if ( !tracer || !tracer.InStateSequence(tracer.CurState,tracer.FindState("Fire")) ) + { + Destroy(); + return; + } + SetOrigin(tracer.pos,true); + angle = tracer.angle; + pitch = tracer.pitch; + roll = tracer.roll; + } + States + { + Spawn: + SENF A 2 Bright; + TNT1 A 2; + SENF C 2 Bright; + TNT1 A 2; + Stop; + } +} + +Class SentryGun : Actor +{ + int rememberedplayer; + Default + { + Health 50; + Mass int.max; + Radius 10; + Height 24; + +SOLID; + +SHOOTABLE; + +NOBLOOD; + +DONTTHRUST; + +FRIENDLY; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + if ( master && master.player ) + { + SetTag(String.Format(StringTable.Localize("$T_OWNEDOSENTRY"),master.player.GetUserName())); + rememberedplayer = master.playernumber(); + } + else + { + SetTag(StringTable.Localize("$T_OSENTRY")); + rememberedplayer = -1; + } + if ( !deathmatch ) + { + if ( !master || !master.player ) bFRIENDLY = false; + return; + } + if ( master && master.player ) SetFriendPlayer(master.player); + else bFRIENDLY = false; + } + override string GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) + { + if ( victim == master ) return String.Format(StringTable.Localize("$O_OWNOSENTRY"),GetTag()); + return String.Format(StringTable.Localize("$O_OSENTRY"),GetTag()); + } + override void Tick() + { + Super.Tick(); + if ( !master && (rememberedplayer != -1) && playeringame[rememberedplayer] ) + master = players[rememberedplayer].mo; + } + bool IsEnemy( Actor a ) + { + if ( !a || !a.bSHOOTABLE || !a.bISMONSTER || (a.Health <= 0) ) return false; + if ( deathmatch ) return ((a != master) && (a.master != master)); + return (!bFRIENDLY || (!a.bFRIENDLY && !a.player)); + } + bool HasTarget() + { + // check for targets in range + let ti = ThinkerIterator.Create("Actor"); + Actor a; + while ( a = Actor(ti.Next()) ) + { + if ( !IsEnemy(a) ) continue; + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + Vector3 vecto = level.Vec3Diff(Vec3Offset(0,0,16),a.Vec3Offset(0,0,a.height/2)); + double distto = vecto.length(); + Vector3 dirto = vecto/distto; + double angleto = atan2(dirto.y,dirto.x); + double pitchto = asin(-dirto.z); + if ( (distto < 6000) && (dirto dot x > 0.95) && !LineTrace(angleto,distto,pitchto,TRF_THRUACTORS,16) ) + { + target = a; + return true; + } + } + // check for targets in a straight line + FLineTraceData d; + LineTrace(angle,200,pitch,0,16,data:d); + if ( (d.HitType == TRACE_HitActor) && IsEnemy(d.HitActor) ) + { + target = d.HitActor; + return true; + } + target = null; + return false; + } + // if there's a target and we have ammo, jump to first state (if set) + // if there's no target but we still have ammo, jump to second state (if set) + // if we have no ammo, jump to third state (if set) + void A_SentryGunCheck( statelabel actstate = null, statelabel idlestate = null, statelabel failstate = null ) + { + if ( !(GetAge()%35) ) special1++; + if ( (special1 >= 200) && failstate ) SetStateLabel(failstate); + else if ( HasTarget() && actstate ) SetStateLabel(actstate); + else if ( !HasTarget() && idlestate ) SetStateLabel(idlestate); + } + void A_SentryGunAttack( statelabel failstate = null ) + { + special1++; + if ( ((special1 >= 200) || !HasTarget()) && failstate ) + { + A_StopSound(CHAN_BODY); + SetStateLabel(failstate); + return; + } + A_AlertMonsters(0,AMF_TARGETEMITTER); + A_PlaySound("sentry/fire",CHAN_WEAPON,pitch:1.6); + Vector3 x, y, z, origin; + [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(pos,x*12+z*16); + double a = FRandom[Sentry](0,360), s = FRandom[Sentry](0,0.05); + Vector3 nx = target?Vec3To(target).unit():x; + Vector3 dir = (nx+y*cos(a)*s+z*sin(a)*s).unit(); + FLineTraceData d; + LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); + if ( d.HitType == TRACE_HitActor ) + { + int dmg = Random[OSentry](6,17); + dmg = d.HitActor.DamageMobj(self,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); + double mm = 3000; + if ( FRandom[OSentry](0,1) < 0.2 ) mm *= 5; + UTMainHandler.DoKnockback(d.HitActor,d.HitDir,mm); + if ( d.HitActor.bNOBLOOD ) + { + let p = Spawn("BulletImpact",d.HitLocation); + p.angle = atan2(d.HitDir.y,d.HitDir.x)+180; + p.pitch = asin(d.HitDir.z); + } + else + { + d.HitActor.TraceBleed(dmg,self); + d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); + } + } + else if ( d.HitType != TRACE_HitNone ) + { + Vector3 hitnormal = -d.HitDir; + if ( d.HitType == TRACE_HitFloor ) hitnormal = d.HitSector.floorplane.Normal; + else if ( d.HitType == TRACE_HitCeiling ) hitnormal = d.HitSector.ceilingplane.Normal; + else if ( d.HitType == TRACE_HitWall ) + { + hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit(); + if ( !d.LineSide ) hitnormal *= -1; + } + let p = Spawn("BulletImpact",d.HitLocation+hitnormal*0.01); + p.angle = atan2(hitnormal.y,hitnormal.x); + p.pitch = asin(-hitnormal.z); + if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); + } + for ( int i=0; i<3; i++ ) + { + let s = Spawn("UTSmoke",origin); + s.alpha *= 0.5; + s.scale *= 0.7; + s.vel += x*2; + } + let c = Spawn("UCasing",level.Vec3Offset(pos,-x*8+y*0.6+z*16)); + c.scale *= 0.5; + c.vel = x*FRandom[Junk](-1.5,1.5)+y*FRandom[Junk](2,4)+z*FRandom[Junk](2,3); + } + States + { + Spawn: + SENT A 15; + Goto StartUp; + StartUp: + SENR A 0 + { + A_PlaySound("sentry/raise",pitch:1.6); + A_AlertMonsters(0,AMF_TARGETEMITTER); + } + SENR ABCDE 4; + Goto Idle; + Idle: + SENI A 1 A_SentryGunCheck("WindUp",null,"ShutDown"); + Wait; + WindUp: + SENW A 0 + { + A_PlaySound("sentry/wind",looping:true,pitch:1.6); + A_AlertMonsters(0,AMF_TARGETEMITTER); + } + SENW ABCDEFGHIJKLMN 1; + Fire: + SENF A 0 + { + let f = Spawn("SentryGunX",pos); + f.angle = angle; + f.pitch = pitch; + f.roll = roll; + f.tracer = self; + } + SENF A 2 A_SentryGunAttack("Unwind"); + SENF B 2; + SENF C 2 A_SentryGunAttack("Unwind"); + SENF D 2; + SENU A 0; // tweening hack + Loop; + Unwind: + SENU A 0 A_PlaySound("sentry/unwind",pitch:1.6); + SENU ABCDEFGHIJKLMN 1; + Goto Idle; + ShutDown: + SEND A 0 A_PlaySound("sentry/raise",pitch:1.6); + SEND ABCDE 4; + SEND E 20; + SEND E -1 A_Die(); + Stop; + Death: + TNT1 A 0 + { + A_StopSound(CHAN_BODY); + A_PlaySound("flare/explode",CHAN_VOICE); + A_NoGravity(); + A_Stop(); + A_SetRenderStyle(1.,STYLE_Add); + SetZ(pos.z+16); + Spawn("FlareXLight",pos); + bMOVEWITHSECTOR = false; + bFORCEXYBILLBOARD = true; + A_SetScale(FRandomPick[ExploS](-1.5,1.5),FRandomPick[ExploS](-1.5,1.5)); + int numpt = Random[ExploS](15,30); + for ( int i=0; i