diff --git a/MudEngine/Commands/CommandExit.cs b/MudEngine/Commands/CommandExit.cs index c9f875b..8d10d4b 100644 --- a/MudEngine/Commands/CommandExit.cs +++ b/MudEngine/Commands/CommandExit.cs @@ -1,9 +1,11 @@ -using System; +//Microsoft.NET Framework +using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Text; +//Mud Designer Game Engine using MudEngine.FileSystem; using MudEngine.GameObjects.Characters; using MudEngine.GameManagement; @@ -12,45 +14,73 @@ using MudEngine.GameObjects.Environment; namespace MudEngine.Commands { + /// + /// The Exit command is used to exit the MUD game. + /// Using this command while connected to a MUD server will perform a disconnect from the server. + /// Using the command while running the game in offline mode will simply shut down the game. + /// public class CommandExit : IGameCommand { + /// + /// Used by the Command Engine to allow for overriding any other commands that contain the same name. + /// TODO: Does Overriding Commands still work? This is part of some old code I wrote several years back and might be broke. + /// public Boolean Override { get; set; } + + /// + /// The name of the command. + /// If Override is set to true, this command will override any other command that contains the same name. + /// public String Name { get; set; } + + /// + /// A collection of strings that contains helpfull information for this Command. + /// When the user enteres 'Help Exit' the game will print the content of this collection. + /// This is treated like a virtual book, each entry in the collection is printed as a new line. + /// public List Help { get; set; } + /// + /// Constructor for the class. + /// public CommandExit() { + //Instance the help collection and add our help information to it. + //Typically the Help content is placed within the constructor, but this particular help document + //needs to access information from the player, so we will build our Help collection in the Execute command. Help = new List(); - Help.Add("Exits the game cleanly."); } + /// + /// Executes the command. + /// This method is called from the Command Engine, it is not recommended that you call this method directly. + /// + /// + /// public void Execute(String command, BaseCharacter player) { + //Check if the game is multiplayer. + //Multiplayer games require disconnecting from the server and letting other players in the same Room know + //that this player has left. if (player.ActiveGame.IsMultiplayer) { - //Let other players know that the user walked in. - for (Int32 i = 0; i != player.ActiveGame.GetPlayerCollection().Length; i++) - { - if (player.ActiveGame.GetPlayerCollection()[i].Name == player.Name) - continue; + //Query the Active Games Player collection so that we can build a collection of Players that need to be + //informed of the Player disconnecting from the Server. + var playerQuery = + from p in player.ActiveGame.GetPlayerCollection() + where !p.Name.StartsWith("New") && p.Name != player.Name && p.CurrentWorldLocation == player.CurrentWorldLocation + select p; - String room = player.ActiveGame.GetPlayerCollection()[i].CurrentRoom.Name; - String realm = player.ActiveGame.GetPlayerCollection()[i].CurrentRoom.Realm; - String zone = player.ActiveGame.GetPlayerCollection()[i].CurrentRoom.Zone; - - if ((room == player.CurrentRoom.Name) && (realm == player.CurrentRoom.Realm) && (zone == player.CurrentRoom.Zone)) - { - player.ActiveGame.GetPlayerCollection()[i].Send(player.Name + " has left."); - } - } + //Inform each player found in our LINQ query that the player has disconnected from the Server. + foreach (BaseCharacter p in playerQuery) + p.Send(player.Name + " has left."); ; + //TODO: If a player is in a Group then s/he needs to be removed upon disconnecting. player.Disconnect(); } else { - //Save the player prior to attempting to shutdown. - //Player saving is handled in the server disconnect code but not in game shutdown. - player.Save(player.ActiveGame.DataPaths.Players); + //Call the game's shutdown method which will save all objects and exit the game gracefully. player.ActiveGame.Shutdown(); } } diff --git a/MudEngine/Commands/CommandGetTime.cs b/MudEngine/Commands/CommandGetTime.cs index b138375..cc5b9a7 100644 --- a/MudEngine/Commands/CommandGetTime.cs +++ b/MudEngine/Commands/CommandGetTime.cs @@ -1,27 +1,59 @@ -using System; +//Microsoft.NET Framework +using System; using System.Collections.Generic; using System.Linq; using System.Text; +//Mud Designer Game Engine using MudEngine.GameObjects.Characters; +using MudEngine.GameManagement; namespace MudEngine.Commands { - public class CommandGetTime : MudEngine.GameManagement.IGameCommand + /// + /// The GetTime command is used to print the current in-game time to the player. + /// This command will print the day, month and year along with hour, minute and seconds. + /// + public class CommandGetTime : IGameCommand { + /// + /// Used by the Command Engine to allow for overriding any other commands that contain the same name. + /// TODO: Does Overriding Commands still work? This is part of some old code I wrote several years back and might be broke. + /// + public Boolean Override { get; set; } + + /// + /// The name of the command. + /// If Override is set to true, this command will override any other command that contains the same name. + /// public String Name { get; set; } - public Boolean Override { get; set; } + /// + /// A collection of strings that contains helpfull information for this Command. + /// When the user enteres 'Help Exit' the game will print the content of this collection. + /// This is treated like a virtual book, each entry in the collection is printed as a new line. + /// public List Help { get; set; } + /// + /// Constructor for the class. + /// public CommandGetTime() { + //Instance the help collection and add our help information to it. Help = new List(); Help.Add("Gives you the current time and date in the game world."); } + /// + /// Executes the Command. + /// This method is called from the Command Engine, it is not recommended that you call this method directly. + /// + /// + /// public void Execute(String command, BaseCharacter player) { + //Send the returned String containing the World Time to the player. player.Send(player.ActiveGame.WorldTime.GetCurrentWorldTime()); } } diff --git a/MudEngine/Commands/CommandLinkRoom.cs b/MudEngine/Commands/CommandLinkRoom.cs index 3a43ee1..b3c959b 100644 --- a/MudEngine/Commands/CommandLinkRoom.cs +++ b/MudEngine/Commands/CommandLinkRoom.cs @@ -1,9 +1,11 @@ -using System; +//Microsoft.NET Framework +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; +//Mud Designer Game Engine using MudEngine.FileSystem; using MudEngine.GameManagement; using MudEngine.GameObjects; @@ -12,29 +14,75 @@ using MudEngine.GameObjects.Environment; namespace MudEngine.Commands { + /// + /// The LinkRoom command is used to Link two previously created Rooms together. + /// Rooms linked together can be traversed by players. + /// This command is used to link Rooms dynamically during run-time by Admins, allowing environments to be created + /// and traversed on-the-fly without the need to modify scripts and re-start the server. + /// public class CommandLinkRoom : IGameCommand { + /// + /// Used by the Command Engine to allow for overriding any other commands that contain the same name. + /// TODO: Does Overriding Commands still work? This is part of some old code I wrote several years back and might be broke. + /// public Boolean Override { get; set; } + + /// + /// The name of the command. + /// If Override is set to true, this command will override any other command that contains the same name. + /// public String Name { get; set; } + + /// + /// A collection of strings that contains helpfull information for this Command. + /// When the user enteres 'Help Exit' the game will print the content of this collection. + /// This is treated like a virtual book, each entry in the collection is printed as a new line. + /// public List Help { get; set; } + /// + /// Constructor for the class. + /// public CommandLinkRoom() { + //Instance the help collection and add our help information to it. Help = new List(); - Help.Add("Use this to link two previously created Rooms together"); - Help.Add("Note that this command is not fully implemented and does not work."); + Help.Add("Use this to link two previously created Rooms together."); + //Incase Admins try to use the command, they will know that it's broken. + //Don't convert this class into a Script until it is fully completed. + Help.Add("NOTE: This command is not fully implemented and does not work."); } + /// + /// Executes the command. + /// This method is called from the Command Engine, it is not recommended that you call this method directly. + /// + /// + /// public void Execute(String command, BaseCharacter player) { + //Check if the Player has the correct privileges to Link Rooms together. + //If they are not a Admin or GM then the command will bail. + //This creates the illusion that this command possibly doesn't exist. + if ((player.Role != SecurityRoles.Admin) && (player.Role != SecurityRoles.GM)) + { + player.Send("Invalid Command."); + return; + } + + //Build the Menu system that will be displayed to Admins. player.Send("Room linkage tool"); player.Send("Please select an option."); player.Send("1: Link Room"); player.Send("2: Exit"); + //Read the input from the Admin. string input = player.ReadInput(); Int32 value = 0; + //Attempt to convert their input from a String into a numerical value. + //If they entered a non-numerical value, then the method will exit try { value = Convert.ToInt32(input); @@ -45,122 +93,204 @@ namespace MudEngine.Commands return; } + //Ask what Room the Admin wants to use as the Departing Room. + //Departing Rooms will be where the travel direction originates from. + //meaning if the Admin selects West as the traveling direction, then traveling West will exit the + //Departing Room and enter the Arrival Room. player.Send(""); player.Send("Please select which Realm your departing Room resides within:"); player.Send(""); + //Instance a new Realm that we can use to reference an existing Realm so that we can find the + //Rooms within it that the Admin is looking for. Boolean isValidRealm = false; Realm realm = new Realm(player.ActiveGame); + //Create a Loop incase the Admin enters an invalid Realm, we can just loop + //back and re-ask the Admin the enter a valid Realm name. while (!isValidRealm) { - isValidRealm = true;//Default to true, assume the user entered a valid name. - foreach (Realm r in player.ActiveGame.World.RealmCollection) - { - player.Send(r.Filename + " | ", false); - } + //Now that we are in the loop, set the isValidRealm to true, as we will assume that + //the user will enter a valid Realm name on the first shot. If we can't find a matching + //Realm, then this will get set to false once iterating through Realms is completed. + isValidRealm = true; + //Print each Realm out to the Admin so they may see a complete list to choose from. + //This is the Realm that their first Room will reside within. + //TODO: Currently only linking Rooms within the same Realm/Zone is supported. Need to fix that. + foreach (Realm r in player.ActiveGame.World.RealmCollection) + player.Send(r.Filename + " | ", false); + + //As for the Admins selection, we will place the Admins input on the same line + //as the last message sent to the Admin by setting the newLine parameter to false. player.Send(""); player.Send("Selection: ", false); - + + //Get the Admins input that should specify what Realm they are wanting. input = player.ReadInput(); + //Check if the Admin entered 'Cancel'. If so, then cancel the Link process. if (input.ToLower() == "cancel") { player.Send("Room Linking aborted."); return; } - //Ensure it's a valid name, if not then loop back and try again. - foreach (Realm r in player.ActiveGame.World.RealmCollection) - { - if (r.Filename.ToLower() == input.ToLower()) - { - isValidRealm = true; - realm = r; - break; - } - else - { - isValidRealm = false; - } - } + //Query the Active Games world collection, finding Realms that match the filename entered by the Admin + var realmQuery = + from r in player.ActiveGame.World.RealmCollection + where r.Filename.ToLower() == input.ToLower() + select r; - if (!isValidRealm) + //Check if the query contains a Realm. + if (realmQuery.FirstOrDefault() != null) + { + //The query does in-fact contain a valid Realm. Assign it to our realm field for use later. + realm = realmQuery.FirstOrDefault(); + //We can set this to true, allowing us to exit out of the loop. + isValidRealm = true; + } + //If the query does not contain a Realm, then we ensure that the loop will continue by forcing + //isValidRealm back to false. + else + { + isValidRealm = false; + //Let the Admin know that they entered an invalid Realm name and that they need to try again. player.Send("That Realm does not exist! Please try again."); + } } + //Let the Admin know that they need to now select which Zone the Room they are wanting to link resides within. player.Send(""); player.Send("Please select which Zone your departing Room resides within:"); player.Send(""); + //Instance a new Zone that we can use to reference an existing Zone so that we can find the + //Rooms within it that the Admin is looking for. Boolean isValidZone = false; Zone zone = new Zone(player.ActiveGame); + //Create a Loop incase the Admin enters an invalid Zone, we can just loop + //back and re-ask the Admin the enter a valid Zone name. while (!isValidZone) { - isValidZone = true;//Default to true, assume the user entered a valid name. + //Now that we are in the loop, set the isValidZone to true, as we will assume that + //the user will enter a valid Zone name on the first shot. If we can't find a matching + //Zone, then this will get set to false once iterating through Zone is completed. + isValidZone = true; + + //Print each Zone out to the Admin so they may see a complete list to choose from. + //This is the Zone that their first Room will reside within. + //TODO: Currently only linking Rooms within the same Realm/Zone is supported. Need to fix that. foreach (Zone z in realm.ZoneCollection) { player.Send(z.Filename + " | ", false); } + //As for the Admins selection, we will place the Admins input on the same line + //as the last message sent to the Admin by setting the newLine parameter to false. player.Send(""); player.Send("Selection: ", false); + //Get the Admins input that should specify what Zone they are wanting. input = player.ReadInput(); + //Check if the Admin entered 'Cancel'. If so, then cancel the Link process. if (input.ToLower() == "cancel") { player.Send("Room Linking aborted."); return; } - //Ensure it's a valid name, if not then loop back and try again. - foreach (Zone z in realm.ZoneCollection) - { - if (z.Filename.ToLower() == input.ToLower()) - { - isValidZone = true; - zone = z; - break; - } - else - { - isValidZone = false; - } - } + //Query the stored Realm's Zone collection, finding Zones that match the filename entered by the Admin + var zoneQuery = + from z in realm.ZoneCollection + where z.Filename.ToLower() == input.ToLower() + select z; - if (!isValidZone) + //Check if the query contains a Zone. + if (zoneQuery.FirstOrDefault() != null) + { + //The query does in-fact contain a valid Zone. Assign it to our zone field for use later. + zone = zoneQuery.FirstOrDefault(); + //We can set this to true, allowing us to exit out of the loop. + isValidZone = true; + } + //If the query does not contain a Zone, then we ensure that the loop will continue by forcing + //isValidZone back to false. + else + { + isValidZone = false; + //Let the Admin know that they entered an invalid Zone name and that they need to try again. player.Send("That Zone does not exist! Please try again."); + } } + //Let the Admin know that they need to now select Room they are wanting to link as the departure Room player.Send(""); player.Send("Please select which Room that you wish to be the departing Room:"); player.Send(""); + //Instance a new Room that we can store a reference to a existing Room. + //We will use this reference to link the departure and arrival Rooms together. Boolean isValidRoom = false; Room departingRoom = new Room(player.ActiveGame); + //Create a Loop incase the Admin enters an invalid Room, we can just loop + //back and re-ask the Admin the enter a valid Room name. while (!isValidRoom) { - isValidRoom = true;//Default to true, assume the user entered a valid name. + //Now that we are in the loop, set the isValidRoom to true, as we will assume that + //the user will enter a valid Room name on the first shot. If we can't find a matching + //Room, then this will get set to false once iterating through Room is completed. + isValidRoom = true; + + //Print each Room out to the Admin so they may see a complete list to choose from. + //This will be their departing Room. + //TODO: Currently only linking Rooms within the same Realm/Zone is supported. Need to fix that. foreach (Room r in zone.RoomCollection) { player.Send(r.Filename + " | ", false); } + //As for the Admins selection, we will place the Admins input on the same line + //as the last message sent to the Admin by setting the newLine parameter to false. player.Send(""); player.Send("Selection: ", false); + //Get the Admins input that should specify what Room they are wanting. input = player.ReadInput(); + //Check if the Admin entered 'Cancel'. If so, then cancel the Link process. if (input.ToLower() == "cancel") { player.Send("Room Linking aborted."); return; } + //Query the referenced zone's Room collection, finding Rooms that match the filename entered by the Admin + var roomQuery = + from r in zone.RoomCollection + where r.Filename.ToLower() == input.ToLower() + select r; + + //Check if the query contains a Room. + if (roomQuery.FirstOrDefault() != null) + { + //The query does in-fact contain a valid Room. Assign it to our departingRoom field for use later. + departingRoom = roomQuery.FirstOrDefault(); + //We can set this to true, allowing us to exit out of the loop. + isValidRoom = true; + } + //If the query does not contain a Room, then we ensure that the loop will continue by forcing + //isValidRoom back to false. + else + { + isValidRoom = false; + //Let the Admin know that they entered an invalid Room name and that they need to try again. + player.Send("That Room does not exist! Please try again."); + } + //Ensure it's a valid name, if not then loop back and try again. foreach (Room r in zone.RoomCollection) { diff --git a/MudEngine/GameObjects/Characters/BaseAI.cs b/MudEngine/GameObjects/Characters/BaseAI.cs deleted file mode 100644 index f3c4e80..0000000 --- a/MudEngine/GameObjects/Characters/BaseAI.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using MudEngine.GameManagement; - -namespace MudEngine.GameObjects.Characters -{ - public class BaseAI : BaseCharacter - { - public BaseAI(Game game) : base(game) - { - } - } -} diff --git a/MudEngine/GameObjects/Characters/BaseCharacter.cs b/MudEngine/GameObjects/Characters/BaseCharacter.cs index 40ee6b0..180db58 100644 --- a/MudEngine/GameObjects/Characters/BaseCharacter.cs +++ b/MudEngine/GameObjects/Characters/BaseCharacter.cs @@ -26,10 +26,33 @@ /// public Room CurrentRoom { get; set; } + public String CurrentWorldLocation + { + get + { + return CurrentRoom.Realm + "." + CurrentRoom.Zone + "." + CurrentRoom.Filename; + } + } + /// /// Gets or Sets if this Character is controlled by the user. If not user controlled then it will be AI controlled. /// - public Boolean IsControlled { get; set; } + public Boolean IsControlled + { + get + { + return _IsControlled; + } + set + { + if (value) + { + //TODO: Begin AI initialization + } + _IsControlled = value; + } + } + private Boolean _IsControlled; /// /// Gets if this user has Admin privileges or not. diff --git a/MudEngine/GameObjects/Characters/BaseStats.cs b/MudEngine/GameObjects/Characters/BaseStats.cs new file mode 100644 index 0000000..48eade9 --- /dev/null +++ b/MudEngine/GameObjects/Characters/BaseStats.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MudEngine.GameObjects.Characters +{ + /// + /// This class is the base class that handles the managing of a Characters stats. + /// It was decided to place stats within their own class, to allow for developers to easily add additional + /// stats or adjust how stats are used within their MUD's without having to modify the Character code themselves. + /// + public class BaseStats + { + /// + /// Strength is a measure of muscle, endurance and stamina combined. + /// Strength affects the ability of characters to lift and carry weights, melee attack rolls, + /// damage rolls (for both melee and ranged weapons,) the Jump, Climb, and Swim skills, + /// several combat actions, and general checks involving moving or breaking stubborn objects. + /// + public Int32 Strength { get; set; } + + /// + /// Dexterity encompasses a number of physical attributes including hand-eye coordination, agility, + /// reflexes, fine motor skills, balance and speed of movement; a high dexterity score indicates + /// superiority in all these attributes. Dexterity affects characters with regard to initiative in combat, + /// ranged attack rolls, Armor Class, Reflex saves, and the Balance, Escape Artist, Hide, Move Silently, + /// Open Lock, Ride, Sleight of Hand, Tumble, and Use Rope skills. It also affects the number of additional + /// attacks of opportunity granted by the Combat Reflexes feat. Dexterity is the ability most influenced by + /// outside influences (such as armor). + /// + public Int32 Dexterity { get; set; } + + /// + /// Constitution is a term which encompasses the character's physique, toughness, health and resistance to disease and poison. + /// The higher a character's Constitution, the more hit points that character will have. + /// Constitution also is important for Fortitude saves, the Concentration skill, and fatigue-based general checks. + /// Constitution also determines the duration of a barbarian's rage. + /// Unlike the other ability scores, which render the character unconscious or immobile when they hit 0, + /// having 0 Constitution is fatal. + /// + public Int32 Constitution { get; set; } + + /// + /// Intelligence is similar to IQ, but also includes mnemonic ability, reasoning and learning ability outside + /// those measured by the written word. Intelligence dictates the number of languages a character can learn, + /// and it influences the number of spells a preparation-based arcane spellcaster (like a Wizard) may cast per + /// day, and the effectiveness of said spells. It also affects how many skill points a character gains per level, + /// the Appraise, Craft, Decipher Script, Disable Device, Forgery, Knowledge, Search, and Spellcraft skills, + /// and bardic knowledge checks. + /// + public Int32 Intelligence { get; set; } + + /// + /// Wisdom is a composite term for the characters enlightenment, judgement, wile, willpower and intuitiveness. + /// Wisdom influences the number of spells a divine spellcaster (like clerics, druids, paladins, and rangers) + /// can cast per day, and the effectiveness of said spells. It also affects Will saving throws, the Heal, Listen, + /// Profession, Sense Motive, Spot, and Survival skills, the effectiveness of the Stunning Fist feat, and a + /// monk's quivering palm attack. + /// + public Int32 Wisdom { get; set; } + + /// + /// Charisma is the measure of the character's combined physical attractiveness, persuasiveness, and personal magnetism. + /// A generally non-beautiful character can have a very high charisma due to strong measures of the other two aspects of charisma. + /// Charisma influences how many spells spontaneous arcane spellcasters (like sorcerers and bards) can cast per day, and the + /// effectiveness of said spells. It also affects Bluff, Diplomacy, Disguise, Gather Information, Handle Animal, + /// Intimidate, Perform, and Use Magic Device checks, how often and how effectively clerics and paladins can turn + /// undead, the wild empathy of druids and rangers, and a paladin's lay on hands ability. + /// + public Int32 Charisma { get; set; } + + } +} diff --git a/MudEngine/MudEngine.csproj b/MudEngine/MudEngine.csproj index bfbcc53..2b12138 100644 --- a/MudEngine/MudEngine.csproj +++ b/MudEngine/MudEngine.csproj @@ -74,8 +74,8 @@ - +