While in the latter case this may result in longer loops, it also reduces GC thrashing by not needing to allocate an iterator every time. This also simplifies the DoBlast code as there is no longer a need to manually traverse portals vertically.
634 lines
16 KiB
Text
634 lines
16 KiB
Text
// Lämp
|
|
|
|
Class LampMoth : Actor
|
|
{
|
|
Actor lamp;
|
|
Vector3 trail, ofs;
|
|
int lifespan;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_MOTH";
|
|
Radius 2;
|
|
Height 4;
|
|
Speed 2;
|
|
DamageFunction 1;
|
|
MeleeRange 16;
|
|
Mass 10;
|
|
Health 50;
|
|
DeathSound "moth/die";
|
|
BloodColor "20 10 10";
|
|
MONSTER;
|
|
-COUNTKILL;
|
|
+THRUACTORS;
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
+FLOAT;
|
|
+NOPAIN;
|
|
+FRIENDLY;
|
|
+LOOKALLAROUND;
|
|
+QUICKTORETALIATE;
|
|
+INTERPOLATEANGLES;
|
|
+NOBLOCKMONST;
|
|
}
|
|
override string GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
|
|
{
|
|
if ( inflictor && (inflictor != self) )
|
|
{
|
|
if ( inflictor == master ) return StringTable.Localize("$O_MOTHSELF"); // not likely to happen
|
|
else return StringTable.Localize("$O_MOTH");
|
|
}
|
|
return StringTable.Localize("$O_MOTH2");
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("moth/fly",CHAN_BODY,CHANF_LOOP,.02,4.,FRandom[Moth](.8,1.2));
|
|
if ( master && master.player ) SetFriendPlayer(master.player);
|
|
else bFRIENDLY = false;
|
|
}
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
// no hurt moff
|
|
if ( source && IsFriend(source) ) damage = 0;
|
|
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
|
}
|
|
bool isEntranced()
|
|
{
|
|
if ( !lamp )
|
|
{
|
|
// look for nearby lamps
|
|
double mindist = 62500.;
|
|
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
|
|
{
|
|
if ( !(a is 'CompanionLamp') ) continue;
|
|
double dist = Distance3DSquared(a);
|
|
if ( (a.frame == 0) || (dist > mindist) && !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
|
|
mindist = dist;
|
|
lamp = a;
|
|
master = a.target;
|
|
if ( CompanionLamp(lamp).moff.Find(self) == CompanionLamp(lamp).moff.Size() )
|
|
CompanionLamp(lamp).moff.Push(self);
|
|
if ( master && master.player ) SetFriendPlayer(master.player);
|
|
else bFRIENDLY = false;
|
|
}
|
|
}
|
|
if ( !lamp || (lamp.frame == 0) || (Distance3DSquared(lamp) > 62500) || !CheckSight(lamp,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) return false;
|
|
if ( target && (target.Health > 0) && CheckSight(target) ) return false;
|
|
return true;
|
|
}
|
|
void A_SmoothWander()
|
|
{
|
|
if ( level.Vec3Diff(pos,trail).length() < speed )
|
|
{
|
|
double ang = FRandom[Moth](0,360);
|
|
double pt = FRandom[Moth](-30,30);
|
|
double dist = FRandom[Moth](20,40);
|
|
ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
|
}
|
|
Vector3 newpos = level.Vec3Offset(pos,ofs);
|
|
if ( level.IsPointInLevel(newpos) ) trail = newpos;
|
|
if ( vel.length() > 0 )
|
|
{
|
|
Vector3 uvel = vel.unit();
|
|
angle += Clamp(deltaangle(angle,atan2(uvel.y,uvel.x)),-5.,5.);
|
|
pitch += Clamp(deltaangle(pitch,asin(-uvel.z)),-5.,5.);
|
|
}
|
|
vel *= .8;
|
|
Vector3 dir = level.Vec3Diff(pos,trail);
|
|
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.05,.4*speed,.5*speed);
|
|
}
|
|
void A_SmoothChase()
|
|
{
|
|
if ( !target || (target.Health <= 0) )
|
|
{
|
|
A_ClearTarget();
|
|
SetStateLabel("Spawn");
|
|
return;
|
|
}
|
|
if ( CheckMeleeRange() )
|
|
{
|
|
SetStateLabel("Melee");
|
|
return;
|
|
}
|
|
Vector3 dest = target.Vec3Offset(0,0,target.height*.75);
|
|
Vector3 dir = level.Vec3Diff(pos,dest);
|
|
if ( dir.length() > 0 )
|
|
{
|
|
Vector3 dirunit = dir.unit();
|
|
FLineTraceData d;
|
|
LineTrace(atan2(dirunit.y,dirunit.x),dir.length(),asin(-dirunit.z),data:d);
|
|
if ( (d.HitType != TRACE_HitActor) && (d.HitActor != target) )
|
|
{
|
|
A_Chase();
|
|
return;
|
|
}
|
|
}
|
|
if ( vel.length() > 0 )
|
|
{
|
|
Vector3 uvel = vel.unit();
|
|
angle = atan2(uvel.y,uvel.x);
|
|
pitch = asin(-uvel.z);
|
|
}
|
|
vel *= .8;
|
|
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.02,.3*speed,2.*speed);
|
|
}
|
|
void A_FollowLamp()
|
|
{
|
|
if ( !lamp )
|
|
{
|
|
SetStateLabel("Spawn");
|
|
return;
|
|
}
|
|
double dst = level.Vec3Diff(pos,trail).length();
|
|
if ( (dst < speed) || (dst > 50) )
|
|
{
|
|
double ang = FRandom[Moth](0,360);
|
|
double pt = FRandom[Moth](-30,30);
|
|
double dist = FRandom[Moth](20,30);
|
|
ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
|
}
|
|
Vector3 newpos = level.Vec3Offset(lamp.Vec3Offset(0,0,lamp.height/2),ofs);
|
|
if ( level.IsPointInLevel(newpos) ) trail = newpos;
|
|
if ( vel.length() > 0 )
|
|
{
|
|
Vector3 uvel = vel.unit();
|
|
angle = atan2(uvel.y,uvel.x);
|
|
pitch = asin(-uvel.z);
|
|
}
|
|
vel *= .8;
|
|
Vector3 dir = level.Vec3Diff(pos,trail);
|
|
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.02,.4*speed,2.*speed);
|
|
Vector3 diff = level.Vec3Diff(pos,lamp.pos);
|
|
if ( (diff.x > -8) && (diff.x < 8) && (diff.y > -8) && (diff.y < 8) && (diff.z > -4) && (diff.z < lamp.height+4) )
|
|
{
|
|
if ( diff.x < 0 ) vel.x -= .2;
|
|
else vel.x += .2;
|
|
if ( diff.y < 0 ) vel.y -= .2;
|
|
else vel.y += .2;
|
|
if ( diff.z < 0 ) vel.z -= .2;
|
|
else vel.z += .2;
|
|
}
|
|
}
|
|
void A_SmoothMove()
|
|
{
|
|
if ( vel.length() > 0 )
|
|
{
|
|
Vector3 uvel = vel.unit();
|
|
angle = atan2(uvel.y,uvel.x);
|
|
pitch = asin(-uvel.z);
|
|
}
|
|
vel *= .8;
|
|
}
|
|
void A_Scrape()
|
|
{
|
|
if ( CheckMeleeRange() )
|
|
{
|
|
A_FaceTarget(0,0);
|
|
lifespan -= 5;
|
|
Vector3 awaydir = level.Vec3Diff(target.Vec3Offset(0,0,target.height),pos).unit();
|
|
vel += awaydir*8.;
|
|
int dmg = target.DamageMobj(self,master?master:Actor(self),GetMissileDamage(0,0),'Melee',Random[Moth](0,8)?DMG_NO_PAIN:0);
|
|
if ( (dmg > 0) && target && !target.bNOBLOOD && !target.bDORMANT && !target.bINVULNERABLE )
|
|
{
|
|
target.TraceBleed(dmg,self);
|
|
target.SpawnBlood(pos,atan2(awaydir.y,awaydir.x)+180,dmg);
|
|
}
|
|
A_StartSound("moth/scrape",CHAN_WEAPON,CHANF_OVERLAP,.2,2.5);
|
|
DamageMobj(target,target,1,'Melee');
|
|
}
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() || (freezetics > 0) ) return;
|
|
if ( isEntranced() )
|
|
{
|
|
lifespan = 100;
|
|
return;
|
|
}
|
|
if ( target && (target.Health > 0) ) lifespan = max(20,lifespan);
|
|
lifespan--;
|
|
if ( lifespan <= 0 )
|
|
{
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.alpha *= .3;
|
|
Destroy();
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 B 0 A_JumpIf(isEntranced(),"See.Entranced");
|
|
XZW1 BC 1
|
|
{
|
|
A_SmoothWander();
|
|
A_Look();
|
|
}
|
|
Loop;
|
|
See: // go for enemies
|
|
XZW1 B 0 A_JumpIf(isEntranced(),"See.Entranced");
|
|
XZW1 BC 1 A_SmoothChase();
|
|
Loop;
|
|
See.Entranced: // follow the lamp
|
|
XZW1 B 0 A_JumpIf(!isEntranced(),"Spawn");
|
|
XZW1 BC 1 A_FollowLamp();
|
|
Loop;
|
|
Melee:
|
|
XZW1 B 0 A_Scrape();
|
|
XZW1 BCBC 1 A_SmoothMove();
|
|
Goto See;
|
|
Death:
|
|
TNT1 A 1
|
|
{
|
|
A_StartSound("moth/die",CHAN_VOICE,CHANF_OVERLAP,.6,2.5);
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.alpha *= .3;
|
|
}
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class LampMoth2 : LampMoth
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_WMOTH";
|
|
DamageFunction 3;
|
|
Speed 3;
|
|
Scale 1.5;
|
|
Health 200;
|
|
}
|
|
}
|
|
|
|
Class CompanionLamp : Actor
|
|
{
|
|
Vector3 Trail;
|
|
Array<LampMoth> moff;
|
|
Actor parent;
|
|
bool justteleport;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_LAMP";
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
+LOOKALLAROUND;
|
|
+FRIENDLY;
|
|
+NOBLOCKMONST;
|
|
Radius 4;
|
|
Height 16;
|
|
}
|
|
// random chance to spawn moths
|
|
void A_Moth()
|
|
{
|
|
// count up
|
|
special1++;
|
|
for ( int i=0; i<moff.Size(); i++ )
|
|
{
|
|
if ( moff[i] && (moff[i].lamp == self) && moff[i].isEntranced() ) continue;
|
|
moff.Delete(i);
|
|
i--;
|
|
}
|
|
if ( (special1%35) || Random[Moth](0,9) || (moff.Size() >= 30) ) return;
|
|
// spawn a moth at a random offset
|
|
double ang = FRandom[Moth](0,360);
|
|
double pt = FRandom[Moth](-30,30);
|
|
double dist = FRandom[Moth](10,30);
|
|
Vector3 ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
|
Vector3 spawnpos = level.Vec3Offset(Vec3Offset(0,0,height/2),ofs);
|
|
if ( !level.IsPointInLevel(spawnpos) ) return;
|
|
// higher chance of white moths if carrying the plush
|
|
int mchance = parent.FindInventory("MothPlushy")?3:9;
|
|
let m = LampMoth(Spawn(Random[Moth](0,mchance)?"LampMoth":"LampMoth2",spawnpos));
|
|
if ( !m.TestMobjLocation() )
|
|
{
|
|
m.Destroy();
|
|
return;
|
|
}
|
|
let s = Spawn("SWWMSmallSmoke",m.pos);
|
|
s.alpha *= .3;
|
|
m.master = parent;
|
|
m.lamp = self;
|
|
m.trail = m.pos;
|
|
moff.Push(m);
|
|
SWWMUtility.AchievementProgressInc("moth",1,parent.player);
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( !parent || !SWWMLamp(master) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Spawn("SWWMItemFog",pos);
|
|
Trail = pos;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !parent || !SWWMLamp(master) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( isFrozen() || (freezetics > 0) ) return;
|
|
// update trailing position
|
|
bool foundspot = false;
|
|
for ( int i=0; i<180; i+=5 )
|
|
{
|
|
for ( int j=1; j>=-1; j-=2 )
|
|
{
|
|
double ang = (parent.angle-180)+i*j;
|
|
Vector3 testpos = level.Vec3Offset(parent.pos,SWWMUtility.RotateVector3((32,0,parent.height-8+1.5*sin(level.maptime*3.)),ang));
|
|
if ( !level.IsPointInLevel(testpos) ) continue;
|
|
Vector3 oldpos = pos;
|
|
Vector3 oldprev = prev;
|
|
Actor oldblockingmobj = blockingmobj;
|
|
Line oldblockingline = blockingline;
|
|
Sector oldblockingfloor = blockingfloor, oldblockingceiling = blockingceiling;
|
|
SetOrigin(testpos,false);
|
|
if ( !TestMobjLocation() || SWWMUtility.BlockingLineIsBlocking(self,Line.ML_BLOCKING|Line.ML_BLOCKEVERYTHING) || BlockingFloor || BlockingCeiling )
|
|
{
|
|
SetOrigin(oldpos,false);
|
|
prev = oldprev;
|
|
blockingmobj = oldblockingmobj;
|
|
blockingline = oldblockingline;
|
|
blockingfloor = oldblockingfloor;
|
|
blockingceiling = oldblockingceiling;
|
|
continue;
|
|
}
|
|
SetOrigin(oldpos,false);
|
|
prev = oldprev;
|
|
blockingmobj = oldblockingmobj;
|
|
blockingline = oldblockingline;
|
|
blockingfloor = oldblockingfloor;
|
|
blockingceiling = oldblockingceiling;
|
|
Trail = testpos;
|
|
foundspot = true;
|
|
}
|
|
// check at most for a 45 degree offset
|
|
if ( foundspot && (i > 45) ) break;
|
|
}
|
|
Vector3 diff = level.Vec3Diff(pos,parent.pos);
|
|
if ( (diff.length() > 400) || justteleport )
|
|
{
|
|
Vector3 rel = level.Vec3Diff(pos,trail);
|
|
justteleport = false;
|
|
Actor f = Spawn("SWWMItemFog",pos);
|
|
f.A_StartSound("lamp/disappear",CHAN_VOICE);
|
|
// carry over the moths
|
|
foreach ( m:moff )
|
|
{
|
|
if ( !m ) continue;
|
|
Vector3 whereto = level.Vec3Offset(m.pos,rel);
|
|
if ( !level.IsPointInLevel(whereto) )
|
|
continue;
|
|
Vector3 oldp = m.pos;
|
|
m.SetOrigin(whereto,false);
|
|
if ( !m.TestMobjLocation() )
|
|
m.SetOrigin(oldp,false);
|
|
}
|
|
SetOrigin(trail,false);
|
|
angle = AngleTo(parent);
|
|
vel *= 0.;
|
|
f = Spawn("SWWMItemFog",pos);
|
|
f.A_StartSound("lamp/appear",CHAN_VOICE);
|
|
return;
|
|
}
|
|
angle += Clamp(deltaangle(angle,AngleTo(parent)),-5.,5.);
|
|
vel *= .8;
|
|
bool blocked = false;
|
|
if ( SWWMUtility.BlockingLineIsBlocking(self,Line.ML_BLOCKING|Line.ML_BLOCKEVERYTHING) )
|
|
{
|
|
// push away from wall
|
|
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
|
if ( !Level.PointOnLineSide(pos.xy,BlockingLine) ) normal *= -1;
|
|
vel += 4.*normal;
|
|
blocked = true;
|
|
}
|
|
if ( BlockingFloor )
|
|
{
|
|
// push away from floor
|
|
Vector3 normal = BlockingFloor.floorplane.Normal;
|
|
// find closest 3d floor for its normal
|
|
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
normal = -BlockingFloor.Get3DFLoor(i).top.Normal;
|
|
break;
|
|
}
|
|
vel += 4.*normal;
|
|
blocked = true;
|
|
}
|
|
if ( BlockingCeiling )
|
|
{
|
|
// push away from ceiling
|
|
Vector3 normal = BlockingCeiling.ceilingplane.Normal;
|
|
// find closest 3d floor for its normal
|
|
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
normal = -BlockingCeiling.Get3DFloor(i).bottom.Normal;
|
|
break;
|
|
}
|
|
vel += 4.*normal;
|
|
blocked = true;
|
|
}
|
|
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < parent.height+8) )
|
|
{
|
|
if ( diff.x < 0 ) vel.x -= .2;
|
|
else vel.x += .2;
|
|
if ( diff.y < 0 ) vel.y -= .2;
|
|
else vel.y += .2;
|
|
if ( diff.z < 0 ) vel.z -= .2;
|
|
else vel.z += .2;
|
|
blocked = true;
|
|
}
|
|
if ( blocked ) return;
|
|
Vector3 dir = level.Vec3Diff(pos,trail);
|
|
if ( dir.length() > 0 )
|
|
vel += dir.unit()*min(dir.length()*.05,20.);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1
|
|
{
|
|
if ( SWWMLamp(master) && SWWMLamp(master).bActive )
|
|
{
|
|
A_StartSound("lamp/on",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
|
return ResolveState("Active");
|
|
}
|
|
return ResolveState(null);
|
|
}
|
|
Wait;
|
|
Active:
|
|
XZW1 B 1
|
|
{
|
|
A_Moth();
|
|
if ( !SWWMLamp(master) || !SWWMLamp(master).bActive )
|
|
{
|
|
A_StartSound("lamp/off",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
|
return ResolveState("Spawn");
|
|
}
|
|
return ResolveState(null);
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class SWWMLamp : Inventory
|
|
{
|
|
Mixin SWWMOverlapPickupSound;
|
|
Mixin SWWMUseToPickup;
|
|
Mixin SWWMRespawn;
|
|
Mixin SWWMRotatingPickup;
|
|
Mixin SWWMPickupGlow;
|
|
Mixin SWWMUnrealStyleDrop;
|
|
|
|
bool bActive, bActivated;
|
|
TextureID OnIcon;
|
|
Actor thelamp;
|
|
int charge;
|
|
|
|
Property Charge : charge;
|
|
|
|
override Inventory CreateCopy( Actor other )
|
|
{
|
|
// additional lore
|
|
SWWMLoreLibrary.Add(other.player,"MothLamp");
|
|
return Super.CreateCopy(other);
|
|
}
|
|
override bool HandlePickup( Inventory item )
|
|
{
|
|
// add charge
|
|
if ( item.GetClass() == GetClass() )
|
|
{
|
|
if ( (Charge >= Default.Charge) && (Amount+item.Amount > MaxAmount) )
|
|
{
|
|
// sell excess
|
|
int sellprice = abs(Stamina)/2;
|
|
SWWMScoreObj.Spawn(sellprice,level.Vec3Offset(Owner.pos,SWWMUtility.Vec3FromAngles(FRandom[ScoreBits](0,360),FRandom[ScoreBits](-90,90))*8.+(0,0,Owner.Height/2)));
|
|
SWWMCredits.Give(Owner.player,sellprice);
|
|
if ( Owner.player )
|
|
{
|
|
if ( Owner.player == players[consoleplayer] ) Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),GetTag(),sellprice);
|
|
else Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRAREM_FEM":"$SWWM_SELLEXTRAREM"),Owner.player.GetUserName(),GetTag(),sellprice);
|
|
}
|
|
}
|
|
else if ( Charge > 0 )
|
|
{
|
|
int AddCharge = Charge+SWWMLamp(item).Charge;
|
|
Charge = min(Default.Charge,AddCharge);
|
|
// if there's charge to spare, increase amount
|
|
if ( AddCharge > Charge )
|
|
{
|
|
if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
|
|
Amount = min(MaxAmount,Amount+item.Amount);
|
|
Charge = AddCharge-Charge;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
|
|
// new copy, increase and take its charge
|
|
Amount = min(MaxAmount,Amount+item.Amount);
|
|
Charge = SWWMLamp(item).Charge;
|
|
}
|
|
item.bPickupGood = true;
|
|
return true;
|
|
}
|
|
return Super.HandlePickup(item);
|
|
}
|
|
override bool Use( bool pickup )
|
|
{
|
|
if ( pickup && !deathmatch ) return false;
|
|
bActivated = true;
|
|
bActive = !bActive;
|
|
if ( !OnIcon ) OnIcon = TexMan.CheckForTexture("graphics/HUD/Icons/I_Lamp.png");
|
|
Icon = bActive?OnIcon:default.Icon;
|
|
// don't consume on use
|
|
Amount++;
|
|
return true;
|
|
}
|
|
override bool ShouldSpawn()
|
|
{
|
|
if ( deathmatch ) return false;
|
|
return Super.ShouldSpawn();
|
|
}
|
|
override void PreTravelled()
|
|
{
|
|
// remove the lamp
|
|
if ( thelamp ) thelamp.Destroy();
|
|
}
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
if ( !thelamp && bActivated )
|
|
{
|
|
thelamp = Spawn("CompanionLamp",level.Vec3Offset(Owner.pos,SWWMUtility.RotateVector3((20,0,24),Owner.angle)));
|
|
CompanionLamp(thelamp).parent = Owner;
|
|
thelamp.master = self;
|
|
let f = Spawn("SWWMItemFog",thelamp.pos);
|
|
f.A_StartSound("lamp/appear",CHAN_VOICE);
|
|
}
|
|
if ( bActive && !(level.maptime%35) && !isFrozen() ) Charge--;
|
|
if ( Charge <= 0 )
|
|
{
|
|
Amount--;
|
|
if ( Amount <= 0 ) DepleteOrDestroy();
|
|
else Charge = default.Charge;
|
|
}
|
|
}
|
|
override void DetachFromOwner()
|
|
{
|
|
Super.DetachFromOwner();
|
|
if ( thelamp )
|
|
{
|
|
let f = Spawn("SWWMItemFog",thelamp.pos);
|
|
f.A_StartSound("lamp/disappear",CHAN_VOICE);
|
|
thelamp.Destroy();
|
|
}
|
|
Icon = default.Icon;
|
|
bActive = false;
|
|
bActivated = false;
|
|
}
|
|
clearscope bool isBlinking() const
|
|
{
|
|
return ( (Charge < 10) && (level.maptime&8) );
|
|
}
|
|
Default
|
|
{
|
|
Tag "$T_LAMP";
|
|
Inventory.Icon "graphics/HUD/Icons/I_LampOff.png";
|
|
Inventory.PickupSound "misc/p_pkup";
|
|
Inventory.PickupMessage "$I_LAMP";
|
|
Inventory.Amount 1;
|
|
Inventory.MaxAmount 5;
|
|
Inventory.InterHubAmount 5;
|
|
Inventory.PickupFlash "SWWMPurplePickupFlash";
|
|
+INVENTORY.ALWAYSPICKUP;
|
|
+INVENTORY.AUTOACTIVATE;
|
|
+INVENTORY.INVBAR;
|
|
+COUNTITEM;
|
|
+INVENTORY.BIGPOWERUP;
|
|
+FLOATBOB;
|
|
+DONTGIB;
|
|
FloatBobStrength 0.25;
|
|
SWWMLamp.Charge 100;
|
|
Stamina 70000;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|