diff --git a/ItemLore.md b/ItemLore.md index 67007da..b6f5904 100644 --- a/ItemLore.md +++ b/ItemLore.md @@ -14,10 +14,13 @@ this item isn't really given to players. It may get use if I or someone else recreate Unreal 1 maps. This mod's version of the Translator has additional features: -- Vertical scrolling for long messages, just use the up/down arrow keys -- Toggling hint display, press page down if there's a glowing icon on the - bottom right. Press page up to return to the message. -- Going through previous messages, by pressing left / right. +- Vertical scrolling for long messages, just use the up/down arrow keys, + mouse wheel or drag along the bar. +- Toggling hint display, if there's a glowing icon on the bottom right, press + page down or click on it. Press page up / click on the bottom left to return + to the message. +- Going through previous messages, by pressing left / right or clicking the + arrows next to the page numbers. ## Amplifier @@ -28,7 +31,8 @@ When active, dramatically enhances the power of the Dispersion Pistol and ASMD. Deadens the sound of emitted from your weapons, making it much easier to launch sneak attacks. -In the original game this item was pretty much useless, but here it works. +This item was pretty much useless in the original game, but here it really does +have a considerable effect. ## Flare @@ -67,17 +71,16 @@ Activating the boots enables you to jump much higher than normal. Once picked up the Boots only last for a short period of time, so take advantage of them while you can. -In addition, the boots can provide resistance to damaging floors. +In addition, the Boots can provide resistance to damaging floors. ## Scuba Gear -Gives you the ability to breathe while underwater. - -Lasts around 2 minutes. +Gives you the ability to breathe while underwater. Lasts around 2 minutes. ## Searchlight -Functions like a flashlight, but has a nearly inexhaustible power supply. +Provides considerably more light than a flashlight and has around half an hour +of battery life. Definitely worth keeping around. ## Nali Fruit Seed @@ -87,10 +90,8 @@ is fully grown, your health gain will be less substantial. ## Voice Box -Creates a sound diversion to distract your enemies. - -In the original this had no effect, but here enemies will actually be alerted -by voice boxes, and will attempt to get rid of the annoying noise. +Creates a sound diversion to distract your enemies, so you can make your +escape while they attempt to get rid of the annoying noise this device emits. ## Bandages diff --git a/Readme.md b/Readme.md index 634265b..1392b9d 100644 --- a/Readme.md +++ b/Readme.md @@ -16,6 +16,7 @@ Doom Tournament (currently the devel branch is required). - Eightball (slot 5) (replaces rocket launcher) - Single rocket mode for Eightball - Flak Cannon (slot 6) (replaces rocket launcher) + - SMP 7243 (slot 0) (replaces bfg9000) - Backpack (replaces backpack, identical to Doom Tournament version) - Unreal 1 HUD - Translator (has to be spawned in, since it would only be useful for mappers) @@ -41,6 +42,8 @@ Doom Tournament (currently the devel branch is required). - Searchlight (must be placed manually for balance reasons) - Jump Boots (replaces radsuit) - SCUBA Gear (replaces radsuit if map has swimmable water) + - Motion Detector (replaces computer map) + - Light & Dark Flares ## In progress @@ -48,7 +51,8 @@ Doom Tournament (currently the devel branch is required). - GES Bio Rifle (slot 8) (replaces plasma rifle) - Rifle (slot 9) (replaces plasma rifle) - Minigun (slot 0) (replaces chaingun) - - SMP 7243 (slot 0) (replaces bfg9000) + + - Minigun Sentry (rare spawn in backpacks) ## Planned @@ -65,7 +69,7 @@ Doom Tournament (currently the devel branch is required). - Stunner (slot 4) (replaces chainsaw) - Razorclaw (slot 1) (replaces chainsaw) - - Teleport Capsules (slot 9) + - Teleport Capsules (slot 1) - Protomag (slot 2) (replaces pistol) - Dual Protomags - Quadshot (slot 3) (replaces shotguns) @@ -73,13 +77,9 @@ Doom Tournament (currently the devel branch is required). - Impaler (slot 7) (replaces plasma rifle) - Flamethrower (slot 6) (replaces plasma rifle) - Fireblaster (slot 5) (replaces rocket launcher) - - Demolisher (slot 0) (replaces bfg9000) + - Demolisher (slot 9) (replaces bfg9000) - Autocannon (slot 0) (replaces bfg9000) - - Motion Detector (replaces computer map) - - Light & Dark Flares - - Minigun Sentry (rare spawn in backpacks) - ## Known bugs - N/A diff --git a/gldefs.txt b/gldefs.txt index eabc7cc..c2735f1 100644 --- a/gldefs.txt +++ b/gldefs.txt @@ -224,13 +224,31 @@ FlickerLight2 FLARELIGHT Size 112 SecondarySize 120 Interval 0.1 - Offset 6 0 1 + Offset 0 1 6 } Object FlareThrownX { frame "FLAR" { light "FLARELIGHT" } } +FlickerLight2 LIGHTFLARE +{ + Color 1.0 1.0 1.0 + Size 112 + SecondarySize 120 + Interval 0.1 + Offset 0 1 6 +} +FlickerLight2 DARKFLARE +{ + Color 0.6 0.6 0.6 + Size 112 + SecondarySize 120 + Interval 0.1 + Subtractive 1 + Offset 0 1 6 +} + FlickerLight2 DAMMOLIGHT1 { Color 0.2 0.2 1.0 @@ -299,6 +317,39 @@ Object AsmdBall Frame "TAZB" { light "ASMDBALLLIGHT" } } +FlickerLight2 SENTRYLIGHT1 +{ + Color 1.0 0.6 0.0 + Size 80 + SecondarySize 96 + Interval 0.1 + Offset 0 4 15 +} +FlickerLight2 SENTRYLIGHT2 +{ + Color 0.4 0.2 0.0 + Size 80 + SecondarySize 96 + Interval 0.1 + Offset 0 4 15 +} + +Object MinigunSentry +{ + Frame "SENFA" { light "SENTRYLIGHT1" } + Frame "SENFB" { light "SENTRYLIGHT2" } + Frame "SENFD" { light "SENTRYLIGHT1" } + Frame "SENFE" { light "SENTRYLIGHT2" } + Frame "SENFG" { light "SENTRYLIGHT1" } + Frame "SENFH" { light "SENTRYLIGHT2" } + Frame "SENFJ" { light "SENTRYLIGHT1" } + Frame "SENFK" { light "SENTRYLIGHT2" } + Frame "SENFM" { light "SENTRYLIGHT1" } + Frame "SENFN" { light "SENTRYLIGHT2" } + Frame "SENFP" { light "SENTRYLIGHT1" } + Frame "SENFQ" { light "SENTRYLIGHT2" } +} + // Shaders / Brightmaps HardwareShader Texture "graphics/MenuBarr.png" { diff --git a/graphics/DetHud.png b/graphics/DetHud.png new file mode 100644 index 0000000..b0a8c08 Binary files /dev/null and b/graphics/DetHud.png differ diff --git a/graphics/DetSpot0.png b/graphics/DetSpot0.png new file mode 100644 index 0000000..2007bb8 Binary files /dev/null and b/graphics/DetSpot0.png differ diff --git a/graphics/DetSpot1.png b/graphics/DetSpot1.png new file mode 100644 index 0000000..9116401 Binary files /dev/null and b/graphics/DetSpot1.png differ diff --git a/keyconf.txt b/keyconf.txt new file mode 100644 index 0000000..e6a8326 --- /dev/null +++ b/keyconf.txt @@ -0,0 +1,4 @@ +addkeysection "$STING_KEYS" Doomreal +addmenukey "$STING_USETRANS" usetrans +alias usetrans "netevent usetranslator" +defaultbind F2 usetrans diff --git a/language.txt b/language.txt index c24da94..b9edf67 100644 --- a/language.txt +++ b/language.txt @@ -32,7 +32,8 @@ O_FLAMEGUN = "%o was charred by %k's Fireblaster."; 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 %k's Sentry."; +O_SENTRY = "%%o shouldn't have walked into the sights of %s."; +O_OWNSENTRY = "%o was gunned down by %p own 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 */ @@ -137,6 +138,7 @@ T_FLAMETHROWER = "Flamethrower"; T_BIGGUN = "Demolisher"; T_SMINI = "Autocannon"; T_SENTRY = "Sentry"; +T_OWNEDSENTRY = "%s's Sentry"; T_TRANSLATOR = "Translator"; T_UARMOR = "Assault Vest"; T_KEVSUIT = "Kevlar Suit"; @@ -178,6 +180,13 @@ D_POWERSHIELD = "The Power Shield has depleted."; D_DAMPENER = "Acoustic Dampener has run out."; D_AMPLIFIER = "Energy Amplifier is out of power."; D_SCUBA = "SCUBA Gear oxygen supply has run out."; +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."; +M_SENTRYDOWN = "Your Sentry has been destroyed."; +M_SENTRYDRY = "Your Sentry has run out of ammo."; +M_SENTRYHIJACK = "Your Sentry has been hijacked."; +M_MSNOROOM = "No room to deploy Sentry."; M_FFNOROOM = "No room to activate Force Field."; S_MINHUD = "Health %d Score %d Ammo %d"; S_MINHUD2 = "Health %d Score %d Ammo %d/%d"; @@ -227,6 +236,8 @@ STING_CMUS = "Music:"; STING_CSND = "Sound Effects:"; STING_CGT = "Producer for GT:"; STING_CBIZ = "Biz:"; +STING_KEYS = "Doomreal"; +STING_USETRANS = "Use Translator"; [es esm esn esg esc esa esd esv eso esr ess esf esl esy esz esb ese esh esi esu] /* Obituaries */ @@ -251,7 +262,8 @@ O_FLAMEGUN = "%o fue carbonizad@[ao_esp] por el Bláster de Fuego de %k."; O_FLAMETHROWER = "%o fue asad@[ao_esp] a conciencia por el Lanzallamas de %k."; 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 Torreta 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_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 */ @@ -352,6 +364,7 @@ T_FLAMETHROWER = "Lanzallamas"; T_BIGGUN = "Demoledor"; T_SMINI = "Autocañón"; T_SENTRY = "Torreta"; +T_OWNEDSENTRY = "Torreta de %s"; T_TRANSLATOR = "Traductor"; T_UARMOR = "Coraza de Asalto"; T_KEVSUIT = "Traje de Kevlar"; @@ -383,6 +396,13 @@ D_POWERSHIELD = "El Escudo de Fuerza se ha agotado."; D_DAMPENER = "El Amortiguador Acústico se ha agotado."; D_AMPLIFIER = "El Amplificador de Energía se ha agotado."; D_SCUBA = "La reserva de oxígeno del Kit de Buceo se ha agotado."; +D_FLASHLIGHT = "Las pilas de la Linterna se han agotado."; +D_SEARCHLIGHT = "El Faro se ha quedado sin energía."; +D_DETECTOR = "El Detector se ha quedado sin energía."; +M_SENTRYDOWN = "Tu Torreta ha sido destruida."; +M_SENTRYDRY = "Tu Torreta se ha quedado sin munición."; +M_SENTRYHIJACK = "Tu Torreta ha sido hackeada."; +M_MSNOROOM = "No hay espacio para colocar la Torreta."; M_FFNOROOM = "No hay espacio para activar el Campo de Fuerza."; S_MINHUD = "Salud %d Puntuación %d Munición %d"; S_MINHUD2 = "Salud %d Puntuación %d Munición %d/%d"; @@ -428,6 +448,7 @@ STING_CMUS = "Música:"; STING_CSND = "Efectos de Sonido:"; STING_CGT = "Productor para GT:"; STING_CBIZ = "Negocios:"; +STING_USETRANS = "Usar Traductor"; [fr] diff --git a/modeldef.umisc b/modeldef.umisc index 32646c5..e707e6a 100644 --- a/modeldef.umisc +++ b/modeldef.umisc @@ -382,6 +382,35 @@ Model "FlareThrownX" FrameIndex FLAR A 0 1 } +Model "LightFlareThrown" +{ + Path "models" + Model 0 "LightFlareM_d.3d" + SurfaceSkin 0 0 "Jflare.png" + AngleOffset -90 + Offset 4 0 1 + Scale 0.1 -0.1 0.12 + USEACTORPITCH + USEROTATIONCENTER + Rotation-Center 0 0 1 + + FrameIndex FLAR A 0 0 +} +Model "DarkFlareThrown" +{ + Path "models" + Model 0 "LightFlareM_d.3d" + SurfaceSkin 0 0 "Jflare.png" + AngleOffset -90 + Offset 4 0 1 + Scale 0.1 -0.1 0.12 + USEACTORPITCH + USEROTATIONCENTER + Rotation-Center 0 0 1 + + FrameIndex FLAR A 0 0 +} + Model "UJumpBoots" { Path "models" @@ -435,9 +464,11 @@ Model "MinigunSentry" { Path "models" Model 0 "SentryGun_d.3d" - SurfaceSkin 0 1 "Minigun_.png" - SurfaceSkin 0 2 "minigun1_.png" + SurfaceSkin 0 1 "Minigun.png" + SurfaceSkin 0 2 "minigun1.png" Scale 0.15 0.15 0.18 + USEACTORPITCH + USEACTORROLL // down FrameIndex SENT A 0 0 @@ -517,16 +548,34 @@ Model "MinigunSentry" FrameIndex SENU Q 0 66 FrameIndex SENU R 0 67 } +Model "MinigunSentryX" +{ + Path "models" + Model 0 "SentryGun_d.3d" + SurfaceSkin 0 0 "JRifle1.png" + Scale 0.15 0.15 0.18 + USEACTORPITCH + USEACTORROLL + + FrameIndex SENF A 0 32 // shoot + FrameIndex SENF D 0 35 // shoot + FrameIndex SENF G 0 38 // shoot + FrameIndex SENF J 0 41 // shoot + FrameIndex SENF M 0 44 // shoot + FrameIndex SENF P 0 47 // shoot +} Model "MinigunSentryBase" { Path "models" Model 0 "SentryBase_d.3d" - SurfaceSkin 0 0 "Minigun_.png" - SurfaceSkin 0 1 "minigun1_.png" - SurfaceSkin 0 2 "JRifle1_.png" + SurfaceSkin 0 0 "Minigun.png" + SurfaceSkin 0 1 "minigun1.png" + SurfaceSkin 0 2 "JRifle1.png" Scale 0.15 0.15 0.18 ZOffset 38 + USEACTORPITCH + USEACTORROLL // down FrameIndex SENT A 0 0 diff --git a/models/LightFlareM_a.3d b/models/LightFlareM_a.3d index 5aa6378..cd2be24 100644 Binary files a/models/LightFlareM_a.3d and b/models/LightFlareM_a.3d differ diff --git a/sndinfo.txt b/sndinfo.txt index 17dd5ed..e08ca02 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -216,3 +216,12 @@ automag/click click automag/reload reload translator/event transa3 + +detector/start detact +detector/stop detend +detector/active detscan + +sentry/raise sentup +sentry/wind sentwind +sentry/fire sentfire +sentry/unwind sentuwnd diff --git a/zscript/biggun.zsc b/zscript/biggun.zsc index 7d6f49e..43aa3bd 100644 --- a/zscript/biggun.zsc +++ b/zscript/biggun.zsc @@ -81,7 +81,7 @@ Class BigGun : UnrealWeapon Tag "$T_BIGGUN"; Inventory.PickupMessage "$I_BIGGUN"; Weapon.UpSound "big/select"; - Weapon.SlotNumber 0; + Weapon.SlotNumber 9; Weapon.SelectionOrder 0; Weapon.AmmoType "BigAmmo"; Weapon.AmmoUse 1; diff --git a/zscript/miscitems.zsc b/zscript/miscitems.zsc index 2625577..a0db32f 100644 --- a/zscript/miscitems.zsc +++ b/zscript/miscitems.zsc @@ -713,6 +713,36 @@ Class BetaFlare : UnrealInventory if ( Owner ) Owner.RemoveInventory(self); Destroy(); } + override bool Use( bool pickup ) + { + if ( pickup || bActive || (charge < defaultcharge) ) return false; + Vector3 x, y, z; + [x, y, z] = dt_CoordUtil.GetAxes(Owner.pitch,Owner.angle,Owner.roll); + Vector3 origin = level.Vec3Offset(Owner.Vec2OffsetZ(0,0,Owner.player.viewz),x*10.-z*8.); + let a = Spawn(ThrownClass,origin); + a.target = Owner; + a.angle = Owner.angle; + a.pitch = Owner.pitch; + a.vel += x*a.speed; + for ( Inventory i=Owner.Inv; i; i=i.Inv ) + { + if ( !(i is 'BetaFlare') ) continue; + UnrealInventory(i).bActive = true; + UnrealInventory(i).Charge = 0; + i.tracer = a; + } + return false; + } + override void DoEffect() + { + Super.DoEffect(); + if ( !bActive ) + { + charge = min(defaultcharge,charge+1); + return; + } + else if ( !tracer ) bActive = false; + } Default { Inventory.MaxAmount 1; @@ -728,6 +758,7 @@ Class LightFlare : BetaFlare { Tag "$T_LFLARES"; Inventory.Icon "I_FlarBL"; + BetaFlare.ThrownClass "LightFlareThrown"; } } @@ -737,19 +768,232 @@ Class DarkFlare : BetaFlare { Tag "$T_DFLARES"; Inventory.Icon "I_FlarBD"; + BetaFlare.ThrownClass "DarkFlareThrown"; } } -Class BetaFlareThrown +Class BetaFlareThrown : Actor { + double pitchvel, anglevel, rollvel; + double desiredangle; + bool rotatetodesired; + double lastpitch, lastangle, lastroll; + Actor b; + Default + { + Radius 6; + Height 6; + +NOBLOCKMAP; + +MISSILE; + +MOVEWITHSECTOR; + +THRUACTORS; + +USEBOUNCESTATE; + +INTERPOLATEANGLES; + +BOUNCEAUTOOFF; + +BOUNCEAUTOOFFFLOORONLY; + +NOTELEPORT; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + DamageType "Exploded"; + Speed 12; + VSpeed 2; + Mass 1; + Gravity 0.35; + BounceType "Hexen"; + WallBounceFactor 0.6; + BounceFactor 0.6; + ReactionTime 350; + } + virtual void A_SpawnEffect() + { + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + pitchvel = FRandom[Junk](5,15)*RandomPick[Junk](-1,1); + anglevel = FRandom[Junk](5,15)*RandomPick[Junk](-1,1); + rollvel = FRandom[Junk](5,15)*RandomPick[Junk](-1,1); + } + override void Tick() + { + lastpitch = pitch; + lastangle = angle; + lastroll = roll; + Super.Tick(); + if ( rotatetodesired ) + { + if ( deltaangle(pitch,0) ~== 0 ) pitch = 0; + else pitch += clamp(deltaangle(pitch,0),-pitchvel,pitchvel); + if ( deltaangle(angle,desiredangle) ~== 0 ) angle = desiredangle; + else angle += clamp(deltaangle(angle,desiredangle),-anglevel,anglevel); + if ( deltaangle(roll,0) ~== 0 ) roll = 0; + else roll += clamp(deltaangle(roll,0),-rollvel,rollvel); + } + else + { + angle += anglevel; + pitch += pitchvel; + roll += rollvel; + } + if ( ReactionTime <= 0 ) return; + if ( GetAge() < 9 ) return; + if ( waterlevel > 0 ) + { + if ( tracer ) tracer.Destroy(); + if ( b ) b.Destroy(); + A_StopSound(CHAN_VOICE); + ReactionTime = 0; + return; + } + if ( !b ) + { + A_PlaySound("flare/on"); + A_PlaySound("flare/loop",CHAN_VOICE,.5,true); + b = Spawn("FlareHitbox",pos); + b.master = self; + } + b.A_AlertMonsters(0,AMF_TARGETEMITTER); + ReactionTime--; + } + States + { + Spawn: + FLAR A 1 + { + if ( GetAge() >= 9 ) A_SpawnEffect(); + return A_JumpIf(ReactionTime<=0,"Death"); + } + Wait; + Bounce: + FLAR A 0 + { + pitch = lastpitch; + angle = lastangle; + roll = lastroll; + rotatetodesired = true; + desiredangle = FRandom[Junk](0,360); + pitchvel = abs(pitchvel)*0.75; + anglevel = abs(anglevel)*0.75; + rollvel = abs(rollvel)*0.75; + } + Goto Spawn; + Death: + FLAR A 0 { anglevel *= 0; } + FLAR A 1 + { + if ( waterlevel > 0 ) + { + if ( b ) b.Destroy(); + A_StopSound(CHAN_VOICE); + return ResolveState("Fizz"); + } + A_SpawnEffect(); + return A_JumpIf(ReactionTime<=0,1); + } + Wait; + FLAR A 0 + { + if ( b ) b.Destroy(); + if ( waterlevel > 0 ) return ResolveState("Fizz"); + A_RemoveLight('PLight'); + A_StopSound(CHAN_VOICE); + A_PlaySound("flare/explode"); + A_Explode(50,50); + A_NoGravity(); + A_Stop(); + A_SetRenderStyle(1.,STYLE_Add); + SetZ(pos.z+9); + Spawn("FlareXLight",pos); + bMOVEWITHSECTOR = false; + bFORCEXYBILLBOARD = true; + A_SetScale(FRandomPick[ExploS](-1.5,1.5),FRandomPick[ExploS](-1.5,1.5)); + return A_Jump(256,"Explo1","Explo2","Explo3","Explo4","Explo5"); + } + Explo1: + EXP1 ABCDEFGH 3 Bright; + Stop; + Explo2: + EXP2 ABCDEFGH 3 Bright; + Stop; + Explo3: + EXP3 ABCDEFGH 3 Bright; + Stop; + Explo4: + EXP4 ABCDEFGH 3 Bright; + Stop; + Explo5: + EXP5 ABCDEFGH 3 Bright; + Stop; + Fizz: + FLAR A 0 A_RemoveLight('PLight'); + FLAR A 1 + { + special1++; + if ( special1 > 150 ) A_FadeOut(); + } + Wait; + } } Class LightFlareThrown : BetaFlareThrown { + override void A_SpawnEffect() + { + Vector3 x, y, z; + double a, s; + [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); + for ( int i=0; i<4; i++ ) + { + a = FRandom[BFlare](0,360); + s = FRandom[BFlare](0,0.15); + let t = Spawn("LightFlareParticle",pos+x*4+z); + t.vel = (x+y*cos(a)*s+z*sin(a)*s).unit()*FRandom[BFlare](1,4); + } + } + override void Tick() + { + Super.Tick(); + if ( GetAge() == 9 ) A_AttachLightDef('PLight','LIGHTFLARE'); + } } Class DarkFlareThrown : BetaFlareThrown { + override void A_SpawnEffect() + { + Vector3 x, y, z; + double a, s; + [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); + for ( int i=0; i<3; i++ ) + { + a = FRandom[BFlare](0,360); + s = FRandom[BFlare](0,0.5); + let t = Spawn("DarkFlareParticle",pos+x*4+z); + t.vel = (x+y*cos(a)*s+z*sin(a)*s).unit()*FRandom[BFlare](4,8); + } + } + override void Tick() + { + Super.Tick(); + if ( GetAge() == 9 ) A_AttachLightDef('PLight','DARKFLARE'); + } +} + +Class LightFlareParticle : UTSmoke +{ + Default + { + StencilColor "FFFFFF"; + Scale 0.2; + } +} +Class DarkFlareParticle : UTSmoke +{ + Default + { + StencilColor "000000"; + Scale 2.2; + } } Class Dampener : UnrealInventory @@ -1123,6 +1367,46 @@ Class SentryItem : UnrealInventory Inventory.RespawnTics 1050; UnrealInventory.Charge 300; } + override bool Use( bool pickup ) + { + if ( pickup ) return false; + if ( bActive ) return false; + Vector3 x, y, z; + [x, y, z] = dt_CoordUtil.GetAxes(Owner.pitch,Owner.angle,Owner.roll); + Vector3 origin = level.Vec3Offset(Owner.Vec2OffsetZ(0,0,Owner.player.viewz),x*90.); + origin = level.Vec3Offset(origin,(0,0,-GetDefaultByType("MinigunSentryBase").Height*.5)); + if ( !level.IsPointInLevel(origin) ) + { + if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$M_MSNOROOM")); + return false; + } + let a = Spawn("MinigunSentryBase",origin); + if ( !a.TestMobjLocation() ) + { + if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$M_MSNOROOM")); + a.Destroy(); + return false; + } + bActive = true; + tracer = a; + a.target = Owner; + a.angle = Owner.angle; + a.pitch = 0; + a.roll = 0; + return false; + } + override void DoEffect() + { + Super.DoEffect(); + if ( !bActive ) return; + if ( !tracer ) + { + bActive = false; + DepleteOrDestroy(); + return; + } + Charge = tracer.Health; + } States { Spawn: @@ -1131,9 +1415,125 @@ Class SentryItem : UnrealInventory } } +// overlay for muzzle flash +Class MinigunSentryX : Actor +{ + Default + { + RenderStyle "Add"; + +NOGRAVITY; + +NOBLOCKMAP; + +INTERPOLATEANGLES; + } + override void Tick() + { + Super.Tick(); + if ( !tracer || !tracer.InStateSequence(tracer.CurState,tracer.FindState("MissileLoop")) ) + { + Destroy(); + return; + } + SetOrigin(tracer.pos,true); + angle = tracer.angle; + pitch = tracer.pitch; + roll = tracer.roll; + } + States + { + Spawn: + SENF A 1 Bright; + TNT1 A 2; + SENF D 1 Bright; + TNT1 A 2; + SENF G 1 Bright; + TNT1 A 2; + SENF J 1 Bright; + TNT1 A 2; + SENF M 1 Bright; + TNT1 A 2; + SENF P 1 Bright; + TNT1 A 2; + Stop; + } +} + // The "head" of the sentry, attaches to the body Class MinigunSentry : Actor { + const maxangle = 60; + const maxpitch = 30; + + Default + { + +NOGRAVITY; + +NOBLOCKMAP; + +INTERPOLATEANGLES; + +FRIENDLY; + } + override void Tick() + { + Super.Tick(); + if ( !master ) + { + Destroy(); + return; + } + Vector3 x, y, z; + [x, y, z] = dt_CoordUtil.GetAxes(master.pitch,master.angle,master.roll); + SetOrigin(level.Vec3Offset(master.pos,z*38),true); + } + double _PitchTo( Actor other ) + { + if ( !other ) return 0; + Vector3 otherpos = level.Vec3Diff(pos,other.Vec3Offset(0,0,other.height/2)); + double dist = otherpos.length(); + if ( dist > 0 ) return -asin(otherpos.z/dist); + return 0; + } + bool TargetVisible() + { + if ( !target || (target.Health <= 0) || !CheckSight(target) ) return false; + double angledelta = DeltaAngle(master.angle,AngleTo(target)); + double pitchdelta = DeltaAngle(master.pitch,_PitchTo(target)); + if ( (abs(angledelta) > maxangle) || (abs(pitchdelta) > maxpitch) ) return false; + return true; + } + void A_SentryFaceTarget() + { + if ( !TargetVisible() ) return; + double angledelta = DeltaAngle(angle,AngleTo(target)); + double pitchdelta = DeltaAngle(pitch,_PitchTo(target)); + double angleturn = clamp(abs(angledelta)*0.1,2,15); + double pitchturn = clamp(abs(pitchdelta)*0.1,2,15); + angle = clamp(angle+clamp(angledelta,-angleturn,angleturn),master.angle-maxangle,master.angle+maxangle); + pitch = clamp(pitch+clamp(pitchdelta,-pitchturn,pitchturn),master.pitch-maxpitch,master.pitch+maxpitch); + } + void A_SentryFaceDir( int dest, statelabel next ) + { + double angledelta = DeltaAngle(angle,master.angle+dest); + double pitchdelta = DeltaAngle(pitch,master.pitch); + if ( max(abs(angledelta),abs(pitchdelta)) < 2 ) + { + angle = master.angle+dest; + pitch = master.pitch; + SetStateLabel(next); + return; + } + angle = clamp(angle+clamp(angledelta,-2,2),master.angle-maxangle,master.angle+maxangle); + pitch = clamp(pitch+clamp(pitchdelta,-2,2),master.pitch-maxpitch,master.pitch+maxpitch); + } + void A_SentryAttack() + { + special1 = max(0,special1-1); + if ( (special1 <= 0) && master.master && master.master.CheckLocalView() ) Console.Printf(StringTable.Localize("$M_SENTRYDRY")); + A_SentryFaceTarget(); + A_PlaySound("sentry/fire",CHAN_WEAPON); + Vector3 x, y, z; + [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); + // TODO actually fire bullets + let c = Spawn("UCasing",level.Vec3Offset(pos,-x*7+y*2+z*2)); + c.vel = x*FRandom[Junk](-1.5,1.5)+y*FRandom[Junk](2,4)+z*FRandom[Junk](2,3); + } States { Spawn: @@ -1142,23 +1542,164 @@ Class MinigunSentry : Actor SENR ABCDEFGHIJKLMNO 3; Goto Idle; Idle: - SENI A 1; + SENI A 1 A_SentryFaceDir(0,1); Wait; - Fire: - SENW ABCDEFGHIJKLMNOPQR 1; - Goto FireLoop; - FireLoop: - SENF ABCDEFGHIJKLMNOPQR 1; + SENI A 1 + { + A_LookEx(LOF_NOSOUNDCHECK|LOF_NOJUMP); + A_SentryFaceDir(-maxangle,1); + return A_JumpIf(TargetVisible(),"See"); + } + Wait; + SENI AAAAAA 1 + { + A_LookEx(LOF_NOSOUNDCHECK|LOF_NOJUMP); + return A_JumpIf(TargetVisible(),"See"); + } + SENI A 1 + { + A_LookEx(LOF_NOSOUNDCHECK|LOF_NOJUMP); + A_SentryFaceDir(maxangle,1); + return A_JumpIf(TargetVisible(),"See"); + } + Wait; + SENI AAAAAA 1 + { + A_LookEx(LOF_NOSOUNDCHECK|LOF_NOJUMP); + return A_JumpIf(TargetVisible(),"See"); + } + Goto Idle+1; + See: + SENI A 1 + { + if ( !TargetVisible() ) + { + A_ClearTarget(); + return ResolveState("Idle"); + } + A_SentryFaceTarget(); + if ( special1 > 0 ) A_Chase(null,"Missile",flags:CHF_DONTMOVE|CHF_DONTTURN); + return ResolveState(null); + } + Wait; + Missile: + SENW A 0 + { + A_PlaySound("sentry/wind",looping:true); + master.SetStateLabel("Missile"); + } + SENW ABCDEFGHIJKLMNOPQR 1 A_SentryFaceTarget(); + Goto MissileLoop; + MissileLoop: + SENF A 0 + { + let f = Spawn("MinigunSentryX",pos); + f.angle = angle; + f.pitch = pitch; + f.roll = roll; + f.tracer = self; + } + SENF A 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF A 1 A_SentryAttack(); + SENF BC 1 A_SentryFaceTarget(); + SENF D 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF D 1 A_SentryAttack(); + SENF EF 1 A_SentryFaceTarget(); + SENF G 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF G 1 A_SentryAttack(); + SENF HI 1 A_SentryFaceTarget(); + SENF J 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF J 1 A_SentryAttack(); + SENF KL 1 A_SentryFaceTarget(); + SENF M 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF M 1 A_SentryAttack(); + SENF NO 1 A_SentryFaceTarget(); + SENF P 0 A_JumpIf(!TargetVisible()||(special1<=0),"MissileEnd"); + SENF P 1 A_SentryAttack(); + SENF QR 1 A_SentryFaceTarget(); Loop; - FireEnd: - SENU ABCDEFGHIJKLMNOPQR 1; - Goto Idle; + MissileEnd: + SENU A 0 + { + A_PlaySound("sentry/unwind"); + master.SetStateLabel("MissileEnd"); + } + SENU ABCDEFGHIJKLMNOPQR 1 A_SentryFaceTarget(); + Goto See; } } // The body of the sentry Class MinigunSentryBase : Actor { + const sentryammo = 200; + + Default + { + Health 300; + Mass int.max; + Radius 12; + Height 48; + +SOLID; + +SHOOTABLE; + +NOBLOOD; + +DONTTHRUST; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + if ( master && master.player ) SetTag(String.Format(StringTable.Localize("$T_OWNEDSENTRY"),master.player.GetUserName()); + else SetTag(StringTable.Localize("$T_SENTRY")); + tracer = Spawn("MinigunSentry",pos); + tracer.special1 = sentryammo; + tracer.master = self; + tracer.angle = angle; + tracer.pitch = pitch; + tracer.roll = roll; + 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 ( master && master.player ) tracer.SetFriendPlayer(master.player); + else tracer.bFRIENDLY = false; + } + override string GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) + { + if ( victim == master ) return String.Format(StringTable.Localize("$O_OWNSENTRY"),GetTag()); + return String.Format(StringTable.Localize("$O_SENTRY"),GetTag()); + } + override void Touch( Actor toucher ) + { + if ( !toucher.player ) return; + if ( deathmatch ) + { + if ( master && (toucher != master) && master.CheckLocalView() ) + Console.Printf(StringTable.Localize("$M_SENTRYHIJACK")); + master = toucher; + SetTag(String.Format(StringTable.Localize("$T_OWNEDSENTRY"),toucher.player.GetUserName()); + tracer.A_ClearTarget(); + tracer.SetFriendPlayer(toucher.player); + } + let amo = toucher.FindInventory("UMiniAmmo"); + if ( !amo || (amo.Amount <= 0) || (tracer.special1 >= sentryammo) ) return; + A_PlaySound("misc/i_pkup",CHAN_ITEM); + int xammo = min(sentryammo-tracer.special1,amo.Amount); + tracer.special1 += xammo; + amo.Amount -= xammo; + } + override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle ) + { + int dmg = Super.DamageMobj(inflictor,source,damage,mod,flags,angle); + if ( !tracer.target ) tracer.target = target; + return dmg; + } + override void Die( Actor source, Actor inflictor, int dmgflags, Name MeansOfDeath ) + { + if ( master && master.CheckLocalView() ) + Console.Printf(StringTable.Localize("$M_SENTRYDOWN")); + if ( tracer ) tracer.Destroy(); + Super.Die(source,inflictor,dmgflags,MeansOfDeath); + } States { Spawn: @@ -1168,13 +1709,13 @@ Class MinigunSentryBase : Actor Idle: SENI A 1; Wait; - Fire: + Missile: SENW ABCDEFGHIJKLMNOPQR 1; - Goto FireLoop; - FireLoop: + Goto MissileLoop; + MissileLoop: SENF ABCDEFGHIJKLMNOPQR 1; Loop; - FireEnd: + MissileEnd: SENU ABCDEFGHIJKLMNOPQR 1; Goto Idle; } diff --git a/zscript/ueightball.zsc b/zscript/ueightball.zsc index 9bc8cd7..3b9a02c 100644 --- a/zscript/ueightball.zsc +++ b/zscript/ueightball.zsc @@ -517,9 +517,9 @@ Class Eightball : UnrealWeapon Ready2: EBLS O 0 A_CheckReload(); EBLL A 0 A_PlaySound("utrl/load",CHAN_ITEM,Dampener.Active(self)?.1:1.); - EBLL ABCDEFGHIJK 2 A_WeaponReady(WRF_NOFIRE); + EBLL ABCDEFGHIJK 2; EBLL L 0 A_PlaySound("utrl/rotate",CHAN_ITEM,Dampener.Active(self)?.01:.1); - EBLL LMNOPQRSTUVWXYZ[ 1 A_WeaponReady(WRF_NOFIRE); + EBLL LMNOPQRSTUVWXYZ[ 1; EBLL Z 0; // force no tweening EBLI A 0; Goto Idle; diff --git a/zscript/unrealcommon.zsc b/zscript/unrealcommon.zsc index b625a15..2a87226 100644 --- a/zscript/unrealcommon.zsc +++ b/zscript/unrealcommon.zsc @@ -1113,6 +1113,11 @@ Class UnrealMainHandler : EventHandler } } } + else if ( (e.Name ~== "usetranslator") && (e.player != -1) && playeringame[e.player] && !menuactive ) + { + let t = players[e.player].mo.FindInventory("UTranslator"); + if ( t ) t.Use(false); + } } override void WorldLoaded( WorldEvent e ) { diff --git a/zscript/unrealhud.zsc b/zscript/unrealhud.zsc index 80c1b2a..fd0ec22 100644 --- a/zscript/unrealhud.zsc +++ b/zscript/unrealhud.zsc @@ -375,7 +375,7 @@ Class UnrealHUD : BaseStatusBar if ( Prev && !Next && !bDrawOne ) x += 32; // this was missing from the original, causing a gap when there's only two items in the inventory if ( Prev ) { - bRed = ((Prev is 'UnrealInventory') && UnrealInventory(Prev).bActive) || (Prev is 'Powerup') || ((Prev is 'UTranslator') && bFlashTranslator); + bRed = ((Prev is 'UnrealInventory') && UnrealInventory(Prev).bActive) || (Prev is 'Powerup') || ((Prev is 'UTranslator') && ((bTranslatorActive) || (bFlashTranslator && ((gametic%8)<4)))); DrawHudIcon(x,y,Prev,bRed); if ( Prev.MaxAmount > 1 ) DrawNumberOf(Prev,x,y); } @@ -389,7 +389,7 @@ Class UnrealHUD : BaseStatusBar if ( SelectedItem.MaxAmount > 1 ) DrawNumberOf(SelectedItem,CurX,y); if ( Next ) { - bRed = ((Next is 'UnrealInventory') && UnrealInventory(Next).bActive) || (Next is 'Powerup') || ((Next is 'UTranslator') && bFlashTranslator); + bRed = ((Next is 'UnrealInventory') && UnrealInventory(Next).bActive) || (Next is 'Powerup') || ((Next is 'UTranslator') && ((bTranslatorActive) || (bFlashTranslator && ((gametic%8)<4)))); DrawHudIcon(x+64,y,Next,bRed); if ( Next.MaxAmount > 1 ) DrawNumberOf(Next,x+64,y); } diff --git a/zscript/upowerups.zsc b/zscript/upowerups.zsc index 947125c..87b2cf2 100644 --- a/zscript/upowerups.zsc +++ b/zscript/upowerups.zsc @@ -322,6 +322,9 @@ Class UJumpBoots : UnrealInventory Class MotionDetector : UnrealInventory { + ui TextureID DetHud, DetSpot[2]; + Array nearscan; + Default { Tag "$T_DETECTOR"; @@ -332,8 +335,76 @@ Class MotionDetector : UnrealInventory Inventory.Icon "I_Detect"; Inventory.PickupMessage "$I_DETECTOR"; Inventory.RespawnTics 1050; - UnrealInventory.Charge 3500; + UnrealInventory.Charge 7000; } + override bool TryPickup( in out Actor toucher ) + { + bool valid = Super.TryPickup(toucher); + if ( valid ) level.allmap = true; + return valid; + } + override void PostRender( double lbottom ) + { + if ( !bActive ) return; + double xpos = UnrealHUD(StatusBar).ClipX-96; + double ypos = 0; + // offset if inventory display present on top right + if ( UnrealHUD(StatusBar).HudMode < 2 ) ypos += 32; + if ( DetHud.IsNull() ) DetHud = TexMan.CheckForTexture("DetHud",TexMan.Type_Any); + if ( DetSpot[0].IsNull() ) DetSpot[0] = TexMan.CheckForTexture("DetSpot0",TexMan.Type_Any); + if ( DetSpot[1].IsNull() ) DetSpot[1] = TexMan.CheckForTexture("DetSpot1",TexMan.Type_Any); + Screen.DrawTexture(DetHud,false,xpos,ypos,DTA_VirtualWidthF,UnrealHUD(StatusBar).ClipX,DTA_VirtualHeightF,UnrealHUD(StatusBar).ClipY,DTA_KeepRatio,true); + double alph = max(0.,0.25+0.75*sin((gametic+UnrealHUD(StatusBar).fractic)*6.)); + Screen.Dim(Color("FF 40 00"),alph*.25,int((xpos+3)*UnrealHUD(StatusBar).scalev.x),int((ypos+3)*UnrealHUD(StatusBar).scalev.y),int(90*UnrealHUD(StatusBar).scalev.x),int(90*UnrealHUD(StatusBar).scalev.y)); + Screen.DrawTexture(DetSpot[0],false,xpos+48,ypos+48,DTA_VirtualWidthF,UnrealHUD(StatusBar).ClipX,DTA_VirtualHeightF,UnrealHUD(StatusBar).ClipY,DTA_KeepRatio,true); + alph = max(0.,0.5+0.5*sin((gametic+UnrealHUD(StatusBar).fractic-10)*6.)); + for ( int i=0; i 48. ) continue; + // this is a long line, but it's not the longest I've ever seen + // oh I have seen things... php code that would make you want to stab your eyes with forks... + Screen.DrawTexture(DetSpot[1],false,xpos+48+relofs.x,ypos+48+relofs.y,DTA_VirtualWidthF,UnrealHUD(StatusBar).ClipX,DTA_VirtualHeightF,UnrealHUD(StatusBar).ClipY,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipLeft,int((xpos+3)*UnrealHUD(StatusBar).scalev.x),DTA_ClipTop,int((ypos+3)*UnrealHUD(StatusBar).scalev.y),DTA_ClipRight,int((xpos+93)*UnrealHUD(StatusBar).scalev.x),DTA_ClipBottom,int((ypos+93)*UnrealHUD(StatusBar).scalev.y)); + } + } + override bool Use( bool pickup ) + { + if ( pickup ) return false; + bActive = !bActive; + if ( bActive ) Owner.A_PlaySound("detector/start",CHAN_ITEM); + else Owner.A_PlaySound("detector/stop",CHAN_ITEM,0.1,false,3.); + return false; + } + override void DoEffect() + { + Super.DoEffect(); + if ( bActive && !tracer ) + { + tracer = Spawn("DetectorSound",Owner.pos); + tracer.target = Owner; + tracer.master = self; + } + else if ( !bActive && tracer ) tracer.Destroy(); + if ( !bActive ) return; + nearscan.Clear(); + let bi = BlockThingsIterator.Create(Owner,2048); + while ( bi.Next() ) + { + if ( !bi.Thing || (bi.Thing == Owner) || !bi.Thing.bISMONSTER || bi.Thing.Health <= 0 ) continue; + nearscan.Push(bi.Thing); + } + if ( (Charge <= 0) || DrainCharge(1) ) + { + Owner.A_PlaySound("detector/stop",CHAN_ITEM,0.1,false,3.); + if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_DETECTOR")); + if ( tracer ) tracer.Destroy(); + DepleteOrDestroy(); + } + } + States { Spawn: @@ -342,6 +413,39 @@ Class MotionDetector : UnrealInventory } } +Class DetectorSound : Actor +{ + Default + { + +NOBLOCKMAP; + +NOGRAVITY; + } + override void Tick() + { + Super.Tick(); + if ( !target || !master ) + { + Destroy(); + return; + } + SetOrigin(target.pos,true); + A_SoundVolume(CHAN_VOICE,target.CheckLocalView()?0.1:0.0); + A_SoundVolume(CHAN_7,!target.CheckLocalView()?0.1:0.0); + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_PlaySound("detector/active",CHAN_VOICE,0.1,true,ATTN_NONE); + A_PlaySound("detector/active",CHAN_7,0.1,true,3.); + } + override void OnDestroy() + { + Super.OnDestroy(); + A_StopSound(CHAN_VOICE); + A_StopSound(CHAN_7); + } +} + Class SCUBAGear : UnrealInventory { Default diff --git a/zscript/utranslocator.zsc b/zscript/utranslocator.zsc index e88b836..22844af 100644 --- a/zscript/utranslocator.zsc +++ b/zscript/utranslocator.zsc @@ -40,7 +40,7 @@ Class UTranslocator : UnrealWeapon { Tag "$T_TELEGUN"; Inventory.PickupMessage "$I_TELEGUN"; - Weapon.SlotNumber 9; + Weapon.SlotNumber 1; Weapon.SelectionOrder 10; Weapon.AmmoType1 "UTranslocatorAmmo"; Weapon.AmmoUse1 1;