// gesture effects Class LoveHeartTrail : SWWMStaticSprite { override void SetupSprite() { Super.SetupSprite(); texture = TexMan.CheckForTexture("DOKIA0"); SetRenderStyle(STYLE_Add); Alpha = .1; } override void OnTick() { scale *= .95; Alpha = max(0.,Alpha-.01); if ( Alpha <= 0. ) Destroy(); } } Class LoveHeartSparkle : SWWMStaticSprite { double scalestep, fadestep; override void SetupSprite() { Super.SetupSprite(); texture = TexMan.CheckForTexture("DOKIA0"); Scale = (.03,.03); Alpha = .5; } override void PostBeginPlay() { Scale *= FRandom[ExploS](.75,1.5); scalestep = FRandom[ExploS](.95,.98); fadestep = FRandom[ExploS](.01,.03); double ang = FRandom[ExploS](0,360); double pt = FRandom[ExploS](-90,90); vel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](2,8); } override void OnTick() { Vector3 dir = vel; double magvel = dir.length(); magvel *= .99; if ( magvel > 0. ) { dir /= magvel; dir += .2*SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90)); vel = dir.unit()*magvel; } Scale *= scalestep; Alpha = max(0.,Alpha-fadestep); if ( Alpha <= 0. ) Destroy(); } } Class LoveHeartBurstLight : PaletteLight { Default { Tag "LovePal"; ReactionTime 15; Args 0,0,0,150; } } Class LoveHeart : Actor { Default { Obituary "$O_DOKIDOKI"; DamageType 'Love'; DamageFunction (clamp(special2,5,15)); Radius 4; Height 4; Speed 10; Scale .2; PROJECTILE; +BLOODLESSIMPACT; +FORCEXYBILLBOARD; +SEEKERMISSILE; +FOILINVUL; +PAINLESS; +NODAMAGETHRUST; +NOFRICTION; +SKYEXPLODE; } override int DoSpecialDamage( Actor target, int damage, Name damagetype ) { bEXTREMEDEATH = false; bNOEXTREMEDEATH = false; SWWMStats s = null; if ( Demolitionist(self.target) ) { s = Demolitionist(self.target).mystats; // guaranteed on first smooch if ( !Demolitionist(self.target).lastkiss || !Random[DemoLines](0,2) ) { if ( gametic > Demolitionist(self.target).lastkiss ) SWWMHandler.AddOneliner("smooch",2,20); Demolitionist(self.target).lastkiss = gametic+140; } } if ( s ) s.smooch++; let raging = RagekitPower(self.target.FindInventory('RagekitPower')); if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) target.bFRIENDLY = false; if ( target.IsFriend(self.target) || SWWMUtility.IdentifyingDog(target) ) { int healamt = clamp(special2,5,15); if ( raging ) { healamt *= 8; raging.DoHitFX(); } if ( target.GiveBody(healamt,target.GetSpawnHealth()) ) SWWMHandler.DoFlash(target,Color(32,224,128,255),10); if ( SWWMUtility.IdentifyingDog(target) ) { // befriend good doggo if ( target.bCOUNTKILL ) { target.bCOUNTKILL = false; level.total_monsters--; } if ( !target.bFRIENDLY && s ) s.befriend++; target.bFRIENDLY = true; if ( deathmatch ) target.SetFriendPlayer(self.target.player); } return 0; } Vector3 dirto = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.Height/2)).unit(); SWWMUtility.DoKnockback(target,dirto,1500.*damage); let bread = target.FindState('Pain'); if ( bread ) target.SetState(bread); if ( raging ) { damage *= 8; raging.DoHitFX(); } if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) { damage = int.max; bEXTREMEDEATH = true; } else if ( target is 'SWWMHangingKeen' ) damage = max(target.Health,damage); // rescued by love :3 return damage; } override int SpecialMissileHit( Actor victim ) { if ( !victim.bSHOOTABLE && (victim != tracer) ) return MHIT_PASS; if ( tracer && (victim != tracer) ) return MHIT_PASS; return MHIT_DEFAULT; } void A_HeartTick() { special1++; if ( !(special1%3) && (special2 > 0) ) special2--; A_SetScale(.2+.02*sin(special1*.25*GameTicRate)); double magvel = vel.length(); if ( magvel > 0 ) { Vector3 dir = vel/magvel; vel = dir*min(30,magvel*1.1); } double steppy = vel.length()/4.; for ( int i=2; i<6; i++ ) { Vector3 dir2 = vel.unit(); let t = SWWMStaticSprite.SpawnAt('LoveHeartTrail',level.Vec3Offset(pos,-dir2*steppy*i)); t.scale = scale; } int numpt = Random[ExploS](1,3); for ( int i=0; i cl ) LinePart = 0; else LinePart = 1; } else { LinePart = 1; // always middle LineSide = 0; } if ( Blocking3DFloor ) { Sector sec = LineSide?BlockingLine.frontsector:BlockingLine.backsector; for ( int i=0; i con; con.Clear(); con.Push(HitLine); Sector s = LineSide?HitLine.backsector:HitLine.frontsector; int found = 0; do { found = 0; foreach ( l:s.Lines ) { if ( !l.sidedef[LineSide] || (l.sidedef[LineSide].GetTexture(LinePart) != HitTexture) ) continue; if ( con.Find(l) < con.Size() ) continue; bool notmatched = true; foreach ( c:con ) { if ( (l.v1 != c.v1) && (l.v2 != c.v2) && (l.v1 != c.v2) && (l.v2 != c.v1) ) continue; notmatched = false; break; } if ( notmatched ) continue; con.Push(l); found++; } } while ( found > 0 ); foreach ( c:con ) c.sidedef[LineSide].SetTexture(LinePart,replacewith); } } else if ( HitSector && HitCeiling ) { if ( Hit3DFloor ) { if ( Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) Hit3DFloor.model.SetTexture(1,replacewith); else Hit3DFloor.model.SetTexture(0,replacewith); } else { // find connected sectors with the same ceiling texture (THIS IS VERY UGLY CODE) Array con; con.Clear(); con.Push(HitSector); int found; do { found = 0; foreach ( s:con ) { foreach ( l:s.Lines ) { // only check two-sided if ( !l.sidedef[1] ) continue; // don't check if there's a height difference if ( (l.frontsector.ceilingplane.ZAtPoint(l.v1.p) != l.backsector.ceilingplane.ZAtPoint(l.v1.p)) || (l.frontsector.ceilingplane.ZAtPoint(l.v2.p) != l.backsector.ceilingplane.ZAtPoint(l.v2.p)) ) continue; if ( (l.frontsector.GetTexture(1) == HitTexture) && (con.Find(l.frontsector) >= con.Size()) ) { found++; con.Push(l.frontsector); } if ( (l.backsector.GetTexture(1) == HitTexture) && (con.Find(l.backsector) >= con.Size()) ) { found++; con.Push(l.backsector); } } } } while ( found > 0 ); foreach ( s:con ) s.SetTexture(1,replacewith); } } else if ( HitSector && !HitCeiling ) { if ( Hit3DFloor ) { if ( Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) Hit3DFloor.model.SetTexture(0,replacewith); else Hit3DFloor.model.SetTexture(1,replacewith); } else { // find connected sectors with the same floor texture (THIS IS VERY UGLY CODE) Array con; con.Clear(); con.Push(HitSector); int found; do { found = 0; foreach ( s:con ) { foreach ( l:s.Lines ) { // only check two-sided if ( !l.sidedef[1] ) continue; // don't check if there's a height difference if ( (l.frontsector.floorplane.ZAtPoint(l.v1.p) != l.backsector.floorplane.ZAtPoint(l.v1.p)) || (l.frontsector.floorplane.ZAtPoint(l.v2.p) != l.backsector.floorplane.ZAtPoint(l.v2.p)) ) continue; if ( (l.frontsector.GetTexture(0) == HitTexture) && (con.Find(l.frontsector) >= con.Size()) ) { found++; con.Push(l.frontsector); } if ( (l.backsector.GetTexture(0) == HitTexture) && (con.Find(l.backsector) >= con.Size()) ) { found++; con.Push(l.backsector); } } } } while ( found > 0 ); foreach ( s:con ) s.SetTexture(0,replacewith); } } } void A_HeartBurst() { CheckDefaceTexture(); // use line if ( BlockingLine ) { int s = Level.PointOnLineSide(pos.xy,BlockingLine); int locknum = SWWMUtility.GetLineLock(BlockingLine); if ( !locknum || (target && target.CheckKeys(locknum,false,true)) ) BlockingLine.RemoteActivate(target,s,SPAC_Use,pos); } if ( swwm_omnibust ) { int dmg = GetMissileDamage(0,0); let raging = RagekitPower(self.target.FindInventory('RagekitPower')); if ( raging ) dmg *= 8; if ( BusterWall.ProjectileBust(self,dmg,SWWMUtility.Vec3FromAngles(angle,pitch)) ) raging.DoHitFX(); } A_SetRenderStyle(1.,STYLE_Add); // RemoveLight causes heavy performance issues, just overwrite with a "blank light" A_AttachLight('LOVELIGHT',DynamicLight.PointLight,0,0,0); //A_RemoveLight('LOVELIGHT'); CheckSplash(40); A_QuakeEx(1.5,1.5,1.5,8,0,300,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D); A_SprayDecal("HeartyGlow",64); A_StartSound("bestsound",CHAN_VOICE); Spawn('LoveHeartBurstLight',pos); int numpt = Random[ExploS](10,15); for ( int i=0; i PlayerPawn(user.player.mo).UseRange ) return false; if ( user is 'Demolitionist' ) { patter = user; let g = SWWMGesture.SetGesture(Demolitionist(patter),GS_Headpat); if ( !g ) return false; // can't headpat at the moment patting = true; g.pats = self; oldtargettics = target.tics; target.tics = -1; patter.player.cheats |= CF_TOTALLYFROZEN; Demolitionist(patter).scriptedinvul = true; target.bDORMANT = true; if ( SWWMUtility.IdentifyingDog(target) ) SWWMUtility.MarkAchievement("mbf",user.player); else if ( SWWMUtility.IdentifyingCaco(target) ) SWWMHandler.AddOneliner("petcaco",2,20); return true; } return false; } }