/// <summary>Executes the command.</summary> /// <param name="actionInput">The full input specified for executing the command.</param> public override void Execute(ActionInput actionInput) { // Generate an item changed owner event. IController sender = actionInput.Controller; ContextualStringBuilder csb = new ContextualStringBuilder(sender.Thing, this.parent); csb.Append(@"$ActiveThing.Name drops $Thing.Name.", ContextualStringUsage.WhenNotBeingPassedToOriginator); csb.Append(@"You drop $Thing.Name.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); SensoryMessage message = new SensoryMessage(SensoryType.Sight, 100, csb); var changeOwnerEvent = new ChangeOwnerEvent(sender.Thing, message, sender.Thing, this.parent, this.thing); // Broadcast as a request and see if anything wants to prevent the event. this.parent.Eventing.OnMovementRequest(changeOwnerEvent, EventScope.ParentsDown); if (!changeOwnerEvent.IsCancelled) { // Always have to remove an item before adding it because of the event observer system. // @@@ TODO: Test, this may be broken now... this.thing.Parent.Remove(this.thing); this.parent.Add(this.thing); //// @@@ BUG: Saving currently throws a NotImplementedException. Disabled for now... this.thing.Save(); this.parent.Save(); // Broadcast the event. this.parent.Eventing.OnMovementEvent(changeOwnerEvent, EventScope.ParentsDown); } }
/// <summary>Initializes a new instance of the SensoryMessage class.</summary> /// <param name="targetedSense">The sense this message is for.</param> /// <param name="messageStrength">The strength of the message.</param> /// <param name="message">The message contents.</param> /// <param name="context">The context for the view processor to use to render the message.</param> public SensoryMessage(SensoryType targetedSense, int messageStrength, ContextualStringBuilder message, Hashtable context) { TargetedSense = targetedSense; MessageStrength = messageStrength; Message = message; Context = context; }
/// <summary> /// Initializes a new instance of the SensoryMessage class. /// </summary> /// <param name="targetedSense">The sense this message is for.</param> /// <param name="messageStrength">The strength of the message.</param> /// <param name="message">The message contents.</param> /// <param name="context">The context for the view processor to use to render the message.</param> public SensoryMessage(SensoryType targetedSense, int messageStrength, ContextualStringBuilder message, Hashtable context) { this.TargetedSense = targetedSense; this.MessageStrength = messageStrength; this.Message = message; this.Context = context; }
/// <summary>Try to log this player into the game.</summary> /// <param name="session">The session.</param> /// <returns>True if the player successfully logged in.</returns> public bool LogIn(Session session) { var player = Parent; // If the player isn't located anywhere yet, try to drop them in the default room. // (Expect that even new characters may gain a starting position via custom character generation // flows which let the user to select a starting spawn area.) var targetPlayerStartingPosition = player.Parent != null ? player.Parent : FindDefaultRoom(); if (targetPlayerStartingPosition == null) { session.WriteLine("Could not place character in the game world. Please contact an administrator."); return(false); } // Prepare a login request and event. var csb = new ContextualStringBuilder(player, targetPlayerStartingPosition); csb.Append($"{session.Thing.Name} enters the world.", ContextualStringUsage.WhenNotBeingPassedToOriginator); csb.Append($"You enter the world.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new PlayerLogInEvent(player, message); // Broadcast the login request to the player's current location (IE their parent room), if applicable. player.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); // Also broadcast the request to any registered global listeners. PlayerManager.Instance.OnPlayerLogInRequest(player, e); // If nothing canceled this event request, carry on with the login. if (!e.IsCancelled) { targetPlayerStartingPosition.Add(player); DateTime universalTime = DateTime.Now.ToUniversalTime(); PlayerData.LastLogin = universalTime.ToString("s", DateTimeFormatInfo.InvariantInfo) + "Z"; PlayerData.LastIPAddress = session.Connection.CurrentIPAddress.ToString(); session.Thing = player; session.User.LastLogInTime = DateTime.Now; // Should this occur when user was authenticated instead? // Broadcast that the player successfully logged in, to their login location. player.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); // TODO: Fix: Sending this before the player has registered as being in the PlayingState causes some problems, // including the last character creation or password prompt from printing again as the user's prompt. // Need to figure out if this can be straightened out in a clean way. // See: https://github.com/DavidRieman/WheelMUD/issues/86#issuecomment-787057858 PlayerManager.Instance.OnPlayerLogIn(player, e); return(true); } return(false); }
/// <summary>Try to log this player into the game.</summary> /// <param name="session">The session.</param> /// <returns>True if the player successfully logged in.</returns> public bool LogIn(Session session) { var player = this.Parent; // If the player isn't located anywhere yet, try to drop them in the default room. // (Expect that even new characters may gain a starting position via custom character generation // flows which let the user to select a starting spawn area.) var targetPlayerStartingPosition = player.Parent != null ? player.Parent : FindDefaultRoom(); if (targetPlayerStartingPosition == null) { session.Write("Could not place character in the game world. Please contact an administrator."); return(false); } // Prepare a login request and event. var csb = new ContextualStringBuilder(player, targetPlayerStartingPosition); csb.Append(@"$ActiveThing.Name enters the world.", ContextualStringUsage.WhenNotBeingPassedToOriginator); csb.Append(@"You enter the world.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new PlayerLogInEvent(player, message); // Broadcast the login request to the player's current location (IE their parent room), if applicable. player.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); // Also broadcast the request to any registered global listeners. PlayerManager.OnPlayerLogInRequest(player, e); // If nothing canceled this event request, carry on with the login. if (!e.IsCancelled) { player.Parent = targetPlayerStartingPosition; DateTime universalTime = DateTime.Now.ToUniversalTime(); this.PlayerData.LastLogin = universalTime.ToString("s", DateTimeFormatInfo.InvariantInfo) + "Z"; this.PlayerData.LastIPAddress = session.Connection.CurrentIPAddress.ToString(); session.Thing = player; session.User.LastLogInTime = DateTime.Now; // Should this occur when user was authenticated instead? // Broadcast that the player successfully logged in, to their login location. player.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); PlayerManager.OnPlayerLogIn(player, e); return(true); } return(false); }
/// <summary>Sets the value of the stat.</summary> /// <param name="value">The new value.</param> /// <param name="sender">The sender of the stat change.</param> /// <param name="message">The contextual message to broadcast with the change.</param> public virtual void SetValue(int value, Thing sender, ContextualStringBuilder message) { lock (lockObject) { int oldValue = currentValue; if (Host != null) { // Check if the player is in the character creation state. Thing hostThing = Host.Thing; if (hostThing != null) { var e = new StatChangeEvent( hostThing, new SensoryMessage(SensoryType.Sight, 100, message), this, value - oldValue, oldValue); hostThing.Eventing.OnCombatRequest(e, EventScope.ParentsDown); if (!e.IsCancelled) { if (value >= maxValue) { currentValue = maxValue; } else if (value <= minValue) { currentValue = minValue; } else { currentValue = value; } hostThing.Eventing.OnCombatEvent(e, EventScope.ParentsDown); } } else { currentValue = value; } } else { currentValue = value; } } }
/// <summary>Try to log this player out of the game.</summary> /// <returns>Indicates whether the logout was successful or not.</returns> public bool LogOut() { var player = this.Parent; // Prepare a logout request and event. var csb = new ContextualStringBuilder(player, player.Parent); csb.Append(@"$ActiveThing.Name exits the world.", ContextualStringUsage.WhenNotBeingPassedToOriginator); csb.Append(@"You exit the world.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new PlayerLogOutEvent(player, message); // Broadcast the logout request to the player's current location (if applicable). player.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); // Also broadcast the request to any registered global listeners. PlayerManager.OnPlayerLogOutRequest(player, e); // If nothing canceled this event request, carry on with the logout. if (!e.IsCancelled) { DateTime universalTime = DateTime.Now.ToUniversalTime(); this.PlayerData.LastLogout = universalTime.ToString("s", DateTimeFormatInfo.InvariantInfo) + "Z"; player.Save(); this.Dispose(); player.Dispose(); // Broadcast that the player successfully logged out, to their parent (IE room). player.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); PlayerManager.OnPlayerLogOut(player, e); return(true); } return(false); }
/// <summary>Open or close this behavior's parent, via the specified actor.</summary> /// <param name="actor">The actor doing the opening or closing.</param> /// <param name="verb">Whether this is an "open" or "close" action.</param> /// <param name="newOpenedState">The new IsOpen state to be set, if the request is not cancelled.</param> private void OpenOrClose(Thing actor, string verb, bool newOpenedState) { // If we're already in the desired opened/closed state, we're already done with state changes. if (newOpenedState == this.IsOpen) { // @@@ TODO: Message to the actor that it is already open/closed. return; } // Prepare the Close/Open game event for sending as a request, and if not cancelled, again as an event. var csb = new ContextualStringBuilder(actor, this.Parent); csb.Append(@"You " + verb + " $TargetThing.Name.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); csb.Append(@"$ActiveThing.Name " + verb + "s you.", ContextualStringUsage.OnlyWhenBeingPassedToReceiver); csb.Append(@"$ActiveThing.Name " + verb + "s $TargetThing.Name.", ContextualStringUsage.WhenNotBeingPassedToReceiverOrOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new OpenCloseEvent(this.Parent, newOpenedState, actor, message); // Broadcast the Open or Close Request and carry on if nothing cancelled it. // Use a temporary ref to our own parent to avoid race conditions like sudden parent removal. var thisThing = this.Parent; if (thisThing != null) { // Broadcast from the parents of the openable/closable thing (IE the rooms an openable exit is attached to). thisThing.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); if (!e.IsCancelled) { // Open or Close the thing. this.IsOpen = newOpenedState; // Broadcast the Open or Close event. thisThing.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); } } }
/// <summary>Decreases the value of the stat.</summary> /// <param name="value">The amount to increase.</param> /// <param name="sender">The sender of the stat change.</param> /// <param name="message">The contextual message to broadcast with the change.</param> public void Decrease(int value, Thing sender, ContextualStringBuilder message) { SetValue(Value - value, sender, message); }
/// <summary>Lock or unlock this behavior's parent, via the specified actor.</summary> /// <param name="actor">The actor doing the locking or unlocking.</param> /// <param name="verb">Whether this is an "lock" or "unlock" action.</param> /// <param name="newLockedState">The new IsLocked state to be set, if the request is not cancelled.</param> private void LockOrUnlock(Thing actor, string verb, bool newLockedState) { // If we're already in the desired locked/unlocked state, we're already done with state changes. if (newLockedState == this.IsLocked) { // @@@ TODO: Message to the actor that it is already locked/unlocked. return; } // Use a temporary ref to our own parent to avoid race conditions like sudden parent removal. var thisThing = this.Parent; if (newLockedState && thisThing != null) { // If we are attempting to lock an opened thing, cancel the lock attempt. var opensClosesBehavior = thisThing.Behaviors.FindFirst<OpensClosesBehavior>(); if (opensClosesBehavior != null && opensClosesBehavior.IsOpen) { // @@@ TODO: Message to the actor that they can't lock an open thing. return; } } // Prepare the Lock/Unlock game event for sending as a request, and if not cancelled, again as an event. var csb = new ContextualStringBuilder(actor, this.Parent); csb.Append(@"You " + verb + " $TargetThing.Name.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); csb.Append(@"$ActiveThing.Name " + verb + "s you.", ContextualStringUsage.OnlyWhenBeingPassedToReceiver); csb.Append(@"$ActiveThing.Name " + verb + "s $TargetThing.Name.", ContextualStringUsage.WhenNotBeingPassedToReceiverOrOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new LockUnlockEvent(this.Parent, false, actor, message); // Broadcast the Lock or Unlock Request and carry on if nothing cancelled it. if (thisThing != null) { // Broadcast from the parents of the lockable/unlockable thing (IE a room or inventory where the lockable resides). thisThing.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); if (!e.IsCancelled) { // Lock or Unlock the thing. this.IsLocked = newLockedState; // Broadcast the Lock or Unlock event. thisThing.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); } } }
/// <summary> /// Open or close this behavior's parent, via the specified actor. /// </summary> /// <param name="actor">The actor doing the opening or closing.</param> /// <param name="verb">Whether this is an "open" or "close" action.</param> /// <param name="newOpenedState">The new IsOpen state to be set, if the request is not cancelled.</param> private void OpenOrClose(Thing actor, string verb, bool newOpenedState) { // If we're already in the desired opened/closed state, we're already done with state changes. if (newOpenedState == this.IsOpen) { // @@@ TODO: Message to the actor that it is already open/closed. return; } // Prepare the Close/Open game event for sending as a request, and if not cancelled, again as an event. var csb = new ContextualStringBuilder(actor, this.Parent); csb.Append(@"You " + verb + " $TargetThing.Name.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); csb.Append(@"$ActiveThing.Name " + verb + "s you.", ContextualStringUsage.OnlyWhenBeingPassedToReceiver); csb.Append(@"$ActiveThing.Name " + verb + "s $TargetThing.Name.", ContextualStringUsage.WhenNotBeingPassedToReceiverOrOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new OpenCloseEvent(this.Parent, newOpenedState, actor, message); // Broadcast the Open or Close Request and carry on if nothing cancelled it. // Use a temporary ref to our own parent to avoid race conditions like sudden parent removal. var thisThing = this.Parent; if (thisThing != null) { // Broadcast from the parents of the openable/closable thing (IE the rooms an openable exit is attached to). thisThing.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); if (!e.IsCancelled) { // Open or Close the thing. this.IsOpen = newOpenedState; // Broadcast the Open or Close event. thisThing.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); } } }
/// <summary> /// Sets the value of the stat. /// </summary> /// <param name="value">The new value.</param> /// <param name="sender">The sender of the stat change.</param> /// <param name="message">The contextual message to broadcast with the change.</param> public virtual void SetValue(int value, Thing sender, ContextualStringBuilder message) { lock (this.lockObject) { int oldValue = this.currentValue; if (this.Host != null) { // Check if the player is in the character creation state. Thing hostThing = this.Host.Thing; if (hostThing != null) { var e = new StatChangeEvent( hostThing, new SensoryMessage(SensoryType.Sight, 100, message), this, value - oldValue, oldValue); hostThing.Eventing.OnCombatRequest(e, EventScope.ParentsDown); if (!e.IsCancelled) { if (value >= this.maxValue) { this.currentValue = this.maxValue; } else if (value <= this.minValue) { this.currentValue = this.minValue; } else { this.currentValue = value; } hostThing.Eventing.OnCombatEvent(e, EventScope.ParentsDown); } } else { this.currentValue = value; } } else { this.currentValue = value; } } }
/// <summary> /// Increases the value of the stat. /// </summary> /// <param name="value">The amount to increase.</param> /// <param name="sender">The sender of the stat change.</param> /// <param name="message">The contextual message to broadcast with the change.</param> public void Increase(int value, Thing sender, ContextualStringBuilder message) { this.SetValue(this.Value + value, sender, message); }
/// <summary> /// Initializes a new instance of the SensoryMessage class. /// </summary> /// <param name="targetedSense">The sense this message is for.</param> /// <param name="messageStrength">The strength of the message.</param> /// <param name="message">The message contents.</param> public SensoryMessage(SensoryType targetedSense, int messageStrength, ContextualStringBuilder message) : this(targetedSense, messageStrength, message, new Hashtable()) { }
/// <summary>Initializes a new instance of the SensoryMessage class.</summary> /// <param name="targetedSense">The sense this message is for.</param> /// <param name="messageStrength">The strength of the message.</param> /// <param name="message">The message contents.</param> public SensoryMessage(SensoryType targetedSense, int messageStrength, ContextualStringBuilder message) : this(targetedSense, messageStrength, message, new Hashtable()) { }
/// <summary>Increases the value of the stat.</summary> /// <param name="value">The amount to increase.</param> /// <param name="sender">The sender of the stat change.</param> /// <param name="message">The contextual message to broadcast with the change.</param> public void Increase(int value, Thing sender, ContextualStringBuilder message) { this.SetValue(this.Value + value, sender, message); }
/// <summary> /// Try to log this player out of the game. /// </summary> /// <returns>Indicates whether the logout was successful or not.</returns> public bool LogOut() { var player = this.Parent; // Prepare a logout request and event. var csb = new ContextualStringBuilder(player, player.Parent); csb.Append(@"$ActiveThing.Name exits the world.", ContextualStringUsage.WhenNotBeingPassedToOriginator); csb.Append(@"You exit the world.", ContextualStringUsage.OnlyWhenBeingPassedToOriginator); var message = new SensoryMessage(SensoryType.Sight, 100, csb); var e = new PlayerLogOutEvent(player, message); // Broadcast the logout request to the player's current location (if applicable). player.Eventing.OnMiscellaneousRequest(e, EventScope.ParentsDown); // Also broadcast the request to any registered global listeners. PlayerManager.OnPlayerLogOutRequest(player, e); // If nothing canceled this event request, carry on with the logout. if (!e.IsCancelled) { DateTime universalTime = DateTime.Now.ToUniversalTime(); this.PlayerData.LastLogout = universalTime.ToString("s", DateTimeFormatInfo.InvariantInfo) + "Z"; player.Save(); this.Dispose(); player.Dispose(); // Broadcast that the player successfully logged out, to their parent (IE room). player.Eventing.OnMiscellaneousEvent(e, EventScope.ParentsDown); PlayerManager.OnPlayerLogOut(player, e); return true; } return false; }