Flashlight/Searchlight implemented.

Proper handling of charge redistribution on multi-copy items (fixes infinitely replenishable charge exploit).
More preparation code for other items.
This commit is contained in:
Marisa the Magician 2019-09-05 16:22:17 +02:00
commit b12c4a4112
17 changed files with 270 additions and 24 deletions

View file

@ -37,6 +37,8 @@ Doom Tournament (currently the devel branch is required).
- Super Health (replaces soulsphere)
- Nali Fruit Seed (replaces health bonus)
- Flare (replaces armor bonus)
- Flashlight (replaces light amplifier)
- Searchlight (must be placed manually for balance reasons)
## In progress
@ -46,8 +48,6 @@ Doom Tournament (currently the devel branch is required).
- Minigun (slot 0) (replaces chaingun)
- SMP 7243 (slot 0) (replaces bfg9000)
- Flashlight (replaces light amplifier)
- Searchlight (must be placed manually for balance reasons)
- Jump Boots (replaces radsuit)
- SCUBA Gear (replaces radsuit if map has swimmable water)

Binary file not shown.

Binary file not shown.

View file

@ -204,6 +204,8 @@ voice/activate voicesnd
univis/toggle invisibl
lite/off fshlite2
automag/select cocking
automag/fire shot
automag/click click

View file

@ -49,6 +49,22 @@ Class BigAmmo3 : BigAmmo
}
}
Class BigBlast : Actor
{
}
Class HitListEntry
{
Actor hitactor;
Vector3 hitlocation, x;
}
Class BigTracer : LineTracer
{
Array<HitListEntry> hitlist;
double penetration; // please don't laugh
}
Class BigGun : UnrealWeapon
{
Default

View file

@ -1,3 +1,11 @@
Class UFireball : Actor
{
}
Class UFireball2 : UFireball
{
}
Class FlameGun : UnrealWeapon
{
Default

View file

@ -10,6 +10,10 @@ Class SMiniAmmo : Ammo
}
}
Class SMiniBlast : Actor
{
}
Class SMiniGun : UnrealWeapon
{
Default

View file

@ -269,11 +269,10 @@ Class VoiceBox : UnrealInventory
Inventory.MaxAmount 3;
UnrealInventory.Charge 600;
}
override void Tick()
{
Super.Tick();
if ( bActive )
override void DoEffect()
{
Super.DoEffect();
if ( !bActive ) return;
if ( box ) Charge = box.ReactionTime;
else
{
@ -283,7 +282,6 @@ Class VoiceBox : UnrealInventory
if ( Amount <= 0 ) DepleteOrDestroy();
}
}
}
override bool Use( bool pickup )
{
if ( pickup || bActive ) return false;
@ -742,6 +740,18 @@ Class DarkFlare : BetaFlare
}
}
Class BetaFlareThrown
{
}
Class LightFlareThrown : BetaFlareThrown
{
}
Class DarkFlareThrown : BetaFlareThrown
{
}
Class Dampener : UnrealInventory
{
static bool Active( Actor Owner )
@ -757,9 +767,9 @@ Class Dampener : UnrealInventory
Owner.A_PlaySound(bActive?"dampener/on":"dampener/off",CHAN_ITEM);
return false;
}
override void Tick()
override void DoEffect()
{
Super.Tick();
Super.DoEffect();
if ( !bActive ) return;
if ( DrainCharge(1) )
{
@ -929,9 +939,64 @@ Class ForcefieldEffect : Actor
}
}
Class UFlashLight1 : DynamicLight
{
int basecolor[3];
Default
{
DynamicLight.Type "Point";
+DynamicLight.SPOT;
+DynamicLight.ATTENUATE;
+DynamicLight.DONTLIGHTSELF;
args 0,0,0,560;
DynamicLight.SpotInnerAngle 3;
DynamicLight.SpotOuterAngle 10;
}
override void Tick()
{
Super.Tick();
if ( !target || !UnrealInventory(master) )
{
Destroy();
return;
}
if ( target.player ) SetOrigin((target.pos.x,target.pos.y,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(UnrealInventory(master).charge/1400.,0.,1.));
args[LIGHT_GREEN] = int(basecolor[1]*clamp(UnrealInventory(master).charge/1400.,0.,1.));
args[LIGHT_BLUE] = int(basecolor[2]*clamp(UnrealInventory(master).charge/1400.,0.,1.));
bDORMANT = (target.health <= 0);
if ( Inventory(target) && target.bInvisible ) bDORMANT = true;
// alert monsters hit by the light
if ( GetClass() != "UFlashLight1" ) 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;
}
}
}
}
Class UFlashLight2 : UFlashLight1
{
Default
{
args 0,0,0,600;
DynamicLight.SpotInnerAngle 0;
DynamicLight.SpotOuterAngle 20;
}
}
Class UFlashlight : UnrealInventory
{
Actor lt[3];
UFlashLight1 lt[2];
Default
{
@ -941,6 +1006,66 @@ Class UFlashlight : UnrealInventory
Inventory.PickupMessage "$I_FLASHLIGHT";
UnrealInventory.Charge 2800;
}
virtual void SetupLights()
{
lt[0].basecolor[0] = 255;
lt[0].basecolor[1] = 240;
lt[0].basecolor[2] = 224;
lt[1].basecolor[0] = 128;
lt[1].basecolor[1] = 120;
lt[1].basecolor[2] = 112;
}
override bool Use( bool pickup )
{
if ( pickup ) return false;
bActive = !bActive;
Owner.A_PlaySound(bActive?"lite/pickup":"lite/off",CHAN_ITEM);
if ( bActive )
{
if ( !lt[0] ) lt[0] = UFlashLight1(Spawn("UFlashLight1",owner.pos));
lt[0].target = owner;
lt[0].master = self;
if ( !lt[1] ) lt[1] = UFlashLight1(Spawn("UFlashLight2",owner.pos));
lt[1].target = owner;
lt[1].master = self;
SetupLights();
}
else
{
if ( lt[0] ) lt[0].Destroy();
if ( lt[1] ) lt[1].Destroy();
}
return false;
}
override void DoEffect()
{
Super.DoEffect();
if ( !bActive ) return;
if ( DrainCharge(1) )
{
Owner.A_PlaySound("lite/off",CHAN_ITEM);
if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_FLASHLIGHT"));
if ( Amount <= 0 ) DepleteOrDestroy();
}
}
override void DetachFromOwner()
{
Super.DetachFromOwner();
if ( lt[0] ) lt[0].Destroy();
if ( lt[1] ) lt[1].Destroy();
}
override void Travelled()
{
Super.Travelled();
if ( !bActive ) return;
if ( !lt[0] ) lt[0] = UFlashLight1(Spawn("UFlashLight1",owner.pos));
lt[0].target = owner;
lt[0].master = self;
if ( !lt[1] ) lt[1] = UFlashLight1(Spawn("UFlashLight2",owner.pos));
lt[1].target = owner;
lt[1].master = self;
SetupLights();
}
States
{
Spawn:
@ -960,6 +1085,31 @@ Class USearchlight : UFlashlight
Inventory.RespawnTics 1050;
UnrealInventory.Charge 70000;
}
override void DoEffect()
{
Super.DoEffect();
if ( !bActive ) return;
if ( DrainCharge(1) )
{
Owner.A_PlaySound("lite/off",CHAN_ITEM);
if ( Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_SEARCHLIGHT"));
if ( Amount <= 0 ) DepleteOrDestroy();
}
}
override void SetupLights()
{
lt[0].basecolor[0] = 255;
lt[0].basecolor[1] = 224;
lt[0].basecolor[2] = 192;
lt[0].SpotInnerAngle = 20;
lt[0].SpotOuterAngle = 35;
lt[0].args[3] = 750;
lt[1].basecolor[0] = 128;
lt[1].basecolor[1] = 112;
lt[1].basecolor[2] = 96;
lt[1].SpotOuterAngle = 50;
lt[1].args[3] = 780;
}
}
Class SentryItem : UnrealInventory

View file

@ -19,6 +19,14 @@ Class FlameAmmo : Ammo
}
}
Class UFlame : Actor
{
}
Class UNapalm : Actor
{
}
Class UFlamethrower : UnrealWeapon
{
Default

View file

@ -10,6 +10,14 @@ Class PeaceAmmo : Ammo
}
}
Class PeaceRocket : Actor
{
}
Class PeaceProj : Actor
{
}
Class Peacemaker : UnrealWeapon
{
Default

View file

@ -35,6 +35,10 @@ Class UShells2 : UShells
}
}
Class QCasing : UCasing
{
}
Class QuadShot : UnrealWeapon
{
Default

View file

@ -24,6 +24,14 @@ Class RazorAmmo : Ammo
}
}
Class RazorBlade : Actor
{
}
Class RazorBladeAlt : RazorBlade
{
}
Class Razorjack : UnrealWeapon
{
Default

View file

@ -23,6 +23,14 @@ Class StunnerAmmo : Ammo
}
}
Class StunTrail : Actor
{
}
Class StunProj : Actor
{
}
Class Stunner : UnrealWeapon
{
Default

View file

@ -40,6 +40,18 @@ Class UBioAmmo2 : UBioAmmo
}
}
Class UBioGel : Actor
{
}
Class UBioGlob : UBioGel
{
}
Class UBioSplash : UBioGel
{
}
Class UBioRifle : UnrealWeapon
{
Default

View file

@ -616,14 +616,28 @@ Class UnrealInventory : Inventory
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
Charge = DefaultCharge;
if ( !Charge ) Charge = DefaultCharge;
InterHubAmount = MaxAmount; // it's annoying to set this per-subclass
}
override bool HandlePickup( Inventory item )
{
if ( (item.GetClass() == GetClass()) && ((MaxAmount > 1) || (DefaultCharge > 0)) )
{
if ( MaxAmount > 1 ) Amount = min(MaxAmount,Amount+item.Amount);
if ( MaxAmount > 1 )
{
if ( UnrealInventory(item).Charge ) // redistribute charge among copies
{
int addcharge = Charge+UnrealInventory(item).Charge;
charge = min(DefaultCharge,addcharge);
// if there's charge to spare, increase amount
if ( addcharge > charge )
{
Amount = min(MaxAmount,Amount+item.Amount);
charge = addcharge-charge;
}
}
else Amount = min(MaxAmount,Amount+item.Amount); // fully charged new copy, just increase
}
else Charge = DefaultCharge; // reset charge
item.bPickupGood = true;
return true;

View file

@ -24,9 +24,9 @@ Class UInvisibility : UnrealInventory
else Owner.TakeInventory("PowerUInvisibility",1);
return false;
}
override void Tick()
override void DoEffect()
{
Super.Tick();
Super.DoEffect();
if ( !bActive ) return;
if ( special1 == -1 )
{
@ -116,9 +116,9 @@ Class Amplifier : UnrealInventory
Owner.A_PlaySound("amplifier/set",CHAN_ITEM);
return false;
}
override void Tick()
override void DoEffect()
{
Super.Tick();
Super.DoEffect();
if ( bActive && !tracer )
{
tracer = Spawn("AmpSound",Owner.pos);
@ -208,9 +208,9 @@ Class UJumpBoots : UnrealInventory
else Owner.TakeInventory("PowerJumpBoots_HighJump",1);
return false;
}
override void Tick()
override void DoEffect()
{
Super.Tick();
Super.DoEffect();
if ( !Owner || !Owner.player ) return;
draincnt++;
if ( (draincnt >= 700) || (bActive && (owner.player.jumptics == -1)) )

View file

@ -18,6 +18,10 @@ Class UTranslocatorAmmo : Ammo
}
}
Class UTranslocatorModule : Actor
{
}
Class UTranslocator : UnrealWeapon
{
override bool TryPickup( in out Actor toucher )