Game Development Community

MMORPG Tutorial article 1 Inventory for the Client.

by Dreamer · 04/14/2005 (2:58 pm) · 90 comments

I am currently writing a Massively Multiplayer Online RolePlaying Game called Dream, and since the actual resources to help are a little skimpy on how to start, I figured I should show what I have accomplished so others can build from it.

To start with we will build a client side inventory system.
To begin an inventory you really need to decide which style you want to choose, since the GUI is a little limited in the area of building dynamic slots, I have decided to go with a "Backpack and List" concept, similar in fact to other systems.

The first step would be to track down one of the various GuiObjectView.cc files floating around, I'm using the one from eXodus,
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4063
but for our purposes any will do.

The next step would be to track down the Array handling tutorial
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4711

and add that to your project and compile.

After you have all that compiled in, we want to take advantage of it and create a new InventoryGUI.gui and place it in client/ui/InventoryGUI.gui then add the following line to your client/init.cs
exec("./ui/InventoryGUI.gui");
Here is the code for the GUI file.
//--- OBJECT WRITE BEGIN ---
new GuiControl(InventoryGUI) {
   profile = "GuiDefaultProfile";
   horizSizing = "right";
   vertSizing = "bottom";
   position = "0 0";
   extent = "640 480";
   minExtent = "8 2";
   visible = "1";

   new GuiWindowCtrl(InventoryWindow) {
      profile = "GuiWindowProfile";
      horizSizing = "right";
      vertSizing = "bottom";
      position = "2 -2";
      extent = "621 476";
      minExtent = "8 2";
      visible = "1";
      text = "Inventory";
      maxLength = "255";
      resizeWidth = "1";
      resizeHeight = "1";
      canMove = "1";
      canClose = "1";
      canMinimize = "1";
      canMaximize = "1";
      minSize = "50 50";
      CloseCommand = "Canvas.PopDialog(InventoryGUI);";
     new GuiObjectView(InventoryItemView) {
	profile = "GuiDefaultProfile";
	horizSizing = "relative";
	vertSizing = "relative";
	position = "0 0";
	extent = "400 400";
	minExtent = "8 2";
	visible = "1";
	helpTag = "0";
	cameraZRot = "0";
	forceFOV = "0";
      };
      new GuiScrollCtrl() {
         profile = "GuiScrollProfile";
         horizSizing = "right";
         vertSizing = "bottom";
         position = "410 30";
         extent = "200 400";
         minExtent = "8 2";
         visible = "1";
         willFirstRespond = "1";
         hScrollBar = "alwaysOn";
         vScrollBar = "alwaysOn";
         constantThumbHeight = "0";
         childMargin = "0 0";

         new GuiTextListCtrl(InventoryListView) {
            profile = "GuiTextArrayProfile";
            horizSizing = "right";
            vertSizing = "bottom";
            position = "2 2";
            extent = "178 2";
            minExtent = "8 2";
            visible = "1";
            enumerate = "0";
            resizeCell = "1";
            columns = "0";
            fitParentWidth = "1";
            clipColumnText = "0";
         };
      };
   };
};
//--- OBJECT WRITE END ---
function toggleInventoryGUI(%val){
	if(%val)
      		InventoryGUI.toggle();
}

function InventoryGUI::OnWake(%this){
	UpdateInventoryListView();
}

//This function updates the InventoryListView
function UpdateInventoryListView(){
	echo("UpdatingInventoryListView, lets start by clearing it");
	InventoryListView.clear();
	echo("Done now looping through Inventory");
	for(%x = 0; %x < $Inventory.count(); %x++){
		InventoryListView.addRow(%x,$Inventory.getKey(%x) SPC $Inventory.getValue(%x));
		echo("Added Item at "@%x);
	}
	echo("Complete!, now sorting and cleaning");
	InventoryListView.sort(0);
	InventoryListView.setSelectedRow(0);
	InventoryListView.scrollVisible(0);
}

function InventoryGUI::toggle(%this)
{
   if (%this.isAwake()){
      Canvas.popDialog(%this);
    }else{
      Canvas.pushDialog(%this);
   }
}

function InventoryListView::OnSelect( %this, %id, %text ){
	InventoryItemView.setEmpty();
	$SelectedItem = getField(InventoryListView.getRowTextById(%id),0);
	$SelectedItem = getWord($SelectedItem,0);
	InventoryItemView.setObject($SelectedItem,$InventoryImage.getValue($InventoryImage.getIndexFromKey($SelectedItem)),"", 0);
}

Make sure to bind the toggleInventoryGUI function in your defaultbinds.cs file under client/scripts.

Now we need to make the client aware that it has an inventory.
Since this will be a MMORPG one design decision that I made was to minimize requests to the server for things like inventory, so I went with a "server push" concept, which only notifies the client when there is an UPDATE to the inventory.

First go to server/scripts/inventory.cs and replace
ShapeBase::setInventory(%this,%data,%value)
function with this
function ShapeBase::setInventory(%this,%data,%value)
{
   // Set the inventory amount for this datablock and invoke
   // inventory callbacks.  All changes to inventory go through this
   // single method.

   // Impose inventory limits
   if (%value < 0)
      %value = 0;
   else {
      %max = %this.maxInventory(%data);
      if (%value > %max)
         %value = %max;
   }

   // Set the value and invoke object callbacks
   %name = %data.getName();
   echo("Adding "@%name@" to client inventory");
   if (%this.inv[%name] != %value) 
   {
      %this.inv[%name] = %value;
      %data.onInventory(%this,%value);
      %this.getDataBlock().onInventory(%data,%value);
      %Image  = %data.shapefile;
      CommandToClient(%this.client,'UpdateInventory',%name,%value,%Image);
      echo("If we are seeing this then client inventory command has been issued");
      echo("\n\n\n\n\nSent the following info to client",%this.client,%name,%value,%Image);
   }
   return %value;
}
Notice the command to client?
Next we need to go to the client/scripts directory and add a file I am calling client_commands.cs (make sure to exec it in your init.cs file after adding it)
$Inventory = new array();
$InventoryImage = new array();
//This function handles Inventory Update messages from server
function clientCmdUpdateInventory(%Item,%Amount,%Image){
	echo("Recieved Inventory Item Update from server");
	if(%Item !$=""){
		echo("Item is not NULL");
		if(!$Inventory.getValue($Inventory.getIndexFromKey(%Item))){
			echo("Evidently a new item");
			$Inventory.add(%Item, %Amount);
			$InventoryImage.add(%Item,%Image);
			echo("Added new item successfully");
		}else{
			$Inventory.erase($Inventory.getIndexFromKey(%Item));
			$Inventory.add(%Item, %Amount);
		}
	}else{
		echo("Whoops!  There was no Item in that call!");
	}
	echo("Recieved "@$Inventory.getValue(%Item)@" of "@%Item);
	echo("If we are seeing this there were no segfaults in UpdateInventory");
	//UpdateInventoryListView();
}
The above allows us to create an array of inventory items, and then adds them to the Inventory List in our backpack.

Thats it, you now have a 3D backpack that will reflect your inventory.

Enjoy!

In my next Tutorial I will explain how to implement a proper targeting system.
Page «Previous 1 2 3 4 5 Last »
#2
04/13/2005 (9:55 am)
It all sounds great, but the GUI system is entirely flexible. Building a dynamic slot system wouldn't be too difficult... It's really just looking at how the entire GUI system works. You'll notice that a GuiControl is really just a SimGroup. And a SimGroup can have items added to it dynamically..

Good work, none the less!

- Brett
#3
04/13/2005 (11:27 am)
Thanks I'm just building from what I know, and having tried to dynamically add slots to an Inventory gui turned into a larger hassle than I was willing to deal with, so I just started from scratch and went with a Backpack and list concept.
#4
04/13/2005 (12:23 pm)
Small formatting suggestion: I like how you are putting links in to the next article, but I'd suggest you make them clickable using the url= and /url tags. It would look like this (except replace the { with [ and the } with ] ):

{url=http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7515}Tutorial 2: MMOG Targeting System{/url}

And it would display like:

Tutorial 2: MMOG Targeting System
#5
04/13/2005 (12:26 pm)
There ya go, fixed it!
#6
04/13/2005 (2:17 pm)
Nice stuff! I've looked at all the ones you've posted so far and they all look great - now if I just had time to play around with them ;)

There was some talk a while back about making an RPG starter pack for Torque, similar to the RTS one that we have now. Can't remember who was working on that, but it would be great if you guys got together :)

Are there plans for future tutorials in this series beyond these 7?
#7
04/13/2005 (2:38 pm)
This series was created mostly to address the fact that having been a Torque Owner for over a year and having seen RPG kit after RPG kit be propsed nothing has ever materialized. I saw the RTS kit and thought, why the heck not make an RPG the way I want to make one, and then either create a kit or a series of tutorials to help others.

So I am making both, when this series is completed (even the advanced stuff) I will be making an RPG starter kit available based on the Dream MMORPG series of tutorials. It will include a complete framework for making nearly any kind of RPG or MMORPG, as well as some nice models and a few sample zones. My target price has not been set, but I'm really wanting to keep it very low, and I am hoping someone from GG will contact me to help me package the thing and sell it.

As to the other question, Yes I already have more in the works, "Logins R Us" (self explanitory)is my next one followed by "Pick a Char any Char" which will go into character creation and stats, after that we will go back to tradeskills with "Forage,Bake and Eat, 'A Cake!'", then we will introduce friendly npcs and some small questy stuff with "You talkin to me?" Then I will finish up the MMORPG intro tutorials with "God and You a guide to religion". After that I plan to start on some advanced tutorials which are as yet unnamed but clean up, streamline and expand upon what has already been done, so stay tuned!
#8
04/13/2005 (3:06 pm)
all i can say is YAAAAAAY *grins from ear to ear*
#9
04/13/2005 (3:10 pm)
One question, what'd ya mean by "you now have a _3D_ backpack"

Oh, thinking about it, im guessing your referring to the 3d representation of the object in the inv screen.. i thought you meant three-dimensional array lol
#10
04/13/2005 (11:14 pm)
it would be nice to add a context menu to this so we can map actions to items... ie "use","drop", "wear/mount","examine"
#11
04/14/2005 (8:12 pm)
Great resource.
#12
04/14/2005 (11:03 pm)
@Ed Something like thats coming in a later resource, this is just to getcha started.

@Ted Thanks I'm glad you like it :)
#13
04/15/2005 (1:26 am)
Ah, music to my ears :-)
#14
04/15/2005 (9:15 am)
FYI, someone emailed me with a very good question this morning.

"Hi Dreamer, I saw your Inventory tutorial and was notcing that you have implemented the array handling engine mod to help with inventory but you only use it client side, on the server side everything looks pretty stock, why is that?"

Here is my answer.

"This was a design decision, initially I was just going to utilize the built in array handlers for Torque on both the client and the server side, however I could not find any way to reliably address a couple of needs that I had clientside, for instance the ability to keep count of how many key,value pairs I had without defining yet another global, also being able to sort and etc. So the decision was made to implement a more proper Array handling for the client only, since the server will automatically inherit a form of array handling for the more advanced stuff as soon as it has a SQLite DB. Also another consideration that was server side and not so much clientside, is that the Array handlers built in to torque take up a lot less memory than the equivalent done via true array handling, and considering the server may have 100-200 clients at any one time each needing inventory and etc, this could cause a HUGE swelling of the memory resources needed to keep the server functioning optimally, this same principle when applied to bandwidth is also the reason we use a server push for inventory updating rather than having the client make requests."
#15
04/18/2005 (12:59 pm)
Argh! That wasn't supposed to be a screenshot it was supposed to be an image to use for the background, a little inspiration for yall :)
#16
04/21/2005 (7:42 pm)
sorry about being so dumb with all this Dreamer but can you post a link to all your torque sorce? like you did for the next tut? :) i am the biggest nob :(
Thanks
#17
04/21/2005 (9:41 pm)
No problem, I've added the links to the resources I implemented here.
#18
04/22/2005 (6:39 am)
The Array handling tutorial link above gives me this error,
Unkown Resource...

The resource associated with this URL has been deleted and is no longer available.
#19
04/22/2005 (6:47 am)
It's working fine here, try again.
#20
04/22/2005 (7:08 am)
cool got it working, here is the link to the array handling i used
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4711
Page «Previous 1 2 3 4 5 Last »