Implementing Multiple Areas (PHP)
Now that we’ve prepared our database for multiple areas, it’s time to build multiple area support into our exploration/combat system.
To begin with, check out version 46 from the Google Code Project:
svn checkout http://building-browsergames-tutorial.googlecode.com/svn/trunk/php/pbbg tutorial -r 46
With that done, we can get down to business. Start by copying all of forest.php’s code over to explore.php, which is going to be the new location of our exploration code. Once that’s done, we need to change the way that our script retrieves the current monster for a player to fight:
129 130 131 132 133 134 135 136 137 138 | $area_id = $_GET['area']; $query = sprintf("SELECT monster FROM area_monsters WHERE area = %s ORDER BY RAND() LIMIT 1", mysql_real_escape_string($area_id)); $result = mysql_query($query); list($monster_id) = mysql_fetch_row($result); $query = sprintf("SELECT name FROM monsters WHERE id = %s", mysql_real_escape_string($monster_id)); $result = mysql_query($query); list($monster) = mysql_fetch_row($result); $smarty->assign('monster',$monster); |
Note: this code should overwrite your old code, starting at line 129. We are modifying the way that monster retrieval works.
With the code changes made, we should set up areas and monsters in areas for our code to retrieve:
INSERT INTO areas(name) VALUES ('Forest'); INSERT INTO areas(name) VALUES ('Woods'); INSERT INTO area_monsters(area,monster) VALUES (1,1); INSERT INTO area_monsters(area,monster) VALUES (1,2); INSERT INTO area_monsters(area,monster) VALUES (2,3);
With the new data inserted, try visiting explore.php?area=1, and see what happens – you should only ever see monsters that belong in the area with the ID of 1.
While you’re testing, you might notice that the title tag of our page still says ‘The Forest’ – while we’re at it, we may as well modify our code to also retrieve the name of the area we are currently visiting:
140 141 142 143 144 145 146 147 | $query = sprintf("SELECT name FROM areas WHERE id = %s", mysql_real_escape_string($_GET['area'])); $result = mysql_query($query); list($area_name) = mysql_fetch_row($result); $smarty->assign('area',$area_name); $smarty->assign('area_id',$_GET['area']); $smarty->display('explore.tpl'); |
That code should be outside any of the code responding to whether our page is being POST’d to or not; we need to set the page title properly whether we’re just encountering something, or completing a fight. You’ll notice that we have also set the area_id – this is because we will need to tweak where the form in our template goes, in addition to renaming it from forest.tpl to explore.tpl:
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <title>The {$area}</title> </head> <body> {if $combat eq ''} <p>You've encountered a {$monster}!</p> <form action='explore.php?area={$area_id}' method='post'> <input type='submit' name='action' value='Attack' /> or <input type='submit' name='action' value='Run Away' /> <input type='hidden' name='monster' value='{$monster}' /> </form> {else} <ul> {foreach from=$combat key=id item=i} <li><strong>{$i.attacker}</strong> attacks {$i.defender} for {$i.damage} damage!</li> {/foreach} </ul> {if $won eq 1} <p>You killed <strong>{$smarty.post.monster}</strong>! You gained <strong>{$gold}</strong> gold, and {$exp} experience.</p> {if $level_up eq 1} <p><strong>You gained a level!</strong></p> {/if} <p>You found a <strong>{$item}</strong>!</p> <p><a href='explore.php?area={$area_id}'>Explore Again</a></p> |
With those changes made, we’re finished! Your game can now support multiple different areas where players can encounter enemies – just link them to explore.php?area=1 for area 1, explore.php?area=2 for area 2, and so on. Here’s what our new code files look like:
explore.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | <?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); $monster_exp = getMonsterStat('exp',$monsterID); $smarty->assign('exp',$monster_exp); $exp_rem = getStat('exp_rem',$userID); $exp_rem -= $monster_exp; $level_up = 0; if($exp_rem <= 0) { // level up! $exp_rem = 100; $level_up = 1; } $smarty->assign('level_up',$level_up); setStat('exp_rem',$userID,$exp_rem); } 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 { $area_id = $_GET['area']; $query = sprintf("SELECT monster FROM area_monsters WHERE area = %s ORDER BY RAND() LIMIT 1", mysql_real_escape_string($area_id)); $result = mysql_query($query); list($monster_id) = mysql_fetch_row($result); $query = sprintf("SELECT name FROM monsters WHERE id = %s", mysql_real_escape_string($monster_id)); $result = mysql_query($query); list($monster) = mysql_fetch_row($result); $smarty->assign('monster',$monster); } $query = sprintf("SELECT name FROM areas WHERE id = %s", mysql_real_escape_string($_GET['area'])); $result = mysql_query($query); list($area_name) = mysql_fetch_row($result); $smarty->assign('area',$area_name); $smarty->assign('area_id',$_GET['area']); $smarty->display('explore.tpl'); ?> |
explore.tpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <html> <head> <title>The {$area}</title> </head> <body> {if $combat eq ''} <p>You've encountered a {$monster}!</p> <form action='explore.php?area={$area_id}' method='post'> <input type='submit' name='action' value='Attack' /> or <input type='submit' name='action' value='Run Away' /> <input type='hidden' name='monster' value='{$monster}' /> </form> {else} <ul> {foreach from=$combat key=id item=i} <li><strong>{$i.attacker}</strong> attacks {$i.defender} for {$i.damage} damage!</li> {/foreach} </ul> {if $won eq 1} <p>You killed <strong>{$smarty.post.monster}</strong>! You gained <strong>{$gold}</strong> gold, and {$exp} experience.</p> {if $level_up eq 1} <p><strong>You gained a level!</strong></p> {/if} <p>You found a <strong>{$item}</strong>!</p> <p><a href='explore.php?area={$area_id}'>Explore Again</a></p> {/if} {if $lost eq 1} <p>You were killed by <strong>{$smarty.post.monster}</strong>.</p> {/if} <p><a href='index.php'>Back to main</a></p> {/if} </body> </html> |
Extra Credit
- Make it so that when the user visits explore.php without passing in an area to visit, a random area is selected for them to explore.
Luke is the primary editor of Building Browsergames, and has written a large portion of the articles that you read here. He generally has no idea what to say when asked to write about himself in the third person.



