Using Items (PHP)

What game is complete without usable items? Not ours, that’s for sure!

With that in mind, today we are going to write the code that makes items usable. To start off, check out a copy of the code for our project:

svn checkout http://building-browsergames-tutorial.googlecode.com/svn/trunk/php/pbbg tutorial -r 37

And then we’re ready to go!

To start off, we’ll build a basic template for our inventory page, and put it inside inventory.tpl:

 

<html>
<head>
	<title>Your Inventory</title>
</head>
<body>
	<p>This is your inventory. Check out all the stuff you've got!</p>
	<ul>
		{foreach from=$inventory key=id item=i}
		<li>
			<strong>{$i.name} x {$i.quantity}</strong>
			<form action='inventory.php' method='post'>
				<input type='hidden' name='item-id' value='{$i.id}' />
				<input type='submit' value='Use' />
			</form>
		</li>
		{/foreach}
	</ul>
</body>
</html>

With the template built, we can get started on the code for inventory.php:

 

<?php
 
require_once 'smarty.php';
 
session_start();
 
require_once 'config.php';		// our database settings
$conn = mysql_connect($dbhost,$dbuser,$dbpass)
	or die('Error connecting to mysql');
mysql_select_db($dbname);
// retrieve player's ID
$query = sprintf("SELECT id FROM users WHERE UPPER(username) = UPPER('%s')",
			mysql_real_escape_string($_SESSION['username']));
$result = mysql_query($query);
list($userID) = mysql_fetch_row($result);
 
$inventory = array();
$query = sprintf("SELECT id, item_id, quantity FROM user_items WHERE user_id = '%s'",
		mysql_real_escape_string($userID));
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)) {
	 $item_query = sprintf("SELECT name FROM items WHERE id = '%s'",
		mysql_real_escape_string($row['item_id']));
	$item_result = mysql_query($item_query);
	list($row['name']) = mysql_fetch_row($item_result);
	array_push($inventory,$row);
}
$smarty->assign('inventory',$inventory);
$smarty->display('inventory.tpl');
 
?>

This code is essentially taken straight from our item shop code – but it’s missing one key thing: the ability to actually use items!

If you remember our initial database preparations, you’ll remember that we inserted a new stat called ‘Item Use Token’ – and this is where that stat will come into play. We will be using the ‘Item Use Token’ stat and something known as a Dispatch Table to easily write arbitrary code that can be run whenever our items are used.

For now, we’ll insert a single item with the use token of ‘potion’:

INSERT INTO items(name,type,price) VALUES ('Red Potion','Usable',10);
	INSERT INTO entity_stats(stat_id,entity_id,value,entity_type) VALUES ((SELECT id FROM stats WHERE short_name='token'),(SELECT id FROM items WHERE name='Red Potion'),'potion','Item');

With that token in place, we will be able to start writing our dispatch table.

Dispatch Tables are a bit of a strange beast; essentially, what you end up doing when you create a dispatch table is creating an associative array, filled with function definitions(instead of values). Then, you use a certain key value to retrieve the function you are supposed to call. This is actually perfect for items system, because it will allow us to easily add new and different types of items anytime we want or need to(by simply adding another token, and then writing the code to handle it). Let’s get started on our dispatch table:

 

function use_potion() {
	echo 'This is code that would run when the user used a potion.';
}
$actions = array('potion' => 'use_potion');
 
if($_POST) {
	if($_POST['item-id']) {
		$query = sprintf("SELECT item_id FROM user_items WHERE user_id = '%s' AND id = '%s'",
			mysql_real_escape_string($userID),
			mysql_real_escape_string($_POST['item-id']));
		$result = mysql_query($query);
		list($itemID) = mysql_fetch_row($result);
		require_once 'items.php';
		$token = getItemStat('token',$itemID);
		call_user_func($actions[$token]);
	}
}

And if you visit your inventory page and click ‘Use’ on the item in your character’s inventory, you should be treated to the simple message ‘This is code that would run when the user used a potion.’. Neat, eh?

While this shows you how to build this functionality for a single item, single items aren’t really what this code is geared towards. So let’s add another item and type:

INSERT INTO items(name,type,price) VALUES ('Crystal Ball','Usable',10);
	INSERT INTO entity_stats(stat_id,entity_id,value,entity_type) VALUES ((SELECT id FROM stats WHERE short_name='token'),(SELECT id FROM items WHERE name='Crystal Ball'),'crystal_ball','Item');

Now that we’ve added another item token to handle, we’ll write the code that gets run when an item with that token gets used:

 

function use_crystal_ball() {
		echo 'This is code that would run when the user used a crystal ball.';
	}
	$actions = array('potion' => 'use_potion','crystal_ball' => 'use_crystal_ball');

And now if you use a crystal ball, you will get the message ‘This is code that would run when the user used a crystal ball.’.

This is why dispatch tables are so useful – it doesn’t take us long at all to write more code to support more items anytime we need to.

With that, our inventory page is essentially finished! You can run any code that should run after any item is used by running it just after the call to call_user_func(), and you can run any code specific to that item token by defining it within the $actions array. Handy!

Here’s the (slightly re-organized) code for inventory.php:

<?php
 
require_once 'smarty.php';
 
session_start();
 
require_once 'config.php';		// our database settings
$conn = mysql_connect($dbhost,$dbuser,$dbpass)
	or die('Error connecting to mysql');
mysql_select_db($dbname);
// retrieve player's ID
$query = sprintf("SELECT id FROM users WHERE UPPER(username) = UPPER('%s')",
			mysql_real_escape_string($_SESSION['username']));
$result = mysql_query($query);
list($userID) = mysql_fetch_row($result);
 
$actions = array('potion' => 'use_potion','crystal_ball' => 'use_crystal_ball');
 
if($_POST) {
	if($_POST['item-id']) {
		$query = sprintf("SELECT item_id FROM user_items WHERE user_id = '%s' AND id = '%s'",
			mysql_real_escape_string($userID),
			mysql_real_escape_string($_POST['item-id']));
		$result = mysql_query($query);
		list($itemID) = mysql_fetch_row($result);
		require_once 'items.php';
		$token = getItemStat('token',$itemID);
		call_user_func($actions[$token]);
	}
}
 
$inventory = array();
$query = sprintf("SELECT id, item_id, quantity FROM user_items WHERE user_id = '%s'",
		mysql_real_escape_string($userID));
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)) {
	 $item_query = sprintf("SELECT name FROM items WHERE id = '%s'",
		mysql_real_escape_string($row['item_id']));
	$item_result = mysql_query($item_query);
	list($row['name']) = mysql_fetch_row($item_result);
	array_push($inventory,$row);
}
$smarty->assign('inventory',$inventory);
$smarty->display('inventory.tpl');
 
function use_potion() {
	echo 'This is code that would run when the user used a potion.';
}
function use_crystal_ball() {
	echo 'This is code that would run when the user used a crystal ball.';
}
?>

And here’s the template(inventory.tpl):

 

<html>
<head>
	<title>Your Inventory</title>
</head>
<body>
	<p>This is your inventory. Check out all the stuff you've got!</p>
	<ul>
		{foreach from=$inventory key=id item=i}
		<li>
			<strong>{$i.name} x {$i.quantity}</strong>
			<form action='inventory.php' method='post'>
				<input type='hidden' name='item-id' value='{$i.id}' />
				<input type='submit' value='Use' />
			</form>
		</li>
		{/foreach}
	</ul>
</body>
</html>

Extra Credit

  • Build healing potions that heal the player for 10, 20, or 30 HP (you may need to add another stat to the item to do this).