flak_m/zscript/pulsegun.zsc
Marisa Kirisame fb96c7523e Portal awareness adjustments to various vector operations.
Got rid of the deprecated Matrix4.GetAxes method. Next step is to get rid of more stuff by migrating to libeye.
Mirrored Translocator model so it shows the actually detailed side. At some point in UT's development it got flipped around for some reason.
Weapon code cleanup (most noticeable on states).
Backported scope shader from Doomreal.
Added optional "dummied out" Sniper zoom sounds from a dubious source.
2019-09-28 20:06:51 +02:00

797 lines
17 KiB
Text

Class PulseAmmo : Ammo
{
Default
{
Tag "$T_PULSEAMMO";
Inventory.PickupMessage "$I_PULSEAMMO";
Inventory.Amount 25;
Inventory.MaxAmount 200;
Ammo.BackpackAmount 40;
Ammo.BackpackMaxAmount 400;
Ammo.DropAmount 10;
}
States
{
Spawn:
PAMO A -1;
Stop;
}
}
Class PulseSpark : Actor
{
Default
{
RenderStyle "Add";
Radius 2;
Height 2;
+NOBLOCKMAP;
+NOGRAVITY;
+MISSILE;
+FORCEXYBILLBOARD;
+THRUACTORS;
+ROLLSPRITE;
+ROLLCENTER;
+NOTELEPORT;
+DONTSPLASH;
+CANBOUNCEWATER;
-BOUNCEAUTOOFF;
BounceType "Doom";
BounceFactor 1.0;
WallBounceFactor 1.0;
Scale 0.03;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !bAMBUSH )
{
roll = FRandom[Pulse](0,360);
let s = Spawn(GetClass(),pos);
s.bAMBUSH = true;
s.vel = vel;
s.scale = scale;
s.roll = roll;
}
}
States
{
Spawn:
PSPK A 1 Bright
{
A_FadeOut(FRandom[Pulse](0.0,0.15));
vel *= 0.96;
}
Wait;
}
}
Class ViewPulseSpark : PulseSpark
{
Vector3 ofs, vvel;
override void PostBeginPlay()
{
Actor.PostBeginPlay();
scale *= FRandom[Puff](0.4,0.9);
alpha *= FRandom[Puff](0.5,2.0);
roll = FRandom[Pulse](0,360);
}
override void Tick()
{
Actor.Tick();
if ( !target || !target.player )
{
Destroy();
return;
}
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
SetOrigin(origin,true);
bInvisible = (players[consoleplayer].camera != target);
if ( isFrozen() ) return;
ofs += vvel;
vvel *= 0.9;
scale *= 0.8;
if ( scale.x <= 0.01 ) Destroy();
}
States
{
Spawn:
PSPK A 1 Bright A_FadeOut(FRandom[Pulse](0.0,0.15));
Wait;
}
}
Class PulseBallLight : DynamicLight
{
double pulseofs;
Default
{
DynamicLight.Type "Point";
Args 64,255,0,10;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
pulseofs = FRandom[Pulse](0,360);
}
override void Tick()
{
Super.Tick();
if ( !target || target.InStateSequence(target.CurState,target.ResolveState("Death")) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
args[LIGHT_INTENSITY] = 10+int(sin(gametic*80+pulseofs)*5);
}
}
Class PulseExplLight : PaletteLight
{
Default
{
Tag "ExplGreen";
ReactionTime 20;
Args 0,0,0,30;
}
}
Class PulseBall : Actor
{
Default
{
Obituary "$O_PULSEGUN";
DamageType 'Pulsed';
RenderStyle "Add";
DamageFunction 20;
PROJECTILE;
+EXPLODEONWATER;
+SKYEXPLODE;
+FORCEXYBILLBOARD;
Scale 0.19;
Speed 22;
Radius 2;
Height 2;
}
override void PostBeginPlay()
{
A_PlaySound("pulse/fly",CHAN_BODY,0.8,true,8.0);
let l = Spawn("PulseBallLight",pos);
l.target = self;
}
action void A_BallExp()
{
A_SetScale(0.45);
A_PlaySound("pulse/hit",CHAN_BODY);
A_SprayDecal("BoltScorch");
Spawn("PulseExplLight",pos);
int numpt = Random[Pulse](20,40);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pulse](-1,1),FRandom[Pulse](-1,1),FRandom[Pulse](-1,1)).unit()*FRandom[Pulse](2,4);
let s = Spawn("PulseSpark",pos);
s.vel = pvel;
}
}
action void A_Trail()
{
if ( Random[Pulse](0,4) ) return;
int numpt = Random[Pulse](2,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pulse](-1,1),FRandom[Pulse](-1,1),FRandom[Pulse](-1,1)).unit()*FRandom[Pulse](2,4);
let s = Spawn("PulseSpark",pos);
s.vel = pvel;
}
}
States
{
Spawn:
PBAL ABCDE 1 Bright A_Trail();
Loop;
Death:
TNT1 A 0 A_BallExp();
PBAL ABCDE 3 Bright;
Stop;
}
}
Class PulseBoltLight : DynamicLight
{
bool lived;
Default
{
DynamicLight.Type "Point";
Args 32,128,0,80;
}
override void Tick()
{
Super.Tick();
if ( !target )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
args[LIGHT_INTENSITY] = Random[Pulse](60,100);
}
}
Class PulseBoltTracer : LineTracer
{
Actor ignore;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignore ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class PulseBoltCap : Actor
{
override void OnDestroy()
{
Super.OnDestroy();
if ( tracer ) tracer.Destroy();
}
override void Tick()
{
Super.Tick();
if ( bAMBUSH ) return;
if ( !tracer )
{
tracer = Spawn(GetClass(),pos);
tracer.bAMBUSH = true;
}
tracer.SetOrigin(pos,true);
}
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.3;
}
States
{
Spawn:
PCAP ABCD 1 Bright;
Loop;
}
}
Class PulseBoltHit : Actor
{
override void OnDestroy()
{
Super.OnDestroy();
if ( tracer ) tracer.Destroy();
}
override void Tick()
{
Super.Tick();
if ( bAMBUSH ) return;
if ( !tracer )
{
tracer = Spawn(GetClass(),pos);
tracer.bAMBUSH = true;
}
}
Default
{
RenderStyle "Add";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
Scale 0.3;
}
States
{
Spawn:
PHIT ABCD 1 Bright;
Loop;
}
}
Class PulseBolt : Actor
{
const beamsize = 81.0;
PulseBoltTracer t;
double accdamage;
int lasthit;
Actor damagedactor;
Actor weffect;
int position;
PulseBolt next;
override void OnDestroy()
{
Super.OnDestroy();
if ( next ) next.Destroy();
if ( weffect ) weffect.Destroy();
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
t = new("PulseBoltTracer");
t.ignore = target;
let l = Spawn("PulseBoltLight",pos);
l.target = self;
}
void CheckBeam( Vector3 x )
{
t.Trace(pos,cursector,x,beamsize,0);
if ( t.Results.HitType != TRACE_HitNone )
{
if ( t.Results.HitType == TRACE_HitActor )
{
if ( !damagedactor )
{
accdamage = min(0.5*(level.time-lasthit),0.1);
t.Results.HitActor.DamageMobj(self,target,int(72*accdamage),'zapped',DMG_USEANGLE,atan2(x.y,x.x));
accdamage = 0;
}
else if ( t.Results.HitActor != damagedactor )
{
t.Results.HitActor.DamageMobj(self,target,int(72*accdamage),'zapped',DMG_USEANGLE,atan2(x.y,x.x));
accdamage = 0;
}
lasthit = level.time;
damagedactor = t.Results.HitActor;
accdamage += 1./TICRATE;
if ( accdamage > 0.17 )
{
t.Results.HitActor.DamageMobj(self,target,int(72*accdamage),'zapped',DMG_USEANGLE,atan2(x.y,x.x));
accdamage = 0;
}
}
Vector3 norm = -x;
if ( t.Results.HitType == TRACE_HitWall )
{
norm = (t.Results.HitLine.delta.y,-t.Results.HitLine.delta.x,0).unit();
if ( t.Results.Side ) norm *= -1;
t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
else if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.ffloor ) norm = -t.Results.ffloor.top.Normal;
else norm = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.ffloor ) norm = -t.Results.ffloor.bottom.Normal;
else norm = t.Results.HitSector.ceilingplane.Normal;
}
int numpt = Random[Pulse](10,20)*!Random[Pulse](0,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (norm+(FRandom[Pulse](-.6,.6),FRandom[Pulse](-.6,.6),FRandom[Pulse](-.6,.6))).unit()*FRandom[Pulse](2,4);
let s = Spawn("PulseSpark",t.Results.HitPos-t.Results.HitVector*4);
s.vel = pvel;
}
if ( weffect && (weffect is 'PulseBoltCap') )
{
weffect.Destroy();
weffect = null;
}
if ( !weffect ) weffect = Spawn("PulseBoltHit",t.Results.HitPos-t.Results.HitVector*4);
else
{
weffect.SetOrigin(t.Results.HitPos-t.Results.HitVector*4,true);
if ( weffect.tracer ) weffect.tracer.SetOrigin(t.Results.HitPos-t.Results.HitVector*4,true);
}
A_SprayDecal("BoltScorch",beamsize+8);
if ( next )
{
accdamage += next.accdamage;
next.Destroy();
next = null;
}
return;
}
else if ( damagedactor )
{
damagedactor.DamageMobj(self,target,int(72*accdamage),'zapped');
accdamage = 0;
damagedactor = null;
}
if ( position >= 9 )
{
int numpt = Random[Pulse](5,10)*!Random[Pulse](0,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (t.Results.HitVector+(FRandom[Pulse](-.6,.6),FRandom[Pulse](-.6,.6),FRandom[Pulse](-.6,.6))).unit()*FRandom[Pulse](2,4);
let s = Spawn("PulseSpark",t.Results.HitPos);
s.vel = pvel;
}
if ( weffect && (weffect is 'PulseBoltHit') )
{
weffect.Destroy();
weffect = null;
}
if ( !weffect ) weffect = Spawn("PulseBoltCap",t.Results.HitPos);
else
{
weffect.SetOrigin(t.Results.HitPos,true);
if ( weffect.tracer ) weffect.tracer.SetOrigin(t.Results.HitPos,true);
}
}
else
{
if ( weffect )
{
weffect.Destroy();
weffect = null;
}
if ( !next )
{
next = PulseBolt(Spawn("PulseBolt",t.Results.HitPos));
next.angle = angle;
next.pitch = pitch;
next.target = target;
next.position = position+1;
}
else next.UpdateBeam(self,x);
}
}
void UpdateBeam( PulseBolt parent, Vector3 x )
{
bRELATIVETOFLOOR = parent.bRELATIVETOFLOOR;
frame = parent.frame;
SetOrigin(level.Vec3Offset(parent.pos,x*beamsize),true);
A_SetAngle(parent.angle);
A_SetPitch(parent.pitch);
CheckBeam(x);
}
Default
{
RenderStyle "Add";
Obituary "$O_PULSEGUN";
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+INTERPOLATEANGLES;
+NOTELEPORT;
}
States
{
Spawn:
PBLT # -1 Bright;
Stop;
Dummy:
PBLT ABCDE -1;
Stop;
}
}
Class StarterBolt : PulseBolt
{
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
if ( !target )
{
Destroy();
return;
}
Vector3 x, y, z, origin;
bRELATIVETOFLOOR = (target.pos.z <= target.floorz); // hack, but kinda works
if ( target.player )
{
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),8*x+4.1*y-2.7*z);
}
else origin = target.Vec3Offset(0,0,target.missileheight);
SetOrigin(origin,true);
A_SetAngle(target.angle);
A_SetPitch(target.BulletSlope());
frame++;
if ( frame > 4 ) frame = 0;
CheckBeam((cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)));
}
}
Class PulseGun : UTWeapon
{
int clipcount;
double sangle;
Actor beam;
transient ui Font usmf;
Property ClipCount : clipcount;
action void A_DrainAmmo()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( (weap.Ammo1.Amount <= 0) || !(player.cmd.buttons&BT_ALTATTACK) )
{
player.SetPSprite(PSP_WEAPON,ResolveState("AltRelease"));
return;
}
if ( invoker.special1 > 0 )
{
invoker.special1--;
return;
}
invoker.special1 = 9;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
invoker.clipcount--;
if ( !flak_pulsereload && (invoker.clipcount <= 0) ) invoker.clipcount = min(invoker.default.clipcount,weap.Ammo1.Amount);
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
UTMainHandler.DoSwing(self,(FRandom[Pulse](-1,1),FRandom[Pulse](-1,1)),0.1,-0.02,3,SWING_Spring,0,2);
A_AlertMonsters();
Vector3 x, y, z;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+4.1*y-2.7*z);
for ( int i=0; i<4; i++ )
{
let s = Spawn("UTViewSmoke",origin);
UTViewSmoke(s).ofs = (10,4.1,-2.7);
s.scale *= 1.8;
s.target = self;
s.SetShade("206010");
s.A_SetRenderStyle(0.4,STYLE_AddShaded);
UTViewSmoke(s).vvel += (FRandom[Pulse](0.2,0.6),FRandom[Pulse](-0.2,0.2),FRandom[Pulse](-0.2,0.2));
}
int numpt = Random[Pulse](8,16);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("ViewPulseSpark",origin);
ViewPulseSpark(s).ofs = (10,4.1,-2.7);
s.target = self;
ViewPulseSpark(s).vvel += (FRandom[Pulse](0.2,0.8),FRandom[Pulse](-0.5,0.5),FRandom[Pulse](-0.5,0.5));
}
}
action void A_PulseRefire()
{
Weapon weap = Weapon(invoker);
if ( !weap || !player ) return;
if ( (invoker.clipcount <= 0) || (weap.Ammo1.Amount <= 0) )
return;
if ( player.cmd.buttons&BT_ALTATTACK )
{
weap.bAltFire = true;
player.SetPSprite(PSP_WEAPON,ResolveState("AltHold"));
}
else if ( player.cmd.buttons&BT_ATTACK )
{
weap.bAltFire = false;
player.SetPSprite(PSP_WEAPON,ResolveState("Fire"));
}
}
action void A_PulseFire()
{
Weapon weap = Weapon(invoker);
if ( !weap ) return;
if ( (weap.Ammo1.Amount <= 0) || !(player.cmd.buttons&BT_ATTACK) )
{
player.SetPSprite(PSP_WEAPON,ResolveState("Release"));
return;
}
if ( invoker.special1 > 0 )
{
invoker.special1--;
return;
}
invoker.special1 = 5;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
invoker.clipcount--;
if ( !flak_pulsereload && (invoker.clipcount <=0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(50,weap.Ammo1.Amount):50;
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
UTMainHandler.DoSwing(self,(FRandom[Pulse](-1,-1),FRandom[Pulse](-1,1)),0.3,-0.1,2,SWING_Spring,0,3);
A_AlertMonsters();
A_QuakeEx(1,1,1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05);
A_Overlay(-2,"MuzzleFlash");
A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
A_OverlayRenderstyle(-2,STYLE_Add);
Vector3 x, y, z;
double a;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-1.8*z);
origin = level.Vec3Offset(origin,y*cos(invoker.sangle)*2+z*sin(invoker.sangle)*2);
invoker.sangle += 100;
Actor p = Spawn("PulseBall",origin);
p.angle = angle;
p.pitch = BulletSlope();
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
p.target = self;
for ( int i=0; i<8; i++ )
{
let s = Spawn("UTViewSmoke",origin);
UTViewSmoke(s).ofs = (10,3,-1.8);
s.scale *= 1.8;
s.target = self;
s.SetShade("206010");
s.A_SetRenderStyle(0.4,STYLE_AddShaded);
UTViewSmoke(s).vvel += (FRandom[Pulse](0.4,0.8),FRandom[Pulse](-0.2,0.2),FRandom[Pulse](-0.2,0.2));
}
int numpt = Random[Pulse](8,16);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("ViewPulseSpark",origin);
ViewPulseSpark(s).ofs = (10,3,-1.8);
s.target = self;
ViewPulseSpark(s).vvel += (FRandom[Pulse](0.4,1.6),FRandom[Pulse](-1.2,1.2),FRandom[Pulse](-1.2,1.2));
}
}
action void A_StartBeam()
{
invoker.special1 = 0;
A_PlaySound("pulse/bolt",CHAN_WEAPON,1.0,true);
Vector3 x, y, z, origin;
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+4.1*y-2.7*z);
invoker.beam = Spawn("StarterBolt",origin);
invoker.beam.angle = angle;
invoker.beam.pitch = BulletSlope();
invoker.beam.target = self;
}
action void A_StopBeam()
{
A_StopSound(CHAN_WEAPON);
if ( invoker.beam ) invoker.beam.Destroy();
}
override void OwnerDied()
{
Super.OwnerDied();
if ( beam ) beam.Destroy();
Owner.A_StopSound(CHAN_WEAPON);
}
override void DetachFromOwner()
{
if ( beam ) beam.Destroy();
Owner.A_StopSound(CHAN_WEAPON);
Super.DetachFromOwner();
}
override void OnDestroy()
{
Super.OnDestroy();
if ( beam ) beam.Destroy();
}
Default
{
Tag "$T_PULSEGUN";
Inventory.PickupMessage "$I_PULSEGUN";
Weapon.UpSound "pulse/select";
Weapon.SlotNumber 5;
Weapon.SelectionOrder 5;
Weapon.AmmoType "PulseAmmo";
Weapon.AmmoUse 1;
Weapon.AmmoType2 "PulseAmmo";
Weapon.AmmoUse2 1;
Weapon.AmmoGive 60;
PulseGun.ClipCount 50;
UTWeapon.DropAmmo 15;
}
States
{
Spawn:
PGNP A -1;
Stop;
PGNP B -1;
Stop;
Ready:
PGNS ABCDEFGHIJKLMNOPQRSTUVW 1 A_WeaponReady(WRF_NOFIRE);
Idle:
PGNI A 1
{
A_CheckReload();
if ( flak_pulsereload )
{
if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) return ResolveState("Reload");
A_WeaponReady(WRF_ALLOWRELOAD);
}
else A_WeaponReady();
return A_JumpIf(!Random[Pulse](0,300),1);
}
Wait;
PGNI ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
{
A_CheckReload();
if ( flak_pulsereload )
{
if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) return ResolveState("Reload");
A_WeaponReady(WRF_ALLOWRELOAD);
}
else A_WeaponReady();
return ResolveState(null);
}
Goto Idle;
Fire:
PGNI A 0
{
invoker.special1 = 0;
A_PlaySound("pulse/fire",CHAN_WEAPON,1.0,true);
}
PGNF ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_PulseFire();
PGF2 ABCDEFGHIJKLMN 1 A_PulseFire();
Goto Fire+1;
Release:
PGNC A 0 A_PlaySound("pulse/down",CHAN_WEAPON);
PGNC ABCDEFGHIJKLMNOPQRSTUVWXY 1 A_PulseRefire();
PGNC Y 0;
Goto Idle;
AltFire:
PGBS ABCDE 1;
Goto AltHold;
AltHold:
PGBL A 0 A_StartBeam();
PGBL ABCDEFGHIJ 1 A_DrainAmmo();
Goto AltHold+1;
AltRelease:
PGBE A 0 A_StopBeam();
PGBE ABCDE 1 A_PulseRefire();
Goto Idle;
Reload:
PGNI A 1;
PGNI A 0 A_JumpIf(invoker.clipcount >= Min(invoker.default.clipcount,invoker.Ammo1.Amount),"Idle");
PGNR A 1
{
A_PlaySound("pulse/reload",CHAN_WEAPON);
if ( self is 'UTPlayer' )
UTPlayer(self).PlayReloading();
}
PGNR BCDEFGHIJKLMNO 1;
PGNR P 1
{
invoker.clipcount = 0;
}
PGNR QRSTUVWXYZ 1;
PGR2 ABCD 1;
PGR2 E 1
{
invoker.clipcount = Min(invoker.default.clipcount,invoker.Ammo1.Amount);
}
PGR2 FGHIJKLMNOPQRSTUVWX 1;
Goto Idle;
Deselect:
PGNS W 1 A_StopSound(CHAN_WEAPON);
PGNS VTSQPNMKJHGEDBA 1;
PGNS A 1 A_Lower(int.max);
Wait;
Select:
PGNS A 1 A_Raise(int.max);
Wait;
MuzzleFlash:
PMUZ A 2 Bright;
Stop;
}
}