Dropping Items (PHP)

Now that we’ve created a way for users to buy items, we need to create a way for users to get items without buying them – like from monsters they kill in combat. Today, we will be adding item drops to our combat system.

To start off, check out a recent copy of our code:

svn checkout http://building-browsergames-tutorial.googlecode.com/svn/trunk/php/pbbg tutorial -r 42
First things first, we need to add a table to our database:

CREATE TABLE monster_items (
	id int NOT NULL AUTO_INCREMENT,
	monster_id int,
	item_id int,
	rarity int,
	PRIMARY KEY(id)
);

We will be using this monster_items table to keep track of the items that users can gain after combat. The monster_id and item_id columns are fairly self-explanatory; the one column that might need some explaining is the rarity column. We will be using this column to determine how frequently different items drop from our monsters – for example, if we put a Sword into our monster_items table, with a rarity of 10, it would have a 10% chance of dropping any time that the monster it was linked to was killed. As you can see, we will be using the rarity column to define the chance of an item dropping.

With our database preparations made, let’s move on to writing some code. First, open up forest.tpl:

<p>You found a <strong>{$item}</strong>!</p>

We will be using that brief addition to display to the user the new item that they just found. Now that we’ve modified our template, let’s make some changes to what happens when a player wins a fight:

 

$rand = rand(0,100);
$query = sprintf("SELECT item_id FROM monster_items WHERE monster_id = %s AND rarity >= %s ORDER BY RAND() LIMIT 1",
	mysql_real_escape_string($monsterID),
	mysql_real_escape_string($rand));
$result = mysql_query($query);
list($itemID) = mysql_fetch_row($result);
$query = sprintf("SELECT count(id) FROM user_items WHERE user_id = '%s' AND item_id = '%s'",
	mysql_real_escape_string($userID),mysql_real_escape_string($itemID));
$result = mysql_query($query);
list($count) = mysql_fetch_row($result);
if ($count > 0) {
	# already has one of the item
	$query = sprintf("UPDATE user_items SET quantity = quantity + 1 WHERE user_id = '%s' AND item_id = '%s'",
		mysql_real_escape_string($userID),
		mysql_real_escape_string($itemID));
} else {
	# has none - new row
	$query = sprintf("INSERT INTO user_items(quantity,user_id,item_id) VALUES (1,'%s','%s')",
		mysql_real_escape_string($userID),
		mysql_real_escape_string($itemID));
}
mysql_query($query);
# retrieve the item name, so that we can display it
$query = sprintf("SELECT name FROM items WHERE id = %s",
	mysql_real_escape_string($itemID));
$result = mysql_query($query);
list($itemName) = mysql_fetch_row($result);
$smarty->assign('item',$itemName);

Most of this code is the same code as we used to add items to the player’s inventory in our item shop from earlier. However, the first 5 lines are what we’re really interested in: they are how we retrieve the random item that the player should receive. First, we generate a value between 0 and 100 – this will be the ‘rarity barrier’. We use the number that we generated to choose items from the monster’s drop table, which have a rarity greater than or equal to our random number. For example, if we were to generate the number 20, any items with a rarity of 20 or greater would be available for the player to win as a result of their combat. Once we’ve retrieved a random item for the player, we add it to their inventory – and that’s that. You’ve seen everything else we do in this code before.

Here’s the new forest.php:

 

<?php
 
require_once 'smarty.php';
require_once 'weapon-stats.php';
require_once 'login-check.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);
 
if($_POST) {
	if($_POST['action'] == 'Attack') {
		require_once 'stats.php';			// player stats
		require_once 'monster-stats.php';	// monster stats
		// to begin with, we'll retrieve our player and our monster stats 
		$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);
		$player = array (
			name		=>	$_SESSION['username'],
			attack 		=>	getStat('atk',$userID),
			defence		=>	getStat('def',$userID),
			curhp		=>	getStat('curhp',$userID)
		);
		$phand = getStat('phand',$userID);
		$atk = getWeaponStat('atk',$phand);
		$player['attack'] += $atk;
		require_once 'armor-stats.php';		// armor stats
		$armor = array('atorso','ahead','alegs','aright','aleft');
		foreach ($armor as $key) {
			$id = getStat($key,$userID);
			$defence = getArmorStat('defence',$id);
			$player['defence'] += $defence;
		}		
 
		$query = sprintf("SELECT id FROM monsters WHERE name = '%s'",
					mysql_real_escape_string($_POST['monster']));
		$result = mysql_query($query);
		list($monsterID) = mysql_fetch_row($result);
		$monster = array (
			name		=>	$_POST['monster'],
			attack		=>	getMonsterStat('atk',$monsterID),
			defence		=>	getMonsterStat('def',$monsterID),
			curhp		=>	getMonsterStat('maxhp',$monsterID)
		);
		$combat = array();
		$turns = 0;		
		while($player['curhp'] > 0 && $monster['curhp'] > 0 && $turns <= 100) {
			if($turns % 2 != 0) {
				$attacker = &$monster;
				$defender = &$player;	
			} else {
				$attacker = &$player;
				$defender = &$monster;
			}
			$damage = 0;
			if($attacker['attack'] > $defender['defence']) {
				$damage = $attacker['attack'] - $defender['defence'];	
			}
			$defender['curhp'] -= $damage;
			$combat[$turns] = array(
				attacker	=>	$attacker['name'],
				defender	=>	$defender['name'],
				damage		=>	$damage
			);
			$turns++;
		}
		setStat('curhp',$userID,$player['curhp']);
		if($player['curhp'] > 0) {
			// player won
			setStat('gc',$userID,getStat('gc',$userID)+getMonsterStat('gc',$monsterID));	
			$smarty->assign('won',1);
			$smarty->assign('gold',getMonsterStat('gc',$monsterID));
			$rand = rand(0,100);
			$query = sprintf("SELECT item_id FROM monster_items WHERE monster_id = %s AND rarity >= %s ORDER BY RAND() LIMIT 1",
				mysql_real_escape_string($monsterID),
				mysql_real_escape_string($rand));
			$result = mysql_query($query);
			list($itemID) = mysql_fetch_row($result);
			$query = sprintf("SELECT count(id) FROM user_items WHERE user_id = '%s' AND item_id = '%s'",
				mysql_real_escape_string($userID),mysql_real_escape_string($itemID));
			$result = mysql_query($query);
			list($count) = mysql_fetch_row($result);
			if ($count > 0) {
				# already has one of the item
				$query = sprintf("UPDATE user_items SET quantity = quantity + 1 WHERE user_id = '%s' AND item_id = '%s'",
					mysql_real_escape_string($userID),
					mysql_real_escape_string($itemID));
			} else {
				# has none - new row
				$query = sprintf("INSERT INTO user_items(quantity,user_id,item_id) VALUES (1,'%s','%s')",
					mysql_real_escape_string($userID),
					mysql_real_escape_string($itemID));
			}
			mysql_query($query);
			# retrieve the item name, so that we can display it
			$query = sprintf("SELECT name FROM items WHERE id = %s",
				mysql_real_escape_string($itemID));
			$result = mysql_query($query);
			list($itemName) = mysql_fetch_row($result);
			$smarty->assign('item',$itemName);				
		} else {
			// monster won
			$smarty->assign('lost',1);	
		}
		$smarty->assign('combat',$combat);
	} else {
		// Running away! Send them back to the main page
		header('Location: index.php');	
	}
} else {
	$query = sprintf("SELECT name FROM monsters ORDER BY RAND() LIMIT 1");
	$result = mysql_query($query);
	list($monster) = mysql_fetch_row($result);
	$smarty->assign('monster',$monster);
}
 
$smarty->display('forest.tpl');
 
?>

Extra Credit

  • As I mentioned, we’re duplicating code here – which is bad. Can you DRY out the code to add an item to a player’s inventory?