Cameron Albert

Ramblings of software development, games and technology.

Archive for the ‘Perenthia PBBG’ Category

Perenthia–Development Diary #2

I have made some decent progress on scaling Perenthia down and re-tooling it for more basic HTML/mobile access. I have the server core complete and am working on the UI components. The first step will be to get the adventuring/combat piece finished and then move on to the crafting system.

The abstract core server pieces I decided to put into a separate library and made the source available on GitHub. I call it “Leo” and it provides the basic game server and provides some interfaces and associated libraries for processing input through the server. Since I am using SignalR for client to server communication I included some code in Leo to provide a input processor and PersistentConnection. The TestConsole project was something I used to initially setup the game libraries. They are now linked into my Perenthia solution and will be updated as I find the need while working on Perenthia.

The Leo source is very early stages and is subject to change frequently. It is also intended primarily for low traffic games such as iOS, Facebook, etc. I will up some instructions once it gets a little more stable.

Here is a screenshot of Perenthia’s new progress so far:

perenthia-ss1

Written by Cameron

May 4, 2012 at 10:56 pm

Perenthia–Development Diary #1

I am working on Perenthia again, doing some re-design work to try and make the game fun but also maintainable by one person, me. I am going to catalog my development journey and the design decisions made along the way. I am trying to take what I learned from the prototype/alpha stage and improve upon it with realistic goals and tasks. Since it is just me working on it, other than contracting out the artwork pieces, I need to stay focused on making it fun and trying to implement the features that I think will be fun.

I have actually already started working on a re-usable library to host multi-player games. I have the basic framework in place which includes a base GameObject that provides a dictionary or properties and implements a behaviors system to allow custom behaviors to be created and attached to objects. I also have a master Game class that can be started and runs the server frame loop, updating the world and any loaded objects, behaviors or game components. I used MEF to define externally loaded components and data libraries. The Game class expects an IWorld implementation, some repositories for objects and users and optionally a list of IGameComponents. The IGameComponents are updated with each frame loop so I figured those would be good for creating things like a weather component, pack AI, etc. The IWorld is also a game component but has a few specific methods defined to access data contained within the world from the Game object. Behaviors are simple objects that can be attached to a game object and also contain and Update method that is called during the game object’s update method.

All this is server side code intended to run a virtual world. In addition to the re-usable game server I have a Perenthia specific library built on top of it that defines the aspects that are unique to Perenthia itself. For instance, Perenthia contains a Skill and Race object and loads that data during the world initialization. The underlying virtual world framework is not aware that skills and races exist, just that game objects are being added, removed and updated.

For data storage I put all world data that loads with the world initialization into SQL tables and a few external flat files. User and player data I am going to store in MongoDb to take advantage of the document storage mechanic and store an entire player object as one record. After using relational data for years to store objects and wiring up code to handle the relationships, etc. I find the document storage a refreshing change. I took a look at EF and code first but could not obtain the same level of ease of use as with a document store. I ended up having to code way too much logic specific to how the objects are stored rather than just saving them.

For the initial game client I decided to go the Html5/Javascript route to be able to present to the majority of players. I am using SignalR for the client to server communication and worked out some generic interfaces in the underlying server framework to allow for switching the communication technology. I am also using ScriptSharp to build the client side game logic. I find that using ScriptSharp is helpful as you get compile time errors and can resolve some issues up front without having to use console.log statements for everything. The string typing of objects does help to catch some case typos, etc. that are common for me when doing Javascript.

I have also created a world building tool for use with Perenthia. It allows me to draw the tiles that make up the world and save them as flat files for world initialization on the server. Using ScriptSharp I also coded some logic on the client to retrieve and load the maps. I can manage some of the SQL table stuff using the world build but want to get it to the point where I can add objects. My plan is to drop objects onto the map where an object resides within a tile, so not completely to scale or anything. My initial thoughts are to be able to add things like shops, taverns, etc. I also want to be able to add things like trees that can be cut down or a rock quarry for mining. I just not have not gotten to fleshing out all those details as of yet but that is the general plan.

What I am currently working on is randomization of creature encounters. I created a table to hold creature templates and then cataloged them by terrain. So for instance, in grass one might encounter a rat or kobold. In addition to that I created a feature in the world builder for editing creature zones. I store some zones in the database and then draw them on the world tiles to create the creature zones. So what I am working on is when a player moves onto a tile, if an encounter should happen I check to see if any creatures exist for the current terrain. If creatures exist I randomly get one with the level range marked by the zone. I am also thinking I may not return any creatures that are too low level for the player so you are not bogged down by encounters in low level areas and can move more freely into higher level areas. Once I get to where I have some decent encounters I am going to move on to the item system.

Written by Cameron

November 21, 2011 at 11:15 pm

Perenthia in the Cloud

Part of the Perenthia re-birth is to get the game running in the cloud on Windows Azure. I started an Azure 1.2 web role project to handle the game. I got some of the basic stuff working like authentication and character creation. With the release of Azure 1.3 SDK I had to install IIS 7.5 components. Since I did not install it previously I had to run “aspnet_regiis –iru” to setup the IIS mappings for .NET 4 and I had to install the “HTTP Redirection” component for IIS 7. After getting all that setup I am up and running with Azure 1.3.

Because I am scaling the game down a bit and restructuring it to run with basic html and a variety of devices I am essentially re-building the game. I have a lot of code to work from and the concepts are already defined but I do have to do some new things. With running in the cloud some of the previous in-memory stuff I was doing has to be setup to store that in-memory data either in the database or in Azure Storage. For instance, when you connect to the game you get a token or session that identifies you. That token contains a reference to an in-memory object that stores the user id and currently selected character of the player. While I could setup and use .NET Sessions with some providers to store the data in Azure Storage I am going to continue to use the existing system I had in place and store in the in-memory data in Azure Table Storage.

I have some additional in-memory data that will either scale back to database driven queries or work the same way as tokens/sessions with table storage.

Written by Cameron

December 1, 2010 at 9:56 pm

Perenthia Chapter 2

After many months of not working on Perenthia I find myself once again desiring to see it complete. The last revision I did on Perenthia was some time ago and involved a Silverlight UI with a C#/SQL 2008 backend.

During the course of development on Perenthia I discovered some of the pitfalls of attempting to develop a role playing game by one self. Mainly, the inability to handle content creation on a large enough scale as to appeal to players and have enough interactivity to make the game fun. I have been thinking lately of ways to reduce that load and still keep the game fun. I have some new ideas I want to put into place and re-release Perenthia with these concepts.

Among the changes I plan to implement will be scaling the game down to function solely in an HTML environment. This will enable me to provide a mobile version that works on all phones with a browser and keep the feature set the same for both phone and web.

I have begun to refactor pieces of the code and am making heavy use of jquery for the character creation screen.

Written by Cameron

November 30, 2010 at 12:00 am

Game Objects – My Design

I thought I would share one of the core concepts I implemented in Perenthia that has been working quite well. Early on in development I decided to create templates and instances in regards to game objects. A template would define common, unchangable properties of an object whereas an instance would be the opposite. An example would be a Sword; there is a template for a Sword that defines its damage value, price, required skill level, etc. 

I have one table that stores all the objects for the game. Since objects being actively used are loaded into memory and the table is indexed for those queries it runs pretty fast. Since I store all of the objects into one table I make heavy use of the XML data type in SQL 2005 to provide a bunch of XML Serialized Key/Value pairs that define the properties of an object. On the code side of things a dictionary provides the underlying data store for properties of the object. For instance, the Sword example above has a Power property that is used in combination with the wielder’s skill in swords to determine damage. The Power property just fetches the Power value from the properties dictionary using the property name. This works a lot like the dependency properties, just my collection serializes to XML for database storage. When an object is loaded from the database the xml from the template is retrieved and added to the object, then the object instance xml is dropped into the object, overriding any template properties of the same name.

This property dictionary also allows me to store whether or not the property belongs to a template or instance. If the property belongs to a template then when the instance is serialized and saved to the database those template properties are excluded. Likewise saving a template does not serialize and save the instance properties. This structure allows for a lot of flexibility with my objects and has been working out quite well. I probably would not have used serialization and xml the way I am if I was doing a typical call to the database qith each request as the extra overhead would not have been worth the effort. However, since my objects are loaded into memory and my saves are background threaded I experience a lot of benefit of an easy to use object.

I shuld note that players character data is stored in a separate table from the core objects table. It follows the same design buy allows me to migrate and change data in the objects table without overwriting player data, since players are essentially the same as mobiles. 

Written by Cameron

May 15, 2009 at 6:34 pm

Perenthia World Builder in Silverlight

I have been working to modify the tool I wrote for building the Perenthia world. The tool started life as a WinForms application, migrated to a WPF application but is now a part of the Silverlight Client UI. Since all of the actions are role based and controlled with permission on the server having it in the main client UI allows me to make use of all of the framework already in place for Perenthia.

The world builder UI connects to the server via WCF services tailored for world building. What this will also give me is the ability to open up the world builder to allow users to create custom dungeons, towns, etc.

Some features of the world builder are a background map of the world so I know where to place rooms, zoom capabilities that scale the map, draggable map and the ability to drag items, creatures, NPCs, etc. onto rooms on the map and configure the properties of each.

Written by Cameron

April 29, 2009 at 12:01 am

Silverlight Window Control

Here is a draggable Window control I wrote in Silverlight for Perenthia. A screen shot of the Window being used can be seen at https://cameronalbert.com/post/2008/11/19/Silverlight-Game-Controls.aspx

Here is the XAML from my generics.xaml file:

 

<!-- Default style for Lionsguard.Window -->
	<Style TargetType="lg:Window">
		<Setter Property="Background" Value="#99000000" />
		<Setter Property="Foreground" Value="#FFFFFFFF" />
		<Setter Property="BorderBrush" Value="#FFC38312" />
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="lg:Window">
					<Grid x:Name="RootElement">
						<Border HorizontalAlignment="Stretch" Margin="0,24,0,0" Padding="4,4,4,4" VerticalAlignment="Stretch" BorderThickness="2,2,2,2" CornerRadius="0,0,4,4">
							<ContentControl x:Name="ContentElement" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
						</Border>
						<Border Height="24" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" CornerRadius="4,4,0,0" BorderThickness="2,2,2,0" x:Name="TitleBarElement" />
						<Button Height="20" HorizontalAlignment="Right" Margin="0,3,3,0" VerticalAlignment="Top" Width="20" Content="X" FontWeight="Bold" FontFamily="Trebuchet MS" FontSize="14" x:Name="CloseButtonElement"/>
						<TextBlock Height="20" Margin="8,3,27,0" VerticalAlignment="Top" Text="Title" TextWrapping="Wrap" x:Name="TitleLabelElement" FontFamily="Georgia" FontSize="16" FontWeight="Bold" HorizontalAlignment="Left"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>

 

And here is the code for the class:

 

using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Lionsguard
{
	[TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
	[TemplatePart(Name = "ContentElement", Type = typeof(ContentControl))]
	[TemplatePart(Name = "TitleBarElement", Type = typeof(Border))]
	[TemplatePart(Name = "CloseButtonElement", Type = typeof(Button))]
	[TemplatePart(Name = "TitleLabelElement", Type = typeof(TextBlock))]
	[ContentProperty("Content")]
	public class Window : Control
	{
		public FrameworkElement RootElement { get; set; }
		private ContentControl ContentElement { get; set; }
		private Border TitleBarElement { get; set; }
		private Button CloseButtonElement { get; set; }
		private TextBlock TitleLabelElement { get; set; }

		private Point MousePosition { get; set; }

		/// <summary>
		/// Gets or sets the Content of the Window.
		/// </summary>
		public UIElement Content
		{
			get { return (UIElement)GetValue(ContentProperty); }
			set { SetValue(ContentProperty, value); }
		}
		public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(UIElement), typeof(Window), new PropertyMetadata(null, new PropertyChangedCallback(Window.OnContentPropertyChanged)));
		private static void OnContentPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets the Title displayed in the title bar of the window.
		/// </summary>
		public string Title
		{
			get { return (string)GetValue(TitleProperty); }
			set { SetValue(TitleProperty, value); }
		}
		public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(Window), new PropertyMetadata("Title", new PropertyChangedCallback(Window.OnTitlePropertyChanged)));
		private static void OnTitlePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets a value indicating whether or not the close button of the title bar should be displayed.
		/// </summary>
		public bool ShowCloseButton
		{
			get { return (bool)GetValue(ShowCloseButtonProperty); }
			set { SetValue(ShowCloseButtonProperty, value); }	
		}
		public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register("ShowCloseButton", typeof(bool), typeof(Window), new PropertyMetadata(true, new PropertyChangedCallback(Window.OnShowCloseButtonPropertyChanged)));
		private static void OnShowCloseButtonPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets a value indicating whether or not the window is currently open and visible.
		/// </summary>
		public bool IsOpen
		{
			get { return this.Visibility == Visibility.Visible; }
		}

		/// <summary>
		/// An event that is raised when the window is closed.
		/// </summary>
		public event EventHandler Closed = delegate { };

		/// <summary>
		/// Initializes a new instance of the Lionsguard.Window class.
		/// </summary>
		public Window()
		{
			this.DefaultStyleKey = typeof(Window);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			this.RootElement = base.GetTemplateChild("RootElement") as FrameworkElement;
			this.ContentElement = base.GetTemplateChild("ContentElement") as ContentControl;
			this.TitleBarElement = base.GetTemplateChild("TitleBarElement") as Border;
			this.CloseButtonElement = base.GetTemplateChild("CloseButtonElement") as Button;
			this.TitleLabelElement = base.GetTemplateChild("TitleLabelElement") as TextBlock;
			
			if (this.RootElement != null)
			{
				this.RootElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnRootElementMouseLeftButtonDown);

				this.TitleBarElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleBarElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleBarElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.TitleLabelElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleLabelElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleLabelElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.CloseButtonElement.Click += new RoutedEventHandler(OnCloseButtonClick);

				this.SetControlValues();
			}
		}

		private void SetControlValues()
		{
			if (this.ContentElement != null && this.Content != null)
			{
				this.ContentElement.Content = this.Content;
			}
			if (this.TitleLabelElement != null)
			{
				this.TitleLabelElement.Text = this.Title;
				ToolTipService.SetToolTip(this.TitleLabelElement, this.Title);
			}
			if (this.CloseButtonElement != null)
			{
				if (this.ShowCloseButton) this.CloseButtonElement.Visibility = Visibility.Visible;
				else this.CloseButtonElement.Visibility = Visibility.Collapsed;
			}
		}

		private void OnRootElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			// Set the Z-Index of the window to the top most position.
			this.BringToFront();
		}

		private void OnCloseButtonClick(object sender, RoutedEventArgs e)
		{
			this.Close();
		}

		private void OnTitleBarMouseLeave(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Arrow;
		}

		private void OnTitleBarMouseEnter(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Hand;
		}

		private void OnTitleBarMouseMove(object sender, MouseEventArgs e)
		{
			Point position = e.GetPosition(null);

			// Prevent the mouse from moving outside the bounds of the parent canvas.
			if (position.X <= 30) position.X = 30;
			if (position.Y <= 30) position.Y = 30;

			Canvas parent = this.Parent as Canvas;
			if (parent != null)
			{
				if (position.X >= (parent.Width - 30)) position.X = parent.Width - 30;
				if (position.Y >= (parent.Height - 30)) position.Y = parent.Height - 30;
			}

			System.Diagnostics.Debug.WriteLine("position = {0}", position);

			double deltaX = position.X - this.MousePosition.X;
			double deltaY = position.Y - this.MousePosition.Y;
			
			Point newPosition = new Point(
				((double)this.GetValue(Canvas.LeftProperty)) + deltaX,
				((double)this.GetValue(Canvas.TopProperty)) + deltaY);

			this.SetValue(Canvas.LeftProperty, newPosition.X);
			this.SetValue(Canvas.TopProperty, newPosition.Y);

			this.MousePosition = position;
		}

		private void OnTitleBarMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			((UIElement)sender).ReleaseMouseCapture();

			this.Cursor = Cursors.Arrow;

			this.TitleBarElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
		}

		private void OnTitleBarMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			this.MousePosition = e.GetPosition(null);
			this.Cursor = Cursors.Hand;

			((UIElement)sender).CaptureMouse();

			this.TitleBarElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
		}

		/// <summary>
		/// Causes the current window to be re-z-indexed to the top most window.
		/// </summary>
		protected void BringToFront()
		{
			// Search the top most "Window" and swap z-indexes.
			Panel parent = this.Parent as Panel; // Panel is the base for Canvas, Grid and StackPanel.
			if (parent != null)
			{
				int currentZIndex = (int)this.GetValue(Canvas.ZIndexProperty);
				var child = (from c in parent.Children where c is Window select c as Window).OrderByDescending(c => (int)c.GetValue(Canvas.ZIndexProperty)).FirstOrDefault();
				if (child != null)
				{
					int topZIndex = (int)child.GetValue(Canvas.ZIndexProperty);
					if (topZIndex == 0) topZIndex = 1; // If the value has not been set then just default it to 1.
					if (topZIndex > currentZIndex)
					{
						this.SetValue(Canvas.ZIndexProperty, topZIndex);
						child.SetValue(Canvas.ZIndexProperty, currentZIndex);
					}
				}
			}
		}

		public void Show()
		{
			this.Visibility = Visibility.Visible;
			// If content is present show that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Visible;
		}

		public void Close()
		{
			this.Visibility = Visibility.Collapsed;
			// If content is present hide that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Collapsed;
			this.Closed(this, EventArgs.Empty);
		}

		public void ToggleWindow()
		{
			if (this.Visibility == Visibility.Visible)
			{
				this.Close();
			}
			else
			{
				this.Show();
			}
		}
	}
}

 

This is how I am using it within the Pernethia UI (The content of window is actually the Character Sheet as displayed in the screenshot from above):

Namespace: xmlns:Lionsguard="clr-namespace:Lionsguard;assembly=Lionsguard.Silverlight"

 

<Lionsguard:Window Height="375" x:Name="diagCharacterSheet" Visibility="Collapsed" Width="634" Canvas.Left="8" Canvas.Top="19" Title="Character Sheet" Style="{StaticResource WindowStyle}" d:IsLocked="True">
    		<Perenthia_Dialogs:CharacterSheetDialog x:Name="diagCharacterSheetContent" SkillChanged="diagCharacterSheetContent_SkillChanged"/>
		</Lionsguard:Window>

 

Enjoy!

 

Written by Cameron

April 22, 2009 at 5:08 pm