diff --git a/Scripts/Engines/Factions/Core/Faction.cs b/Scripts/Engines/Factions/Core/Faction.cs index 523a64a97..617b90970 100644 --- a/Scripts/Engines/Factions/Core/Faction.cs +++ b/Scripts/Engines/Factions/Core/Faction.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using Server; using Server.Items; using Server.Guilds; @@ -9,13 +10,14 @@ using Server.Targeting; using Server.Accounting; using Server.Commands; using Server.Commands.Generic; -using System.Collections.Generic; namespace Server.Factions { [CustomEnum( new string[]{ "Minax", "Council of Mages", "True Britannians", "Shadowlords" } )] public abstract class Faction : IComparable { + public int ZeroRankOffset; + private FactionDefinition m_Definition; private FactionState m_State; private StrongholdRegion m_StrongholdRegion; @@ -72,7 +74,7 @@ namespace Server.Factions set{ m_State.Members = value; } } - public static readonly TimeSpan LeavePeriod = TimeSpan.FromDays( 7.0 ); + public static readonly TimeSpan LeavePeriod = TimeSpan.FromDays( 3.0 ); public bool FactionMessageReady { @@ -139,6 +141,55 @@ namespace Server.Factions } } + public static void HandleAtrophy() + { + foreach ( Faction f in Factions ) + { + if ( !f.State.IsAtrophyReady ) + return; + } + + List activePlayers = new List(); + + foreach ( Faction f in Factions ) + { + foreach ( PlayerState ps in f.Members ) + { + if ( ps.KillPoints > 0 && ps.IsActive ) + activePlayers.Add( ps ); + } + } + + int distrib = 0; + + foreach ( Faction f in Factions ) + distrib += f.State.CheckAtrophy(); + + if ( activePlayers.Count == 0 ) + return; + + for ( int i = 0; i < distrib; ++i ) + activePlayers[Utility.Random( activePlayers.Count )].KillPoints++; + } + + public static void DistributePoints( int distrib ) { + List activePlayers = new List(); + + foreach ( Faction f in Factions ) { + foreach ( PlayerState ps in f.Members ) { + if ( ps.KillPoints > 0 && ps.IsActive ) { + activePlayers.Add( ps ); + } + } + } + + if ( activePlayers.Count > 0 ) { + for ( int i = 0; i < distrib; ++i ) { + activePlayers[Utility.Random( activePlayers.Count )].KillPoints++; + } + } + } + public void BeginHonorLeadership( Mobile from ) { from.SendLocalizedMessage( 502090 ); // Click on the player whom you wish to honor. @@ -167,6 +218,7 @@ namespace Server.Factions } else { + recvState.LastHonorTime = DateTime.Now; giveState.KillPoints -= 5; recvState.KillPoints += 4; @@ -182,8 +234,7 @@ namespace Server.Factions public virtual void AddMember( Mobile mob ) { - PlayerState ps = new PlayerState( mob, this, Members ); - Members.Add( ps ); + Members.Insert( ZeroRankOffset, new PlayerState( mob, this, Members ) ); mob.AddToBackpack( FactionItem.Imbue( new Robe(), this, false, Definition.HuePrimary ) ); mob.SendLocalizedMessage( 1010374 ); // You have been granted a robe which signifies your faction @@ -193,8 +244,6 @@ namespace Server.Factions mob.FixedEffect( 0x373A, 10, 30 ); mob.PlaySound( 0x209 ); - - OnRankUpdate( ps ); } public static bool IsNearType( Mobile mob, Type type, int range ) @@ -226,20 +275,6 @@ namespace Server.Factions public static bool IsNearType( Mobile mob, Type[] types, int range ) { - /* - bool mobs = type.IsSubclassOf( typeof( Mobile ) ); - bool items = type.IsSubclassOf( typeof( Item ) ); - - IPooledEnumerable eable; - - if( mobs ) - eable = mob.GetMobilesInRange( range ); - else if( items ) - eable = mob.GetItemsInRange( range ); - else - return false; - * */ - IPooledEnumerable eable = mob.GetObjectsInRange( range ); foreach( object obj in eable ) @@ -265,15 +300,28 @@ namespace Server.Factions if ( pl == null || !Members.Contains( pl ) ) return; + int killPoints = pl.KillPoints; + + if ( pl.RankIndex != -1 ) { + while ( ( pl.RankIndex + 1 ) < ZeroRankOffset ) { + PlayerState pNext = Members[pl.RankIndex+1] as PlayerState; + Members[pl.RankIndex+1] = pl; + Members[pl.RankIndex] = pNext; + pl.RankIndex++; + pNext.RankIndex--; + } + + ZeroRankOffset--; + } + Members.Remove( pl ); - PlayerMobile pm = (PlayerMobile) pl.Mobile; - if( pm == null ) + PlayerMobile pm = (PlayerMobile)pl.Mobile; + if ( pm == null ) return; Mobile mob = pl.Mobile; - if ( pm.FactionPlayerState == pl ) - { + if ( pm.FactionPlayerState == pl ) { pm.FactionPlayerState = null; mob.InvalidateProperties(); @@ -295,6 +343,9 @@ namespace Server.Factions pm.ValidateEquipment(); } + + if ( killPoints > 0 ) + DistributePoints( killPoints ); } public void RemoveMember( Mobile mob ) @@ -304,6 +355,8 @@ namespace Server.Factions if ( pl == null || !Members.Contains( pl ) ) return; + int killPoints = pl.KillPoints; + if( mob.Backpack != null ) { //Ordinarily, through normal faction removal, this will never find any sigils. @@ -314,6 +367,18 @@ namespace Server.Factions ((Sigil)sigils[i]).ReturnHome(); } + if ( pl.RankIndex != -1 ) { + while ( ( pl.RankIndex + 1 ) < ZeroRankOffset ) { + PlayerState pNext = Members[pl.RankIndex+1]; + Members[pl.RankIndex+1] = pl; + Members[pl.RankIndex] = pNext; + pl.RankIndex++; + pNext.RankIndex--; + } + + ZeroRankOffset--; + } + Members.Remove( pl ); if ( mob is PlayerMobile ) @@ -338,6 +403,9 @@ namespace Server.Factions if ( mob is PlayerMobile ) ((PlayerMobile)mob).ValidateEquipment(); + + if ( killPoints > 0 ) + DistributePoints( killPoints ); } public void JoinGuilded( PlayerMobile mob, Guild guild ) @@ -494,57 +562,13 @@ namespace Server.Factions return true; } - protected virtual void OnRankUpdate( PlayerState pl ) - { - } - - public void UpdateRanks() - { - List members = Members; - - List list = new List( members ); - - list.Sort(); - - RankDefinition[] ranks = m_Definition.Ranks; - - for ( int i = 0; i < list.Count; ++i ) - { - PlayerState pl = list[i]; - - int percent; - - if ( list.Count == 1 ) - percent = 1000; - else - percent = (i * 1000) / (list.Count - 1); - - RankDefinition rank = null; - - for ( int j = 0; j < ranks.Length; ++j ) - { - RankDefinition check = ranks[j]; - - if ( percent >= check.Required ) - { - rank = check; - break; - } - } - - if ( pl.Rank != rank ) - { - pl.Rank = rank; - OnRankUpdate( pl ); - } - } - } - public static void Initialize() { EventSink.Login += new LoginEventHandler( EventSink_Login ); EventSink.Logout += new LogoutEventHandler( EventSink_Logout ); + Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 10.0 ), new TimerCallback( HandleAtrophy ) ); + Timer.DelayCall( TimeSpan.FromSeconds( 30.0 ), TimeSpan.FromSeconds( 30.0 ), new TimerCallback( ProcessTick ) ); CommandSystem.Register( "FactionElection", AccessLevel.GameMaster, new CommandEventHandler( FactionElection_OnCommand ) ); @@ -1018,7 +1042,11 @@ namespace Server.Factions if ( killerState != null && killerPack != null ) { - if ( Sigil.ExistsOn( killer ) ) + if ( killer.GetDistanceToSqrt( victim ) > 64 ) { + sigil.ReturnHome(); + killer.SendLocalizedMessage( 1042230 ); // The sigil has gone back to its home location. + } + else if ( Sigil.ExistsOn( killer ) ) { sigil.ReturnHome(); killer.SendLocalizedMessage( 1010258 ); // The sigil has gone back to its home location because you already have a sigil. @@ -1050,7 +1078,6 @@ namespace Server.Factions if ( silver > 0 ) killer.SendLocalizedMessage( 1042748, silver.ToString( "N0" ) ); // Thou hast earned ~1_AMOUNT~ silver for vanquishing the vile creature. - } #region Ethics @@ -1117,6 +1144,11 @@ namespace Server.Factions { if ( victimState.KillPoints > 0 ) { + victimState.IsActive = true; + + if ( 1 > Utility.Random( 3 ) ) + killerState.IsActive = true; + int silver = 0; silver = killerState.Faction.AwardSilver( killer, award * 40 ); diff --git a/Scripts/Engines/Factions/Core/FactionItem.cs b/Scripts/Engines/Factions/Core/FactionItem.cs index a238498a5..bce0b1e9b 100644 --- a/Scripts/Engines/Factions/Core/FactionItem.cs +++ b/Scripts/Engines/Factions/Core/FactionItem.cs @@ -35,6 +35,14 @@ namespace Server.Factions m_Expiration = DateTime.Now + ExpirationPeriod; } + public void CheckAttach() + { + if ( !HasExpired ) + Attach(); + else + Detach(); + } + public void Attach() { if ( m_Item is IFactionItem ) diff --git a/Scripts/Engines/Factions/Core/FactionState.cs b/Scripts/Engines/Factions/Core/FactionState.cs index 47b4da614..1fddd425a 100644 --- a/Scripts/Engines/Factions/Core/FactionState.cs +++ b/Scripts/Engines/Factions/Core/FactionState.cs @@ -13,12 +13,15 @@ namespace Server.Factions private Election m_Election; private List m_FactionItems; private List m_FactionTraps; + private DateTime m_LastAtrophy; private const int BroadcastsPerPeriod = 2; private static readonly TimeSpan BroadcastPeriod = TimeSpan.FromHours( 1.0 ); private DateTime[] m_LastBroadcasts = new DateTime[BroadcastsPerPeriod]; + public DateTime LastAtrophy{ get{ return m_LastAtrophy; } set{ m_LastAtrophy = value; } } + public bool FactionMessageReady { get @@ -33,6 +36,38 @@ namespace Server.Factions } } + public bool IsAtrophyReady{ get{ return DateTime.Now >= (m_LastAtrophy + TimeSpan.FromHours( 47.0 )); } } + + public int CheckAtrophy() + { + if ( DateTime.Now < (m_LastAtrophy + TimeSpan.FromHours( 47.0 )) ) + return 0; + + int distrib = 0; + m_LastAtrophy = DateTime.Now; + + List members = new List( m_Members ); + + for ( int i = 0; i < members.Count; ++i ) + { + PlayerState ps = members[i]; + + if ( ps.IsActive ) + { + ps.IsActive = false; + continue; + } + else if ( ps.KillPoints > 0 ) + { + int atrophy = ( ps.KillPoints + 9 ) / 10; + ps.KillPoints -= atrophy; + distrib += atrophy; + } + } + + return distrib; + } + public void RegisterBroadcast() { for ( int i = 0; i < m_LastBroadcasts.Length; ++i ) @@ -124,6 +159,11 @@ namespace Server.Factions switch ( version ) { + case 5: + { + m_LastAtrophy = reader.ReadDateTime(); + goto case 4; + } case 4: { int count = reader.ReadEncodedInt(); @@ -152,6 +192,9 @@ namespace Server.Factions m_Commander = reader.ReadMobile(); + if ( version < 5 ) + m_LastAtrophy = DateTime.Now; + if ( version < 4 ) { DateTime time = reader.ReadDateTime(); @@ -176,7 +219,18 @@ namespace Server.Factions } m_Faction.State = this; - m_Faction.UpdateRanks(); + + m_Faction.ZeroRankOffset = m_Members.Count; + m_Members.Sort(); + + for ( int i = m_Members.Count - 1; i >= 0; i-- ) { + PlayerState player = m_Members[i]; + + if ( player.KillPoints <= 0 ) + m_Faction.ZeroRankOffset = i; + else + player.RankIndex = i; + } m_FactionItems = new List(); @@ -188,10 +242,7 @@ namespace Server.Factions { FactionItem factionItem = new FactionItem( reader, m_Faction ); - if ( !factionItem.HasExpired ) - factionItem.Attach(); - else - Timer.DelayCall( TimeSpan.Zero, new TimerCallback( factionItem.Detach ) ); // sandbox detachment + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( factionItem.CheckAttach ) ); // sandbox attachment } } @@ -220,7 +271,9 @@ namespace Server.Factions public void Serialize( GenericWriter writer ) { - writer.WriteEncodedInt( (int) 4 ); // version + writer.WriteEncodedInt( (int) 5 ); // version + + writer.Write( m_LastAtrophy ); writer.WriteEncodedInt( (int) m_LastBroadcasts.Length ); diff --git a/Scripts/Engines/Factions/Core/PlayerState.cs b/Scripts/Engines/Factions/Core/PlayerState.cs index c00546d51..ff9e34fbc 100644 --- a/Scripts/Engines/Factions/Core/PlayerState.cs +++ b/Scripts/Engines/Factions/Core/PlayerState.cs @@ -15,23 +15,132 @@ namespace Server.Factions private MerchantTitle m_MerchantTitle; private RankDefinition m_Rank; private List m_SilverGiven; + private bool m_IsActive; private Town m_Sheriff; private Town m_Finance; + private DateTime m_LastHonorTime; + public Mobile Mobile{ get{ return m_Mobile; } } public Faction Faction{ get{ return m_Faction; } } public List Owner { get { return m_Owner; } } - public int KillPoints{ get{ return m_KillPoints; } set{ m_KillPoints = value; } } public MerchantTitle MerchantTitle{ get{ return m_MerchantTitle; } set{ m_MerchantTitle = value; Invalidate(); } } - public RankDefinition Rank{ get{ return m_Rank; } set{ m_Rank = value; Invalidate(); } } public Town Sheriff{ get{ return m_Sheriff; } set{ m_Sheriff = value; Invalidate(); } } public Town Finance{ get{ return m_Finance; } set{ m_Finance = value; Invalidate(); } } public List SilverGiven { get { return m_SilverGiven; } } + public int KillPoints { + get { return m_KillPoints; } + set { + if ( m_KillPoints != value ) { + if ( value > m_KillPoints ) { + if ( m_KillPoints <= 0 ) { + if ( value <= 0 ) { + m_KillPoints = value; + Invalidate(); + return; + } + + m_Owner.Remove( this ); + m_Owner.Insert( m_Faction.ZeroRankOffset, this ); + + m_RankIndex = m_Faction.ZeroRankOffset; + m_Faction.ZeroRankOffset++; + } + while ( ( m_RankIndex - 1 ) >= 0 ) { + PlayerState p = m_Owner[m_RankIndex-1] as PlayerState; + if ( value > p.KillPoints ) { + m_Owner[m_RankIndex] = p; + m_Owner[m_RankIndex-1] = this; + RankIndex--; + p.RankIndex++; + } + else + break; + } + } + else { + if ( value <= 0 ) { + if ( m_KillPoints <= 0 ) { + m_KillPoints = value; + Invalidate(); + return; + } + + while ( ( m_RankIndex + 1 ) < m_Faction.ZeroRankOffset ) { + PlayerState p = m_Owner[m_RankIndex+1] as PlayerState; + m_Owner[m_RankIndex+1] = this; + m_Owner[m_RankIndex] = p; + RankIndex++; + p.RankIndex--; + } + + m_RankIndex = -1; + m_Faction.ZeroRankOffset--; + } + else { + while ( ( m_RankIndex + 1 ) < m_Faction.ZeroRankOffset ) { + PlayerState p = m_Owner[m_RankIndex+1] as PlayerState; + if ( value < p.KillPoints ) { + m_Owner[m_RankIndex+1] = this; + m_Owner[m_RankIndex] = p; + RankIndex++; + p.RankIndex--; + } + else + break; + } + } + } + + m_KillPoints = value; + Invalidate(); + } + } + } + + private bool m_InvalidateRank = true; + private int m_RankIndex = -1; + + public int RankIndex { get { return m_RankIndex; } set { if ( m_RankIndex != value ) { m_RankIndex = value; m_InvalidateRank = true; } } } + + public RankDefinition Rank { + get { + if ( m_InvalidateRank ) { + RankDefinition[] ranks = m_Faction.Definition.Ranks; + int percent; + + if ( m_Owner.Count == 1 ) + percent = 1000; + else if ( m_RankIndex == -1 ) + percent = 0; + else + percent = ( ( m_Faction.ZeroRankOffset - m_RankIndex ) * 1000 ) / m_Faction.ZeroRankOffset; + + for ( int i = 0; i < ranks.Length; i++ ) { + RankDefinition check = ranks[i]; + + if ( percent >= check.Required ) { + m_Rank = check; + m_InvalidateRank = false; + break; + } + } + + Invalidate(); + } + + return m_Rank; + } + } + + public DateTime LastHonorTime{ get{ return m_LastHonorTime; } set{ m_LastHonorTime = value; } } public DateTime Leaving{ get{ return m_Leaving; } set{ m_Leaving = value; } } public bool IsLeaving{ get{ return ( m_Leaving > DateTime.MinValue ); } } + public bool IsActive{ get{ return m_IsActive; } set{ m_IsActive = value; } } + public bool CanGiveSilverTo( Mobile mob ) { if ( m_SilverGiven == null ) @@ -61,7 +170,11 @@ namespace Server.Factions public void Invalidate() { if ( m_Mobile is PlayerMobile ) - ((PlayerMobile)m_Mobile).InvalidateProperties(); + { + PlayerMobile pm = (PlayerMobile)m_Mobile; + pm.InvalidateProperties(); + pm.InvalidateMyRunUO(); + } } public void Attach() @@ -76,8 +189,6 @@ namespace Server.Factions m_Faction = faction; m_Owner = owner; - m_Rank = faction.Definition.Ranks[faction.Definition.Ranks.Length - 1]; - Attach(); Invalidate(); } @@ -91,6 +202,12 @@ namespace Server.Factions switch ( version ) { + case 1: + { + m_IsActive = reader.ReadBool(); + m_LastHonorTime = reader.ReadDateTime(); + goto case 0; + } case 0: { m_Mobile = reader.ReadMobile(); @@ -109,7 +226,10 @@ namespace Server.Factions public void Serialize( GenericWriter writer ) { - writer.WriteEncodedInt( (int) 0 ); // version + writer.WriteEncodedInt( (int) 1 ); // version + + writer.Write( m_IsActive ); + writer.Write( m_LastHonorTime ); writer.Write( (Mobile) m_Mobile ); diff --git a/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs index ac5b2d553..113dec2d8 100644 --- a/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs +++ b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs @@ -277,7 +277,7 @@ namespace Server.Factions if ( mob.Hidden && mob.AccessLevel > AccessLevel.Player ) return false; - if ( !mob.Alive ) + if ( !mob.Alive || mob.IsDeadBondedPet ) return false; Faction faction = Faction.Find( mob, true ); diff --git a/Server/Mobile.cs b/Server/Mobile.cs index 6cdff7497..811b58e75 100644 --- a/Server/Mobile.cs +++ b/Server/Mobile.cs @@ -9823,7 +9823,7 @@ namespace Server if( sendPublicStats || sendPrivateStats ) { - ourState.Send( new MobileStatusExtended( m ) ); + ourState.Send( new MobileStatusExtended( m, m_NetState ) ); } else if( sendAll ) { @@ -10555,7 +10555,7 @@ namespace Server public virtual void OnStatsQuery( Mobile from ) { if( from.Map == this.Map && Utility.InUpdateRange( this, from ) && from.CanSee( this ) ) - from.Send( new MobileStatus( from, this ) ); + from.Send( new MobileStatus( from, this, m_NetState ) ); if( from == this ) Send( new StatLockInfo( this ) ); diff --git a/Server/Network/Packets.cs b/Server/Network/Packets.cs index 2b96810c8..4f7dbb4de 100644 --- a/Server/Network/Packets.cs +++ b/Server/Network/Packets.cs @@ -2725,12 +2725,16 @@ namespace Server.Network public sealed class MobileStatusExtended : Packet { - public MobileStatusExtended( Mobile m ) : base( 0x11 ) + public MobileStatusExtended( Mobile m ) : this( m, m.NetState ) + { + } + + public MobileStatusExtended( Mobile m, NetState ns ) : base( 0x11 ) { string name = m.Name; if ( name == null ) name = ""; - bool sendMaxWeight = (Core.ML && m.NetState != null && m.NetState.SupportsExpansion( Expansion.ML )); + bool sendMaxWeight = (Core.ML && ns != null && ns.SupportsExpansion( Expansion.ML )); this.EnsureCapacity( sendMaxWeight ? 91 :88 ); @@ -2797,12 +2801,16 @@ namespace Server.Network public sealed class MobileStatus : Packet { - public MobileStatus( Mobile beholder, Mobile beheld ) : base( 0x11 ) + public MobileStatus( Mobile beholder, Mobile beheld ) : this( beholder, beheld, beheld.NetState ) + { + } + + public MobileStatus( Mobile beholder, Mobile beheld, NetState ns ) : base( 0x11 ) { string name = beheld.Name; if ( name == null ) name = ""; - bool sendMaxWeight = (Core.ML && beheld.NetState != null && beheld.NetState.SupportsExpansion( Expansion.ML )); + bool sendMaxWeight = (Core.ML && ns != null && ns.SupportsExpansion( Expansion.ML )); this.EnsureCapacity( 43 + (beholder == beheld ? (sendMaxWeight ? 48 : 45) : 0) );