// stuff related to gestures Class HHitList { Actor a; Vector3 dir; } enum EGestureSlot { // special use GS_Headpat = -50, GS_Grenade, GS_EmptyMelee, // no gesture GS_Null = 0, // general gestures GS_Wave = 1, GS_ThumbsUp, GS_Victory, GS_BlowKiss }; // First person gestures Class SWWMGesture : SWWMWeapon { Weapon formerweapon, lastformerweapon; int whichgesture, nextgesture; bool deaded, queued; Class whichweapon; Array > sweapon; int gonect; HeadpatTracker pats; // for headpat gesture, our current tracker // these should prevent autoswitch when out of ammo override bool ReportHUDAmmo() { return false; } override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) { return false; } override bool Use( bool pickup ) { return false; } override bool Used( Actor user ) { return false; } override void Touch( Actor toucher ) { } override void DoEffect() { Super.DoEffect(); // show nametag if former weapon changes if ( (lastformerweapon != formerweapon) && Owner && (Owner.player == players[consoleplayer]) && (displaynametags&2) ) EventHandler.SendInterfaceEvent(consoleplayer,"swwmnametag."..formerweapon.GetTag()); lastformerweapon = formerweapon; // if we're supposed to headpat but we're NOT the pending/ready weapon, something's very wrong here // try to fix that by force if ( pats && Owner && Owner.player && (Owner.player.ReadyWeapon != self) && (Owner.player.PendingWeapon != self) ) SetGesture(Owner.player.mo,GS_Headpat,true); if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) return; let psp = Owner.player.FindPSprite(PSP_WEAPON); if ( !psp ) return; if ( (Owner.Health <= 0) && (psp.CurState != ResolveState('Deselect')) ) Owner.player.SetPSprite(PSP_WEAPON,ResolveState('Deselect')); // check if we're waving at a dying enemy if ( (psp.frame >= 3) && (psp.frame <= 12) && (psp.sprite == GetSpriteIndex('XZW1')) ) CheckWave(); } private void CheckWave() { bool breakout = false; foreach ( s:level.Sectors ) { for ( Actor a=s.thinglist; a; a=a.snext ) { if ( (a.Health > 0) || (a.tics == -1) || !(a.bISMONSTER || a.player) || !a.IsHostile(Owner) ) continue; // check if we can see it if ( !SWWMUtility.InPlayerFOV(Owner.player,a) ) continue; // someone's dying SWWMUtility.MarkAchievement("wave",Owner.player); breakout = true; break; } if ( breakout ) break; } } static SWWMGesture SetGesture( PlayerPawn mo, int which, bool force = false ) { if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo if ( mo.Health <= 0 ) return null; // dead if ( !force && (mo.player.cheats&CF_TOTALLYFROZEN) ) return null; // frozen today (unless forced) SWWMGesture w = SWWMGesture(mo.FindInventory('SWWMGesture')); if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) { // already gesturing // just queue another one if ( which <= 0 ) return null; // these gestures can't be queued else { w.nextgesture = which; w.queued = true; } return null; } if ( !w ) { w = SWWMGesture(Spawn('SWWMGesture')); w.AttachToOwner(mo); } if ( mo.player.PendingWeapon != WP_NOCHANGE ) w.formerweapon = mo.player.PendingWeapon; else w.formerweapon = mo.player.ReadyWeapon; w.lastformerweapon = w.formerweapon; w.whichweapon = null; w.whichgesture = which; mo.player.PendingWeapon = w; return w; } // "special" gestures are run by switching to another "weapon" static SWWMGesture SetSpecialGesture( PlayerPawn mo, Class a, bool forced = false ) { if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo if ( mo.Health <= 0 ) return null; // dead if ( !forced && (mo.player.cheats&CF_TOTALLYFROZEN) ) return null; // frozen today (unless forced) if ( !a ) return null; SWWMGesture w = SWWMGesture(mo.FindInventory('SWWMGesture')); if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) { // already gesturing // queue if unique foreach ( sw:w.sweapon ) { if ( sw != a ) continue; return null; } w.sweapon.Push(a); return null; } if ( !w ) { w = SWWMGesture(Spawn('SWWMGesture')); w.AttachToOwner(mo); } if ( mo.player.PendingWeapon != WP_NOCHANGE ) w.formerweapon = mo.player.PendingWeapon; else w.formerweapon = mo.player.ReadyWeapon; w.lastformerweapon = w.formerweapon; w.whichgesture = GS_Null; w.whichweapon = a; mo.player.PendingWeapon = w; return w; } action void A_CallPlayerGesture( statelabel st, statelabel cst ) { if ( invoker.Owner.Health <= 0 ) return; if ( (player.crouchdir == -1) && invoker.Owner.FindState(cst) ) invoker.Owner.SetStateLabel(cst); else if ( invoker.Owner.FindState(st) ) invoker.Owner.SetStateLabel(st); } action void A_FinishGesture() { if ( invoker.sweapon.Size() > 0 ) { invoker.whichgesture = GS_Null; invoker.whichweapon = invoker.sweapon[0]; // push back invoker.sweapon.Delete(0); player.SetPSprite(PSP_WEAPON,ResolveState('Ready')); return; } if ( invoker.queued ) { invoker.whichweapon = null; invoker.whichgesture = invoker.nextgesture; invoker.queued = false; player.SetPSprite(PSP_WEAPON,ResolveState('Ready')); return; } if ( invoker.formerweapon ) player.PendingWeapon = invoker.formerweapon; else { player.PendingWeapon = player.mo.BestWeapon(null); if ( (player.PendingWeapon is 'SWWMGesture') || (player.PendingWeapon is 'SWWMItemGesture') ) player.PendingWeapon = null; } player.SetPSprite(PSP_WEAPON,ResolveState('Deselect')); } action void A_Headpat() { A_StartSound("demolitionist/petting",CHAN_WEAPON,CHANF_OVERLAP,.4); let pt = invoker.pats; if ( !pt ) return; int numpt = Random[ExploS](6,9); Vector3 dir = SWWMUtility.Vec3FromAngles(angle,pitch); Vector3 patpos = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz-4),dir*30.); for ( int i=0; i 1 ) A_StartSound(String.Format("voice/%s/kiss%d",myvoice,idx),CHAN_DEMOVOICEAUX,CHANF_OVERLAP); if ( loudlv > 2 ) A_StartSound(String.Format("voice/%s/kiss%d",myvoice,idx),CHAN_DEMOVOICEAUX2,CHANF_OVERLAP); if ( loudlv > 3 ) A_StartSound(String.Format("voice/%s/kiss%d",myvoice,idx),CHAN_DEMOVOICEAUX3,CHANF_OVERLAP); } } action void A_BlowKiss() { let weap = Weapon(invoker); if ( !weap ) return; Vector3 dir; Vector3 origin = SWWMUtility.GetFireOffset(self,10,0,-1); let [x2, y2, z2] = SWWMUtility.GetPlayerAxesAutoAimed(self); let p = Spawn('LoveHeart',origin); p.target = self; p.angle = atan2(x2.y,x2.x); p.pitch = asin(-x2.z); p.vel = x2*p.speed; // try to catch target in cone of vision Array hits; hits.Clear(); int rings = 1; FLineTraceData d; for ( double i=0; i<.2; i+=.02 ) { for ( int j=0; j<360; j+=(360/rings) ) { dir = SWWMUtility.ConeSpread(x2,y2,z2,j,i); LineTrace(atan2(dir.y,dir.x),8000.,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); if ( d.HitType != TRACE_HitActor ) continue; bool addme = true; foreach ( hit:hits ) { if ( hit.a != d.HitActor ) continue; if ( (hit.dir dot x2) < (dir dot x2) ) hit.dir = dir; // closer to centerpoint addme = false; break; } if ( !addme ) continue; let nhit = new('HHitList'); nhit.a = d.HitActor; nhit.dir = dir; hits.Push(nhit); } rings += 5; } int closest = -1; double closestdot = -1; for ( int i=0; i 0 ) { gest.whichgesture = GS_Null; gest.whichweapon = gest.sweapon[0]; // push back gest.sweapon.Delete(0); // go back to the main gesture player.ReadyWeapon = gest; player.SetPSPrite(PSP_WEAPON,gest.ResolveState('Ready')); invoker.gotused = true; return; } if ( gest.queued ) { gest.whichweapon = null; gest.whichgesture = gest.nextgesture; gest.queued = false; // go back to the main gesture player.ReadyWeapon = gest; player.SetPSPrite(PSP_WEAPON,gest.ResolveState('Ready')); invoker.gotused = true; return; } // switch to old weapon player.ReadyWeapon = gest; if ( gest.formerweapon ) player.PendingWeapon = gest.formerweapon; else { player.PendingWeapon = player.mo.BestWeapon(null); if ( (player.PendingWeapon is 'SWWMGesture') || (player.PendingWeapon is 'SWWMItemGesture') ) player.PendingWeapon = null; } player.SetPSPrite(PSP_WEAPON,gest.ResolveState('SoftDeselect')); invoker.gotused = true; } override void SetTags( String colname ) { // gestures use model 0 A_ChangeModel("",0,"","",0,"models","DemoTags"..colname..".png",CMDL_USESURFACESKIN,-1); } Default { +WEAPON.CHEATNOTWEAPON; +WEAPON.NO_AUTO_SWITCH; +WEAPON.WIMPY_WEAPON; +WEAPON.NOAUTOSWITCHTO; +SWWMWEAPON.HIDEINMENU; +INVENTORY.UNDROPPABLE; +INVENTORY.UNTOSSABLE; +INVENTORY.UNCLEARABLE; Weapon.SelectionOrder int.max; SWWMWeapon.BobFactor 0., 0.; } States { Select: XZW1 A 1 A_FullRaise(); Goto Ready; Ready: Fire: XZW1 A 1 A_Log("\cgUnimplemented pickup sequence for "..invoker.GetClassName().."\c-"); XZW1 A -1 A_FinishGesture(); Stop; AltFire: XZW1 A 1 A_Log("\cgUnimplemented use sequence for "..invoker.GetClassName().."\c-"); XZW1 A -1 A_FinishGesture(); Stop; Deselect: XZW1 A -1 A_FullLower(); Stop; } }