Introduction
I come from the age when text adventures were serious fun, ZORK and Hitchhikers guide to the galaxy were two of my favourite text adventures. OK so maybe the golden age of the text adventure is still stuck in the 1980’s but I still remember those glory days. So the game I am building is certainly not up there in the same territory as ZORK but I hope you can see the sort of cool things you can do with Js/Jquery and HTML 5.
The game is pretty simple use the N,S,E and W to move around the dungeon and a few other useful commands. Search for the Dragon and if you have the right weapon and equipment you may be able to kill it. If you use a mobile browser you should see some buttons that make life a little easier than using a little virtual keyboard.
Here is the game : http://domssite.com/search/
Who is this for
Anyone interested in learning Javascript, jQuery and HTML 5.
How to use the tutorial
Its not an ordinary tutorial so you need to accept that first. Its based on reading and understanding code and code comments. I have used heaps of comments throughout the code to explain whats going on. There’s a lot of Arrays, so you’ll see lots of code using arrays and its not a bad starting place for working with js structures. The whole tutorial is 1 single file – whilst a single large file is not the best way to build large web apps – its not a bad approach showing everything in one file.
The first part of the puzzle : the HTML
<!--Text Adventure : Search for the Dragon --> <!--***************************************** Text Adventure - A JS/JQuery/HTML5 Tutorial by comments The game : Standard Text Aventure where you can move from room to room, collecting items and killing monsters Goal is to get to the final room of the dungeon and kill the dragon Start in room 1 Monsters dont move Monsters can be killed by certain weapons Should work in both mobile and non-mobile browsers History : 0.1 Initial version before Hilary's comments/advice ***************************************** --> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Search</title> <script src="Scripts/jquery-1.7.1.min.js"></script> <link href="Content/Site.css" rel="stylesheet" /> </head> <body> <div class="content"> <style> </style> <div id="screenContainer"> <div id="display"> <div id="input"> <form> <!--Screen Title--> <h1>Search... </h1> <!--Screen Sub-Title--> <h3>For a Dragon...Text Adventure Game by @DungeonBard </h3> <div id="GameOverDiv"> </div> <div id="GameDiv"> <!--The following div is hidden or shown depending on if its a non-mobile browser--> <div id="Keyboard"> <label>Command:</label> <input type="text" name="userInput" class="buttonMod" id="userInput" /> </div> <!--The following div is hidden or shown depending on if its a mobile browser--> <div id="controllers"> <!--We use a table for the simple alignment of the buttons--> <table> <tr> <td></td> <td> <input type='button' class='button' value='N' title="North" style="left: 200px" /></td> <td></td> </tr> <tr> <td> <input type='button' class='button' value='W' title="West" /></td> <td></td> <td> <input type='button' class='button' value='E' title="East" /></td> </tr> <tr> <td></td> <td> <input type='button' class='button' value='S' title="South" /></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> </tr> <tr> <td> <input type='button' class='button' value='P' title="Pickup" /></td> <td> <input type='button' class='button' value='A' title="About" /></td> <td> <input type='button' class='button' value='?' title="Help" /></td> </tr> </table> </div> </div> <div id="output"></div> </form> </div> </div> </div> </body> </html>
Part 2 : the Javascript
<script type="text/javascript"> //Our main js code called by Jquery on doc ready $(document).ready(function () { //game variables var message, //screen message to display hits = 10, //hit points for the player lightLevel = 100, //current light level currentRoom = 0, //initial room exitRoom = 31, //final room of the dungeon IsGameOver = false; //Maintain the state of the game IsOgreAlive = true, //Stores the state of the Ogre - Alive/Dead IsDragonAlive = true; //this is the gameover state //All the commands we use in the game var gameWords = new Array("HELP", "Find/earch", "N-orth", "S-outh", "W-est", "E-east','A-About"); //All the rooms in the game var rooms = new Array("Dungeon Entrance", "Corridor of uncertainty", 'Ancient old cavern', "Great Cavern", "Underground River", "Stream", 'Dungeon Stream', "Dungeon Pool", "Large Cavern", "Rough Tunnell", "Long Tunnell", "Dark Room", "Dark Room", "Cold Room", "Old Tunnel", "Cold Room", "Old Cavern", "Short Corridor", "Short Corridor", "Grey Room", "Green Room", "Old Prison Cell", "Underground River", "Large Cavern", "Rough Tunnell", "Long Tunnell", "Dark Room", "Dark Room", "Cold Room", "Old Tunnel", "Dragons Room"); //Each exit relates to the index ie. Exits[0] SE which means rooms[0] the long path has two exits on the South and East. If we look //down to the //Movement Code section you can see how we work out which rooms are connected to which var exits = new Array("E", "SWE", "WE", "SWE", "WE", "WE", "SWE", "WS", "NSE", "SE", "WE", "NW", "SE", "W", "SNE", "NSW", "NS", "NS", "SE", "WE", "NWE", "SWE", "WS", "N", "N", "NWE", "NWE", "WE", "WE", "NW", "NE", "W"); //All out game objects var GameObjects = new Array('', "Painting", "Knife", "Wand of Firebolts", "Goblet", "Wand of Wind", "Coins", "Helmet", "Candle", "Torch", "Iron Shield", "Armour", "Oil", "AXE", "ROPE", "BOAT", "AEROSOL", "CANDLE", "KEY"); //Inventory array Contains all the things you can carry var inventory = new Array(); inventory[0] = 2; //lets start our player off with a knife //location of game objects - these objects relate to a array index - so Object[1] the Painting is located //in rooms[2] the small garden - 999 indicates out of play var objectLocations = [999, 1, 999, 3, 4, 5, 6, 7, 8, 10, 11, 15, 14, 12, 18, 19, 16, 17, 9] //This function detects if the browser if a mobile - you'll see when we call this we apply the function isMobile() { return navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/iPhone|iPad|iPod/i) || navigator.userAgent.match(/Opera Mini/i) || navigator.userAgent.match(/IEMobile/i); } //The next line checks for a mobile browser and if it find st it will hide the buttons or hide the text box if (isMobile()) { //hide the text box - we dont need that for a mobile browser as its hard to use mobile keyboard for lots of commands $("#Keyboard").hide(); } else { //hide the buttons as we don't want that for the normal web experience $('#controllers').hide(); //jquery command to force the textbox to take focus $("#userInput").focus(); } //javascript function to pickup the object in this room var pickup = function (roomIndex) { var itemIndex; if (objectLocations[roomIndex] > 0 && objectLocations[roomIndex] < 100) { itemIndex = objectLocations[roomIndex]; inventory[inventory.length] = itemIndex; objectLocations[roomIndex] = 999; alert(objectLocations[roomIndex]); } } //This function loops through the object location array and returns function getObjectForRoom(currentRoom) { var roomIndex = -1; for (var i = 0; i < objectLocations.length ; i++) { if (objectLocations[i] == currentRoom) roomIndex = i; } return roomIndex } //This is a method/function that shows the game screen. If we look in deatil at this function we can see that //it uses another function DisplayText to show each line of the screen. function DisplayGameScreen() { //clear the output div $display.empty(); //Display the screen output text - note this does not include the buttons DisplayText("You are now in the :"); DisplayText(rooms[currentRoom]); DisplayText("Exits: " + ShowAdjacentRooms(exits[currentRoom]) + "<br />"); DisplayText('DB:' + currentRoom + 'Light:' + lightLevel + "Hits:" + hits); if (getObjectForRoom(currentRoom) != -1) { var index = getObjectForRoom(currentRoom); DisplayText("You can see " + GameObjects[index]); } //If there is something in our inventory then display it if (inventory.length > 0) { DisplayText("You are carrying: "); for (var i = 0; i < inventory.length ; i++) { DisplayText("-" + GameObjects[inventory[i]]); } } if (message != null) DisplayText(message.toUpperCase()); //Game over code if (IsDragonAlive) { $('#GameOverDiv').hide(); $('#GameDiv').show(); } else { $('#GameOverDiv').show(); $('#GameDiv').hide(); } message = "What?"; } //Replaces the indexOf js function as i have found it doesn't always work for me!!!!!!!! function checkIndex(issFullArray, issToCheck) { for (i = 0; i < issFullArray.length; i++) { if (issFullArray[i] == issToCheck) { return true; } } return false; } //Uses the text for a room to build a string that shows which rooms are next to the current room function ShowAdjacentRooms(e) { var newExits = ""; if (e != null) { for (i = 0; i < e.length; i++) { if (i === e.length - 1) { newExits += e.substring(i, i + 1); } else if (i === e.length - 2) { newExits += e.substring(i, i + 1) + " & "; } else { newExits += e.substring(i, i + 1) + ", "; } } } return newExits; } //Simple js function to display a line of text function DisplayText(text) { $display.html($display.html().toString() + text + "<br>"); } //Each round we call this function to do all the main game processing function ProcessGameRound(command) { //Remove any spaces from the command text trimCommand = $.trim(command); //Process command takes the players action ProcessCommand(command); //NOw that we have taken the players logic we need to activate the main game room logic if (currentRoom == 10 && OgreAlive) { //if you are fighting the ogre and you have the spells if (checkIndex(inventory, 3)) { message += "\<br\>YOU attack the ogre with magic spells and kill him!"; OgreAlive = false; } else { message += "\<br\>Ogre attacks you!"; hits--; } } //If you are in the final room and the dragon is still alive if (currentRoom == 31 && IsDragonAlive) { //if you are fighting the dragon and you have the oil, burning torch if (checkIndex(inventory, 5) && checkIndex(inventory, 9) && checkIndex(inventory, 12)) { message += "\<br\>You attack the dragon with oil, burning torch and the wand of Wind - It creates and kill him!"; IsDragonAlive = false; //End Game } else { message += "\<br\>The dragon attacks you with firebreath and kills you!"; hits = 0; } } if (currentRoom == 25) { //if you are fighting the gas room burning torch if (checkIndex(inventory, 10)) { message += "\<br\>The gas in the room is ignited by the torch - You become a human BBQ and die!"; hits = 0; } } DisplayGameScreen(); } function ProcessCommand(command) { var direction = command; message = "OK"; switch (command) { //Movement Code case "N": if (exits[currentRoom].indexOf(direction) > -1) currentRoom -= 8; else message = "Can't move there"; break; case "S": if (exits[currentRoom].indexOf(direction) > -1) currentRoom += 8; else message = "Can't move there"; break; case "E": if (exits[currentRoom].indexOf(direction) > -1) currentRoom++; else message = "Can't move there"; break; case "W": if (exits[currentRoom].indexOf(direction) > -1) currentRoom--; else message = "Can't move there"; break; //End of Movement Code case "P": pickup(currentRoom); break case "A": if (exits[currentRoom].indexOf(direction) > -1) message = "About ... Game built for #1GAM, LD48 (failed) and my friend Hilary"; break case "?": message = "The following commands are valid: N S E W P A ?"; break } } //JQuery selector that handles the form submit - $('#input form').submit(function (evt) { ProcessGameRound($('#userInput').val().toUpperCase()); $('#userInput').val(''); evt.preventDefault(); }); //sets the output div to the display variable $display = $('#output'); // This is jQuery selector that picks up an event from the button - in this case we look at the value of the button ie. its text and use that //to call the same function as we would call from the equivalent keyboard command $(".button").click(function (e) { switch (this.value) { case "N": ProcessGameRound('N'); break; case "S": ProcessGameRound('S'); break; case "E": ProcessGameRound('E'); break; case "W": ProcessGameRound('W'); break; case "F": ProcessGameRound('F'); break; case "P": pickup(currentRoom); break; case "A": ProcessGameRound('A'); break; } }); DisplayGameScreen(); }); </script>
Are the bugs
Yep – Totally , lots that the point – we’ll start cleaning them up in the next version
Next
I’ll add in a series of improvements in version 2 of this game.
Source Control link
Reblogged this on Sutoprise Avenue, A SutoCom Source.
hey was doing this it is cool but mum has messed my laptop she is serosly retarded and her bf kenny is dumb.he did something to my dog cant prove nutting but I know its him cos he told me sort of like laffing. you should make this have the main guy have like chainsor arms and like something else like that????
Yayz u used my comment (: this is cool but when r u doing next one cs I am bustin hey did u like my idea??? I am a god of code and kennny is so dumb he thinks he is beast but he wears pejamas with pockets haha what a loser and mum think hes so ganstaaaa
Hey! I like the code and the use of buttons for descisions. I will be using your HTML part of the game and changing a few things around to suit myself in development of my own text based game. I hope that would okay with you!
Totally cool – go for it – use anything you need from my code in these tutorials and samples !!!! Sorry its taken me so long to get back to you – been super crazy at work. Anything else drop me a message.
Hi,
I’m just starting out with JS and CSS and I was wondering if there was a way to see the entire code for this? The bits I can see look very useful, especially as I’m trying to do something similar to this, but I can’t see a download link, and there doesn’t seem to be a way to copy/past all of it (the lines just cut off).
Thanks,
Immy
Hi – thanks for the comment – I have added a link at the bottom of the doc but in case you miss it – the source code is :
https://github.com/ozidom/search
Was a fairly rough rushed effort but feel free to comment/fork whatever
Hi,
Thanks for getting back to me so quickly! 🙂 However, there isn’t actually anything in there…just says:
“There is nothing to see here yet. Move along now”
Cheers,
Immy
Immy try signing into github (its free if u dont have an account) then try again
SpaceRogueRevoltion, BaseRogue and RogueCoder have code in them, however LightnigRogue and search do not. Doesn’t matter if I am signed in or not, I get the same thing either way.
Oooops – my bad look like I didn’t push up – bad me – hang on – I’ll do a push shortly…
Have you expanded on this at all? The link to the demo example seems to be less functional than expected; for example, you cannot take objects, etc.
I was going to work on a more functional version using ROT.JS stay tuned…
Pingback: Workbook block 4 – Spelet (html) | Areving's Blog
Nice job. I posted at http://codepen.io/jimwhitfield/full/GpvxXK/
I know I’m blowing your comments up, I’m trying not to. after mapping out the original map and looking over the code, i realized it shouldn’t matter if you go one way and cant return to that point (maybe there’s a cliff for instance), so I mapped the rooms (lol I should of done that first it was so easy) needless to say regardless of all the changes I made I couldn’t access rooms[10], and I looked and looked until it hit me. JS counts from 0, and so I counted and sure enough there are only 31 rooms in the array and the since the grid is 4×8 there should be 32, now I’m going to see If that fixes the wrong item being picked up in certain rooms.
No it’s great, love your comments – I’ll try and grab some time on the weekend to start to fix some of this. Mind you we begin 7DRL this weekend (see next post) so that combined with my work being very busy isn’t a great combination. Seriously appreciate the comments!!!
I came across this looking for an idea, but I checked it out and I have to say, I LOVE the bugs in this, its good for people who are learning to experience the bugs and learn why something doesn’t work as expected and how to fix them. good job.
Great to help out by introducing bugs!!! Hey would be really cool if you let me know the specific bugs. Dom.
well, I haven’t finished the adventure yet, but the first thing I noticed was: going E, S, then E again puts you right back at the entrance. then I noticed choosing p for picking up the painting returns 999 or out of play, (apparently deliberate) but the painting is still picked up. I also noticed when trying to pickup the key, I get the same 999, however it doesn’t pickup the key, instead it picks up iron shield. this is as far as I got before I made my comment. ill keep playing, and notate the script as I go with everything I find if you want?
Oh, and when returning to the entrance, by going E, S, then E again, inventory also happens to reset.
Thats terrible, I’ve got a few things to fix 🙂
here are the actual bugs I have found, which fixes the game for the most part:
changing exits[0] from S to SE since that intent is clear in the codes comments.
there are some rooms that don’t go back the opposite direction, shouldn’t be a bug unless its considered one due to intent, but not sure on the intentions so those didn’t make the list even though they led me to chasing symptoms.
add a room to the rooms list to equal 32 rooms, didn’t test if this was a bug or a feature by design but it seems logical to have 32 rooms considering its a 4×8.
IsGameOver = false; to IsGameOver = false, as list of variables continues being defined beyond this point.
var objectLocations = [] to var objectLocations = []; this doesn’t cause any bugs that I know of on modern browsers, but its not proper.
The ultimate bug is this one, this alone prevents rooms[10] from being accessed and resets player to the beginning losing all inventory. it just requires changing:
if (currentRoom == 10 && OgreAlive) to if (currentRoom == 10 && IsOgreAlive)
OgreAlive = false; to IsOgreAlive = false;
and just a small recommendation, to the input tag with ID=”userInput” add autocomplete=”off”.
my code isnt working in brackets why
can you show me the code that is not working
Can you please update the demo and github link?
Thanks.