ModernUO/Scripts/Engines/ConPVP/DuelContext.cs
2018-09-14 08:48:39 -07:00

2626 lines
64 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Server.Commands;
using Server.Engines.PartySystem;
using Server.Factions;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
using Server.Spells.Bushido;
using Server.Spells.Chivalry;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
using Server.Spells.Seventh;
using Server.Spells.Spellweaving;
namespace Server.Engines.ConPVP
{
public delegate void CountdownCallback( int count );
public class DuelContext
{
private Mobile m_Initiator;
private ArrayList m_Participants;
private Ruleset m_Ruleset;
private Arena m_Arena;
private bool m_Registered = true;
private bool m_Finished, m_Started;
private bool m_ReadyWait;
private int m_ReadyCount;
private bool m_Rematch;
public bool Rematch => m_Rematch;
public bool ReadyWait => m_ReadyWait;
public int ReadyCount => m_ReadyCount;
public bool Registered => m_Registered;
public bool Finished => m_Finished;
public bool Started => m_Started;
public Mobile Initiator => m_Initiator;
public ArrayList Participants => m_Participants;
public Ruleset Ruleset => m_Ruleset;
public Arena Arena => m_Arena;
private bool CantDoAnything( Mobile mob )
{
if ( m_EventGame != null )
return m_EventGame.CantDoAnything( mob );
return false;
}
public static bool IsFreeConsume( Mobile mob )
{
if ( !(mob is PlayerMobile pm) || pm.DuelContext?.m_EventGame == null )
return false;
return pm.DuelContext.m_EventGame.FreeConsume;
}
public void DelayBounce( TimeSpan ts, Mobile mob, Container corpse )
{
Timer.DelayCall( ts, new TimerStateCallback( DelayBounce_Callback ), new object[]{ mob, corpse } );
}
public static bool AllowSpecialMove( Mobile from, string name, SpecialMove move )
{
if ( !(from is PlayerMobile pm) )
return true;
DuelContext dc = pm.DuelContext;
// No DuelContext, or InstaAllowSpecialMove
return dc?.InstAllowSpecialMove( from, name, move ) != false;
}
public bool InstAllowSpecialMove( Mobile from, string name, SpecialMove move )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl == null || pl.Eliminated )
return true;
if ( CantDoAnything( from ) )
return false;
string title = null;
if ( move is NinjaMove )
title = "Bushido";
else if ( move is SamuraiMove )
title = "Ninjitsu";
if ( title == null || name == null || m_Ruleset.GetOption( title, name ) )
return true;
from.SendMessage( "The dueling ruleset prevents you from using this move." );
return false;
}
public bool AllowSpellCast( Mobile from, Spell spell )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl == null || pl.Eliminated )
return true;
if ( CantDoAnything( from ) )
return false;
if ( spell is Server.Spells.Fourth.RecallSpell )
from.SendMessage( "You may not cast this spell." );
string title = null, option = null;
if ( spell is ArcanistSpell )
{
title = "Spellweaving";
option = spell.Name;
}
else if ( spell is PaladinSpell )
{
title = "Chivalry";
option = spell.Name;
}
else if ( spell is NecromancerSpell )
{
title = "Necromancy";
option = spell.Name;
}
else if ( spell is NinjaSpell )
{
title = "Ninjitsu";
option = spell.Name;
}
else if ( spell is SamuraiSpell )
{
title = "Bushido";
option = spell.Name;
}
else if ( spell is MagerySpell magerySpell )
{
switch( magerySpell.Circle )
{
case SpellCircle.First: title = "1st Circle"; break;
case SpellCircle.Second: title = "2nd Circle"; break;
case SpellCircle.Third: title = "3rd Circle"; break;
case SpellCircle.Fourth: title = "4th Circle"; break;
case SpellCircle.Fifth: title = "5th Circle"; break;
case SpellCircle.Sixth: title = "6th Circle"; break;
case SpellCircle.Seventh: title = "7th Circle"; break;
case SpellCircle.Eighth: title = "8th Circle"; break;
}
option = magerySpell.Name;
}
else
{
title = "Other Spell";
option = spell.Name;
}
if ( title == null || option == null || m_Ruleset.GetOption( title, option ) )
return true;
from.SendMessage( "The dueling ruleset prevents you from casting this spell." );
return false;
}
public bool AllowItemEquip( Mobile from, Item item )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl == null || pl.Eliminated )
return true;
if ( item is Dagger || CheckItemEquip( from, item ) )
return true;
from.SendMessage( "The dueling ruleset prevents you from equiping this item." );
return false;
}
public static bool AllowSpecialAbility( Mobile from, string name, bool message )
{
if ( !(from is PlayerMobile pm) )
return true;
DuelContext dc = pm.DuelContext;
// No DuelContext or InstAllowSpecialAbility
return dc?.InstAllowSpecialAbility( from, name, message ) != false;
}
public bool InstAllowSpecialAbility( Mobile from, string name, bool message )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl?.Eliminated != false )
return true;
if ( CantDoAnything( from ) )
return false;
if ( m_Ruleset.GetOption( "Combat Abilities", name ) )
return true;
if ( message )
from.SendMessage( "The dueling ruleset prevents you from using this combat ability." );
return false;
}
public bool CheckItemEquip( Mobile from, Item item )
{
if ( item is Fists )
{
if ( !m_Ruleset.GetOption( "Weapons", "Wrestling" ) )
return false;
}
else if ( item is BaseArmor armor )
{
if ( armor.ProtectionLevel > ArmorProtectionLevel.Regular && !m_Ruleset.GetOption( "Armor", "Magical" ) )
return false;
if ( !Core.AOS && armor.Resource != armor.DefaultResource && !m_Ruleset.GetOption( "Armor", "Colored" ) )
return false;
if ( armor is BaseShield && !m_Ruleset.GetOption( "Armor", "Shields" ) )
return false;
}
else if ( item is BaseWeapon weapon )
{
if ( (weapon.DamageLevel > WeaponDamageLevel.Regular || weapon.AccuracyLevel > WeaponAccuracyLevel.Regular) && !m_Ruleset.GetOption( "Weapons", "Magical" ) )
return false;
if ( !Core.AOS && weapon.Resource != CraftResource.Iron && weapon.Resource != CraftResource.None && !m_Ruleset.GetOption( "Weapons", "Runics" ) )
return false;
if ( weapon is BaseRanged && !m_Ruleset.GetOption( "Weapons", "Ranged" ) )
return false;
if ( !(weapon is BaseRanged) && !m_Ruleset.GetOption( "Weapons", "Melee" ) )
return false;
if ( weapon.PoisonCharges > 0 && weapon.Poison != null && !m_Ruleset.GetOption( "Weapons", "Poisoned" ) )
return false;
if ( weapon is BaseWand && !m_Ruleset.GetOption( "Items", "Wands" ) )
return false;
}
return true;
}
public bool AllowSkillUse( Mobile from, SkillName skill )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl == null || pl.Eliminated )
return true;
if ( CantDoAnything( from ) )
return false;
int id = (int)skill;
if ( id >= 0 && id < SkillInfo.Table.Length )
{
if ( m_Ruleset.GetOption( "Skills", SkillInfo.Table[id].Name ) )
return true;
}
from.SendMessage( "The dueling ruleset prevents you from using this skill." );
return false;
}
public bool AllowItemUse( Mobile from, Item item )
{
if ( !m_StartedBeginCountdown )
return true;
DuelPlayer pl = Find( from );
if ( pl == null || pl.Eliminated )
return true;
if ( !(item is BaseRefreshPotion) )
{
if ( CantDoAnything( from ) )
return false;
}
string title = null, option = null;
if ( item is BasePotion )
{
title = "Potions";
if ( item is BaseAgilityPotion )
option = "Agility";
else if ( item is BaseCurePotion )
option = "Cure";
else if ( item is BaseHealPotion )
option = "Heal";
else if ( item is NightSightPotion )
option = "Nightsight";
else if ( item is BasePoisonPotion )
option = "Poison";
else if ( item is BaseStrengthPotion )
option = "Strength";
else if ( item is BaseExplosionPotion )
option = "Explosion";
else if ( item is BaseRefreshPotion )
option = "Refresh";
}
else if ( item is Bandage )
{
title = "Items";
option = "Bandages";
}
else if ( item is TrappableContainer container )
{
if ( container.TrapType != TrapType.None )
{
title = "Items";
option = "Trapped Containers";
}
}
else if ( item is Bola )
{
title = "Items";
option = "Bolas";
}
else if ( item is OrangePetals )
{
title = "Items";
option = "Orange Petals";
}
else if ( item is EtherealMount || item.Layer == Layer.Mount )
{
title = "Items";
option = "Mounts";
}
else if ( item is LeatherNinjaBelt )
{
title = "Items";
option = "Shurikens";
}
else if ( item is Fukiya )
{
title = "Items";
option = "Fukiya Darts";
}
else if ( item is FireHorn )
{
title = "Items";
option = "Fire Horns";
}
else if ( item is BaseWand )
{
title = "Items";
option = "Wands";
}
if ( title != null && option != null && m_StartedBeginCountdown && !m_Started )
{
from.SendMessage( "You may not use this item before the duel begins." );
return false;
}
if ( item is BasePotion && !(item is BaseExplosionPotion) && !(item is BaseRefreshPotion) && IsSuddenDeath )
{
from.SendMessage( 0x22, "You may not drink potions in sudden death." );
return false;
}
if ( item is Bandage && IsSuddenDeath )
{
from.SendMessage( 0x22, "You may not use bandages in sudden death." );
return false;
}
if ( title == null || option == null || m_Ruleset.GetOption( title, option ) )
return true;
from.SendMessage( "The dueling ruleset prevents you from using this item." );
return false;
}
private void DelayBounce_Callback( object state )
{
object[] states = (object[])state;
Mobile mob = (Mobile) states[0];
Container corpse = (Container) states[1];
RemoveAggressions( mob );
SendOutside( mob );
Refresh( mob, corpse );
Debuff( mob );
CancelSpell( mob );
mob.Frozen = false;
}
public void OnMapChanged( Mobile mob )
{
OnLocationChanged( mob );
}
public void OnLocationChanged( Mobile mob )
{
if ( !m_Registered || !m_StartedBeginCountdown || m_Finished )
return;
Arena arena = m_Arena;
if ( arena == null )
return;
if ( mob.Map == arena.Facet && arena.Bounds.Contains( mob.Location ) )
return;
DuelPlayer pl = Find( mob );
if ( pl == null || pl.Eliminated )
return;
if ( mob.Map == Map.Internal ) {
// they've logged out
if ( mob.LogoutMap == arena.Facet && arena.Bounds.Contains( mob.LogoutLocation ) ) {
// they logged out inside the arena.. set them to eject on login
mob.LogoutLocation = arena.Outside;
}
}
pl.Eliminated = true;
mob.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have forfeited your position in the duel." );
mob.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has forfeited by leaving the dueling arena.", mob.Name ) );
Participant winner = CheckCompletion();
if ( winner != null )
Finish( winner );
}
private bool m_Yielding;
public void OnDeath( Mobile mob, Container corpse )
{
if ( !m_Registered || !m_Started )
return;
DuelPlayer pl = Find( mob );
if ( pl != null && !pl.Eliminated )
{
if ( m_EventGame != null && !m_EventGame.OnDeath( mob, corpse ) )
return;
pl.Eliminated = true;
if ( mob.Poison != null )
mob.Poison = null;
Requip( mob, corpse );
DelayBounce( TimeSpan.FromSeconds( 4.0 ), mob, corpse );
Participant winner = CheckCompletion();
if ( winner != null )
{
Finish( winner );
}
else if ( !m_Yielding )
{
mob.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have been defeated." );
mob.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has been defeated.", mob.Name ) );
}
}
}
public bool CheckFull()
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
if ( p.HasOpenSlot )
return false;
}
return true;
}
public void Requip( Mobile from, Container cont )
{
if ( !(cont is Corpse corpse) )
return;
List<Item> items = new List<Item>( corpse.Items );
bool didntFit = false;
Container pack = from.Backpack;
for ( int i = 0; !didntFit && i < items.Count; ++i )
{
Item item = items[i];
if ( (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair) || !item.Movable )
continue;
if ( pack != null )
pack.DropItem( item );
else
didntFit = true;
}
corpse.Carved = true;
if ( corpse.ItemID == 0x2006 )
{
corpse.ProcessDelta();
corpse.SendRemovePacket();
corpse.ItemID = Utility.Random( 0xECA, 9 ); // bone graphic
corpse.Hue = 0;
corpse.ProcessDelta();
Mobile killer = from.FindMostRecentDamager( false );
if ( killer != null && killer.Player )
killer.AddToBackpack( new Head( m_Tournament == null ? HeadType.Duel : HeadType.Tournament, from.Name ) );
}
from.PlaySound( 0x3E3 );
if (didntFit)
from.SendLocalizedMessage(1062472); // You gather some of your belongings. The rest remain on the corpse.
else
from.SendLocalizedMessage(1062471); // You quickly gather all of your belongings.
}
public void Refresh( Mobile mob, Container cont )
{
if ( !mob.Alive )
{
mob.Resurrect();
if ( mob.FindItemOnLayer( Layer.OuterTorso ) is DeathRobe robe )
robe.Delete();
if ( cont is Corpse corpse )
{
for ( int i = 0; i < corpse.EquipItems.Count; ++i )
{
Item item = corpse.EquipItems[i];
if ( item.Movable && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair && item.IsChildOf( mob.Backpack ) )
mob.EquipItem( item );
}
}
}
mob.Hits = mob.HitsMax;
mob.Stam = mob.StamMax;
mob.Mana = mob.ManaMax;
mob.Poison = null;
}
public void SendOutside( Mobile mob )
{
if ( m_Arena == null )
return;
mob.Combatant = null;
mob.MoveToWorld( m_Arena.Outside, m_Arena.Facet );
}
private Point3D m_GatePoint;
private Map m_GateFacet;
public void Finish( Participant winner )
{
if ( m_Finished )
return;
EndAutoTie();
StopSDTimers();
m_Finished = true;
for ( int i = 0; i < winner.Players.Length; ++i )
{
DuelPlayer pl = winner.Players[i];
if ( pl != null && !pl.Eliminated )
DelayBounce( TimeSpan.FromSeconds( 8.0 ), pl.Mobile, null );
}
winner.Broadcast( 0x59, null, winner.Players.Length == 1 ? "{0} has won the duel." : "{0} and {1} team have won the duel.", winner.Players.Length == 1 ? "You have won the duel." : "Your team has won the duel." );
if ( m_Tournament != null && winner.TournyPart != null )
{
m_Match.Winner = winner.TournyPart;
winner.TournyPart.WonMatch( m_Match );
m_Tournament.HandleWon( m_Arena, m_Match, winner.TournyPart );
}
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant loser = (Participant)m_Participants[i];
if ( loser != winner )
{
loser.Broadcast( 0x22, null, loser.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", loser.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." );
if ( m_Tournament != null && loser.TournyPart != null )
loser.TournyPart.LostMatch( m_Match );
}
for ( int j = 0; j < loser.Players.Length; ++j )
{
if ( loser.Players[j] != null )
{
RemoveAggressions( loser.Players[j].Mobile );
loser.Players[j].Mobile.Delta( MobileDelta.Noto );
loser.Players[j].Mobile.CloseGump( typeof( BeginGump ) );
if ( m_Tournament != null )
loser.Players[j].Mobile.SendEverything();
}
}
}
if ( IsOneVsOne )
{
DuelPlayer dp1 = ((Participant)m_Participants[0]).Players[0];
DuelPlayer dp2 = ((Participant)m_Participants[1]).Players[0];
if ( dp1 != null && dp2 != null )
{
Award( dp1.Mobile, dp2.Mobile, dp1.Participant == winner );
Award( dp2.Mobile, dp1.Mobile, dp2.Participant == winner );
}
}
if ( m_EventGame != null )
m_EventGame.OnStop();
Timer.DelayCall( TimeSpan.FromSeconds( 9.0 ), new TimerCallback( UnregisterRematch ) );
}
public void Award( Mobile us, Mobile them, bool won )
{
Ladder ladder = ( m_Arena == null ? Ladder.Instance : m_Arena.AcquireLadder() );
if ( ladder == null )
return;
LadderEntry ourEntry = ladder.Find( us );
LadderEntry theirEntry = ladder.Find( them );
if ( ourEntry == null || theirEntry == null )
return;
int xpGain = Ladder.GetExperienceGain( ourEntry, theirEntry, won );
if ( xpGain == 0 )
return;
if ( m_Tournament != null )
xpGain *= ( xpGain > 0 ? 5 : 2 );
if ( won )
++ourEntry.Wins;
else
++ourEntry.Losses;
int oldLevel = Ladder.GetLevel( ourEntry.Experience );
ourEntry.Experience += xpGain;
if ( ourEntry.Experience < 0 )
ourEntry.Experience = 0;
ladder.UpdateEntry( ourEntry );
int newLevel = Ladder.GetLevel( ourEntry.Experience );
if ( newLevel > oldLevel )
us.SendMessage( 0x59, "You have achieved level {0}!", newLevel );
else if ( newLevel < oldLevel )
us.SendMessage( 0x22, "You have lost a level. You are now at {0}.", newLevel );
}
public void UnregisterRematch()
{
Unregister(true);
}
public void Unregister()
{
Unregister(false);
}
public void Unregister( bool queryRematch )
{
DestroyWall();
if ( !m_Registered )
return;
m_Registered = false;
if ( m_Arena != null )
m_Arena.Evict();
StopSDTimers();
Type[] types = new[]{ typeof( BeginGump ), typeof( DuelContextGump ), typeof( ParticipantGump ), typeof( PickRulesetGump ), typeof( ReadyGump ), typeof( ReadyUpGump ), typeof( RulesetGump ) };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
if ( pl.Mobile is PlayerMobile mobile )
mobile.DuelPlayer = null;
for ( int k = 0; k < types.Length; ++k )
pl.Mobile.CloseGump( types[k] );
}
}
if ( queryRematch && m_Tournament == null )
QueryRematch();
}
public void QueryRematch()
{
DuelContext dc = new DuelContext( m_Initiator, m_Ruleset.Layout, false );
dc.m_Ruleset = m_Ruleset;
dc.m_Rematch = true;
dc.m_Participants.Clear();
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant oldPart = (Participant)m_Participants[i];
Participant newPart = new Participant( dc, oldPart.Players.Length );
for ( int j = 0; j < oldPart.Players.Length; ++j )
{
DuelPlayer oldPlayer = oldPart.Players[j];
if ( oldPlayer != null )
newPart.Players[j] = new DuelPlayer( oldPlayer.Mobile, newPart );
}
dc.m_Participants.Add( newPart );
}
dc.CloseAllGumps();
dc.SendReadyUpGump();
}
public DuelPlayer Find( Mobile mob )
{
if ( mob is PlayerMobile pm )
{
if ( pm.DuelContext == this )
return pm.DuelPlayer;
return null;
}
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
DuelPlayer pl = p.Find( mob );
if ( pl != null )
return pl;
}
return null;
}
public bool IsAlly( Mobile m1, Mobile m2 )
{
DuelPlayer pl1 = Find( m1 );
DuelPlayer pl2 = Find( m2 );
return ( pl1 != null && pl2 != null && pl1.Participant == pl2.Participant );
}
public Participant CheckCompletion()
{
Participant winner = null;
bool hasWinner = false;
int eliminated = 0;
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
if ( p.Eliminated )
{
++eliminated;
if ( eliminated == (m_Participants.Count - 1) )
hasWinner = true;
}
else
{
winner = p;
}
}
if ( hasWinner )
return winner == null ? (Participant) m_Participants[0] : winner;
return null;
}
private Timer m_Countdown;
public void StartCountdown( int count, CountdownCallback cb )
{
cb(count);
m_Countdown=Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ), count, new TimerStateCallback( Countdown_Callback ), new object[]{ count-1, cb } );
}
public void StopCountdown()
{
if ( m_Countdown != null )
m_Countdown.Stop();
m_Countdown = null;
}
private void Countdown_Callback( object state )
{
object[] states = (object[])state;
int count = (int)states[0];
CountdownCallback cb = (CountdownCallback)states[1];
if ( count==0 )
{
if ( m_Countdown != null )
m_Countdown.Stop();
m_Countdown=null;
}
cb( count );
states[0] = count - 1;
}
private Timer m_AutoTieTimer;
private bool m_Tied;
public bool Tied => m_Tied;
private bool m_IsSuddenDeath;
public bool IsSuddenDeath{ get => m_IsSuddenDeath;
set => m_IsSuddenDeath = value;
}
private Timer m_SDWarnTimer, m_SDActivateTimer;
public void StopSDTimers()
{
if ( m_SDWarnTimer != null )
m_SDWarnTimer.Stop();
m_SDWarnTimer = null;
if ( m_SDActivateTimer != null )
m_SDActivateTimer.Stop();
m_SDActivateTimer = null;
}
public void StartSuddenDeath( TimeSpan timeUntilActive )
{
if ( m_SDWarnTimer != null )
m_SDWarnTimer.Stop();
m_SDWarnTimer = Timer.DelayCall( TimeSpan.FromMinutes( timeUntilActive.TotalMinutes * 0.9 ), new TimerCallback( WarnSuddenDeath ) );
if ( m_SDActivateTimer != null )
m_SDActivateTimer.Stop();
m_SDActivateTimer = Timer.DelayCall( timeUntilActive, new TimerCallback( ActivateSuddenDeath ) );
}
public void WarnSuddenDeath()
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null || pl.Eliminated )
continue;
pl.Mobile.SendSound( 0x1E1 );
pl.Mobile.SendMessage( 0x22, "Warning! Warning! Warning!" );
pl.Mobile.SendMessage( 0x22, "Sudden death will be active soon!" );
}
}
if ( m_Tournament != null )
m_Tournament.Alert( m_Arena, "Sudden death will be active soon!" );
if ( m_SDWarnTimer != null )
m_SDWarnTimer.Stop();
m_SDWarnTimer = null;
}
public static bool CheckSuddenDeath( Mobile mob )
{
return mob is PlayerMobile pm && pm.DuelPlayer?.Eliminated == false && pm.DuelContext?.IsSuddenDeath == true;
}
public void ActivateSuddenDeath()
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null || pl.Eliminated )
continue;
pl.Mobile.SendSound( 0x1E1 );
pl.Mobile.SendMessage( 0x22, "Warning! Warning! Warning!" );
pl.Mobile.SendMessage( 0x22, "Sudden death has ACTIVATED. You are now unable to perform any beneficial actions." );
}
}
if ( m_Tournament != null )
m_Tournament.Alert( m_Arena, "Sudden death has been activated!" );
m_IsSuddenDeath = true;
if ( m_SDActivateTimer != null )
m_SDActivateTimer.Stop();
m_SDActivateTimer = null;
}
public void BeginAutoTie()
{
if ( m_AutoTieTimer != null )
m_AutoTieTimer.Stop();
TimeSpan ts = ( m_Tournament == null || m_Tournament.TournyType == TournyType.Standard )
? AutoTieDelay
: TimeSpan.FromMinutes( 90.0 );
m_AutoTieTimer = Timer.DelayCall( ts, new TimerCallback( InvokeAutoTie ) );
}
public void EndAutoTie()
{
if ( m_AutoTieTimer != null )
m_AutoTieTimer.Stop();
m_AutoTieTimer = null;
}
public void InvokeAutoTie()
{
m_AutoTieTimer = null;
if ( !m_Started || m_Finished )
return;
m_Tied = true;
m_Finished = true;
StopSDTimers();
ArrayList remaining = new ArrayList();
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
if ( p.Eliminated )
{
p.Broadcast( 0x22, null, p.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", p.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." );
}
else
{
p.Broadcast( 0x59, null, p.Players.Length == 1 ? "{0} has tied the duel due to time expiration." : "{0} and {1} team have tied the duel due to time expiration.", p.Players.Length == 1 ? "You have tied the duel due to time expiration." : "Your team has tied the duel due to time expiration." );
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl != null && !pl.Eliminated )
DelayBounce( TimeSpan.FromSeconds( 8.0 ), pl.Mobile, null );
}
if ( p.TournyPart != null )
remaining.Add( p.TournyPart );
}
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl != null )
{
pl.Mobile.Delta( MobileDelta.Noto );
pl.Mobile.SendEverything();
}
}
}
if ( m_Tournament != null )
m_Tournament.HandleTie( m_Arena, m_Match, remaining );
Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Unregister ) );
}
public bool IsOneVsOne
{
get
{
if ( m_Participants.Count != 2 )
return false;
if ( ((Participant)m_Participants[0]).Players.Length != 1 )
return false;
if ( ((Participant)m_Participants[1]).Players.Length != 1 )
return false;
return true;
}
}
public static void Initialize()
{
EventSink.Speech += new SpeechEventHandler( EventSink_Speech );
EventSink.Login += new LoginEventHandler( EventSink_Login );
CommandSystem.Register( "vli", AccessLevel.GameMaster, new CommandEventHandler( vli_oc ) );
}
private static void vli_oc( CommandEventArgs e )
{
e.Mobile.BeginTarget( -1, false, Targeting.TargetFlags.None, new TargetCallback( vli_ot ) );
}
private static void vli_ot( Mobile from, object obj )
{
if ( obj is PlayerMobile pm )
{
Ladder ladder = Ladder.Instance;
if ( ladder == null )
return;
LadderEntry entry = ladder.Find( pm );
if ( entry != null )
from.SendGump( new PropertiesGump( from, entry ) );
}
}
private static TimeSpan CombatDelay = TimeSpan.FromSeconds( 30.0 );
private static TimeSpan AutoTieDelay = TimeSpan.FromMinutes( 15.0 );
public static bool CheckCombat( Mobile m )
{
for ( int i = 0; i < m.Aggressed.Count; ++i )
{
AggressorInfo info = m.Aggressed[i];
if ( info.Defender.Player && (DateTime.UtcNow - info.LastCombatTime) < CombatDelay )
return true;
}
for ( int i = 0; i < m.Aggressors.Count; ++i )
{
AggressorInfo info = m.Aggressors[i];
if ( info.Attacker.Player && (DateTime.UtcNow - info.LastCombatTime) < CombatDelay )
return true;
}
return false;
}
private static void EventSink_Login( LoginEventArgs e )
{
if ( !(e.Mobile is PlayerMobile pm) )
return;
DuelContext dc = pm.DuelContext;
if ( dc == null )
return;
if ( dc.ReadyWait && pm.DuelPlayer.Ready && !dc.Started && !dc.StartedBeginCountdown && !dc.Finished )
{
if ( dc.m_Tournament == null )
pm.SendGump( new ReadyGump( pm, dc, dc.m_ReadyCount ) );
}
else if ( dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished )
{
if ( dc.m_Tournament == null )
pm.SendGump( new ReadyUpGump( pm, dc ) );
}
else if ( dc.Initiator == pm && !dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished )
pm.SendGump( new DuelContextGump( pm, dc ) );
}
private static void ViewLadder_OnTarget( Mobile from, object obj, object state )
{
if ( obj is PlayerMobile pm )
{
Ladder ladder = (Ladder)state;
LadderEntry entry = ladder.Find( pm );
if ( entry == null )
return; // sanity
string text = String.Format( "{{0}} are ranked {0} at level {1}.", LadderGump.Rank( entry.Index + 1 ), Ladder.GetLevel( entry.Experience ) );
pm.PrivateOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, from==pm?"You":"They" ), from.NetState );
}
else if ( obj is Mobile mob )
{
if ( mob.Body.IsHuman )
mob.PrivateOverheadMessage( MessageType.Regular, mob.SpeechHue, false, "I'm not a duelist, and quite frankly, I resent the implication.", from.NetState );
else
mob.PrivateOverheadMessage( MessageType.Regular, 0x3B2, true, "It's probably better than you.", from.NetState );
}
else
{
from.SendMessage( "That's not a player." );
}
}
private static void EventSink_Speech( SpeechEventArgs e )
{
if ( e.Handled )
return;
if ( !(e.Mobile is PlayerMobile pm) )
return;
if ( Insensitive.Contains( e.Speech, "i wish to duel" ) )
{
if ( !pm.CheckAlive() )
{
}
else if ( pm.Region.IsPartOf( typeof( Regions.Jail ) ) )
{
}
else if ( CheckCombat( pm ) )
{
e.Mobile.SendMessage( 0x22, "You have recently been in combat with another player and must wait before starting a duel." );
}
else if ( pm.DuelContext != null )
{
if ( pm.DuelContext.Initiator == pm )
e.Mobile.SendMessage( 0x22, "You have already started a duel." );
else
e.Mobile.SendMessage( 0x22, "You have already been challenged in a duel." );
}
else if ( TournamentController.IsActive )
{
e.Mobile.SendMessage( 0x22, "You may not start a duel while a tournament is active." );
}
else
{
pm.SendGump( new DuelContextGump( pm, new DuelContext( pm, RulesetLayout.Root ) ) );
e.Handled = true;
}
}
else if ( Insensitive.Equals( e.Speech, "change arena preferences" ) )
{
if ( !pm.CheckAlive() )
{
}
else
{
Preferences prefs = Preferences.Instance;
if ( prefs != null )
{
e.Mobile.CloseGump( typeof( PreferencesGump ) );
e.Mobile.SendGump( new PreferencesGump( e.Mobile, prefs ) );
}
}
}
else if ( Insensitive.Equals( e.Speech, "showladder" ) )
{
e.Blocked=true;
if ( !pm.CheckAlive() )
{
}
else
{
Ladder instance = Ladder.Instance;
if ( instance == null )
{
//pm.SendMessage( "Ladder not yet initialized." );
}
else
{
LadderEntry entry = instance.Find( pm );
if ( entry == null )
return; // sanity
string text = String.Format( "{{0}} {{1}} ranked {0} at level {1}.", LadderGump.Rank( entry.Index + 1 ), Ladder.GetLevel( entry.Experience ) );
pm.LocalOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, "You", "are" ) );
pm.NonlocalOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, pm.Name, "is" ) );
//pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) );
//pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) );
}
}
}
else if ( Insensitive.Equals( e.Speech, "viewladder" ) )
{
e.Blocked=true;
if ( !pm.CheckAlive() )
{
}
else
{
Ladder instance = Ladder.Instance;
if ( instance == null )
{
//pm.SendMessage( "Ladder not yet initialized." );
}
else
{
pm.SendMessage( "Target a player to view their ranking and level." );
pm.BeginTarget( 16, false, Targeting.TargetFlags.None, new TargetStateCallback( ViewLadder_OnTarget ), instance );
}
}
}
else if ( Insensitive.Contains( e.Speech, "i yield" ) )
{
if ( !pm.CheckAlive() )
{
}
else if ( pm.DuelContext == null )
{
}
else if ( pm.DuelContext.Finished )
{
e.Mobile.SendMessage( 0x22, "The duel is already finished." );
}
else if ( !pm.DuelContext.Started )
{
DuelContext dc = pm.DuelContext;
Mobile init = dc.Initiator;
if ( pm.DuelContext.StartedBeginCountdown )
{
e.Mobile.SendMessage( 0x22, "The duel has not yet started." );
}
else
{
DuelPlayer pl = pm.DuelContext.Find( pm );
if ( pl == null )
return;
Participant p = pl.Participant;
if ( !pm.DuelContext.ReadyWait ) // still setting stuff up
{
p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." );
if ( init == pm )
{
dc.Unregister();
}
else
{
p.Nullify( pl );
pm.DuelPlayer=null;
NetState ns = init.NetState;
if ( ns != null )
{
foreach ( Gump g in ns.Gumps )
{
if (g is ParticipantGump pg && pg.Participant == p)
{
init.SendGump( new ParticipantGump( init, dc, p ) );
break;
}
if ( g is DuelContextGump dcg && dcg.Context == dc )
{
init.SendGump( new DuelContextGump( init, dc ) );
break;
}
}
}
}
}
else if ( !pm.DuelContext.StartedReadyCountdown ) // at ready stage
{
p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." );
dc.m_Yielding=true;
dc.RejectReady( pm, null );
dc.m_Yielding=false;
if ( init == pm )
{
dc.Unregister();
}
else if ( dc.m_Registered )
{
p.Nullify( pl );
pm.DuelPlayer=null;
NetState ns = init.NetState;
if ( ns != null )
{
bool send = true;
foreach ( Gump g in ns.Gumps )
{
if ( g is ParticipantGump pg && pg.Participant == p )
{
init.SendGump( new ParticipantGump( init, dc, p ) );
send=false;
break;
}
if ( g is DuelContextGump dcg && dcg.Context == dc )
{
init.SendGump( new DuelContextGump( init, dc ) );
send=false;
break;
}
}
if ( send )
init.SendGump( new DuelContextGump( init, dc ) );
}
}
}
else
{
pm.DuelContext.m_Countdown?.Stop();
pm.DuelContext.m_Countdown = null;
pm.DuelContext.m_StartedReadyCountdown=false;
p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." );
dc.m_Yielding=true;
dc.RejectReady( pm, null );
dc.m_Yielding=false;
if ( init == pm )
{
dc.Unregister();
}
else if ( dc.m_Registered )
{
p.Nullify( pl );
pm.DuelPlayer=null;
NetState ns = init.NetState;
if ( ns != null )
{
bool send = true;
foreach ( Gump g in ns.Gumps )
{
if ( g is ParticipantGump pg && pg.Participant == p )
{
init.SendGump( new ParticipantGump( init, dc, p ) );
send=false;
break;
}
if ( g is DuelContextGump dcg && dcg.Context == dc )
{
init.SendGump( new DuelContextGump( init, dc ) );
send=false;
break;
}
}
if ( send )
init.SendGump( new DuelContextGump( init, dc ) );
}
}
}
}
}
else
{
DuelPlayer pl = pm.DuelContext.Find( pm );
if ( pl != null )
{
if ( pm.DuelContext.IsOneVsOne )
{
e.Mobile.SendMessage( 0x22, "You may not yield a 1 on 1 match." );
}
else if ( pl.Eliminated )
{
e.Mobile.SendMessage( 0x22, "You have already been eliminated." );
}
else
{
pm.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have yielded." );
pm.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has yielded.", pm.Name ) );
pm.DuelContext.m_Yielding=true;
pm.Kill();
pm.DuelContext.m_Yielding=false;
if ( pm.Alive ) // invul, ...
{
pl.Eliminated = true;
pm.DuelContext.RemoveAggressions( pm );
pm.DuelContext.SendOutside( pm );
pm.DuelContext.Refresh( pm, null );
Debuff( pm );
CancelSpell( pm );
pm.Frozen = false;
Participant winner = pm.DuelContext.CheckCompletion();
if ( winner != null )
pm.DuelContext.Finish( winner );
}
}
}
else
{
e.Mobile.SendMessage( 0x22, "BUG: Unable to find duel context." );
}
}
}
}
public DuelContext( Mobile initiator, RulesetLayout layout ) : this( initiator, layout, true )
{
}
public DuelContext( Mobile initiator, RulesetLayout layout, bool addNew )
{
m_Initiator = initiator;
m_Participants = new ArrayList();
m_Ruleset = new Ruleset( layout );
m_Ruleset.ApplyDefault( layout.Defaults[0] );
if ( addNew )
{
m_Participants.Add( new Participant( this, 1 ) );
m_Participants.Add( new Participant( this, 1 ) );
((Participant)m_Participants[0]).Add( initiator );
}
}
public void CloseAllGumps()
{
Type[] types = new[]{ typeof( DuelContextGump ), typeof( ParticipantGump ), typeof( RulesetGump ) };
int[] defs = new[]{ -1, -1, -1 };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
Mobile mob = pl.Mobile;
for ( int k = 0; k < types.Length; ++k )
mob.CloseGump( types[k] );
//mob.CloseGump( types[k], defs[k] );
}
}
}
public void RejectReady( Mobile rejector, string page )
{
if ( m_StartedReadyCountdown )
return; // sanity
Type[] types = new[]{ typeof( DuelContextGump ), typeof( ReadyUpGump ), typeof( ReadyGump ) };
int[] defs = new[]{ -1, -1, -1 };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
pl.Ready = false;
Mobile mob = pl.Mobile;
if ( page == null ) // yield
{
if ( mob != rejector )
mob.SendMessage( 0x22, "{0} has yielded.", rejector.Name );
}
else
{
if ( mob == rejector )
mob.SendMessage( 0x22, "You have rejected the {0}.", m_Rematch ? "rematch" : page );
else
mob.SendMessage( 0x22, "{0} has rejected the {1}.", rejector.Name, m_Rematch ? "rematch" : page );
}
for ( int k = 0; k < types.Length; ++k )
mob.CloseGump( types[k] );
//mob.CloseGump( types[k], defs[k] );
}
}
if ( m_Rematch )
Unregister();
else if ( !m_Yielding )
m_Initiator.SendGump( new DuelContextGump( m_Initiator, this ) );
m_ReadyWait = false;
m_ReadyCount = 0;
}
public void SendReadyGump()
{
SendReadyGump( -1 );
}
public static void Debuff( Mobile mob )
{
mob.RemoveStatMod( "[Magic] Str Offset" );
mob.RemoveStatMod( "[Magic] Dex Offset" );
mob.RemoveStatMod( "[Magic] Int Offset" );
mob.RemoveStatMod( "Concussion" );
mob.RemoveStatMod( "blood-rose" );
mob.RemoveStatMod( "clarity-potion" );
OrangePetals.RemoveContext( mob );
mob.Paralyzed = false;
mob.Hidden = false;
if ( !Core.AOS )
{
mob.MagicDamageAbsorb = 0;
mob.MeleeDamageAbsorb = 0;
Spells.Second.ProtectionSpell.Registry.Remove( mob );
Spells.Fourth.ArchProtectionSpell.RemoveEntry( mob );
mob.EndAction( typeof( DefensiveSpell ) );
}
TransformationSpellHelper.RemoveContext( mob, true );
AnimalForm.RemoveContext( mob, true );
if ( DisguiseTimers.IsDisguised( mob ) )
DisguiseTimers.StopTimer( mob );
if ( !mob.CanBeginAction( typeof( PolymorphSpell ) ) )
{
mob.BodyMod = 0;
mob.HueMod = -1;
mob.EndAction( typeof( PolymorphSpell ) );
}
BaseArmor.ValidateMobile( mob );
BaseClothing.ValidateMobile( mob );
mob.Hits = mob.HitsMax;
mob.Stam = mob.StamMax;
mob.Mana = mob.ManaMax;
mob.Poison = null;
}
public static void CancelSpell( Mobile mob )
{
if ( mob.Spell is Spell spell )
spell.Disturb( DisturbType.Kill );
Targeting.Target.Cancel( mob );
}
private bool m_StartedBeginCountdown;
private bool m_StartedReadyCountdown;
public bool StartedBeginCountdown => m_StartedBeginCountdown;
public bool StartedReadyCountdown => m_StartedReadyCountdown;
private class InternalWall : Item
{
public InternalWall() : base( 0x80 )
{
Movable = false;
}
public void Appear( Point3D loc, Map map )
{
MoveToWorld( loc, map );
Effects.SendLocationParticles( this, 0x376A, 9, 10, 5025 );
}
public InternalWall( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
Delete();
}
}
private ArrayList m_Walls = new ArrayList();
public void DestroyWall()
{
for ( int i = 0; i < m_Walls.Count; ++i )
((Item)m_Walls[i]).Delete();
m_Walls.Clear();
}
public void CreateWall()
{
if ( m_Arena == null )
return;
Point3D start = m_Arena.Points.EdgeWest;
Point3D wall = m_Arena.Wall;
int dx = start.X - wall.X;
int dy = start.Y - wall.Y;
int rx = dx - dy;
int ry = dx + dy;
bool eastToWest;
if ( rx >= 0 && ry >= 0 )
eastToWest = false;
else if ( rx >= 0 )
eastToWest = true;
else if ( ry >= 0 )
eastToWest = true;
else
eastToWest = false;
Effects.PlaySound( wall, m_Arena.Facet, 0x1F6 );
for ( int i = -1; i <= 1; ++i )
{
Point3D loc = new Point3D( eastToWest ? wall.X + i : wall.X, eastToWest ? wall.Y : wall.Y + i, wall.Z );
InternalWall created = new InternalWall();
created.Appear( loc, m_Arena.Facet );
m_Walls.Add( created );
}
}
public void BuildParties()
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
if ( p.Players.Length > 1 )
{
ArrayList players = new ArrayList();
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp == null )
continue;
players.Add( dp.Mobile );
}
if ( players.Count > 1 )
{
for ( int leaderIndex = 0; (leaderIndex+1) < players.Count; leaderIndex += Party.Capacity )
{
Mobile leader = (Mobile) players[leaderIndex];
Party party = Party.Get( leader );
if ( party == null )
{
leader.Party = party = new Party( leader );
}
else if ( party.Leader != leader )
{
party.SendPublicMessage( leader, "I leave this party to fight in a duel." );
party.Remove( leader );
leader.Party = party = new Party( leader );
}
for ( int j = leaderIndex+1; j < players.Count && j < leaderIndex+Party.Capacity; ++j )
{
Mobile player = (Mobile)players[j];
Party existing = Party.Get( player );
if ( existing == party )
continue;
if ( (party.Members.Count + party.Candidates.Count) >= Party.Capacity )
{
player.SendMessage( "You could not be added to the team party because it is at full capacity." );
leader.SendMessage( "{0} could not be added to the team party because it is at full capacity." );
}
else
{
if ( existing != null )
{
existing.SendPublicMessage( player, "I leave this party to fight in a duel." );
existing.Remove( player );
}
party.OnAccept( player, true );
}
}
}
}
}
}
}
public void ClearIllegalItems()
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
ClearIllegalItems( pl.Mobile );
}
}
}
public void ClearIllegalItems( Mobile mob )
{
if ( mob.StunReady && !AllowSpecialAbility( mob, "Stun", false ) )
mob.StunReady = false;
if ( mob.DisarmReady && !AllowSpecialAbility( mob, "Disarm", false ) )
mob.DisarmReady = false;
Container pack = mob.Backpack;
if ( pack == null )
return;
for ( int i = mob.Items.Count - 1; i >= 0; --i )
{
if ( i >= mob.Items.Count )
continue; // sanity
Item item = mob.Items[i];
if ( !CheckItemEquip( mob, item ) )
{
pack.DropItem( item );
if ( item is BaseWeapon )
mob.SendLocalizedMessage( 1062001, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer wield your ~1_WEAPON~
else if ( item is BaseArmor && !(item is BaseShield) )
mob.SendLocalizedMessage( 1062002, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer wear your ~1_ARMOR~
else
mob.SendLocalizedMessage( 1062003, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer equip your ~1_SHIELD~
}
}
Item inHand = mob.Holding;
if ( inHand != null && !CheckItemEquip( mob, inHand ) )
{
mob.Holding = null;
BounceInfo bi = inHand.GetBounce();
if ( bi.m_Parent == mob )
pack.DropItem( inHand );
else
inHand.Bounce( mob );
inHand.ClearBounce();
}
}
private void MessageRuleset( Mobile mob ) {
if ( m_Ruleset == null ) {
return;
}
Ruleset ruleset = m_Ruleset;
Ruleset basedef = ruleset.Base;
mob.SendMessage( "Ruleset: {0}", basedef.Title );
BitArray defs;
if ( ruleset.Flavors.Count > 0 )
{
defs = new BitArray( basedef.Options );
for ( int i = 0; i < ruleset.Flavors.Count; ++i ) {
defs.Or( ((Ruleset)ruleset.Flavors[i]).Options );
mob.SendMessage( " + {0}", ((Ruleset)ruleset.Flavors[i]).Title );
}
}
else
{
defs = basedef.Options;
}
int changes = 0;
BitArray opts = ruleset.Options;
for ( int i = 0; i < opts.Length; ++i )
{
if ( defs[i] != opts[i] ) {
string name = ruleset.Layout.FindByIndex( i );
if ( name != null ) // sanity
{
++changes;
if ( changes == 1 ) {
mob.SendMessage( "Modifications:" );
}
mob.SendMessage( "{0}: {1}", name, opts[i] ? "enabled" : "disabled" );
}
}
}
}
public void SendBeginGump( int count )
{
if ( !m_Registered || m_Finished )
return;
if ( count == 10 )
{
CreateWall();
BuildParties();
ClearIllegalItems();
}
else if ( count == 0 )
{
DestroyWall();
}
m_StartedBeginCountdown = true;
if ( count == 0 )
{
m_Started = true;
BeginAutoTie();
}
Type[] types = new[]{ typeof( ReadyGump ), typeof( ReadyUpGump ), typeof( BeginGump ) };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
Mobile mob = pl.Mobile;
if ( count > 0 )
{
if ( count == 10 )
CloseAndSendGump( mob, new BeginGump( count ), types );
mob.Frozen = true;
}
else
{
mob.CloseGump( typeof( BeginGump ) );
mob.Frozen = false;
}
}
}
}
private ArrayList m_Entered = new ArrayList();
private class ReturnEntry
{
private Mobile m_Mobile;
private Point3D m_Location;
private Map m_Facet;
private DateTime m_Expire;
public Mobile Mobile => m_Mobile;
public Point3D Location => m_Location;
public Map Facet => m_Facet;
public void Return()
{
if ( m_Facet == Map.Internal || m_Facet == null )
return;
if ( m_Mobile.Map == Map.Internal )
{
m_Mobile.LogoutLocation = m_Location;
m_Mobile.LogoutMap = m_Facet;
}
else
{
m_Mobile.Location = m_Location;
m_Mobile.Map = m_Facet;
}
}
public ReturnEntry( Mobile mob )
{
m_Mobile = mob;
Update();
}
public ReturnEntry( Mobile mob, Point3D loc, Map facet )
{
m_Mobile = mob;
m_Location = loc;
m_Facet = facet;
m_Expire = DateTime.UtcNow + TimeSpan.FromMinutes( 30.0 );
}
public bool Expired => ( DateTime.UtcNow >= m_Expire );
public void Update()
{
m_Expire = DateTime.UtcNow + TimeSpan.FromMinutes( 30.0 );
if ( m_Mobile.Map == Map.Internal )
{
m_Facet = m_Mobile.LogoutMap;
m_Location = m_Mobile.LogoutLocation;
}
else
{
m_Facet = m_Mobile.Map;
m_Location = m_Mobile.Location;
}
}
}
private class ExitTeleporter : Item
{
private ArrayList m_Entries;
public override string DefaultName => "return teleporter";
public ExitTeleporter() : base( 0x1822 )
{
m_Entries = new ArrayList();
Hue = 0x482;
Movable = false;
}
public void Register( Mobile mob )
{
ReturnEntry entry = Find( mob );
if ( entry != null )
{
entry.Update();
return;
}
m_Entries.Add( new ReturnEntry( mob ) );
}
private ReturnEntry Find( Mobile mob )
{
for ( int i = 0; i < m_Entries.Count; ++i )
{
ReturnEntry entry = (ReturnEntry)m_Entries[i];
if ( entry.Mobile == mob )
return entry;
else if ( entry.Expired )
m_Entries.RemoveAt( i-- );
}
return null;
}
public override bool OnMoveOver( Mobile m )
{
if ( !base.OnMoveOver( m ) )
return false;
ReturnEntry entry = Find( m );
if ( entry != null )
{
entry.Return();
Effects.PlaySound( GetWorldLocation(), Map, 0x1FE );
Effects.PlaySound( m.Location, m.Map, 0x1FE );
m_Entries.Remove( entry );
return false;
}
else
{
m.SendLocalizedMessage( 1049383 ); // The teleporter doesn't seem to work for you.
return true;
}
}
public ExitTeleporter( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
writer.WriteEncodedInt( (int) m_Entries.Count );
for ( int i = 0; i < m_Entries.Count; ++i )
{
ReturnEntry entry = (ReturnEntry)m_Entries[i];
writer.Write( (Mobile) entry.Mobile );
writer.Write( (Point3D) entry.Location );
writer.Write( (Map) entry.Facet );
if ( entry.Expired )
m_Entries.RemoveAt( i-- );
}
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
switch ( version )
{
case 0:
{
int count = reader.ReadEncodedInt();
m_Entries = new ArrayList( count );
for ( int i = 0; i < count; ++i )
{
Mobile mob = reader.ReadMobile();
Point3D loc = reader.ReadPoint3D();
Map map = reader.ReadMap();
m_Entries.Add( new ReturnEntry( mob, loc, map ) );
}
break;
}
}
}
}
private class ArenaMoongate : ConfirmationMoongate
{
private ExitTeleporter m_Teleporter;
public override string DefaultName => "spectator moongate";
public ArenaMoongate( Point3D target, Map map, ExitTeleporter tp ) : base( target, map )
{
m_Teleporter = tp;
ItemID = 0x1FD4;
Dispellable = false;
GumpWidth = 300;
GumpHeight = 150;
MessageColor = 0xFFC000;
MessageString = "Are you sure you wish to spectate this duel?";
TitleColor = 0x7800;
TitleNumber = 1062051; // Gate Warning
Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Delete ) );
}
public override void CheckGate(Mobile m, int range)
{
if ( DuelContext.CheckCombat( m ) )
{
m.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." );
}
else
{
base.CheckGate( m, range );
}
}
public override void UseGate( Mobile m )
{
if ( DuelContext.CheckCombat( m ) )
{
m.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." );
}
else
{
if ( m_Teleporter != null && !m_Teleporter.Deleted )
m_Teleporter.Register( m );
base.UseGate( m );
}
}
public void Appear( Point3D loc, Map map )
{
Effects.PlaySound( loc, map, 0x20E );
MoveToWorld( loc, map );
}
public ArenaMoongate( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
Delete();
}
}
public void RemoveAggressions( Mobile mob )
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = (DuelPlayer)p.Players[j];
if ( dp == null || dp.Mobile == mob )
continue;
mob.RemoveAggressed( dp.Mobile );
mob.RemoveAggressor( dp.Mobile );
dp.Mobile.RemoveAggressed( mob );
dp.Mobile.RemoveAggressor( mob );
}
}
}
public void SendReadyUpGump()
{
if ( !m_Registered )
return;
m_ReadyWait = true;
m_ReadyCount = -1;
Type[] types = new[]{ typeof( ReadyUpGump ) };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
Mobile mob = pl.Mobile;
if ( mob != null )
{
if ( m_Tournament == null )
CloseAndSendGump( mob, new ReadyUpGump( mob, this ), types );
}
}
}
}
public string ValidateStart()
{
if ( m_Tournament == null && TournamentController.IsActive )
return "a tournament is active";
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp == null )
return "a slot is empty";
if ( dp.Mobile.Region.IsPartOf( typeof( Regions.Jail ) ) )
return String.Format( "{0} is in jail", dp.Mobile.Name );
if ( Sigil.ExistsOn( dp.Mobile ) )
return String.Format( "{0} is holding a sigil", dp.Mobile.Name );
if ( !dp.Mobile.Alive )
{
if ( m_Tournament == null )
return String.Format( "{0} is dead", dp.Mobile.Name );
else
dp.Mobile.Resurrect();
}
if ( m_Tournament == null && CheckCombat( dp.Mobile ) )
return String.Format( "{0} is in combat", dp.Mobile.Name );
if ( dp.Mobile.Mounted )
{
IMount mount = dp.Mobile.Mount;
if ( m_Tournament != null && mount != null )
mount.Rider = null;
else
return String.Format( "{0} is mounted", dp.Mobile.Name );
}
}
}
return null;
}
public Arena m_OverrideArena;
public Tournament m_Tournament;
public TournyMatch m_Match;
public EventGame m_EventGame;
public Tournament Tournament => m_Tournament;
public void SendReadyGump( int count )
{
if ( !m_Registered )
return;
if ( count != -1 )
m_StartedReadyCountdown = true;
m_ReadyCount = count;
if ( count == 0 )
{
string error = ValidateStart();
if ( error != null )
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp != null )
dp.Mobile.SendMessage( "The duel could not be started because {0}.", error );
}
}
StartCountdown( 10, new CountdownCallback( SendReadyGump ) );
return;
}
m_ReadyWait = false;
List<Mobile> players = new List<Mobile>();
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp != null )
players.Add( dp.Mobile );
}
}
Arena arena = m_OverrideArena;
if ( arena == null )
arena = Arena.FindArena( players );
if ( arena == null )
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp != null )
dp.Mobile.SendMessage( "The duel could not be started because there are no arenas. If you want to stop waiting for a free arena, yield the duel." );
}
}
StartCountdown( 10, new CountdownCallback( SendReadyGump ) );
return;
}
if ( !arena.IsOccupied )
{
m_Arena = arena;
if ( m_Initiator.Map == Map.Internal )
{
m_GatePoint = m_Initiator.LogoutLocation;
m_GateFacet = m_Initiator.LogoutMap;
}
else
{
m_GatePoint = m_Initiator.Location;
m_GateFacet = m_Initiator.Map;
}
if ( !(arena.Teleporter is ExitTeleporter tp) )
{
arena.Teleporter = tp = new ExitTeleporter();
tp.MoveToWorld( arena.GateOut == Point3D.Zero ? arena.Outside : arena.GateOut, arena.Facet );
}
ArenaMoongate mg = new ArenaMoongate( arena.GateIn == Point3D.Zero ? arena.Outside : arena.GateIn, arena.Facet, tp );
m_StartedBeginCountdown = true;
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
tp.Register( pl.Mobile );
pl.Mobile.Frozen = false; // reset timer just in case
pl.Mobile.Frozen = true;
Debuff( pl.Mobile );
CancelSpell( pl.Mobile );
pl.Mobile.Delta( MobileDelta.Noto );
}
arena.MoveInside( p.Players, i );
}
if ( m_EventGame != null )
m_EventGame.OnStart();
StartCountdown( 10, new CountdownCallback( SendBeginGump ) );
mg.Appear( m_GatePoint, m_GateFacet );
}
else
{
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp != null )
dp.Mobile.SendMessage( "The duel could not be started because all arenas are full. If you want to stop waiting for a free arena, yield the duel." );
}
}
StartCountdown( 10, new CountdownCallback( SendReadyGump ) );
}
return;
}
m_ReadyWait = true;
bool isAllReady = true;
Type[] types = new[]{ typeof( ReadyGump ) };
for ( int i = 0; i < m_Participants.Count; ++i )
{
Participant p = (Participant)m_Participants[i];
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer pl = p.Players[j];
if ( pl == null )
continue;
Mobile mob = pl.Mobile;
if ( pl.Ready )
{
if ( m_Tournament == null )
CloseAndSendGump( mob, new ReadyGump( mob, this, count ), types );
}
else
{
isAllReady = false;
}
}
}
if ( count == -1 && isAllReady )
StartCountdown( 3, new CountdownCallback( SendReadyGump ) );
}
public static void CloseAndSendGump( Mobile mob, Gump g, params Type[] types )
{
CloseAndSendGump( mob.NetState, g, types );
}
public static void CloseAndSendGump( NetState ns, Gump g, params Type[] types )
{
if ( ns != null ) {
Mobile mob = ns.Mobile;
if ( mob != null ) {
foreach ( Type type in types ) {
mob.CloseGump( type );
}
mob.SendGump( g );
}
}
/*if ( ns == null )
return;
for ( int i = 0; i < types.Length; ++i )
ns.Send( new CloseGump( Gump.GetTypeID( types[i] ), 0 ) );
g.SendTo( ns );
ns.AddGump( g );
Packet[] packets = new Packet[types.Length + 1];
for ( int i = 0; i < types.Length; ++i )
packets[i] = new CloseGump( Gump.GetTypeID( types[i] ), 0 );
packets[types.Length] = (Packet) typeof( Gump ).InvokeMember( "Compile", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, g, null, null );
bool compress = ns.CompressionEnabled;
ns.CompressionEnabled = false;
ns.Send( BindPackets( compress, packets ) );
ns.CompressionEnabled = compress;*/
}
/*public static Packet BindPackets( bool compress, params Packet[] packets )
{
if ( packets.Length == 0 )
throw new ArgumentException( "No packets to bind", "packets" );
byte[][] compiled = new byte[packets.Length][];
int[] lengths = new int[packets.Length];
int length = 0;
for ( int i = 0; i < packets.Length; ++i )
{
compiled[i] = packets[i].Compile( compress, out lengths[i] );
length += lengths[i];
}
return new BoundPackets( length, compiled, lengths );
}
private class BoundPackets : Packet
{
public BoundPackets( int length, byte[][] compiled, int[] lengths ) : base( 0, length )
{
m_Stream.Seek( 0, System.IO.SeekOrigin.Begin );
for ( int i = 0; i < compiled.Length; ++i )
m_Stream.Write( compiled[i], 0, lengths[i] );
}
}*/
}
}